aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2020-04-27 22:08:56 -0400
committerBob Zeleznik <zzzman@gmail.com>2020-04-27 22:08:56 -0400
commit1f0d326a6c8735f67c6e37b19f4656e645e38c43 (patch)
tree65605e4183c7d79f1d193b9c7d6b32940d7ee8db /src/client/views/nodes
parent26e683056cddcbe8f90547c77519daa15c37518d (diff)
parent2f371a09f7305cbc44e9358af310078ce0cb4b3c (diff)
Merge branch 'master' into richTextSchemaS
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/AudioBox.scss4
-rw-r--r--src/client/views/nodes/AudioBox.tsx50
-rw-r--r--src/client/views/nodes/ButtonBox.tsx97
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx31
-rw-r--r--src/client/views/nodes/ColorBox.scss1
-rw-r--r--src/client/views/nodes/ColorBox.tsx8
-rw-r--r--src/client/views/nodes/DocumentBox.tsx20
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx123
-rw-r--r--src/client/views/nodes/DocumentView.scss18
-rw-r--r--src/client/views/nodes/DocumentView.tsx468
-rw-r--r--src/client/views/nodes/FontIconBox.scss12
-rw-r--r--src/client/views/nodes/FontIconBox.tsx3
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx184
-rw-r--r--src/client/views/nodes/FormattedTextBoxComment.tsx26
-rw-r--r--src/client/views/nodes/ImageBox.scss10
-rw-r--r--src/client/views/nodes/ImageBox.tsx160
-rw-r--r--src/client/views/nodes/KeyValueBox.scss1
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx23
-rw-r--r--src/client/views/nodes/LabelBox.scss (renamed from src/client/views/nodes/ButtonBox.scss)13
-rw-r--r--src/client/views/nodes/LabelBox.tsx96
-rw-r--r--src/client/views/nodes/LinkAnchorBox.scss (renamed from src/client/views/nodes/DocuLinkBox.scss)8
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx (renamed from src/client/views/nodes/DocuLinkBox.tsx)69
-rw-r--r--src/client/views/nodes/LinkBox.tsx5
-rw-r--r--src/client/views/nodes/PDFBox.scss3
-rw-r--r--src/client/views/nodes/PDFBox.tsx11
-rw-r--r--src/client/views/nodes/PresBox.scss6
-rw-r--r--src/client/views/nodes/PresBox.tsx116
-rw-r--r--src/client/views/nodes/QueryBox.scss1
-rw-r--r--src/client/views/nodes/QueryBox.tsx17
-rw-r--r--src/client/views/nodes/RadialMenu.scss13
-rw-r--r--src/client/views/nodes/RadialMenu.tsx9
-rw-r--r--src/client/views/nodes/ScreenshotBox.scss5
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx42
-rw-r--r--src/client/views/nodes/ScriptingBox.scss35
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx98
-rw-r--r--src/client/views/nodes/SliderBox.scss1
-rw-r--r--src/client/views/nodes/SliderBox.tsx53
-rw-r--r--src/client/views/nodes/VideoBox.scss7
-rw-r--r--src/client/views/nodes/VideoBox.tsx79
-rw-r--r--src/client/views/nodes/WebBox.scss38
-rw-r--r--src/client/views/nodes/WebBox.tsx219
42 files changed, 1255 insertions, 930 deletions
diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss
index fb16b8365..53b54d7e4 100644
--- a/src/client/views/nodes/AudioBox.scss
+++ b/src/client/views/nodes/AudioBox.scss
@@ -88,7 +88,7 @@
opacity:0.9;
background-color: transparent;
box-shadow: black 2px 2px 1px;
- .docuLinkBox-cont {
+ .linkAnchorBox-cont {
position: relative !important;
height: 100% !important;
width: 100% !important;
@@ -103,7 +103,7 @@
box-shadow: black 1px 1px 1px;
margin-left: -1;
margin-top: -2;
- .docuLinkBox-cont {
+ .linkAnchorBox-cont {
position: relative !important;
height: 100% !important;
width: 100% !important;
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index ff9630273..6ff6d1b42 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
import "./AudioBox.scss";
import { Cast, DateCast, NumCast } from "../../../new_fields/Types";
import { AudioField, nullAudio } from "../../../new_fields/URLField";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { makeInterface, createSchema } from "../../../new_fields/Schema";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils";
@@ -20,6 +20,7 @@ import { DocumentView } from "./DocumentView";
import { Docs } from "../../documents/Documents";
import { ComputedField } from "../../../new_fields/ScriptField";
import { Networking } from "../../Network";
+import { Upload } from "../../../server/SharedMediaTypes";
// testing testing
@@ -39,7 +40,7 @@ type AudioDocument = makeInterface<[typeof documentSchema, typeof audioSchema]>;
const AudioDocument = makeInterface(documentSchema, audioSchema);
@observer
-export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocument>(AudioDocument) {
+export class AudioBox extends ViewBoxBaseComponent<FieldViewProps, AudioDocument>(AudioDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
public static Enabled = false;
@@ -71,7 +72,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
scrollLinkId => {
if (scrollLinkId) {
DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
- const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast((l.anchor1 as Doc).timecode) : NumCast((l.anchor2 as Doc).timecode);
+ const linkTime = Doc.AreProtosEqual(l.anchor1 as Doc, this.dataDoc) ? NumCast(l.anchor1_timecode) : NumCast(l.anchor2_timecode);
setTimeout(() => { this.playFromTime(linkTime); Doc.linkFollowHighlight(l); }, 250);
});
Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
@@ -80,10 +81,10 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
selected => {
const sel = selected.length ? selected[0].props.Document : undefined;
- this.Document.playOnSelect && this.recordingStart && sel && sel.creationDate && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
- this.Document.playOnSelect && this.recordingStart && !sel && this.pause();
+ this.layoutDoc.playOnSelect && this.recordingStart && sel && sel.creationDate && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
+ this.layoutDoc.playOnSelect && this.recordingStart && !sel && this.pause();
});
- this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.Document.playOnSelect && this.playFromTime(AudioBox._scrubTime));
+ this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
}
timecodeChanged = () => {
@@ -92,17 +93,16 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
DocListCast(this.dataDoc.links).map(l => {
let la1 = l.anchor1 as Doc;
- const la2 = l.anchor2 as Doc;
- let linkTime = NumCast(la2.timecode);
+ let linkTime = NumCast(l.anchor2_timecode);
if (Doc.AreProtosEqual(la1, this.dataDoc)) {
- linkTime = NumCast(la1.timecode);
+ linkTime = NumCast(l.anchor1_timecode);
la1 = l.anchor2 as Doc;
}
- if (linkTime > NumCast(this.Document.currentTimecode) && linkTime < htmlEle.currentTime) {
+ if (linkTime > NumCast(this.layoutDoc.currentTimecode) && linkTime < htmlEle.currentTime) {
Doc.linkFollowHighlight(la1);
}
});
- this.Document.currentTimecode = htmlEle.currentTime;
+ this.layoutDoc.currentTimecode = htmlEle.currentTime;
}
}
@@ -136,7 +136,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
updateRecordTime = () => {
if (this.audioState === "recording") {
setTimeout(this.updateRecordTime, 30);
- this.Document.currentTimecode = (new Date().getTime() - this._recordStart) / 1000;
+ this.layoutDoc.currentTimecode = (new Date().getTime() - this._recordStart) / 1000;
}
}
@@ -147,7 +147,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
AudioBox.ActiveRecordings.push(this.props.Document);
this._recorder.ondataavailable = async (e: any) => {
const [{ result }] = await Networking.UploadFilesToServer(e.data);
- this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+ if (!(result instanceof Error)) {
+ this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client));
+ }
};
this._recordStart = new Date().getTime();
runInAction(() => this.audioState = "recording");
@@ -158,9 +160,9 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
- funcs.push({ description: (this.Document.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.Document.playOnSelect = !this.Document.playOnSelect, icon: "expand-arrows-alt" });
+ funcs.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when document selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
- ContextMenu.Instance.addItem({ description: "Audio Funcs...", subitems: funcs, icon: "asterisk" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
stopRecording = action(() => {
@@ -185,7 +187,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
e.stopPropagation();
}
onStop = (e: any) => {
- this.Document.playOnSelect = !this.Document.playOnSelect;
+ this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect;
e.stopPropagation();
}
onFile = (e: any) => {
@@ -195,8 +197,8 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
_width: NumCast(this.props.Document._width), _height: 3 * NumCast(this.props.Document._height)
});
Doc.GetProto(newDoc).recordingSource = this.dataDoc;
- Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`this.recordingSource["${this.props.fieldKey}-recordingStart"]`);
- Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("this.recordingSource.audioState");
+ Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
+ Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("self.recordingSource.audioState");
this.props.addDocument?.(newDoc);
e.stopPropagation();
}
@@ -227,7 +229,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
{!this.path ?
<div className="audiobox-buttons">
<div className="audiobox-dictation" onClick={this.onFile}>
- <FontAwesomeIcon style={{ width: "30px", background: this.Document.playOnSelect ? "yellow" : "dimGray" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
+ <FontAwesomeIcon style={{ width: "30px", background: this.layoutDoc.playOnSelect ? "yellow" : "dimGray" }} icon="file-alt" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
</div>
<button className={`audiobox-record${interactive}`} style={{ backgroundColor: this.audioState === "recording" ? "red" : "black" }}>
{this.audioState === "recording" ? "STOP" : "RECORD"}
@@ -236,13 +238,13 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
<div className="audiobox-controls">
<div className="audiobox-player" onClick={this.onPlay}>
<div className="audiobox-playhead"> <FontAwesomeIcon style={{ width: "100%" }} icon={this.audioState === "paused" ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
- <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%", background: this.Document.playOnSelect ? "yellow" : "dimGray" }} icon="hand-point-left" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
+ <div className="audiobox-playhead" onClick={this.onStop}><FontAwesomeIcon style={{ width: "100%", background: this.layoutDoc.playOnSelect ? "yellow" : "dimGray" }} icon="hand-point-left" size={this.props.PanelHeight() < 36 ? "1x" : "2x"} /></div>
<div className="audiobox-timeline" onClick={e => e.stopPropagation()}
onPointerDown={e => {
if (e.button === 0 && !e.ctrlKey) {
const rect = (e.target as any).getBoundingClientRect();
const wasPaused = this.audioState === "paused";
- this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
+ this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration);
wasPaused && this.pause();
e.stopPropagation();
}
@@ -250,11 +252,11 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
{DocListCast(this.dataDoc.links).map((l, i) => {
let la1 = l.anchor1 as Doc;
let la2 = l.anchor2 as Doc;
- let linkTime = NumCast(la2.timecode);
+ let linkTime = NumCast(l.anchor2_timecode);
if (Doc.AreProtosEqual(la1, this.dataDoc)) {
la1 = l.anchor2 as Doc;
la2 = l.anchor1 as Doc;
- linkTime = NumCast(la1.timecode);
+ linkTime = NumCast(l.anchor1_timecode);
}
return !linkTime ? (null) :
<div className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container"} key={l[Id]} style={{ left: `${linkTime / NumCast(this.dataDoc.duration, 1) * 100}%` }}>
@@ -274,7 +276,7 @@ export class AudioBox extends DocExtendableComponent<FieldViewProps, AudioDocume
onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { const wasPaused = this.audioState === "paused"; this.playFrom(linkTime); wasPaused && this.pause(); e.stopPropagation(); } }} />
</div>;
})}
- <div className="audiobox-current" style={{ left: `${NumCast(this.Document.currentTimecode) / NumCast(this.dataDoc.duration, 1) * 100}%` }} />
+ <div className="audiobox-current" style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / NumCast(this.dataDoc.duration, 1) * 100}%` }} />
{this.audio}
</div>
</div>
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
deleted file mode 100644
index 1b70ff824..000000000
--- a/src/client/views/nodes/ButtonBox.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit } from '@fortawesome/free-regular-svg-icons';
-import { action, computed } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, DocListCast } from '../../../new_fields/Doc';
-import { List } from '../../../new_fields/List';
-import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, StrCast, Cast, FieldValue } from '../../../new_fields/Types';
-import { DragManager } from '../../util/DragManager';
-import { undoBatch } from '../../util/UndoManager';
-import { DocComponent } from '../DocComponent';
-import './ButtonBox.scss';
-import { FieldView, FieldViewProps } from './FieldView';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { ContextMenu } from '../ContextMenu';
-import { documentSchema } from '../../../new_fields/documentSchemas';
-
-
-library.add(faEdit as any);
-
-const ButtonSchema = createSchema({
- onClick: ScriptField,
- buttonParams: listSpec("string"),
- text: "string"
-});
-
-type ButtonDocument = makeInterface<[typeof ButtonSchema, typeof documentSchema]>;
-const ButtonDocument = makeInterface(ButtonSchema, documentSchema);
-
-@observer
-export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(ButtonDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ButtonBox, fieldKey); }
- private dropDisposer?: DragManager.DragDropDisposer;
-
- @computed get dataDoc() {
- return this.props.DataDoc &&
- (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) ||
- this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document);
- }
-
-
- protected createDropTarget = (ele: HTMLDivElement) => {
- this.dropDisposer?.();
- if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this));
- }
- }
-
- specificContextMenu = (e: React.MouseEvent): void => {
- const funcs: ContextMenuProps[] = [];
- funcs.push({
- description: "Clear Script Params", event: () => {
- const params = FieldValue(this.Document.buttonParams);
- params?.map(p => this.props.Document[p] = undefined);
- }, icon: "trash"
- });
-
- ContextMenu.Instance.addItem({ description: "OnClick...", subitems: funcs, icon: "asterisk" });
- }
-
- @undoBatch
- @action
- drop = (e: Event, de: DragManager.DropEvent) => {
- const docDragData = de.complete.docDragData;
- const params = this.Document.buttonParams;
- const missingParams = params?.filter(p => this.props.Document[p] === undefined);
- if (docDragData && missingParams?.includes((e.target as any).textContent)) {
- this.props.Document[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
- d.onDragStart ? docDragData.draggedDocuments[i] : d));
- e.stopPropagation();
- }
- }
- // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
- render() {
- const params = this.Document.buttonParams;
- const missingParams = params?.filter(p => this.props.Document[p] === undefined);
- params?.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ...
- return (
- <div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
- style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
- <div className="buttonBox-mainButton" style={{
- background: this.Document.backgroundColor, color: this.Document.color || "inherit",
- fontSize: this.Document.fontSize, letterSpacing: this.Document.letterSpacing || "", textTransform: (this.Document.textTransform as any) || ""
- }} >
- <div className="buttonBox-mainButtonCenter">
- {(this.Document.text || this.Document.title)}
- </div>
- </div>
- <div className="buttonBox-params" >
- {!missingParams || !missingParams.length ? (null) : missingParams.map(m => <div key={m} className="buttonBox-missingParam">{m}</div>)}
- </div>
- </div>
- );
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index f9f5f449c..1c7d116c5 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,4 +1,3 @@
-import anime from "animejs";
import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
@@ -13,7 +12,8 @@ import { TraceMobx } from "../../../new_fields/util";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
- dataProvider?: (doc: Doc) => { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string } | undefined;
+ dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, highlight?: boolean, z: number, transition?: string } | undefined;
+ sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
x?: number;
y?: number;
z?: number;
@@ -24,24 +24,32 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
jitterRotation: number;
transition?: string;
fitToBox?: boolean;
+ replica: string;
}
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
@observable _animPos: number[] | undefined = undefined;
+ random(min: number, max: number) { // min should not be equal to max
+ const mseed = Math.abs(this.X * this.Y);
+ const seed = (mseed * 9301 + 49297) % 233280;
+ const rnd = seed / 233280;
+ return min + rnd * (max - min);
+ }
get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
- get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${anime.random(-1, 1) * this.props.jitterRotation}deg)`; }
+ get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
get X() { return this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
get Y() { return this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
get Highlight() { return this.dataProvider?.highlight; }
- get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.layoutDoc[WidthSym](); }
+ get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.sizeProvider && this.sizeProvider ? this.sizeProvider.width : this.layoutDoc[WidthSym](); }
get height() {
- const hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
+ const hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.sizeProvider && this.sizeProvider ? this.sizeProvider.height : this.layoutDoc[HeightSym]();
return (hgt === undefined && this.nativeWidth && this.nativeHeight) ? this.width * this.nativeHeight / this.nativeWidth : hgt;
}
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
- @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document) ? this.props.dataProvider(this.props.Document) : undefined; }
+ @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
+ @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); }
@computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
@@ -59,10 +67,14 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
return undefined;
}
+ nudge = (x: number, y: number) => {
+ this.props.Document.x = NumCast(this.props.Document.x) + x;
+ this.props.Document.y = NumCast(this.props.Document.y) + y;
+ }
contentScaling = () => this.nativeWidth > 0 && !this.props.fitToBox && !this.freezeDimensions ? this.width / this.nativeWidth : 1;
- panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth?.());
- panelHeight = () => (this.dataProvider?.height || this.props.PanelHeight?.());
+ panelWidth = () => (this.sizeProvider?.width || this.props.PanelWidth?.());
+ panelHeight = () => (this.sizeProvider?.height || this.props.PanelHeight?.());
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
.scale(1 / this.contentScaling())
@@ -88,11 +100,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
height: this.height,
zIndex: this.ZInd,
display: this.ZInd === -99 ? "none" : undefined,
- pointerEvents: this.props.Document.isBackground ? "none" : undefined
+ pointerEvents: this.props.Document.isBackground ? "none" : this.props.pointerEvents ? "all" : undefined
}} >
{!this.props.fitToBox ?
<DocumentView {...this.props}
+ nudge={this.nudge}
dragDivName={"collectionFreeFormDocumentView-container"}
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
diff --git a/src/client/views/nodes/ColorBox.scss b/src/client/views/nodes/ColorBox.scss
index bf334c939..da3266dc1 100644
--- a/src/client/views/nodes/ColorBox.scss
+++ b/src/client/views/nodes/ColorBox.scss
@@ -3,6 +3,7 @@
height:100%;
position: relative;
pointer-events: none;
+ transform-origin: top left;
.sketch-picker {
margin:auto;
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index d34d63d01..6e4341b27 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -6,7 +6,7 @@ import { makeInterface } from "../../../new_fields/Schema";
import { StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { SelectionManager } from "../../util/SelectionManager";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
import "./ColorBox.scss";
import { FieldView, FieldViewProps } from './FieldView';
@@ -15,14 +15,14 @@ type ColorDocument = makeInterface<[typeof documentSchema]>;
const ColorDocument = makeInterface(documentSchema);
@observer
-export class ColorBox extends DocExtendableComponent<FieldViewProps, ColorDocument>(ColorDocument) {
+export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument>(ColorDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
render() {
- const selDoc = SelectionManager.SelectedDocuments()?.[0]?.Document;
+ const selDoc = SelectionManager.SelectedDocuments()?.[0]?.rootDoc;
return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
- style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+ style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<SketchPicker onChange={InkingControl.Instance.switchColor}
color={StrCast(CurrentUserUtils.ActivePen ? CurrentUserUtils.ActivePen.backgroundColor : undefined,
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 0e2685d41..0d18baaed 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -9,7 +9,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { emptyPath } from "../../../Utils";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
import "./DocumentBox.scss";
import { FieldView, FieldViewProps } from "./FieldView";
@@ -18,12 +18,12 @@ import { TraceMobx } from "../../../new_fields/util";
import { DocumentView } from "./DocumentView";
import { Docs } from "../../documents/Documents";
-type DocBoxSchema = makeInterface<[typeof documentSchema]>;
-const DocBoxDocument = makeInterface(documentSchema);
+type DocHolderBoxSchema = makeInterface<[typeof documentSchema]>;
+const DocHolderBoxDocument = makeInterface(documentSchema);
@observer
-export class DocumentBox extends DocAnnotatableComponent<FieldViewProps, DocBoxSchema>(DocBoxDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocumentBox, fieldKey); }
+export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, DocHolderBoxSchema>(DocHolderBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocHolderBox, fieldKey); }
_prevSelectionDisposer: IReactionDisposer | undefined;
_selections: Doc[] = [];
_curSelection = -1;
@@ -45,7 +45,7 @@ export class DocumentBox extends DocAnnotatableComponent<FieldViewProps, DocBoxS
funcs.push({ description: (this.props.Document.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => Doc.GetProto(this.props.Document).excludeCollections = !this.props.Document.excludeCollections, icon: "expand-arrows-alt" });
funcs.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" });
- ContextMenu.Instance.addItem({ description: "DocumentBox Funcs...", subitems: funcs, icon: "asterisk" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
@computed get contentDoc() {
return (this.props.Document.isTemplateDoc || this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document));
@@ -54,7 +54,7 @@ export class DocumentBox extends DocAnnotatableComponent<FieldViewProps, DocBoxS
this.contentDoc[this.props.fieldKey] = this.props.Document[this.props.fieldKey];
}
showSelection = () => {
- this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(this,this.excludeCollections,[_last_])?.[0]`);
+ this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`);
}
isSelectionLocked = () => {
const kvpstring = Field.toKeyValueString(this.contentDoc, this.props.fieldKey);
@@ -111,7 +111,7 @@ export class DocumentBox extends DocAnnotatableComponent<FieldViewProps, DocBoxS
const childTemplateName = StrCast(this.props.Document.childTemplateName);
if (containedDoc && childTemplateName && !containedDoc["layout_" + childTemplateName]) {
setTimeout(() => {
- DocumentView.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName);
+ Doc.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName);
Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined);
}, 0);
}
@@ -120,8 +120,8 @@ export class DocumentBox extends DocAnnotatableComponent<FieldViewProps, DocBoxS
DataDocument={undefined}
LibraryPath={emptyPath}
CollectionView={this as any} // bcz: hack! need to pass a prop that can be used to select the container (ie, 'this') when the up selector in document decorations is clicked. currently, the up selector allows only a containing collection to be selected
- fitToBox={this.props.fitToBox}
- layoutKey={"layout_" + childTemplateName}
+ fitToBox={true}
+ layoutKey={childTemplateName ? "layout_" + childTemplateName : "layout"}
rootSelected={this.props.isSelected}
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index dc71ba280..cd78ac7b3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,9 +1,8 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt } from "../../../new_fields/Doc";
-import { Cast, StrCast } from "../../../new_fields/Types";
-import { OmitKeys, Without } from "../../../Utils";
-import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
+import { Doc, Opt, Field } from "../../../new_fields/Doc";
+import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { OmitKeys, Without, emptyPath } from "../../../Utils";
import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
@@ -11,10 +10,11 @@ import { CollectionSchemaView } from "../collections/CollectionSchemaView";
import { CollectionView } from "../collections/CollectionView";
import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
import { AudioBox } from "./AudioBox";
-import { ButtonBox } from "./ButtonBox";
+import { LabelBox } from "./LabelBox";
import { SliderBox } from "./SliderBox";
import { LinkBox } from "./LinkBox";
-import { DocumentBox } from "./DocumentBox";
+import { ScriptingBox } from "./ScriptingBox";
+import { DocHolderBox } from "./DocumentBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FontIconBox } from "./FontIconBox";
@@ -27,7 +27,7 @@ import { PresBox } from "./PresBox";
import { QueryBox } from "./QueryBox";
import { ColorBox } from "./ColorBox";
import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
-import { DocuLinkBox } from "./DocuLinkBox";
+import { LinkAnchorBox } from "./LinkAnchorBox";
import { PresElementBox } from "../presentationview/PresElementBox";
import { ScreenshotBox } from "./ScreenshotBox";
import { VideoBox } from "./VideoBox";
@@ -35,8 +35,10 @@ import { WebBox } from "./WebBox";
import { InkingStroke } from "../InkingStroke";
import React = require("react");
import { RecommendationsBox } from "../RecommendationsBox";
-
import { TraceMobx } from "../../../new_fields/util";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import XRegExp = require("xregexp");
+
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -53,6 +55,42 @@ class ObserverJsxParser1 extends JsxParser {
const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
+
+interface HTMLtagProps {
+ Document: Doc;
+ RootDoc: Doc;
+ htmltag: string;
+ onClick?: ScriptField;
+ onInput?: ScriptField;
+}
+//"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'><ImageBox {...props} fieldKey={'data'}/><HTMLspan width='100%' marginTop='50%' height='10%' position='absolute' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}grey'>{this.title}</HTMLspan></HTMLdiv>"@observer
+@observer
+export class HTMLtag extends React.Component<HTMLtagProps> {
+ click = (e: React.MouseEvent) => {
+ const clickScript = (this.props as any).onClick as Opt<ScriptField>;
+ clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc });
+ }
+ onInput = (e: React.FormEvent<HTMLDivElement>) => {
+ const onInputScript = (this.props as any).onInput as Opt<ScriptField>;
+ onInputScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, value: (e.target as any).textContent });
+ }
+ render() {
+ const style: { [key: string]: any } = {};
+ const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
+ Object.keys(divKeys).map((prop: string) => {
+ const p = (this.props as any)[prop] as string;
+ const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value
+ return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || "";
+ };
+ style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer);
+ });
+ const Tag = this.props.htmltag as keyof JSX.IntrinsicElements;
+ return <Tag style={style} onClick={this.click} onInput={this.onInput as any}>
+ {this.props.children}
+ </Tag>;
+ }
+}
+
@observer
export class DocumentContentsView extends React.Component<DocumentViewProps & {
isSelected: (outsideReaction: boolean) => boolean,
@@ -66,7 +104,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
@computed get layout(): string {
TraceMobx();
if (!this.layoutDoc) return "<p>awaiting layout</p>";
- const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string");
+ // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); // bcz: replaced this with below... is it right?
+ const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
if (this.props.layoutKey === "layout_keyValue") {
return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
} else
@@ -87,36 +126,82 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
}
get layoutDoc() {
const params = StrCast(this.props.Document.PARAMS);
- const template: Doc = this.props.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+ // 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.LayoutDoc?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
+ const template: Doc = this.props.LayoutDoc?.() ||
+ (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
+ Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
}
- CreateBindings(): JsxBindings {
+ CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
const list = {
...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit,
+ RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
Document: this.layoutDoc,
DataDoc: this.dataDoc,
+ onClick: onClick,
+ onInput: onInput
};
return { props: list };
}
render() {
TraceMobx();
- return (this.props.renderDepth > 12 || !this.layout || !this.layoutDoc) ? (null) :
+ let layoutFrame = this.layout;
+
+ // 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;
+ };
+ 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 RootDoc={props.RootDoc} Document={props.Document} htmltag='${p1}'`;
+ };
+ layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
+
+ // replace /HTML<tag> with </HTMLdiv> as in: </HTMLdiv> becomes </HTMLtag>
+ const replacer3 = (match: any, p1: string, offset: any, string: any) => {
+ return `</HTMLtag`;
+ };
+ layoutFrame = layoutFrame.replace(/<\/HTML([a-zA-Z0-9_-]+)/g, replacer3);
+
+ // add onClick function to props
+ const makeFuncProp = (func: string) => {
+ const splits = layoutFrame.split(`func=`);
+ if (splits.length > 1) {
+ const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
+ layoutFrame = splits[0] + ` ${func}={props.onClick} ` + splits[1].substring(code[1].end + 1);
+ return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" });
+ }
+ return undefined;
+ // add input function to props
+ };
+ const onClick = makeFuncProp("onClick");
+ const onInput = makeFuncProp("onInput");
+
+ const bindings = this.CreateBindings(onClick, onInput);
+ // layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
+
+ return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc) ? (null) :
this.props.forceLayout === "FormattedTextBox" && this.props.forceFieldKey ?
- <FormattedTextBox {...this.CreateBindings().props} fieldKey={this.props.forceFieldKey} />
+ <FormattedTextBox {...bindings.props} fieldKey={this.props.forceFieldKey} />
:
<ObserverJsxParser
+ key={42}
blacklistedAttrs={[]}
+ renderInWrapper={false}
components={{
- FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, ButtonBox, SliderBox, FieldView,
+ FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
- PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
- ColorBox, DashWebRTCVideo, DocuLinkBox, InkingStroke, DocumentBox, LinkBox,
- RecommendationsBox, ScreenshotBox
+ PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
+ ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
+ RecommendationsBox, ScreenshotBox, HTMLtag
}}
- bindings={this.CreateBindings()}
- jsx={this.layout}
+ bindings={bindings}
+ jsx={layoutFrame}
showWarnings={true}
onError={(test: any) => { console.log(test); }}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index ce7bcd206..dea09cb30 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -34,19 +34,13 @@
overflow-y: scroll;
height: calc(100% - 20px);
}
- .documentView-docuLinkAnchor {
+ .documentView-linkAnchorBoxAnchor {
display:flex;
overflow: hidden;
- }
- .documentView-docuLinkWrapper {
- pointer-events: none;
- position: absolute;
- transform-origin: top left;
- width: 100%;
- height: 100%;
- top:0;
- left:0;
- z-index: 1;
+
+ .documentView-node {
+ width:10px !important;
+ }
}
.documentView-lock {
@@ -81,7 +75,7 @@
display: inline-block;
width: 100%;
height: 100%;
- pointer-events: none;
+ border-radius: inherit;
.documentView-styleContentWrapper {
width: 100%;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2df5c9bbd..83eb12436 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,21 +1,24 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, runInAction, trace, observable } from "mobx";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
-import { Doc, DocListCast, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
import { Document, PositionDocument } from '../../../new_fields/documentSchemas';
import { Id } from '../../../new_fields/FieldSymbols';
import { InkTool } from '../../../new_fields/InkField';
import { RichTextField } from '../../../new_fields/RichTextField';
import { listSpec } from "../../../new_fields/Schema";
+import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
import { ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField';
import { TraceMobx } from '../../../new_fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, returnOne, returnTransparent, returnTrue, Utils, OmitKeys, returnZero } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
+import { ClientRecommender } from '../../ClientRecommender';
import { DocServer } from "../../DocServer";
import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
@@ -24,6 +27,7 @@ import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
import { Scripting } from '../../util/Scripting';
+import { SearchUtil } from '../../util/SearchUtil';
import { SelectionManager } from "../../util/SelectionManager";
import SharingManager from '../../util/SharingManager';
import { Transform } from "../../util/Transform";
@@ -35,18 +39,11 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
import { InkingControl } from '../InkingControl';
-import { OverlayView } from '../OverlayView';
-import { ScriptBox } from '../ScriptBox';
-import { ScriptingRepl } from '../ScriptingRepl';
+import { KeyphraseQueryView } from '../KeyphraseQueryView';
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
-import React = require("react");
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField';
-import { ClientRecommender } from '../../ClientRecommender';
-import { SearchUtil } from '../../util/SearchUtil';
import { RadialMenu } from './RadialMenu';
-import { KeyphraseQueryView } from '../KeyphraseQueryView';
+import React = require("react");
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -64,12 +61,14 @@ export interface DocumentViewProps {
LayoutDoc?: () => Opt<Doc>;
LibraryPath: Doc[];
fitToBox?: boolean;
+ contextMenuItems?: () => { script: ScriptField, label: string }[];
rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
onClick?: ScriptField;
onPointerDown?: ScriptField;
onPointerUp?: ScriptField;
dropAction?: dropActionType;
dragDivName?: string;
+ nudge?: (x: number, y: number) => void;
addDocument?: (doc: Doc) => boolean;
removeDocument?: (doc: Doc) => boolean;
moveDocument?: (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean;
@@ -78,6 +77,7 @@ export interface DocumentViewProps {
ContentScaling: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
+ pointerEvents?: boolean;
focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
parentActive: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
@@ -115,7 +115,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
@computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
@computed get nativeHeight() { return NumCast(this.layoutDoc._nativeHeight, this.props.NativeHeight() || (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
- @computed get onClickHandler() { return this.props.onClick || this.layoutDoc.onClick || this.Document.onClick; }
+ @computed get onClickHandler() { return this.props.onClick || Cast(this.layoutDoc.onClick, ScriptField, null) || this.Document.onClick; }
@computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; }
@computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; }
NativeWidth = () => this.nativeWidth;
@@ -191,32 +191,36 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@action
componentDidMount() {
- this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
+ this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document));
this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)));
this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)));
// this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)));
- !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this);
+ if (!this.props.dontRegisterView) {
+ DocumentManager.Instance.DocumentViews.push(this);
+ }
}
@action
componentDidUpdate() {
- this._dropDisposer && this._dropDisposer();
- this._gestureEventDisposer && this._gestureEventDisposer();
- this.multiTouchDisposer && this.multiTouchDisposer();
- this.holdDisposer && this.holdDisposer();
- this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)));
- this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)));
- this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)));
- this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)));
+ this._dropDisposer?.();
+ this._gestureEventDisposer?.();
+ this.multiTouchDisposer?.();
+ this.holdDisposer?.();
+ if (this._mainCont.current) {
+ this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document);
+ this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this));
+ this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this));
+ this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this));
+ }
}
@action
componentWillUnmount() {
- this._dropDisposer && this._dropDisposer();
- this._gestureEventDisposer && this._gestureEventDisposer();
- this.multiTouchDisposer && this.multiTouchDisposer();
- this.holdDisposer && this.holdDisposer();
+ this._dropDisposer?.();
+ this._gestureEventDisposer?.();
+ this.multiTouchDisposer?.();
+ this.holdDisposer?.();
Doc.UnBrushDoc(this.props.Document);
if (!this.props.dontRegisterView) {
const index = DocumentManager.Instance.DocumentViews.indexOf(this);
@@ -230,6 +234,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
dragData.dropAction = dropAction;
+ dragData.removeDocument = this.props.removeDocument;
dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument;
dragData.dragDivName = this.props.dragDivName;
DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart });
@@ -276,46 +281,55 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
- onClick = (e: React.MouseEvent | React.PointerEvent) => {
+ onClick = action((e: React.MouseEvent | React.PointerEvent) => {
if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
let stopPropagate = true;
let preventDefault = true;
- this.props.bringToFront(this.props.Document);
+ !this.props.Document.isBackground && this.props.bringToFront(this.props.Document);
if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
- const fullScreenAlias = Doc.MakeAlias(this.props.Document);
- if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
- fullScreenAlias.layoutKey = "layout_fullScreen";
+ if (!(e.nativeEvent as any).formattedHandled) {
+ const fullScreenAlias = Doc.MakeAlias(this.props.Document);
+ if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) {
+ fullScreenAlias.layoutKey = "layout_fullScreen";
+ }
+ UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap");
+ SelectionManager.DeselectAll();
+ Doc.UnBrushDoc(this.props.Document);
}
- UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, "inTab"), "double tap");
- SelectionManager.DeselectAll();
- Doc.UnBrushDoc(this.props.Document);
- } else if (this.onClickHandler?.script) {
- SelectionManager.DeselectAll();
- const func = () => this.onClickHandler!.script.run({
- this: this.props.Document,
- self: Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document,
+ } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+ //SelectionManager.DeselectAll();
+ const func = () => this.onClickHandler.script.run({
+ this: this.layoutDoc,
+ self: this.rootDoc,
thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey
- }, console.log);// && !this.props.Document.isButton && this.select(false);
- if (this.props.Document !== Doc.UserDoc().undoBtn && this.props.Document !== Doc.UserDoc().redoBtn) {
+ }, console.log);
+ if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) {
UndoManager.RunInBatch(func, "on click");
} else func();
- } else if (this.Document.type === DocumentType.BUTTON) {
- UndoManager.RunInBatch(() => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click");
- } else if (this.Document.isButton) {
+ } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
+ UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick");
+ //ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click");
+ } else if (this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
} else {
- if (this.props.Document.isTemplateForField && !(e.ctrlKey || e.button > 0)) {
+ if ((this.props.Document.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
} else {
- SelectionManager.SelectDoc(this, e.ctrlKey);
+ // if (this.props.Document.type === DocumentType.RTF) {
+ // DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0];
+ // DocumentView._focusHack = [DocumentView._focusHack[0] + NumCast(this.props.Document.x), DocumentView._focusHack[1] + NumCast(this.props.Document.y)];
+
+ // this.props.focus(this.props.Document, false);
+ // }
+ SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey);
}
preventDefault = false;
}
stopPropagate && e.stopPropagation();
preventDefault && e.preventDefault();
}
- }
+ });
// follows a link - if the target is on screen, it highlights/pans to it.
// if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
@@ -327,14 +341,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const targetFocusAfterDocFocus = () => {
const where = StrCast(this.Document.followLinkLocation) || followLoc;
const hackToCallFinishAfterFocus = () => {
- setTimeout(() => finished?.(), 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.
+ finished && setTimeout(finished, 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.
return false; // we must return false here so that the zoom to the document is not reversed. If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false
};
- this.props.addDocTab(doc, where) && this.props.focus(doc, true, undefined, hackToCallFinishAfterFocus); // add the target and focus on it.
+ this.props.addDocTab(doc, where) && this.props.focus(doc, BoolCast(this.Document.followLinkZoom, true), undefined, hackToCallFinishAfterFocus); // add the target and focus on it.
return where !== "inPlace"; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target)
};
- // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
- this.props.focus(this.props.Document, true, 1, targetFocusAfterDocFocus);
+ if (!this.Document.followLinkZoom) {
+ targetFocusAfterDocFocus();
+ } else {
+ // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
+ this.props.focus(this.props.Document, BoolCast(this.Document.followLinkZoom, true), 1, targetFocusAfterDocFocus);
+ }
};
await DocumentManager.Instance.FollowLink(undefined, this.props.Document, createViewFunc, shiftKey, this.props.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
}
@@ -389,6 +407,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
+ public iconify() {
+ const layoutKey = Cast(this.props.Document.layoutKey, "string", null);
+ const collapse = layoutKey !== "layout_icon";
+ if (collapse) {
+ this.switchViews(collapse, "icon");
+ if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.props.Document.deiconifyLayout = layoutKey.replace("layout_", "");
+ } else {
+ const deiconifyLayout = Cast(this.props.Document.deiconifyLayout, "string", null);
+ this.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
+ this.props.Document.deiconifyLayout = undefined;
+ }
+ }
+
@action
handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
@@ -465,15 +496,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
return;
}
- if (!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) {
- this._downX = e.clientX;
- this._downY = e.clientY;
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.Document.onDragStart) &&
+ // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
+ !((this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0))) {
if ((this.active || this.Document.onDragStart || this.onClickHandler) &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
- !this.Document.lockedPosition &&
!this.Document.inOverlay) {
e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
+
}
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
@@ -507,8 +540,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onPointerUp = (e: PointerEvent): void => {
this.cleanUpInteractions();
- if (this.onPointerUpHandler && this.onPointerUpHandler.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
- this.onPointerUpHandler.script.run({ this: this.Document.isTemplateForField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log);
+ if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+ this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
document.removeEventListener("pointerup", this.onPointerUp);
return;
}
@@ -531,74 +564,45 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument?.(this.props.Document); }
- // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView)
- static makeCustomViewClicked = (doc: Doc, creator: (documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => {
- const batch = UndoManager.StartBatch("makeCustomViewClicked");
- runInAction(() => {
- doc.layoutKey = "layout_" + templateSignature;
- if (doc[doc.layoutKey] === undefined) {
- DocumentView.createCustomView(doc, creator, templateSignature, docLayoutTemplate);
- }
- });
- batch.end();
- }
- static createCustomView = (doc: Doc, creator: (documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc, templateSignature: string = "custom", docLayoutTemplate?: Doc) => {
- const iconViews = DocListCast(Cast(Doc.UserDoc().iconViews, Doc, null)?.data);
- const templBtns = DocListCast(Cast(Doc.UserDoc().templateButtons, Doc, null)?.data);
- const noteTypes = DocListCast(Cast(Doc.UserDoc().noteTypes, Doc, null)?.data);
- const allTemplates = iconViews.concat(templBtns).concat(noteTypes).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc);
- const templateName = templateSignature.replace(/\(.*\)/, "");
- // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized
- // first try to find a template that matches the specific document type (<typeName>_<templateName>). otherwise, fallback to a general match on <templateName>
- !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === doc.type + "_" + templateName && (docLayoutTemplate = tempDoc));
- !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
-
- const customName = "layout_" + templateSignature;
- const _width = NumCast(doc._width);
- const _height = NumCast(doc._height);
- const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false };
-
- let fieldTemplate: Opt<Doc>;
- if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
- fieldTemplate = Docs.Create.TextDocument("", options);
- } else if (doc.data instanceof PdfField) {
- fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
- } else if (doc.data instanceof VideoField) {
- fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
- } else if (doc.data instanceof AudioField) {
- fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
- } else if (doc.data instanceof ImageField) {
- fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
- }
- const docTemplate = docLayoutTemplate || creator(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
-
- fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate));
- Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
- }
@undoBatch
- toggleButtonBehavior = (): void => {
- if (this.Document.isButton || this.Document.onClick || this.Document.ignoreClick) {
- this.Document.isButton = false;
+ toggleLinkButtonBehavior = (): void => {
+ if (this.Document.isLinkButton || this.Document.onClick || this.Document.ignoreClick) {
+ this.Document.isLinkButton = false;
this.Document.ignoreClick = false;
this.Document.onClick = undefined;
} else {
- this.Document.isButton = true;
+ this.Document.isLinkButton = true;
+ this.Document.followLinkZoom = false;
this.Document.followLinkLocation = undefined;
}
}
@undoBatch
toggleFollowInPlace = (): void => {
- if (this.Document.isButton) {
- this.Document.isButton = false;
+ if (this.Document.isLinkButton) {
+ this.Document.isLinkButton = false;
} else {
- this.Document.isButton = true;
+ this.Document.isLinkButton = true;
+ this.Document.followLinkZoom = true;
this.Document.followLinkLocation = "inPlace";
}
}
@undoBatch
+ toggleFollowOnRight = (): void => {
+ if (this.Document.isLinkButton) {
+ this.Document.isLinkButton = false;
+ } else {
+ this.Document.isLinkButton = true;
+ this.Document.followLinkZoom = false;
+ const first = DocListCast(this.Document.links).find(d => d instanceof Doc);
+ first && (first.hidden = true);
+ this.Document.followLinkLocation = "onRight";
+ }
+ }
+
+ @undoBatch
@action
drop = async (e: Event, de: DragManager.DropEvent) => {
if (de.complete.annoDragData) {
@@ -639,25 +643,18 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
makeIntoPortal = async () => {
const portalLink = DocListCast(this.Document.links).find(d => d.anchor1 === this.props.Document);
if (!portalLink) {
- const portal = Docs.Create.FreeformDocument([], { _width: (this.layoutDoc._width || 0) + 10, _height: this.layoutDoc._height || 0, title: StrCast(this.props.Document.title) + ".portal" });
+ const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" });
DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to");
}
- this.Document.isButton = true;
+ this.Document.followLinkZoom = true;
+ this.Document.isLinkButton = true;
}
@undoBatch
@action
- setCustomView = (custom: boolean, layout: string): void => {
- Doc.setNativeView(this.props.Document);
- if (custom) {
- DocumentView.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
- }
- }
-
- @undoBatch
- @action
- makeBackground = (): void => {
- this.Document.isBackground = !this.Document.isBackground;
+ toggleBackground = (temporary: boolean): void => {
+ this.Document.overflow = temporary ? "visible" : "hidden";
+ this.Document.isBackground = !temporary ? !this.Document.isBackground : (this.Document.isBackground ? undefined : true);
this.Document.isBackground && this.props.bringToFront(this.Document, true);
}
@@ -695,41 +692,44 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const cm = ContextMenu.Instance;
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
- const existing = cm.findByDescription("Layout...");
- const layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
- layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
- layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
-
- layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
- layoutItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
- layoutItems.push({ description: !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
- layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
- layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
- layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
- layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
- !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" });
-
- const open = ContextMenu.Instance.findByDescription("Open...");
+ const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
+ Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) =>
+ cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+ this.props.contextMenuItems?.().forEach(item =>
+ cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
+
+
+ let open = cm.findByDescription("Add a Perspective...");
const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
- openItems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this, this.props.LibraryPath), icon: "desktop" });
- openItems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, "inTab", this.props.LibraryPath), icon: "folder" });
- openItems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, "onRight", this.props.LibraryPath), icon: "caret-square-right" });
- openItems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" });
- openItems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" });
openItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
templateDoc && openItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
- openItems.push({ description: "Open Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
- !open && cm.addItem({ description: "Open...", subitems: openItems, icon: "external-link-alt" });
+ if (!open) {
+ open = { description: "Add a Perspective....", subitems: openItems, icon: "external-link-alt" };
+ cm.addItem(open);
+ }
+ let options = cm.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+ optionItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+ optionItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
+ optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+ optionItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
+ if (!options) {
+ options = { description: "Options...", subitems: optionItems, icon: "compass" };
+ cm.addItem(options);
+ }
+
+ cm.moveAfter(options, open);
const existingOnClick = cm.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(this, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.props.Document.layoutKey}")`), icon: "window-restore" });
onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
- onClicks.push({ description: this.Document.isButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
- onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleButtonBehavior, icon: "concierge-bell" });
- onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) });
+ onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" });
+ onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" });
+ onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" });
+ onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" });
!existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
const funcs: ContextMenuProps[] = [];
@@ -742,6 +742,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const more = cm.findByDescription("More...");
const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
+ moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
+ moreItems.push({ description: !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
if (!ClientUtils.RELEASE) {
// let copies: ContextMenuProps[] = [];
@@ -794,11 +796,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
icon: "brain"
});
- cm.addItem({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" });
-
-
- moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
+ moreItems.push({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" });
+ moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" });
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
runInAction(() => {
@@ -841,7 +841,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
SelectionManager.SelectDoc(this, false);
}
});
- const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), "");
+ const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), "");
cm.addItem({
description: `path: ${path}`, event: () => {
this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true);
@@ -861,7 +861,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
await Promise.all(allDocs.map((doc: Doc) => {
let isMainDoc: boolean = false;
const dataDoc = Doc.GetProto(doc);
- if (doc.type === DocumentType.TEXT) {
+ if (doc.type === DocumentType.RTF) {
if (dataDoc === Doc.GetProto(this.props.Document)) {
isMainDoc = true;
}
@@ -964,8 +964,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const fallback = Cast(this.props.Document.layoutKey, "string");
return typeof fallback === "string" ? fallback : "layout";
}
- rootSelected = (outsideReaction: boolean) => {
- return this.isSelected(outsideReaction) || (this.props.Document.forceActive && this.props.rootSelected?.(outsideReaction) ? true : false);
+ rootSelected = (outsideReaction?: boolean) => {
+ return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
}
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
panelWidth = () => this.props.PanelWidth();
@@ -973,37 +973,42 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
screenToLocalTransform = () => this.props.ScreenToLocalTransform();
@computed get contents() {
TraceMobx();
- return (<DocumentContentsView ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- NativeWidth={this.NativeWidth}
- NativeHeight={this.NativeHeight}
- Document={this.props.Document}
- DataDoc={this.props.DataDoc}
- LayoutDoc={this.props.LayoutDoc}
- makeLink={this.makeLink}
- rootSelected={this.rootSelected}
- fitToBox={this.props.fitToBox}
- LibraryPath={this.props.LibraryPath}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.props.moveDocument}
- ScreenToLocalTransform={this.screenToLocalTransform}
- renderDepth={this.props.renderDepth}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
- focus={this.props.focus}
- parentActive={this.props.parentActive}
- whenActiveChanged={this.props.whenActiveChanged}
- bringToFront={this.props.bringToFront}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- backgroundColor={this.props.backgroundColor}
- ContentScaling={this.childScaling}
- ChromeHeight={this.chromeHeight}
- isSelected={this.isSelected}
- select={this.select}
- onClick={this.onClickHandler}
- layoutKey={this.finalLayoutKey} />);
+ return (<>
+ <DocumentContentsView key={1} ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ NativeWidth={this.NativeWidth}
+ NativeHeight={this.NativeHeight}
+ Document={this.props.Document}
+ DataDoc={this.props.DataDoc}
+ LayoutDoc={this.props.LayoutDoc}
+ makeLink={this.makeLink}
+ rootSelected={this.rootSelected}
+ dontRegisterView={this.props.dontRegisterView}
+ fitToBox={this.props.fitToBox}
+ LibraryPath={this.props.LibraryPath}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ moveDocument={this.props.moveDocument}
+ ScreenToLocalTransform={this.screenToLocalTransform}
+ renderDepth={this.props.renderDepth}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
+ focus={this.props.focus}
+ parentActive={this.props.parentActive}
+ whenActiveChanged={this.props.whenActiveChanged}
+ bringToFront={this.props.bringToFront}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ backgroundColor={this.props.backgroundColor}
+ ContentScaling={this.childScaling}
+ ChromeHeight={this.chromeHeight}
+ isSelected={this.isSelected}
+ select={this.select}
+ onClick={this.onClickHandler}
+ layoutKey={this.finalLayoutKey} />
+ {this.anchors}
+ </>
+ );
}
linkEndpoint = (linkDoc: Doc) => Doc.LinkEndpoint(linkDoc, this.props.Document);
@@ -1012,36 +1017,39 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// would be good to generalize this some way.
isNonTemporalLink = (linkDoc: Doc) => {
const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc;
- return anchor.type === DocumentType.AUDIO ? false : true;
+ const ept = Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1_timecode : linkDoc.anchor2_timecode;
+ return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true;
}
- @observable _link: Opt<Doc>;
- makeLink = () => {
- return this._link;
- }
- hideLinkAnchor = (doc: Doc) => undoBatch(doc => doc.hidden = true)();
+ @observable _link: Opt<Doc>; // see DocumentButtonBar for explanation of how this works
+ makeLink = () => this._link; // pass the link placeholde to child views so they can react to make a specialized anchor. This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined.
+
+ @undoBatch
+ hideLinkAnchor = (doc: Doc) => doc.hidden = true
anchorPanelWidth = () => this.props.PanelWidth() || 1;
anchorPanelHeight = () => this.props.PanelHeight() || 1;
@computed get anchors() {
TraceMobx();
- return DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
- <div className="documentView-docuLinkWrapper" key={d[Id]}>
- <DocumentView {...this.props}
- Document={d}
- ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the DocuLinkBox
- PanelWidth={this.anchorPanelWidth}
- PanelHeight={this.anchorPanelHeight}
- layoutKey={this.linkEndpoint(d)}
- ContentScaling={returnOne}
- backgroundColor={returnTransparent}
- removeDocument={this.hideLinkAnchor} />
- </div>);
+ return this.layoutDoc.presBox ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) =>
+ <DocumentView {...this.props} key={i + 1}
+ Document={d}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.Document} // bcz: hack this.props.Document is not a collection Need a better prop for passing the containing document to the LinkAnchorBox
+ PanelWidth={this.anchorPanelWidth}
+ PanelHeight={this.anchorPanelHeight}
+ layoutKey={this.linkEndpoint(d)}
+ ContentScaling={returnOne}
+ backgroundColor={returnTransparent}
+ removeDocument={this.hideLinkAnchor}
+ pointerEvents={false}
+ LayoutDoc={undefined}
+ />);
}
@computed get innards() {
TraceMobx();
if (!this.props.PanelWidth()) { // this happens when the document is a tree view label
- return <div className="documentView-docuLinkAnchor" >
+ return <div className="documentView-linkAnchorBoxAnchor" >
{StrCast(this.props.Document.title)}
{this.anchors}
</div>;
@@ -1051,7 +1059,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const showCaption = StrCast(this.layoutDoc._showCaption);
const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
const captionView = (!showCaption ? (null) :
- <div className="documentView-captionWrapper">
+ <div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
hideOnLeave={true}
forceLayout={"FormattedTextBox"}
@@ -1064,9 +1072,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
layoutKey={this.finalLayoutKey} />
</div>);
const titleView = (!showTitle ? (null) :
- <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} style={{
+ <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
position: showTextTitle ? "relative" : "absolute",
- pointerEvents: SelectionManager.GetIsDragging() || this.onClickHandler || this.Document.ignoreClick ? "none" : "all",
+ pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
contents={(this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
@@ -1075,24 +1083,26 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)}
/>
</div>);
- return <>
- {this.anchors}
- {!showTitle && !showCaption ?
- this.contents :
- <div className="documentView-styleWrapper" >
- <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? `calc(100% - ${this.chromeHeight()}px)` : "100%", top: showTextTitle ? this.chromeHeight() : undefined }}>
- {this.contents}
- </div>
- {titleView}
- {captionView}
- </div>
- }
- </>;
+ return !showTitle && !showCaption ?
+ this.contents :
+ <div className="documentView-styleWrapper" >
+ {this.Document.type !== DocumentType.RTF ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
+ {captionView}
+ </div>;
}
@computed get ignorePointerEvents() {
- return (this.Document.isBackground && !this.isSelected()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
+ return this.props.pointerEvents === false ||
+ (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) ||
+ (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None);
+ }
+ @undoBatch
+ @action
+ setCustomView = (custom: boolean, layout: string): void => {
+ Doc.setNativeView(this.props.Document);
+ if (custom) {
+ Doc.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
+ }
}
-
@observable _animate = 0;
switchViews = action((custom: boolean, view: string) => {
SelectionManager.SetIsDragging(true);
@@ -1128,20 +1138,24 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
transformOrigin: this._animate ? "center center" : undefined,
transform: this._animate ? `scale(${this._animate})` : undefined,
transition: !this._animate ? StrCast(this.Document.transition) : this._animate < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
- pointerEvents: this.ignorePointerEvents ? "none" : "all",
+ pointerEvents: this.ignorePointerEvents ? "none" : undefined,
color: StrCast(this.layoutDoc.color, "inherit"),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined,
background: finalColor,
- opacity: this.Document.opacity
+ opacity: this.Document.opacity,
+ fontFamily: StrCast(this.Document._fontFamily, "inherit"),
+ fontSize: Cast(this.Document._fontSize, "number", null)
}}>
- {this.Document.isBackground ? <div className="documentView-lock"> <FontAwesomeIcon icon="unlock" size="lg" /> </div> : (null)}
{this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <>
{this.innards}
<div className="documentView-contentBlocker" />
</> :
this.innards}
+ {(this.Document.isBackground !== undefined || this.isSelected(false)) && this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ?
+ <div className="documentView-lock" onClick={() => this.toggleBackground(true)}> <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} size="lg" /> </div>
+ : (null)}
</div>;
{ this._showKPQuery ? <KeyphraseQueryView keyphrases={this._queries}></KeyphraseQueryView> : undefined; }
}
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index f0fe7a54e..68b00a5be 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -8,6 +8,18 @@
border-radius: 100%;
transform-origin: top left;
+ .fontIconBox-label {
+ background: gray;
+ color:white;
+ margin-left: -10px;
+ border-radius: 8px;
+ width:100%;
+ position: absolute;
+ text-align: center;
+ font-size: 8px;
+ margin-top:4px;
+ }
+
svg {
width: 95% !important;
height: 95%;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index d4da21239..c6ea6a882 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -56,7 +56,8 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
background: StrCast(referenceLayout.backgroundColor),
boxShadow: this.props.Document.ischecked ? `4px 4px 12px black` : undefined
}}>
- <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} color={this._foregroundColor} size="sm" />
+ <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={this._foregroundColor} size="sm" />
+ {!this.rootDoc.label ? (null) : <div className="fontIconBox-label"> {StrCast(this.rootDoc.label).substring(0, 5)} </div>}
</button>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 7d40b3149..3bedb7127 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -24,8 +24,6 @@
overflow-y: auto;
overflow-x: hidden;
color: initial;
- height: 100%;
- pointer-events: all;
max-height: 100%;
display: flex;
flex-direction: row;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index fd19f14f8..2c4d3f1d9 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -13,22 +13,24 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
import { ReplaceStep } from 'prosemirror-transform';
import { EditorView } from "prosemirror-view";
import { DateField } from '../../../new_fields/DateField';
-import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
import { documentSchema } from '../../../new_fields/documentSchemas';
import { Id } from '../../../new_fields/FieldSymbols';
import { InkTool } from '../../../new_fields/InkField';
+import { PrefetchProxy } from '../../../new_fields/Proxy';
import { RichTextField } from "../../../new_fields/RichTextField";
import { RichTextUtils } from '../../../new_fields/RichTextUtils';
import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast, StrCast, BoolCast, DateCast } from "../../../new_fields/Types";
+import { Cast, DateCast, NumCast, StrCast } from "../../../new_fields/Types";
import { TraceMobx } from '../../../new_fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, Utils, returnTrue, returnZero } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils } from '../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DictationManager } from '../../util/DictationManager';
import { DragManager } from "../../util/DragManager";
+import { makeTemplate } from '../../util/DropConverter';
import buildKeymap from "../../util/ProsemirrorExampleTransfer";
import RichTextMenu from '../../util/RichTextMenu';
import { RichTextRules } from "../../util/RichTextRules";
@@ -49,7 +51,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager";
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentButtonBar } from '../DocumentButtonBar';
import { InkingControl } from "../InkingControl";
import { AudioBox } from './AudioBox';
@@ -57,8 +59,6 @@ import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
import React = require("react");
-import { PrefetchProxy } from '../../../new_fields/Proxy';
-import { makeTemplate } from '../../util/DropConverter';
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -66,6 +66,8 @@ library.add(faSmile, faTextHeight, faUpload);
export interface FormattedTextBoxProps {
hideOnLeave?: boolean;
makeLink?: () => Opt<Doc>;
+ xMargin?: number;
+ yMargin?: number;
}
const richTextSchema = createSchema({
@@ -80,7 +82,7 @@ const RichTextDocument = makeInterface(richTextSchema, documentSchema);
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@observer
-export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
public static Instance: FormattedTextBox;
@@ -94,17 +96,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
private _lastX = 0;
private _lastY = 0;
private _undoTyping?: UndoManager.Batch;
- private _searchReactionDisposer?: Lambda;
- private _recordReactionDisposer: Opt<IReactionDisposer>;
- private _scrollToRegionReactionDisposer: Opt<IReactionDisposer>;
- private _reactionDisposer: Opt<IReactionDisposer>;
- private _heightReactionDisposer: Opt<IReactionDisposer>;
- private _proxyReactionDisposer: Opt<IReactionDisposer>;
- private _pullReactionDisposer: Opt<IReactionDisposer>;
- private _pushReactionDisposer: Opt<IReactionDisposer>;
- private _buttonBarReactionDisposer: Opt<IReactionDisposer>;
- private _linkMakerDisposer: Opt<IReactionDisposer>;
- private _scrollDisposer: Opt<IReactionDisposer>;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
private dropDisposer?: DragManager.DragDropDisposer;
@computed get _recording() { return this.dataDoc.audioState === "recording"; }
@@ -205,7 +197,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
const tsel = this._editorView.state.selection.$from;
tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000)));
- const curText = state.doc.textBetween(0, state.doc.content.size, "\n\n");
+ const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField);
if (!this._applyingChange) {
this._applyingChange = true;
@@ -226,13 +218,30 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
updateTitle = () => {
if ((this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
- StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
+ StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.rootDoc.customTitle) {
const str = this._editorView.state.doc.textContent;
const titlestr = str.substr(0, Math.min(40, str.length));
this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
}
+ // needs a better API for taking in a set of words with target documents instead of just one target
+ public hyperlinkTerms = (terms: string[], target: Doc) => {
+ if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
+ const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
+ const tr = this._editorView.state.tr;
+ const flattened: TextSelection[] = [];
+ res.map(r => r.map(h => flattened.push(h)));
+ const lastSel = Math.min(flattened.length - 1, this._searchIndex);
+ this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ const alink = DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, "automatic")!;
+ const link = this._editorView.state.schema.marks.link.create({
+ href: Utils.prepend("/doc/" + alink[Id]),
+ title: "a link", location: location, linkId: alink[Id], targetId: target[Id]
+ });
+ this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
+ }
+ }
public highlightSearchTerms = (terms: string[]) => {
if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) {
const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
@@ -264,7 +273,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
protected createDropTarget = (ele: HTMLDivElement) => {
this.ProseRef = ele;
this.dropDisposer?.();
- ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
+ ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
}
@undoBatch
@@ -273,7 +282,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
if (de.complete.docDragData) {
const draggedDoc = de.complete.docDragData.draggedDocuments.length && de.complete.docDragData.draggedDocuments[0];
// replace text contents whend dragging with Alt
- if (draggedDoc && draggedDoc.type === DocumentType.TEXT && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) {
+ if (draggedDoc && draggedDoc.type === DocumentType.RTF && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) {
if (draggedDoc.data instanceof RichTextField) {
Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
e.stopPropagation();
@@ -352,10 +361,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
updateHighlights = () => {
clearStyleSheetRules(FormattedTextBox._userStyleSheet);
if (FormattedTextBox._highlights.indexOf("Text from Others") !== -1) {
- addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-remote", { background: "yellow" });
+ addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-remote", { background: "yellow" });
}
if (FormattedTextBox._highlights.indexOf("My Text") !== -1) {
- addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { background: "moccasin" });
+ addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { background: "moccasin" });
}
if (FormattedTextBox._highlights.indexOf("Todo Items") !== -1) {
addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "todo", { outline: "black solid 1px" });
@@ -370,15 +379,15 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "ignore", { "font-size": "1" });
}
if (FormattedTextBox._highlights.indexOf("By Recent Minute") !== -1) {
- addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
+ addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
const min = Math.round(Date.now() / 1000 / 60);
- numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
+ numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
setTimeout(() => this.updateHighlights());
}
if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) {
- addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
+ addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
const hr = Math.round(Date.now() / 1000 / 60 / 60);
- numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "userMark-hr-" + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
+ numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-hr-" + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
}
}
@@ -407,21 +416,25 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
return Cast(Doc.UserDoc().defaultTextLayout, Doc, null) || StrCast(Doc.UserDoc().defaultTextLayout, null);
}
specificContextMenu = (e: React.MouseEvent): void => {
+ const cm = ContextMenu.Instance;
+
const funcs: ContextMenuProps[] = [];
this.props.Document.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" });
funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
!this.props.Document.rootDocument && funcs.push({
description: "Make Template", event: () => {
- this.props.Document.isTemplateDoc = makeTemplate(this.props.Document, true);
- Doc.AddDocToList(Cast(Doc.UserDoc().noteTypes, Doc, null), "data", this.props.Document);
+ this.props.Document.isTemplateDoc = makeTemplate(this.props.Document);
+ Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.props.Document);
}, icon: "eye"
});
funcs.push({ description: "Toggle Single Line", event: () => this.props.Document._singleLine = !this.props.Document._singleLine, icon: "expand-arrows-alt" });
funcs.push({ description: "Toggle Sidebar", event: () => this.props.Document._showSidebar = !this.props.Document._showSidebar, icon: "expand-arrows-alt" });
- funcs.push({ description: "Toggle Audio", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
+ funcs.push({ description: "Toggle Dictation Icon", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" });
funcs.push({ description: "Toggle Menubar", event: () => this.toggleMenubar(), icon: "expand-arrows-alt" });
+
+ const highlighting: ContextMenuProps[] = [];
["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
- funcs.push({
+ highlighting.push({
description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
e.stopPropagation();
if (FormattedTextBox._highlights.indexOf(option) === -1) {
@@ -432,8 +445,37 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this.updateHighlights();
}, icon: "expand-arrows-alt"
}));
+ funcs.push({ description: "highlighting...", subitems: highlighting, icon: "hand-point-right" });
+
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+
+ const change = cm.findByDescription("Change Perspective...");
+ const changeItems: ContextMenuProps[] = change && "subitems" in change ? change.subitems : [];
+
+ const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
+ DocListCast(noteTypesDoc?.data).forEach(note => {
+ changeItems.push({
+ description: StrCast(note.title), event: undoBatch(() => {
+ Doc.setNativeView(this.props.Document);
+ Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
+ }), icon: "eye"
+ });
+ });
+ changeItems.push({ description: "FreeForm", event: undoBatch(() => Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" });
+ !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
+
+ const open = cm.findByDescription("Add a Perspective...");
+ const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+
+ openItems.push({
+ description: "FreeForm", event: undoBatch(() => {
+ const alias = Doc.MakeAlias(this.rootDoc);
+ Doc.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform");
+ this.props.addDocTab(alias, "onRight");
+ }), icon: "eye"
+ });
+ !open && cm.addItem({ description: "Add a Perspective...", subitems: openItems, icon: "external-link-alt" });
- ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: funcs, icon: "asterisk" });
}
recordDictation = () => {
@@ -542,7 +584,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
componentDidMount() {
- this._buttonBarReactionDisposer = reaction(
+ this._disposers.buttonBar = reaction(
() => DocumentButtonBar.Instance,
instance => {
if (instance) {
@@ -551,7 +593,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
);
- this._linkMakerDisposer = reaction(
+ this._disposers.linkMaker = reaction(
() => this.props.makeLink?.(),
(linkDoc: Opt<Doc>) => {
if (linkDoc) {
@@ -562,8 +604,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
},
{ fireImmediately: true }
);
-
- this._reactionDisposer = reaction(
+ this._disposers.editorState = reaction(
() => {
if (this.dataDoc[this.props.fieldKey + "-noTemplate"] || !this.props.Document[this.props.fieldKey + "-textTemplate"]) {
return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data;
@@ -578,8 +619,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
);
-
- this._pullReactionDisposer = reaction(
+ this._disposers.pullDoc = reaction(
() => this.props.Document[Pulls],
() => {
if (!DocumentButtonBar.hasPulledHack) {
@@ -589,8 +629,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
);
-
- this._pushReactionDisposer = reaction(
+ this._disposers.pushDoc = reaction(
() => this.props.Document[Pushes],
() => {
if (!DocumentButtonBar.hasPushedHack) {
@@ -599,19 +638,18 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}
);
-
- this._heightReactionDisposer = reaction(
+ this._disposers.height = reaction(
() => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight],
() => this.tryUpdateHeight()
);
this.setupEditor(this.config, this.props.fieldKey);
- this._searchReactionDisposer = reaction(() => this.layoutDoc.searchMatch,
+ this._disposers.search = reaction(() => this.rootDoc.searchMatch,
search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(),
{ fireImmediately: true });
- this._recordReactionDisposer = reaction(() => this._recording,
+ this._disposers.record = reaction(() => this._recording,
() => {
if (this._recording) {
setTimeout(action(() => {
@@ -621,8 +659,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
} else setTimeout(() => this.stopDictation(true), 0);
}
);
-
- this._scrollToRegionReactionDisposer = reaction(
+ this._disposers.scrollToRegion = reaction(
() => StrCast(this.layoutDoc.scrollToLinkID),
async (scrollToLinkID) => {
const findLinkFrag = (frag: Fragment, editor: EditorView) => {
@@ -667,8 +704,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
},
{ fireImmediately: true }
);
-
- this._scrollDisposer = reaction(() => NumCast(this.props.Document.scrollPos),
+ this._disposers.scroll = reaction(() => NumCast(this.props.Document.scrollPos),
pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true }
);
@@ -734,7 +770,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
}, 0);
dataDoc.title = exportState.title;
- this.Document.customTitle = true;
+ this.rootDoc.customTitle = true;
dataDoc.unchanged = true;
} else {
delete dataDoc[GoogleRef];
@@ -832,13 +868,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this._editorView = new EditorView(this.ProseRef, {
state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
handleScrollToSelection: (editorView) => {
- const ref = editorView.domAtPos(editorView.state.selection.from);
- let refNode = ref.node as any;
- while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement;
- const r1 = refNode?.getBoundingClientRect();
- const r3 = self._ref.current!.getBoundingClientRect();
- if (r1.top < r3.top || r1.top > r3.bottom) {
- r1 && (self._scrollRef.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
+ const docPos = editorView.coordsAtPos(editorView.state.selection.from);
+ const viewRect = self._ref.current!.getBoundingClientRect();
+ if (docPos.top < viewRect.top || docPos.top > viewRect.bottom) {
+ docPos && (self._scrollRef.current!.scrollTop += (docPos.top - viewRect.top) * self.props.ScreenToLocalTransform().Scale);
}
return true;
},
@@ -867,11 +900,12 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
});
const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field);
if (startupText) {
- this._editorView.dispatch(this._editorView.state.tr.insertText(startupText));
+ const { state: { tr }, dispatch } = this._editorView;
+ dispatch(tr.insertText(startupText));
}
}
- const selectOnLoad = (Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document)[Id] === FormattedTextBox.SelectOnLoad;
+ const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad;
if (selectOnLoad && !this.props.dontRegisterView) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
@@ -897,17 +931,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
componentWillUnmount() {
- this._scrollDisposer?.();
- this._scrollToRegionReactionDisposer?.();
- this._reactionDisposer?.();
- this._proxyReactionDisposer?.();
- this._pushReactionDisposer?.();
- this._pullReactionDisposer?.();
- this._heightReactionDisposer?.();
- this._searchReactionDisposer?.();
- this._recordReactionDisposer?.();
- this._buttonBarReactionDisposer?.();
- this._linkMakerDisposer?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
this._editorView?.destroy();
}
@@ -932,7 +956,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this.doLinkOnDeselect();
FormattedTextBox._downEvent = true;
FormattedTextBoxComment.textBox = this;
- if (this.props.onClick && e.button === 0) {
+ if (this.props.onClick && e.button === 0 && !this.props.isSelected(false)) {
e.preventDefault();
}
if (e.button === 0 && this.active(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
@@ -1176,7 +1200,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
this.layoutDoc.limitHeight = undefined;
this.layoutDoc._autoHeight = false;
}
- const nh = this.Document.isTemplateForField ? 0 : NumCast(this.dataDoc._nativeHeight, 0);
+ const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.dataDoc._nativeHeight, 0);
const dh = NumCast(this.layoutDoc._height, 0);
const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
if (Math.abs(newHeight - dh) > 1) { // bcz: Argh! without this, we get into a React crash if the same document is opened in a freeform view and in the treeview. no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle
@@ -1202,13 +1226,13 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
return (
<div className={`formattedTextBox-cont`} ref={this._ref}
style={{
- height: this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined,
- background: this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"]),
+ height: this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : `calc(100% - ${this.props.ChromeHeight?.() || 0}px`,
+ background: StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
- color: this.props.hideOnLeave ? "white" : "inherit",
- pointerEvents: interactive ? "none" : "all",
- fontSize: NumCast(this.layoutDoc.fontSize, 13),
- fontFamily: StrCast(this.layoutDoc.fontFamily, "Crimson Text"),
+ color: StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
+ pointerEvents: interactive ? "none" : undefined,
+ fontSize: Cast(this.layoutDoc._fontSize, "number", null),
+ fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyPress}
@@ -1226,8 +1250,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
<div className={`formattedTextBox-outer`} style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, }} onScroll={this.onscrolled} ref={this._scrollRef}>
<div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget}
style={{
- padding: `${NumCast(this.Document._xMargin, 0)}px ${NumCast(this.Document._yMargin, 0)}px`,
- pointerEvents: ((this.Document.isButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined
+ padding: `${NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0)}px`,
+ pointerEvents: ((this.layoutDoc.isLinkButton || this.props.onClick) && !this.props.isSelected()) ? "none" : undefined
}} />
</div>
{!this.props.Document._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 4f6244d9a..dfea0f6bb 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -2,7 +2,7 @@ import { Mark, ResolvedPos } from "prosemirror-model";
import { EditorState, Plugin } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, DocCastAsync } from "../../../new_fields/Doc";
import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath } from "../../../Utils";
import { DocServer } from "../../DocServer";
@@ -15,6 +15,7 @@ import './FormattedTextBoxComment.scss';
import React = require("react");
import { Docs } from "../../documents/Documents";
import wiki from "wikijs";
+import { DocumentType } from "../../documents/DocumentTypes";
export let formattedTextBoxCommentPlugin = new Plugin({
view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -83,8 +84,12 @@ export class FormattedTextBoxComment {
const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
const textBox = FormattedTextBoxComment.textBox;
if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
- (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
+ textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ } else {
+ DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
+ (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ }
} else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) {
textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), "onRight");
}
@@ -100,6 +105,7 @@ export class FormattedTextBoxComment {
public static Hide() {
FormattedTextBoxComment.textBox = undefined;
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
}
public static SetState(textBox: any, start: number, end: number, mark: Mark) {
FormattedTextBoxComment.textBox = textBox;
@@ -167,14 +173,18 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipText.textContent = "target not found...";
(FormattedTextBoxComment.tooltipText as any).href = "";
const docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
- docTarget && DocServer.GetRefField(docTarget).then(linkDoc => {
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ } catch (e) { }
+ docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = mark.attrs.href;
FormattedTextBoxComment.linkDoc = linkDoc;
- const target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
- try {
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
- } catch (e) { }
+ const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
+ const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
+ if (anchor !== target && anchor && target) {
+ target.scrollY = NumCast(anchor?.y);
+ }
if (target) {
ReactDOM.render(<ContentFittingDocumentView
Document={target}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 7bbf4a368..15148d01d 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -1,6 +1,4 @@
-.imageBox,
-.imageBox-dragging {
- pointer-events: all;
+.imageBox {
border-radius: inherit;
width: 100%;
height: 100%;
@@ -12,12 +10,6 @@
}
}
-.imageBox-dragging {
- .imageBox-fader {
- pointer-events: none;
- }
-}
-
#upload-icon {
position: absolute;
bottom: 0;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8818b8098..08917d281 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -24,7 +24,7 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from "../../views/ContextMenu";
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenuProps } from '../ContextMenuItem';
-import { DocAnnotatableComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
import FaceRectangles from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
@@ -65,7 +65,7 @@ const uploadIcons = {
};
@observer
-export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
+export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
@@ -76,11 +76,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
protected createDropTarget = (ele: HTMLDivElement) => {
this._dropDisposer && this._dropDisposer();
- ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
- }
-
- get fieldKey() {
- return this.props.fieldKey.startsWith("@") ? StrCast(this.props.Document[this.props.fieldKey]) : this.props.fieldKey;
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
}
@undoBatch
@@ -146,19 +142,19 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
@undoBatch
rotate = action(() => {
- const nw = NumCast(this.Document[this.fieldKey + "-nativeWidth"]);
- const nh = NumCast(this.Document[this.fieldKey + "-nativeHeight"]);
- const w = this.Document._width;
- const h = this.Document._height;
+ const nw = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
+ const nh = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+ const w = this.layoutDoc._width;
+ const h = this.layoutDoc._height;
this.dataDoc[this.fieldKey + "-rotation"] = (NumCast(this.dataDoc[this.fieldKey + "-rotation"]) + 90) % 360;
this.dataDoc[this.fieldKey + "-nativeWidth"] = nh;
this.dataDoc[this.fieldKey + "-nativeHeight"] = nw;
- this.Document._width = h;
- this.Document._height = w;
+ this.layoutDoc._width = h;
+ this.layoutDoc._height = w;
});
specificContextMenu = (e: React.MouseEvent): void => {
- const field = Cast(this.Document[this.fieldKey], ImageField);
+ const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
@@ -184,15 +180,13 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
//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: "Image Funcs...", subitems: funcs, icon: "asterisk" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
}
extractFaces = () => {
const converter = (results: any) => {
- const faceDocs = new List<Doc>();
- results.reduce((face: CognitiveServices.Image.Face, faceDocs: List<Doc>) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List<Doc>());
- return faceDocs;
+ return results.map((face: CognitiveServices.Image.Face) => Docs.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!);
};
this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-faces"], this.url, Service.Face, converter);
}
@@ -229,8 +223,8 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
return url.href;//Why is this here
}
const ext = path.extname(url.href);
- const suffix = this.props.renderDepth < 1 ? "_o" : this._curSuffix;
- return url.href.replace(ext, suffix + ext);
+ this._curSuffix = this.props.renderDepth < 1 ? "_o" : this.layoutDoc[WidthSym]() < 100 ? "_s" : "_m";
+ return url.href.replace(ext, this._curSuffix + ext);
}
@observable _smallRetryCount = 1;
@@ -243,46 +237,46 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
@action onError = (error: any) => {
const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount;
- if (timeout < 10) {
- // setTimeout(this.retryPath, 500);
- }
- const original = StrCast(this.dataDoc.originalUrl);
- if (error.type === "error" && original) {
- this.dataDoc[this.fieldKey] = new ImageField(original);
+ if (timeout < 5) {
+ setTimeout(this.retryPath, 500);
+ } else {
+ const original = StrCast(this.dataDoc[this.fieldKey + "-originalUrl"]);
+ if (error.type === "error" && original) {
+ this.dataDoc[this.fieldKey] = new ImageField(original);
+ }
}
}
_curSuffix = "_m";
resize = (imgPath: string) => {
const cachedNativeSize = {
- width: NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]),
- height: NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"])
+ width: imgPath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]) : 0,
+ height: imgPath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]) : 0,
};
- const docAspect = this.Document[HeightSym]() / this.Document[WidthSym]();
+ const docAspect = this.layoutDoc[HeightSym]() / this.layoutDoc[WidthSym]();
const cachedAspect = cachedNativeSize.height / cachedNativeSize.width;
if (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(NumCast(this.layoutDoc._width) / NumCast(this.layoutDoc._height) - cachedNativeSize.width / cachedNativeSize.height) > 0.05) {
if (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) {
- requestImageSize(imgPath).then((inquiredSize: any) => {
+ requestImageSize(imgPath).then(action((inquiredSize: any) => {
const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180;
const rotatedNativeSize = rotation === 90 || rotation === 270 ? { height: inquiredSize.width, width: inquiredSize.height } : inquiredSize;
const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width;
- setTimeout(action(() => {
- if (this.Document[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) {
- this.Document._height = this.Document[WidthSym]() * rotatedAspect;
- this.dataDoc[this.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = rotatedNativeSize.width;
- this.dataDoc[this.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = rotatedNativeSize.height;
- }
- }), 0);
- }).catch((err: any) => console.log(err));
+ if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) {
+ this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect;
+ this.dataDoc[this.fieldKey + "-nativeWidth"] = this.layoutDoc._nativeWidth = this.layoutDoc._width;
+ this.dataDoc[this.fieldKey + "-nativeHeight"] = this.layoutDoc._nativeHeight = this.layoutDoc._height;
+ this.dataDoc[this.fieldKey + "-path"] = imgPath;
+ }
+ })).catch(console.log);
} else if (Math.abs(1 - docAspect / cachedAspect) > 0.1) {
- this.Document._width = this.Document[WidthSym]() || cachedNativeSize.width;
- this.Document._height = this.Document[WidthSym]() * cachedAspect;
+ this.layoutDoc._width = this.layoutDoc[WidthSym]() || cachedNativeSize.width;
+ this.layoutDoc._height = this.layoutDoc[WidthSym]() * cachedAspect;
}
- } else if (this.Document._nativeWidth !== cachedNativeSize.width || this.Document._nativeHeight !== cachedNativeSize.height) {
- !(this.Document[StrCast(this.props.Document.layoutKey)] instanceof Doc) && setTimeout(() => {
- if (!(this.Document[StrCast(this.props.Document.layoutKey)] instanceof Doc)) {
- this.Document._nativeWidth = cachedNativeSize.width;
- this.Document._nativeHeight = cachedNativeSize.height;
+ } else if (this.layoutDoc._nativeWidth !== cachedNativeSize.width || this.layoutDoc._nativeHeight !== cachedNativeSize.height) {
+ !(this.layoutDoc[StrCast(this.layoutDoc.layoutKey)] instanceof Doc) && setTimeout(() => {
+ if (!(this.layoutDoc[StrCast(this.layoutDoc.layoutKey)] instanceof Doc)) {
+ this.layoutDoc._nativeWidth = cachedNativeSize.width;
+ this.layoutDoc._nativeHeight = cachedNativeSize.height;
}
}, 0);
}
@@ -311,8 +305,9 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
audioDown = () => this.recordAudioAnnotation();
considerGooglePhotosLink = () => {
- const remoteUrl = this.Document.googlePhotosUrl;
+ const remoteUrl = this.dataDoc.googlePhotosUrl;
return !remoteUrl ? (null) : (<img
+ style={{ transform: `scale(${this.props.ContentScaling()})`, transformOrigin: "bottom right" }}
id={"google-photos"}
src={"/assets/google_photos.png"}
onClick={() => window.open(remoteUrl)}
@@ -320,7 +315,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
considerGooglePhotosTags = () => {
- const tags = this.Document.googlePhotosTags;
+ const tags = this.dataDoc.googlePhotosTags;
return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />);
}
@@ -337,13 +332,14 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
return (
<img
id={"upload-icon"}
+ style={{ transform: `scale(${1 / this.props.ContentScaling()})`, 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.originalUrl = primary;
+ dataDoc[this.props.fieldKey + "-originalUrl"] = primary;
let succeeded = true;
let data: ImageField | undefined;
try {
@@ -364,46 +360,42 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
@computed get nativeSize() {
- const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50;
+ TraceMobx();
+ const pw = this.props.PanelWidth?.() || 50;
const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], pw);
const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], 1);
return { nativeWidth, nativeHeight };
}
+ // this._curSuffix = "";
+ // if (w > 20) {
+ // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
+ // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
+ // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
@computed get paths() {
- let paths = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
- // this._curSuffix = "";
- // if (w > 20) {
- const alts = DocListCast(this.dataDoc[this.fieldKey + "-alternates"]);
- const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
- const field = this.dataDoc[this.fieldKey];
- // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
- // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
- // else if (this._largeRetryCount < 10) this._curSuffix = "_l";
- if (field instanceof ImageField) paths = [this.choosePath(field.url)];
- paths.push(...altpaths);
- return paths;
+ const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+ const alts = DocListCast(this.dataDoc[this.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
+ const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+ const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
+ return paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
}
@computed get content() {
TraceMobx();
- const srcpath = this.paths[NumCast(this.props.Document.curPage, 0)];
+ const srcpath = this.paths[0];
const fadepath = this.paths[Math.min(1, this.paths.length - 1)];
const { nativeWidth, nativeHeight } = this.nativeSize;
const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
const aspect = (rotation % 180) ? nativeHeight / nativeWidth : 1;
- const pwidth = this.props.PanelWidth();
- const pheight = this.props.PanelHeight();
- const shift = (rotation % 180) ? (pheight - pwidth) / aspect / 2 + (pheight - pwidth) / 2 : 0;
-
+ const shift = (rotation % 180) ? (nativeHeight - nativeWidth) * (1 - 1 / aspect) : 0;
this.resize(srcpath);
- return <div className="imageBox-cont" key={this.props.Document[Id]} ref={this.createDropTarget}>
+ return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget}>
<div className="imageBox-fader" >
<img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
+ style={{ transform: `scale(${aspect}) translate(0px, ${shift}px) rotate(${rotation}deg)` }}
width={nativeWidth}
ref={this._imgRef}
onError={this.onError} />
@@ -416,7 +408,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
ref={this._imgRef}
onError={this.onError} /></div>}
</div>
- {!this.props.Document._showAudio ? (null) :
+ {!this.layoutDoc._showAudio ? (null) :
<div className="imageBox-audioBackground"
onPointerDown={this.audioDown}
onPointerEnter={this.onPointerEnter}
@@ -432,21 +424,31 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
</div>;
}
+ // adjust y position to center image in panel aspect is bigger than image aspect.
+ // bcz :note, this is broken for rotated images
+ get ycenter() {
+ const { nativeWidth, nativeHeight } = this.nativeSize;
+ const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
+ const aspect = (rotation % 180) ? nativeWidth / nativeHeight : nativeHeight / nativeWidth;
+ return this.props.PanelHeight() / this.props.PanelWidth() > aspect ?
+ (this.props.PanelHeight() - this.props.PanelWidth() * aspect) / 2 : 0;
+ }
+
+ screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.ycenter / this.props.ContentScaling());
+
contentFunc = () => [this.content];
render() {
TraceMobx();
- const { nativeWidth, nativeHeight } = this.nativeSize;
- const aspect = nativeWidth / nativeHeight;
- const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
- return (<div className={`imageBox${dragging}`} onContextMenu={this.specificContextMenu}
+ return (<div className={`imageBox`} onContextMenu={this.specificContextMenu}
style={{
- transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
- width: this.props.PanelWidth() ? `${this.props.PanelWidth()}px` : `${100 / this.props.ContentScaling()}%`,
- height: this.props.PanelWidth() ? `${this.props.PanelWidth() / aspect}px` : `${100 / this.props.ContentScaling()}%`,
- pointerEvents: this.props.Document.isBackground ? "none" : undefined,
- borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
+ transform: this.props.PanelWidth() ? `translate(0px, ${this.ycenter}px)` : `scale(${this.props.ContentScaling()})`,
+ width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
+ height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
+ pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
+ borderRadius: `${Number(StrCast(this.layoutDoc.borderRoundisng).replace("px", "")) / this.props.ContentScaling()}px`
}} >
<CollectionFreeFormView {...this.props}
+ forceScaling={true}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
NativeHeight={returnZero}
@@ -463,7 +465,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
moveDocument={this.moveDocument}
addDocument={this.addDocument}
CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ ScreenToLocalTransform={this.screenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index a26880c9e..eb7c2f32b 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -8,7 +8,6 @@
border-radius: $border-radius;
box-sizing: border-box;
display: inline-block;
- pointer-events: all;
cursor: default;
.imageBox-cont img {
width: auto;
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 7aad6f90e..2970674a2 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -16,6 +16,8 @@ import { FieldView, FieldViewProps } from './FieldView';
import "./KeyValueBox.scss";
import { KeyValuePair } from "./KeyValuePair";
import React = require("react");
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
export type KVPScript = {
script: CompiledScript;
@@ -34,11 +36,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
@observable private _keyInput: string = "";
@observable private _valueInput: string = "";
@computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); }
- get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; }
-
- constructor(props: FieldViewProps) {
- super(props);
- }
+ get fieldDocToLayout() { return this.props.fieldKey ? Cast(this.props.Document[this.props.fieldKey], Doc, null) : this.props.Document; }
@action
onEnterKey = (e: React.KeyboardEvent): void => {
@@ -234,13 +232,26 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
return new Doc;
}
+ specificContextMenu = (e: React.MouseEvent): void => {
+ const cm = ContextMenu.Instance;
+ const open = cm.findByDescription("Change Perspective...");
+ const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : [];
+ openItems.push({
+ description: "Default Perspective", event: () => {
+ this.props.addDocTab(this.fieldDocToLayout, "inTab");
+ this.props.addDocTab(this.props.Document, "close");
+ }, icon: "image"
+ });
+ !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
+ }
+
render() {
const dividerDragger = this.splitPercentage === 0 ? (null) :
<div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
<div className="keyValueBox-dividerDraggerThumb" onPointerDown={this.onDividerDown} />
</div>;
- return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} ref={this._mainCont}>
+ return (<div className="keyValueBox-cont" onWheel={this.onPointerWheel} onContextMenu={this.specificContextMenu} ref={this._mainCont}>
<table className="keyValueBox-table">
<tbody className="keyValueBox-tbody">
<tr className="keyValueBox-header">
diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/LabelBox.scss
index 293af289d..7c7e92379 100644
--- a/src/client/views/nodes/ButtonBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -1,13 +1,12 @@
-.buttonBox-outerDiv {
+.labelBox-outerDiv {
width: 100%;
height: 100%;
- pointer-events: all;
border-radius: inherit;
display: flex;
flex-direction: column;
}
-.buttonBox-mainButton {
+.labelBox-mainButton {
width: 100%;
height: 100%;
border-radius: inherit;
@@ -17,21 +16,19 @@
display:flex;
}
-.buttonBox-mainButtonCenter {
+.labelBox-mainButtonCenter {
overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
display: inline;
align-items: center;
margin: auto;
}
-.buttonBox-params {
+.labelBox-params {
display: flex;
flex-direction: row;
}
-.buttonBox-missingParam {
+.labelBox-missingParam {
width: 100%;
background: lightgray;
border: dimGray solid 1px;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
new file mode 100644
index 000000000..3cdec8acb
--- /dev/null
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -0,0 +1,96 @@
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faEdit } from '@fortawesome/free-regular-svg-icons';
+import { action } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast } from '../../../new_fields/Doc';
+import { documentSchema } from '../../../new_fields/documentSchemas';
+import { List } from '../../../new_fields/List';
+import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
+import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { FieldView, FieldViewProps } from './FieldView';
+import './LabelBox.scss';
+
+
+library.add(faEdit as any);
+
+const LabelSchema = createSchema({});
+
+type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>;
+const LabelDocument = makeInterface(LabelSchema, documentSchema);
+
+@observer
+export class LabelBox extends ViewBoxBaseComponent<FieldViewProps, LabelDocument>(LabelDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
+ private dropDisposer?: DragManager.DragDropDisposer;
+
+ protected createDropTarget = (ele: HTMLDivElement) => {
+ this.dropDisposer?.();
+ if (ele) {
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document);
+ }
+ }
+
+ get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; }
+ specificContextMenu = (e: React.MouseEvent): void => {
+ const funcs: ContextMenuProps[] = [];
+ funcs.push({
+ description: "Clear Script Params", event: () => {
+ const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
+ params?.map(p => this.paramsDoc[p] = undefined);
+ }, icon: "trash"
+ });
+
+ ContextMenu.Instance.addItem({ description: "OnClick...", subitems: funcs, icon: "asterisk" });
+ }
+
+ @undoBatch
+ @action
+ drop = (e: Event, de: DragManager.DropEvent) => {
+ const docDragData = de.complete.docDragData;
+ const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
+ const missingParams = params?.filter(p => !this.paramsDoc[p]);
+ if (docDragData && missingParams?.includes((e.target as any).textContent)) {
+ this.paramsDoc[(e.target as any).textContent] = new List<Doc>(docDragData.droppedDocuments.map((d, i) =>
+ d.onDragStart ? docDragData.draggedDocuments[i] : d));
+ e.stopPropagation();
+ }
+ }
+ // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
+ render() {
+ 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 ...
+ return (
+ <div className="labelBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
+ style={{ boxShadow: this.layoutDoc.opacity ? StrCast(this.layoutDoc.boxShadow) : "" }}>
+ <div className="labelBox-mainButton" style={{
+ background: StrCast(this.layoutDoc.backgroundColor),
+ color: StrCast(this.layoutDoc.color, "inherit"),
+ fontSize: NumCast(this.layoutDoc._fontSize) || "inherit",
+ fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit",
+ letterSpacing: StrCast(this.layoutDoc.letterSpacing),
+ textTransform: StrCast(this.layoutDoc.textTransform) as any,
+ paddingLeft: NumCast(this.layoutDoc._xPadding),
+ paddingRight: NumCast(this.layoutDoc._xPadding),
+ paddingTop: NumCast(this.layoutDoc._yPadding),
+ paddingBottom: NumCast(this.layoutDoc._yPadding),
+ textOverflow: this.layoutDoc._singleLine ? "ellipsis" : undefined,
+ whiteSpace: this.layoutDoc._singleLine ? "nowrap" : "pre-wrap"
+ }} >
+ <div className="labelBox-mainButtonCenter">
+ {StrCast(this.rootDoc.text, StrCast(this.rootDoc.title))}
+ </div>
+ </div>
+ <div className="labelBox-fieldKeyParams" >
+ {!missingParams?.length ? (null) : missingParams.map(m => <div key={m} className="labelBox-missingParam">{m}</div>)}
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocuLinkBox.scss b/src/client/views/nodes/LinkAnchorBox.scss
index f2c203548..710f2178b 100644
--- a/src/client/views/nodes/DocuLinkBox.scss
+++ b/src/client/views/nodes/LinkAnchorBox.scss
@@ -1,13 +1,13 @@
-.docuLinkBox-cont, .docuLinkBox-cont-small {
+.linkAnchorBox-cont, .linkAnchorBox-cont-small {
cursor: default;
position: absolute;
width: 15;
height: 15;
border-radius: 20px;
- pointer-events: all;
user-select: none;
+ pointer-events: all;
- .docuLinkBox-linkCloser {
+ .linkAnchorBox-linkCloser {
position: absolute;
width: 18;
height: 18;
@@ -23,7 +23,7 @@
}
}
-.docuLinkBox-cont-small {
+.linkAnchorBox-cont-small {
width:5px;
height:5px;
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 31ce58079..6c50abf21 100644
--- a/src/client/views/nodes/DocuLinkBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -4,11 +4,11 @@ import { Doc, DocListCast } from "../../../new_fields/Doc";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { makeInterface } from "../../../new_fields/Schema";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { Utils, setupMoveUpEvents } from '../../../Utils';
+import { Utils, setupMoveUpEvents, emptyFunction } from '../../../Utils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager } from "../../util/DragManager";
-import { DocComponent } from "../DocComponent";
-import "./DocuLinkBox.scss";
+import { ViewBoxBaseComponent } from "../DocComponent";
+import "./LinkAnchorBox.scss";
import { FieldView, FieldViewProps } from "./FieldView";
import React = require("react");
import { ContextMenuProps } from "../ContextMenuItem";
@@ -17,16 +17,17 @@ import { LinkEditor } from "../linking/LinkEditor";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SelectionManager } from "../../util/SelectionManager";
import { TraceMobx } from "../../../new_fields/util";
+import { DocumentView } from "./DocumentView";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-type DocLinkSchema = makeInterface<[typeof documentSchema]>;
-const DocLinkDocument = makeInterface(documentSchema);
+type LinkAnchorSchema = makeInterface<[typeof documentSchema]>;
+const LinkAnchorDocument = makeInterface(documentSchema);
@observer
-export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(DocLinkDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocuLinkBox, fieldKey); }
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
_doubleTap = false;
_lastTap: number = 0;
_ref = React.createRef<HTMLDivElement>();
@@ -39,7 +40,7 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
@observable _forceOpen = false;
onPointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.onPointerMove, () => { }, this.onClick);
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
}
onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
@@ -49,47 +50,48 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
const dragdist = Math.sqrt((pt[0] - down[0]) * (pt[0] - down[0]) + (pt[1] - down[1]) * (pt[1] - down[1]));
if (separation > 100) {
- const dragData = new DragManager.DocumentDragData([this.props.Document]);
+ const dragData = new DragManager.DocumentDragData([this.rootDoc]);
dragData.dropAction = "alias";
- dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y", "isButton"];
+ dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y", "isLinkButton"];
DragManager.StartDocumentDrag([this._ref.current!], dragData, down[0], down[1]);
return true;
} else if (dragdist > separation) {
- this.props.Document[this.props.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
- this.props.Document[this.props.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
+ this.layoutDoc[this.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
+ this.layoutDoc[this.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
}
}
return false;
});
@action
- onClick = (e: PointerEvent) => {
- this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0);
- this._lastTap = Date.now();
- if ((e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) {
+ onClick = (e: React.MouseEvent) => {
+ if ((e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton)) {
this.props.select(false);
}
- if (!this._doubleTap) {
+ if (!this._doubleTap && !e.ctrlKey && e.button < 2) {
const anchorContainerDoc = this.props.ContainingCollectionDoc; // bcz: hack! need a better prop for passing the anchor's container
this._editing = true;
anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
- if (anchorContainerDoc && !this.props.Document.onClick && !this._isOpen) {
+ if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
this._timeout = setTimeout(action(() => {
- DocumentManager.Instance.FollowLink(this.props.Document, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false);
+ DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "inTab")), false);
this._editing = false;
}), 300 - (Date.now() - this._lastTap));
}
} else {
this._timeout && clearTimeout(this._timeout);
this._timeout = undefined;
+ this._doubleTap = false;
+ this.openLinkEditor(e);
+ e.stopPropagation();
}
}
openLinkDocOnRight = (e: React.MouseEvent) => {
- this.props.addDocTab(this.props.Document, "onRight");
+ this.props.addDocTab(this.rootDoc, "onRight");
}
openLinkTargetOnRight = (e: React.MouseEvent) => {
- const alias = Doc.MakeAlias(Cast(this.props.Document[this.props.fieldKey], Doc, null));
- alias.isButton = undefined;
+ const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
+ alias.isLinkButton = undefined;
alias.isBackground = undefined;
alias.layoutKey = "layout";
this.props.addDocTab(alias, "onRight");
@@ -105,30 +107,31 @@ export class DocuLinkBox extends DocComponent<FieldViewProps, DocLinkSchema>(Doc
funcs.push({ description: "Open Link Target on Right", event: () => this.openLinkTargetOnRight(e), icon: "eye" });
funcs.push({ description: "Open Link on Right", event: () => this.openLinkDocOnRight(e), icon: "eye" });
funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" });
+ funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" });
- ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
render() {
TraceMobx();
- const x = this.props.PanelWidth() > 1 ? NumCast(this.props.Document[this.props.fieldKey + "_x"], 100) : 0;
- const y = this.props.PanelWidth() > 1 ? NumCast(this.props.Document[this.props.fieldKey + "_y"], 100) : 0;
- const c = StrCast(this.props.Document.backgroundColor, "lightblue");
- const anchor = this.props.fieldKey === "anchor1" ? "anchor2" : "anchor1";
+ const x = this.props.PanelWidth() > 1 ? NumCast(this.layoutDoc[this.fieldKey + "_x"], 100) : 0;
+ const y = this.props.PanelWidth() > 1 ? NumCast(this.layoutDoc[this.fieldKey + "_y"], 100) : 0;
+ const c = StrCast(this.layoutDoc.backgroundColor, "lightblue");
+ const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .15;
- const timecode = this.props.Document[anchor + "Timecode"];
- const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
+ const timecode = this.dataDoc[anchor + "_timecode"];
+ const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title) + (timecode !== undefined ? ":" + timecode : "");
const flyout = (
- <div className="docuLinkBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.props.Document)}>
- <LinkEditor sourceDoc={Cast(this.props.Document[this.props.fieldKey], Doc, null)} hideback={true} linkDoc={this.props.Document} showLinks={action(() => { })} />
- {!this._forceOpen ? (null) : <div className="docuLinkBox-linkCloser" onPointerDown={action(() => this._isOpen = this._editing = this._forceOpen = false)}>
+ <div className="linkAnchorBoxBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.rootDoc)}>
+ <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => { })} />
+ {!this._forceOpen ? (null) : <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => this._isOpen = this._editing = this._forceOpen = false)}>
<FontAwesomeIcon color="dimGray" icon={"times"} size={"sm"} />
</div>}
</div>
);
const small = this.props.PanelWidth() <= 1;
- return <div className={`docuLinkBox-cont${small ? "-small" : ""}`} onPointerDown={this.onPointerDown} title={targetTitle} onContextMenu={this.specificContextMenu}
+ return <div className={`linkAnchorBox-cont${small ? "-small" : ""}`} onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
ref={this._ref} style={{
background: c,
left: !small ? `calc(${x}% - 7.5px)` : undefined,
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 542c86049..740f2ef04 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -4,7 +4,7 @@ import { documentSchema } from "../../../new_fields/documentSchemas";
import { makeInterface, listSpec } from "../../../new_fields/Schema";
import { returnFalse, returnZero } from "../../../Utils";
import { CollectionTreeView } from "../collections/CollectionTreeView";
-import { DocExtendableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from './FieldView';
import "./LinkBox.scss";
import { Cast } from "../../../new_fields/Types";
@@ -13,11 +13,10 @@ type LinkDocument = makeInterface<[typeof documentSchema]>;
const LinkDocument = makeInterface(documentSchema);
@observer
-export class LinkBox extends DocExtendableComponent<FieldViewProps, LinkDocument>(LinkDocument) {
+export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
render() {
return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
- onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
style={{ background: this.props.backgroundColor?.(this.props.Document) }} >
<CollectionTreeView {...this.props}
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 7a3d2e92b..bccf0f291 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -199,9 +199,6 @@
.pdfBox {
pointer-events: none;
- .collectionFreeFormView-none {
- pointer-events: none;
- }
.pdfViewer-text {
.textLayer {
span {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index f8c008a2d..3712c648e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -9,25 +9,24 @@ import { ScriptField } from '../../../new_fields/ScriptField';
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { PdfField, URLField } from "../../../new_fields/URLField";
import { Utils } from '../../../Utils';
-import { KeyCodes } from '../../northstar/utils/KeyCodes';
import { undoBatch } from '../../util/UndoManager';
import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { PDFViewer } from "../pdf/PDFViewer";
import { FieldView, FieldViewProps } from './FieldView';
import { pageSchema } from "./ImageBox";
+import { KeyCodes } from '../../util/KeyCodes';
import "./PDFBox.scss";
import React = require("react");
import { documentSchema } from '../../../new_fields/documentSchemas';
-import { url } from 'inspector';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@observer
-export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) {
+export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocument>(PdfDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
private _keyValue: string = "";
private _valueValue: string = "";
@@ -211,7 +210,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" });
funcs.push({ description: "Toggle Fit Width " + (this.Document._fitWidth ? "Off" : "On"), event: () => this.Document._fitWidth = !this.Document._fitWidth, icon: "expand-arrows-alt" });
- ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
@computed get contentScaling() { return this.props.ContentScaling(); }
@@ -249,7 +248,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
_pdfjsRequested = false;
render() {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null);
- if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true;
+ if (this.props.isSelected() || this.props.renderDepth <= 1 || this.props.Document.scrollY !== undefined) this._everActive = true;
if (pdfUrl && (this._everActive || this.props.Document._scrollTop || (this.dataDoc[this.props.fieldKey + "-nativeWidth"] && this.props.ScreenToLocalTransform().Scale < 2.5))) {
if (pdfUrl instanceof PdfField && this._pdf) {
return this.renderPdfView;
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index ba8389fda..78c19f351 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -10,8 +10,12 @@
letter-spacing: 2px;
overflow: hidden;
transition: 0.7s opacity ease;
- pointer-events: all;
+ .presBox-listCont {
+ position: absolute;
+ height: calc(100% - 25px);
+ width: 100%;
+ }
.presBox-buttons {
padding: 10px;
width: 100%;
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 73d09b4e1..80d043db1 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -4,10 +4,11 @@ import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faH
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Doc, DocListCast, DocCastAsync } from "../../../new_fields/Doc";
import { InkTool } from "../../../new_fields/InkField";
-import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { returnFalse } from "../../../Utils";
+import { documentSchema } from "../../../new_fields/documentSchemas";
import { DocumentManager } from "../../util/DocumentManager";
import { undoBatch } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
@@ -15,6 +16,8 @@ import { CollectionView, CollectionViewType } from "../collections/CollectionVie
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
import "./PresBox.scss";
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { makeInterface } from "../../../new_fields/Schema";
library.add(faArrowLeft);
library.add(faArrowRight);
@@ -26,24 +29,27 @@ library.add(faTimes);
library.add(faMinus);
library.add(faEdit);
+type PresBoxSchema = makeInterface<[typeof documentSchema]>;
+const PresBoxDocument = makeInterface(documentSchema);
+
@observer
-export class PresBox extends React.Component<FieldViewProps> {
+export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
_childReaction: IReactionDisposer | undefined;
@observable _isChildActive = false;
componentDidMount() {
- this.props.Document._forceRenderEngine = "timeline";
- this.props.Document._replacedChrome = "replaced";
+ this.layoutDoc._forceRenderEngine = "timeline";
+ this.layoutDoc._replacedChrome = "replaced";
this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true });
}
componentWillUnmount() {
this._childReaction?.();
}
- @computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); }
- @computed get currentIndex() { return NumCast(this.props.Document._itemIndex); }
+ @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
+ @computed get currentIndex() { return NumCast(this.layoutDoc._itemIndex); }
- updateCurrentPresentation = action(() => Doc.UserDoc().curPresentation = this.props.Document);
+ updateCurrentPresentation = action(() => Doc.UserDoc().activePresentation = this.rootDoc);
next = () => {
this.updateCurrentPresentation();
@@ -78,8 +84,8 @@ export class PresBox extends React.Component<FieldViewProps> {
}
whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
- active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) &&
- (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
+ active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) &&
+ (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
/**
* This is the method that checks for the actions that need to be performed
@@ -131,11 +137,10 @@ export class PresBox extends React.Component<FieldViewProps> {
*/
navigateToElement = async (curDoc: Doc, fromDocIndex: number) => {
this.updateCurrentPresentation();
- const fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc;
let docToJump = curDoc;
let willZoom = false;
- const presDocs = DocListCast(this.props.Document[this.props.fieldKey]);
+ const presDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
let nextSelected = presDocs.indexOf(curDoc);
const currentDocGroups: Doc[] = [];
for (; nextSelected < presDocs.length - 1; nextSelected++) {
@@ -157,29 +162,28 @@ export class PresBox extends React.Component<FieldViewProps> {
});
//docToJump stayed same meaning, it was not in the group or was the last element in the group
- const aliasOf = await Cast(docToJump.aliasOf, Doc);
- const srcContext = aliasOf && await Cast(aliasOf.context, Doc);
+ const aliasOf = await DocCastAsync(docToJump.aliasOf);
+ const srcContext = aliasOf && await DocCastAsync(aliasOf.context);
if (docToJump === curDoc) {
//checking if curDoc has navigation open
- const target = await Cast(curDoc.presentationTargetDoc, Doc);
+ const target = await DocCastAsync(curDoc.presentationTargetDoc);
if (curDoc.navButton && target) {
DocumentManager.Instance.jumpToDocument(target, false, undefined, srcContext);
} else if (curDoc.zoomButton && target) {
//awaiting jump so that new scale can be found, since jumping is async
await DocumentManager.Instance.jumpToDocument(target, true, undefined, srcContext);
}
- return;
+ } else {
+ //awaiting jump so that new scale can be found, since jumping is async
+ const presTargetDoc = await DocCastAsync(docToJump.presentationTargetDoc);
+ presTargetDoc && await DocumentManager.Instance.jumpToDocument(presTargetDoc, willZoom, undefined, srcContext);
}
-
- //awaiting jump so that new scale can be found, since jumping is async
- const presTargetDoc = await docToJump.presentationTargetDoc as Doc;
- await DocumentManager.Instance.jumpToDocument(presTargetDoc, willZoom, undefined, srcContext);
}
@undoBatch
public removeDocument = (doc: Doc) => {
- return Doc.RemoveDocFromList(this.props.Document, this.props.fieldKey, doc);
+ return Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
}
//The function that is called when a document is clicked or reached through next or back.
@@ -188,10 +192,10 @@ export class PresBox extends React.Component<FieldViewProps> {
this.updateCurrentPresentation();
Doc.UnBrushAllDocs();
if (index >= 0 && index < this.childDocs.length) {
- this.props.Document._itemIndex = index;
+ this.layoutDoc._itemIndex = index;
- if (!this.props.Document.presStatus) {
- this.props.Document.presStatus = true;
+ if (!this.layoutDoc.presStatus) {
+ this.layoutDoc.presStatus = true;
this.startPresentation(index);
}
@@ -204,10 +208,10 @@ export class PresBox extends React.Component<FieldViewProps> {
//The function that starts or resets presentaton functionally, depending on status flag.
startOrResetPres = () => {
this.updateCurrentPresentation();
- if (this.props.Document.presStatus) {
+ if (this.layoutDoc.presStatus) {
this.resetPresentation();
} else {
- this.props.Document.presStatus = true;
+ this.layoutDoc.presStatus = true;
this.startPresentation(0);
this.gotoDocument(0, this.currentIndex);
}
@@ -216,7 +220,7 @@ export class PresBox extends React.Component<FieldViewProps> {
addDocument = (doc: Doc) => {
const newPinDoc = Doc.MakeAlias(doc);
newPinDoc.presentationTargetDoc = doc;
- return Doc.AddDocToList(this.props.Document, this.props.fieldKey, newPinDoc);
+ return Doc.AddDocToList(this.dataDoc, this.fieldKey, newPinDoc);
}
@@ -225,8 +229,8 @@ export class PresBox extends React.Component<FieldViewProps> {
resetPresentation = () => {
this.updateCurrentPresentation();
this.childDocs.forEach(doc => (doc.presentationTargetDoc as Doc).opacity = 1);
- this.props.Document._itemIndex = 0;
- this.props.Document.presStatus = false;
+ this.layoutDoc._itemIndex = 0;
+ this.layoutDoc.presStatus = false;
}
//The function that starts the presentation, also checking if actions should be applied
@@ -246,31 +250,31 @@ export class PresBox extends React.Component<FieldViewProps> {
});
}
- updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: number) => {
- if (BoolCast(this.props.Document.inOverlay) !== (mode === CollectionViewType.Invalid)) {
- if (this.props.Document.inOverlay) {
- Doc.RemoveDocFromList((Doc.UserDoc().overlays as Doc), undefined, this.props.Document);
- CollectionDockingView.AddRightSplit(this.props.Document);
- this.props.Document.inOverlay = false;
+ updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: CollectionViewType) => {
+ if (BoolCast(this.layoutDoc.inOverlay) !== (mode === CollectionViewType.Invalid)) {
+ if (this.layoutDoc.inOverlay) {
+ Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+ CollectionDockingView.AddRightSplit(this.rootDoc);
+ this.layoutDoc.inOverlay = false;
} else {
- this.props.Document.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
- this.props.Document.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
- this.props.addDocTab?.(this.props.Document, "close");
- Doc.AddDocToList((Doc.UserDoc().overlays as Doc), undefined, this.props.Document);
+ this.layoutDoc.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
+ this.layoutDoc.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
+ this.props.addDocTab?.(this.rootDoc, "close");
+ Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
}
}
}));
- initializeViewAliases = (docList: Doc[], viewtype: number) => {
+ initializeViewAliases = (docList: Doc[], viewtype: CollectionViewType) => {
const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46;
docList.forEach(doc => {
- doc.presBox = this.props.Document; // give contained documents a reference to the presentation
+ doc.presBox = this.rootDoc; // give contained documents a reference to the presentation
doc.collapsedHeight = hgt; // set the collpased height for documents based on the type of view (Tree or Stack) they will be displaye din
});
}
selectElement = (doc: Doc) => {
- this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.props.Document._itemIndex));
+ this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.layoutDoc._itemIndex));
}
getTransform = () => {
@@ -283,17 +287,17 @@ export class PresBox extends React.Component<FieldViewProps> {
@undoBatch
viewChanged = action((e: React.ChangeEvent) => {
//@ts-ignore
- this.props.Document._viewType = Number(e.target.selectedOptions[0].value);
- this.props.Document._viewType === CollectionViewType.Stacking && (this.props.Document._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
- this.updateMinimize(e, Number(this.props.Document._viewType));
+ this.layoutDoc._viewType = e.target.selectedOptions[0].value;
+ this.layoutDoc._viewType === CollectionViewType.Stacking && (this.layoutDoc._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
+ this.updateMinimize(e, StrCast(this.layoutDoc._viewType));
});
- childLayoutTemplate = () => this.props.Document._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined;
+ childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc()["template-presentation"], Doc, null) : undefined;
render() {
- const mode = NumCast(this.props.Document._viewType, CollectionViewType.Invalid);
+ const mode = StrCast(this.layoutDoc._viewType) as CollectionViewType;
this.initializeViewAliases(this.childDocs, mode);
- return <div className="presBox-cont" style={{ minWidth: this.props.Document.inOverlay ? 240 : undefined, pointerEvents: this.active() || this.props.Document.inOverlay ? "all" : "none" }} >
- <div className="presBox-buttons" style={{ display: this.props.Document._chromeStatus === "disabled" ? "none" : undefined }}>
+ return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined, pointerEvents: this.active() || this.layoutDoc.inOverlay ? "all" : "none" }} >
+ <div className="presBox-buttons" style={{ display: this.layoutDoc._chromeStatus === "disabled" ? "none" : undefined }}>
<select className="collectionViewBaseChrome-viewPicker"
onPointerDown={e => e.stopPropagation()}
onChange={this.viewChanged}
@@ -304,15 +308,21 @@ export class PresBox extends React.Component<FieldViewProps> {
<option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel}>Slides</option>
</select>
<button className="presBox-button" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
- <button className="presBox-button" title={"Reset Presentation" + this.props.Document.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}>
- <FontAwesomeIcon icon={this.props.Document.presStatus ? "stop" : "play"} />
+ <button className="presBox-button" title={"Reset Presentation" + this.layoutDoc.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}>
+ <FontAwesomeIcon icon={this.layoutDoc.presStatus ? "stop" : "play"} />
</button>
<button className="presBox-button" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
</div>
<div className="presBox-listCont" >
{mode !== CollectionViewType.Invalid ?
- <CollectionView {...this.props} PanelHeight={this.panelHeight} moveDocument={returnFalse} childLayoutTemplate={this.childLayoutTemplate}
- addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
+ <CollectionView {...this.props}
+ PanelHeight={this.panelHeight}
+ moveDocument={returnFalse}
+ childLayoutTemplate={this.childLayoutTemplate}
+ addDocument={this.addDocument}
+ removeDocument={returnFalse}
+ focus={this.selectElement}
+ ScreenToLocalTransform={this.getTransform} />
: (null)
}
</div>
diff --git a/src/client/views/nodes/QueryBox.scss b/src/client/views/nodes/QueryBox.scss
index 82f64054c..b5f90aa1e 100644
--- a/src/client/views/nodes/QueryBox.scss
+++ b/src/client/views/nodes/QueryBox.scss
@@ -2,5 +2,4 @@
width: 100%;
height: 100%;
position: absolute;
- pointer-events: all;
} \ No newline at end of file
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index 7016b4f04..76885eada 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -3,19 +3,20 @@ import { IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { Id } from '../../../new_fields/FieldSymbols';
-import { makeInterface } from "../../../new_fields/Schema";
-import { StrCast } from "../../../new_fields/Types";
+import { makeInterface, listSpec } from "../../../new_fields/Schema";
+import { StrCast, Cast } from "../../../new_fields/Types";
import { SelectionManager } from "../../util/SelectionManager";
-import { DocAnnotatableComponent } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { SearchBox } from "../search/SearchBox";
import { FieldView, FieldViewProps } from './FieldView';
import "./QueryBox.scss";
+import { List } from "../../../new_fields/List";
type QueryDocument = makeInterface<[typeof documentSchema]>;
const QueryDocument = makeInterface(documentSchema);
@observer
-export class QueryBox extends DocAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
+export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); }
_docListChangedReaction: IReactionDisposer | undefined;
componentDidMount() {
@@ -28,7 +29,13 @@ export class QueryBox extends DocAnnotatableComponent<FieldViewProps, QueryDocum
render() {
const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging";
return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
- <SearchBox id={this.props.Document[Id]} searchQuery={StrCast(this.dataDoc.searchQuery)} filterQquery={StrCast(this.dataDoc.filterQuery)} />
+ <SearchBox
+ id={this.props.Document[Id]}
+ setSearchQuery={q => this.dataDoc.searchQuery = q}
+ searchQuery={StrCast(this.dataDoc.searchQuery)}
+ setSearchFileTypes={q => this.dataDoc.searchFileTypes = new List<string>(q)}
+ searchFileTypes={Cast(this.dataDoc.searchFileTypes, listSpec("string"), [])}
+ filterQquery={StrCast(this.dataDoc.filterQuery)} />
</div >;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/RadialMenu.scss b/src/client/views/nodes/RadialMenu.scss
index ce0c263ef..daa620d12 100644
--- a/src/client/views/nodes/RadialMenu.scss
+++ b/src/client/views/nodes/RadialMenu.scss
@@ -67,17 +67,4 @@ s
margin-left: 5px;
text-align: left;
display: inline; //need this?
-}
-
-
-
-.icon-background {
- pointer-events: all;
- height:100%;
- margin-top: 15px;
- background-color: transparent;
- width: 35px;
- text-align: center;
- font-size: 20px;
- margin-left: 5px;
} \ No newline at end of file
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 0ffed78de..ddfdb67b4 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -1,12 +1,9 @@
import React = require("react");
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { action, observable, computed, IReactionDisposer, reaction, runInAction } from "mobx";
-import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import Measure from "react-measure";
-import "./RadialMenu.scss";
-import MobileInkOverlay from "../../../mobile/MobileInkOverlay";
import MobileInterface from "../../../mobile/MobileInterface";
+import "./RadialMenu.scss";
+import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem";
@observer
export class RadialMenu extends React.Component {
diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss
index 6cc184948..141960f60 100644
--- a/src/client/views/nodes/ScreenshotBox.scss
+++ b/src/client/views/nodes/ScreenshotBox.scss
@@ -1,5 +1,4 @@
.screenshotBox {
- pointer-events: all;
transform-origin: top left;
background: white;
color: black;
@@ -21,10 +20,6 @@
height: Auto;
}
-.screenshotBox-content-interactive, .screenshotBox-content-fullScreen {
- pointer-events: all;
-}
-
.screenshotBox-uiButtons {
background:dimgray;
border: orange solid 1px;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 58ff4971a..125690dc7 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -7,15 +7,14 @@ import { observer } from "mobx-react";
import * as rp from 'request-promise';
import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas";
import { makeInterface } from "../../../new_fields/Schema";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast } from "../../../new_fields/Types";
+import { Cast, NumCast } from "../../../new_fields/Types";
import { VideoField } from "../../../new_fields/URLField";
import { emptyFunction, returnFalse, returnOne, Utils, returnZero } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
import "./ScreenshotBox.scss";
@@ -27,7 +26,7 @@ const ScreenshotDocument = makeInterface(documentSchema, positionSchema);
library.add(faVideo);
@observer
-export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
+export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
private _reactionDisposer?: IReactionDisposer;
private _videoRef: HTMLVideoElement | null = null;
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
@@ -38,22 +37,21 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
videoLoad = () => {
const aspect = this.player!.videoWidth / this.player!.videoHeight;
- const nativeWidth = (this.Document._nativeWidth || 0);
- const nativeHeight = (this.Document._nativeHeight || 0);
+ const nativeWidth = (this.layoutDoc._nativeWidth || 0);
+ const nativeHeight = (this.layoutDoc._nativeHeight || 0);
if (!nativeWidth || !nativeHeight) {
- if (!this.Document._nativeWidth) this.Document._nativeWidth = 400;
- this.Document._nativeHeight = (this.Document._nativeWidth || 0) / aspect;
- this.Document._height = (this.Document._width || 0) / aspect;
+ if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 400;
+ this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / aspect;
+ this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
}
- if (!this.Document.duration) this.Document.duration = this.player!.duration;
}
@action public Snapshot() {
- const width = this.Document._width || 0;
- const height = this.Document._height || 0;
+ const width = NumCast(this.layoutDoc._width);
+ const height = NumCast(this.layoutDoc._height);
const canvas = document.createElement('canvas');
canvas.width = 640;
- canvas.height = 640 * (this.Document._nativeHeight || 0) / (this.Document._nativeWidth || 1);
+ canvas.height = 640 * NumCast(this.layoutDoc._nativeHeight) / NumCast(this.layoutDoc._nativeWidth, 1);
const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
ctx.rect(0, 0, canvas.width, canvas.height);
@@ -71,7 +69,7 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
setTimeout(() => {
if (returnedFilename) {
const imageSummary = Docs.Create.ImageDocument(Utils.prepend(returnedFilename), {
- x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
+ x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y),
_width: 150, _height: height / width * 150, title: "--screenshot--"
});
this.props.addDocument?.(imageSummary);
@@ -111,7 +109,7 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
}
@observable _screenCapture = false;
specificContextMenu = (e: React.MouseEvent): void => {
- const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const field = Cast(this.dataDoc[this.fieldKey], VideoField);
if (field) {
const url = field.url.href;
const subitems: ContextMenuProps[] = [];
@@ -122,7 +120,7 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
}), icon: "expand-arrows-alt"
});
- ContextMenu.Instance.addItem({ description: "Screenshot Funcs...", subitems: subitems, icon: "video" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
}
}
@@ -172,16 +170,16 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
PanelWidth={this.props.PanelWidth}
NativeHeight={returnZero}
NativeWidth={returnZero}
- annotationsKey={this.annotationKey}
+ annotationsKey={""}
focus={this.props.focus}
isSelected={this.props.isSelected}
isAnnotationOverlay={true}
select={emptyFunction}
- active={this.annotationsActive}
+ active={returnFalse}
ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
+ whenActiveChanged={emptyFunction}
+ removeDocument={returnFalse}
+ moveDocument={returnFalse}
addDocument={returnFalse}
CollectionView={undefined}
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
@@ -190,7 +188,7 @@ export class ScreenshotBox extends DocAnnotatableComponent<FieldViewProps, Scree
{this.contentFunc}
</CollectionFreeFormView>
</div>
- {this.active() ? this.uIButtons : (null)}
+ {this.props.isSelected() ? this.uIButtons : (null)}
</div >);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
new file mode 100644
index 000000000..43695f00d
--- /dev/null
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -0,0 +1,35 @@
+.scriptingBox-outerDiv {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background-color: rgb(241, 239, 235);
+ padding: 10px;
+ .scriptingBox-inputDiv {
+ display: flex;
+ flex-direction: column;
+ height: calc(100% - 30px);
+ .scriptingBox-errorMessage {
+ overflow: auto;
+ }
+ .scripting-params {
+ background: "beige";
+ }
+ .scriptingBox-textArea {
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+ resize: none;
+ padding: 7px;
+ }
+ }
+
+ .scriptingBox-toolbar {
+ width: 100%;
+ height: 30px;
+ .scriptingBox-button {
+ width: 50%
+ }
+ }
+}
+
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
new file mode 100644
index 000000000..c607d6614
--- /dev/null
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -0,0 +1,98 @@
+import { action, observable, computed } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { documentSchema } from "../../../new_fields/documentSchemas";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { StrCast, ScriptCast, Cast } from "../../../new_fields/Types";
+import { InteractionUtils } from "../../util/InteractionUtils";
+import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
+import { EditableView } from "../EditableView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import "./ScriptingBox.scss";
+import { OverlayView } from "../OverlayView";
+import { DocumentIconContainer } from "./DocumentIcon";
+import { List } from "../../../new_fields/List";
+
+const ScriptingSchema = createSchema({});
+type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
+const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema);
+
+@observer
+export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) {
+ protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
+ public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); }
+
+ _overlayDisposer?: () => void;
+
+ @observable private _errorMessage: string = "";
+
+ @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], StrCast(this.layoutDoc[this.props.fieldKey + "-rawScript"])); }
+ @computed get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), Cast(this.layoutDoc[this.props.fieldKey + "-params"], listSpec("string"), [])); }
+ set rawScript(value) { this.dataDoc[this.props.fieldKey + "-rawScript"] = value; }
+ set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = value; }
+
+ @action
+ componentDidMount() {
+ this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript;
+ }
+
+ componentWillUnmount() { this._overlayDisposer?.(); }
+
+ @action
+ onCompile = () => {
+ const params = this.compileParams.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam);
+ const result = CompileScript(this.rawScript, {
+ editable: true,
+ transformer: DocumentIconContainer.getTransformer(),
+ params,
+ typecheck: false
+ });
+ this._errorMessage = isCompileError(result) ? result.errors.map(e => e.messageText).join("\n") : "";
+ return this.dataDoc[this.props.fieldKey] = result.compiled ? new ScriptField(result) : undefined;
+ }
+
+ @action
+ onRun = () => {
+ this.onCompile()?.script.run({}, err => this._errorMessage = err.map((e: any) => e.messageText).join("\n"));
+ }
+
+ onFocus = () => {
+ this._overlayDisposer?.();
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ }
+
+ render() {
+ const params = <EditableView
+ contents={this.compileParams.join(" ")}
+ display={"block"}
+ maxHeight={72}
+ height={35}
+ fontSize={28}
+ GetValue={() => ""}
+ SetValue={value => { this.compileParams = new List<string>(value.split(" ").filter(s => s !== " ")); return true; }}
+ />;
+ return (
+ <div className="scriptingBox-outerDiv"
+ onWheel={e => this.props.isSelected(true) && e.stopPropagation()}>
+ <div className="scriptingBox-inputDiv"
+ onPointerDown={e => this.props.isSelected(true) && e.stopPropagation()} >
+ <textarea className="scriptingBox-textarea"
+ placeholder="write your script here"
+ onChange={e => this.rawScript = e.target.value}
+ value={this.rawScript}
+ onFocus={this.onFocus}
+ onBlur={e => this._overlayDisposer?.()} />
+ <div className="scriptingBox-errorMessage" style={{ background: this._errorMessage ? "red" : "" }}>{this._errorMessage}</div>
+ <div className="scriptingBox-params" >{params}</div>
+ </div>
+ {this.rootDoc.layout === "layout" ? <div></div> : (null)}
+ <div className="scriptingBox-toolbar">
+ <button className="scriptingBox-button" onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button>
+ <button className="scriptingBox-button" onPointerDown={e => { this.onRun(); e.stopPropagation(); }}>Run</button>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss
index 4ef277d8c..78015bd70 100644
--- a/src/client/views/nodes/SliderBox.scss
+++ b/src/client/views/nodes/SliderBox.scss
@@ -1,7 +1,6 @@
.sliderBox-outerDiv {
width: 100%;
height: 100%;
- pointer-events: all;
border-radius: inherit;
display: flex;
flex-direction: column;
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 844d95d11..cb2526769 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -1,22 +1,20 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEdit } from '@fortawesome/free-regular-svg-icons';
-import { computed, runInAction } from 'mobx';
+import { runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Handles, Rail, Slider, Tracks, Ticks } from 'react-compound-slider';
-import { Doc } from '../../../new_fields/Doc';
+import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
import { documentSchema } from '../../../new_fields/documentSchemas';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
+import { createSchema, makeInterface } from '../../../new_fields/Schema';
import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, FieldValue, StrCast, NumCast, Cast } from '../../../new_fields/Types';
-import { DragManager } from '../../util/DragManager';
+import { Cast, NumCast, StrCast } from '../../../new_fields/Types';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent } from '../DocComponent';
-import './SliderBox.scss';
-import { Handle, TooltipRail, Track, Tick } from './SliderBox-components';
-import { FieldView, FieldViewProps } from './FieldView';
+import { ViewBoxBaseComponent } from '../DocComponent';
import { ScriptBox } from '../ScriptBox';
+import { FieldView, FieldViewProps } from './FieldView';
+import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
+import './SliderBox.scss';
library.add(faEdit as any);
@@ -32,36 +30,33 @@ type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema]
const SliderDocument = makeInterface(SliderSchema, documentSchema);
@observer
-export class SliderBox extends DocComponent<FieldViewProps, SliderDocument>(SliderDocument) {
+export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocument>(SliderDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); }
- private dropDisposer?: DragManager.DragDropDisposer;
-
- @computed get dataDoc() {
- return this.props.DataDoc &&
- (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) ||
- this.props.DataDoc.layout === this.Document) ? this.props.DataDoc : Doc.GetProto(this.Document);
- }
+ get minThumbKey() { return this.fieldKey + "-minThumb"; }
+ get maxThumbKey() { return this.fieldKey + "-maxThumb"; }
+ get minKey() { return this.fieldKey + "-min"; }
+ get maxKey() { return this.fieldKey + "-max"; }
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Edit Thumb Change Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Thumb Change ...", this.props.Document, "onThumbChange", obj.x, obj.y) });
- ContextMenu.Instance.addItem({ description: "Slider Funcs...", subitems: funcs, icon: "asterisk" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
onChange = (values: readonly number[]) => runInAction(() => {
- this.Document._sliderMinThumb = values[0];
- this.Document._sliderMaxThumb = values[1];
- Cast(this.Document.onThumbChanged, ScriptField, null)?.script.run({ range: values, this: this.props.Document });
+ this.dataDoc[this.minThumbKey] = values[0];
+ this.dataDoc[this.maxThumbKey] = values[1];
+ Cast(this.layoutDoc.onThumbChanged, ScriptField, null)?.script.run({ self: this.rootDoc, range: values, this: this.layoutDoc });
})
render() {
- const domain = [NumCast(this.props.Document._sliderMin), NumCast(this.props.Document._sliderMax)];
- const defaultValues = [NumCast(this.props.Document._sliderMinThumb), NumCast(this.props.Document._sliderMaxThumb)];
- return (
+ const domain = [NumCast(this.layoutDoc[this.minKey]), NumCast(this.layoutDoc[this.maxKey])];
+ const defaultValues = [NumCast(this.dataDoc[this.minThumbKey]), NumCast(this.dataDoc[this.maxThumbKey])];
+ return domain[1] <= domain[0] ? (null) : (
<div className="sliderBox-outerDiv" onContextMenu={this.specificContextMenu} onPointerDown={e => e.stopPropagation()}
- style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
+ style={{ boxShadow: this.layoutDoc.opacity === 0 ? undefined : StrCast(this.layoutDoc.boxShadow, "") }}>
<div className="sliderBox-mainButton" onContextMenu={this.specificContextMenu} style={{
- background: this.Document.backgroundColor, color: this.Document.color || "black",
- fontSize: this.Document.fontSize, letterSpacing: this.Document.letterSpacing || ""
+ background: StrCast(this.layoutDoc.backgroundColor), color: StrCast(this.layoutDoc.color, "black"),
+ fontSize: NumCast(this.layoutDoc._fontSize), letterSpacing: StrCast(this.layoutDoc.letterSpacing)
}} >
<Slider
mode={2}
@@ -77,7 +72,7 @@ export class SliderBox extends DocComponent<FieldViewProps, SliderDocument>(Slid
{({ handles, activeHandleID, getHandleProps }) => (
<div className="slider-handles">
{handles.map((handle, i) => {
- const value = i === 0 ? this.Document._sliderMinThumb : this.Document._sliderMaxThumb;
+ const value = i === 0 ? defaultValues[0] : defaultValues[1];
return (
<div title={String(value)}>
<Handle
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index fabbf5196..0c0854ac2 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,5 +1,4 @@
.videoBox {
- pointer-events: all;
transform-origin: top left;
.videoBox-viewer {
opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
@@ -24,9 +23,9 @@
height: 100%;
}
-.videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen {
- pointer-events: all;
-}
+// .videoBox-content-interactive, .videoBox-content-fullScreen, .videoBox-content-YouTube-fullScreen {
+// pointer-events: all;
+// }
.videoBox-time{
color : white;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d384ad12f..613929bca 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -9,14 +9,14 @@ import { Doc } from "../../../new_fields/Doc";
import { InkTool } from "../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
import { VideoField } from "../../../new_fields/URLField";
import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
@@ -33,7 +33,7 @@ const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema);
library.add(faVideo);
@observer
-export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
+export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
static _youtubeIframeCounter: number = 0;
private _reactionDisposer?: IReactionDisposer;
private _youtubeReactionDisposer?: IReactionDisposer;
@@ -55,14 +55,10 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
videoLoad = () => {
const aspect = this.player!.videoWidth / this.player!.videoHeight;
- const nativeWidth = (this.Document._nativeWidth || 0);
- const nativeHeight = (this.Document._nativeHeight || 0);
- if (!nativeWidth || !nativeHeight) {
- if (!this.Document._nativeWidth) this.Document._nativeWidth = this.player!.videoWidth;
- this.Document._nativeHeight = (this.Document._nativeWidth || 0) / aspect;
- this.Document._height = (this.Document._width || 0) / aspect;
- }
- if (!this.Document.duration) this.Document.duration = this.player!.duration;
+ this.layoutDoc._nativeWidth = this.player!.videoWidth;
+ this.layoutDoc._nativeHeight = (this.layoutDoc._nativeWidth || 0) / aspect;
+ this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect;
+ this.dataDoc[this.fieldKey + "-" + "duration"] = this.player!.duration;
}
@action public Play = (update: boolean = true) => {
@@ -90,7 +86,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
@action public FullScreen() {
this._fullScreen = true;
this.player && this.player.requestFullscreen();
- this._youtubePlayer && this.props.addDocTab(this.props.Document, "inTab");
+ this._youtubePlayer && this.props.addDocTab(this.rootDoc, "inTab");
}
choosePath(url: string) {
@@ -101,11 +97,11 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
@action public Snapshot() {
- const width = this.Document._width || 0;
- const height = this.Document._height || 0;
+ const width = (this.layoutDoc._width || 0);
+ const height = (this.layoutDoc._height || 0);
const canvas = document.createElement('canvas');
canvas.width = 640;
- canvas.height = 640 * (this.Document._nativeHeight || 0) / (this.Document._nativeWidth || 1);
+ canvas.height = 640 * (this.layoutDoc._nativeHeight || 0) / (this.layoutDoc._nativeWidth || 1);
const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
ctx.rect(0, 0, canvas.width, canvas.height);
@@ -116,25 +112,28 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
if (!this._videoRef) { // can't find a way to take snapshots of videos
const b = Docs.Create.ButtonDocument({
- x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
- _width: 150, _height: 50, title: (this.Document.currentTimecode || 0).toString()
+ x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
+ _width: 150, _height: 50, title: (this.layoutDoc.currentTimecode || 0).toString()
});
- b.onClick = ScriptField.MakeScript(`this.currentTimecode = ${(this.Document.currentTimecode || 0)}`);
+ b.onClick = ScriptField.MakeScript(`this.currentTimecode = ${(this.layoutDoc.currentTimecode || 0)}`);
} else {
//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 filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.Document.title).replace(/\..*$/, "") + "_" + (this.Document.currentTimecode || 0).toString().replace(/\./, "_")));
+ const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc.currentTimecode || 0).toString().replace(/\./, "_")));
VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
if (returnedFilename) {
const url = this.choosePath(Utils.prepend(returnedFilename));
const imageSummary = Docs.Create.ImageDocument(url, {
- x: (this.Document.x || 0) + width, y: (this.Document.y || 0),
- _width: 150, _height: height / width * 150, title: "--snapshot" + (this.Document.currentTimecode || 0) + " image-"
+ _nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
+ x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
+ _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc.currentTimecode || 0) + " image-"
});
- imageSummary.isButton = true;
+ Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
+ Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
+ imageSummary.isLinkButton = true;
this.props.addDocument && this.props.addDocument(imageSummary);
- DocUtils.MakeLink({ doc: imageSummary }, { doc: this.props.Document }, "video snapshot");
+ DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
}
});
}
@@ -142,8 +141,8 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
@action
updateTimecode = () => {
- this.player && (this.Document.currentTimecode = this.player.currentTime);
- this._youtubePlayer && (this.Document.currentTimecode = this._youtubePlayer.getCurrentTime());
+ this.player && (this.layoutDoc.currentTimecode = this.player.currentTime);
+ this._youtubePlayer && (this.layoutDoc.currentTimecode = this._youtubePlayer.getCurrentTime());
}
componentDidMount() {
@@ -151,12 +150,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
if (this.youtubeVideoId) {
const youtubeaspect = 400 / 315;
- const nativeWidth = (this.Document._nativeWidth || 0);
- const nativeHeight = (this.Document._nativeHeight || 0);
+ const nativeWidth = (this.layoutDoc._nativeWidth || 0);
+ const nativeHeight = (this.layoutDoc._nativeHeight || 0);
if (!nativeWidth || !nativeHeight) {
- if (!this.Document._nativeWidth) this.Document._nativeWidth = 600;
- this.Document._nativeHeight = (this.Document._nativeWidth || 0) / youtubeaspect;
- this.Document._height = (this.Document._width || 0) / youtubeaspect;
+ if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 600;
+ this.layoutDoc._nativeHeight = (this.layoutDoc._nativeWidth || 0) / youtubeaspect;
+ this.layoutDoc._height = (this.layoutDoc._width || 0) / youtubeaspect;
}
}
}
@@ -174,7 +173,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
this._videoRef!.ontimeupdate = this.updateTimecode;
vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
this._reactionDisposer && this._reactionDisposer();
- this._reactionDisposer = reaction(() => this.Document.currentTimecode || 0,
+ this._reactionDisposer = reaction(() => (this.layoutDoc.currentTimecode || 0),
time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
}
}
@@ -210,12 +209,12 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
}), icon: "expand-arrows-alt"
});
- ContextMenu.Instance.addItem({ description: "Video Funcs...", subitems: subitems, icon: "video" });
+ ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
}
}
@computed get content() {
- const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+ const field = Cast(this.dataDoc[this.fieldKey], VideoField);
const interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
return !field ? <div>Loading</div> :
@@ -259,7 +258,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
const onYoutubePlayerReady = (event: any) => {
this._reactionDisposer && this._reactionDisposer();
this._youtubeReactionDisposer && this._youtubeReactionDisposer();
- this._reactionDisposer = reaction(() => this.Document.currentTimecode, () => !this._playing && this.Seek(this.Document.currentTimecode || 0));
+ this._reactionDisposer = reaction(() => this.layoutDoc.currentTimecode, () => !this._playing && this.Seek((this.layoutDoc.currentTimecode || 0)));
this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
const interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting;
iframe.style.pointerEvents = interactive ? "all" : "none";
@@ -274,7 +273,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
}
private get uIButtons() {
- const curTime = (this.Document.currentTimecode || 0);
+ const curTime = (this.layoutDoc.currentTimecode || 0);
return ([<div className="videoBox-time" key="time" onPointerDown={this.onResetDown} >
<span>{"" + Math.round(curTime)}</span>
<span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
@@ -316,7 +315,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
onResetMove = (e: PointerEvent) => {
this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY);
- this.Seek(Math.max(0, (this.Document.currentTimecode || 0) + Math.sign(e.movementX) * 0.0333));
+ this.Seek(Math.max(0, (this.layoutDoc.currentTimecode || 0) + Math.sign(e.movementX) * 0.0333));
e.stopImmediatePropagation();
}
@@ -324,22 +323,22 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
onResetUp = (e: PointerEvent) => {
document.removeEventListener("pointermove", this.onResetMove, true);
document.removeEventListener("pointerup", this.onResetUp, true);
- this._isResetClick < 10 && (this.Document.currentTimecode = 0);
+ this._isResetClick < 10 && (this.layoutDoc.currentTimecode = 0);
}
@computed get youtubeContent() {
this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
- const start = untracked(() => Math.round(this.Document.currentTimecode || 0));
+ const start = untracked(() => Math.round((this.layoutDoc.currentTimecode || 0)));
return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
- onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.Document._nativeWidth || 640)} height={(this.Document._nativeHeight || 390)}
+ onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.layoutDoc._nativeWidth || 640)} height={(this.layoutDoc._nativeHeight || 390)}
src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
}
@action.bound
addDocumentWithTimestamp(doc: Doc): boolean {
- const curTime = (this.Document.currentTimecode || -1);
+ const curTime = (this.layoutDoc.currentTimecode || -1);
curTime !== -1 && (doc.displayTimecode = curTime);
return this.addDocument(doc);
}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index b41687c11..af84a7d95 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -3,9 +3,22 @@
.webBox-container, .webBox-container-dragging {
transform-origin: top left;
+ .webBox-outerContent {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: auto;
+ .webBox-innerContent {
+ width:100%;
+ }
+ }
+ div.webBox-outerContent::-webkit-scrollbar-thumb {
+ display:none;
+ }
}
-.webBox-cont,
-.webBox-cont-dragging {
+.webBox-cont {
padding: 0vw;
position: absolute;
top: 0;
@@ -18,8 +31,6 @@
}
.webBox-cont-interactive {
- pointer-events: all;
-
span {
user-select: text !important;
}
@@ -35,22 +46,26 @@
width: 100%;
height: 100%;
position: absolute;
- pointer-events: all;
}
-.webBox-button {
- padding: 0vw;
- border: none;
+.webBox-buttons {
+ margin-left: 44;
+ background:lightGray;
width: 100%;
- height: 100%;
+}
+.webBox-freeze {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 5px;
+ width: 30px;
}
-.webView-urlEditor {
+.webBox-urlEditor {
position: relative;
opacity: 0.9;
z-index: 9001;
transition: top .5s;
- background: lightgrey;
padding: 10px;
@@ -101,7 +116,6 @@
width: 100%;
height: 100%;
position: absolute;
- pointer-events: all;
.indicator {
position: absolute;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index ea5d601ec..4e383e468 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,24 +1,22 @@
import { library } from "@fortawesome/fontawesome-svg-core";
-import { faStickyNote } from '@fortawesome/free-solid-svg-icons';
-import { action, computed, observable } from "mobx";
+import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons';
+import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { Doc, FieldResult } from "../../../new_fields/Doc";
import { documentSchema } from "../../../new_fields/documentSchemas";
import { HtmlField } from "../../../new_fields/HtmlField";
import { InkTool } from "../../../new_fields/InkField";
import { makeInterface } from "../../../new_fields/Schema";
-import { Cast, NumCast } from "../../../new_fields/Types";
+import { Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types";
import { WebField } from "../../../new_fields/URLField";
import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { ImageUtils } from "../../util/Import & Export/ImageUtils";
-import { SelectionManager } from "../../util/SelectionManager";
-import { DocAnnotatableComponent } from "../DocComponent";
+import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
-import { KeyValueBox } from "./KeyValueBox";
import "./WebBox.scss";
import React = require("react");
import * as WebRequest from 'web-request';
@@ -26,33 +24,62 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
const htmlToText = require("html-to-text");
-
library.add(faStickyNote);
type WebDocument = makeInterface<[typeof documentSchema]>;
const WebDocument = makeInterface(documentSchema);
@observer
-export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
+export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
- @observable private collapsed: boolean = true;
- @observable private url: string = "hello";
+ get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) === "disabled"; }
+ set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; }
+ @observable private _url: string = "hello";
+ @observable private _pressX: number = 0;
+ @observable private _pressY: number = 0;
private _longPressSecondsHack?: NodeJS.Timeout;
+ private _outerRef = React.createRef<HTMLDivElement>();
private _iframeRef = React.createRef<HTMLIFrameElement>();
private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
private _iframeDragRef = React.createRef<HTMLDivElement>();
- @observable private _pressX: number = 0;
- @observable private _pressY: number = 0;
-
+ private _reactionDisposer?: IReactionDisposer;
+ private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+
+ iframeLoaded = action((e: any) => {
+ this._iframeRef.current!.contentDocument?.addEventListener('pointerdown', this.iframedown, false);
+ this._iframeRef.current!.contentDocument?.addEventListener('scroll', this.iframeScrolled, false);
+ this.layoutDoc.scrollHeight = this._iframeRef.current!.contentDocument?.children?.[0].scrollHeight || 1000;
+ this._iframeRef.current!.contentDocument!.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop);
+ this._reactionDisposer?.();
+ this._reactionDisposer = reaction(() => this.layoutDoc.scrollY,
+ (scrollY) => {
+ if (scrollY !== undefined) {
+ this._outerRef.current!.scrollTop = scrollY;
+ this.layoutDoc.scrollY = undefined;
+ }
+ },
+ { fireImmediately: true }
+ );
+ });
+ setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
+ iframedown = (e: PointerEvent) => {
+ this._setPreviewCursor?.(e.screenX, e.screenY, false);
+ }
+ iframeScrolled = (e: any) => {
+ const scroll = e.target?.children?.[0].scrollTop;
+ this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scroll;
+ }
async componentDidMount() {
this.setURL();
+ this._iframeRef.current!.setAttribute("enable-annotation", "true");
+
document.addEventListener("pointerup", this.onLongPressUp);
document.addEventListener("pointermove", this.onLongPressMove);
- const field = Cast(this.props.Document[this.props.fieldKey], WebField);
+ const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
if (field?.url.href.indexOf("youtube") !== -1) {
const youtubeaspect = 400 / 315;
const nativeWidth = NumCast(this.layoutDoc._nativeWidth);
@@ -66,29 +93,31 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
const result = await WebRequest.get(Utils.CorsProxy(field.url.href));
this.dataDoc.text = htmlToText.fromString(result.content);
}
-
}
componentWillUnmount() {
+ this._reactionDisposer?.();
document.removeEventListener("pointerup", this.onLongPressUp);
document.removeEventListener("pointermove", this.onLongPressMove);
+ this._iframeRef.current!.contentDocument?.removeEventListener('pointerdown', this.iframedown);
+ this._iframeRef.current!.contentDocument?.removeEventListener('scroll', this.iframeScrolled);
}
@action
onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.url = e.target.value;
+ this._url = e.target.value;
}
@action
submitURL = () => {
- this.dataDoc[this.props.fieldKey] = new WebField(new URL(this.url));
+ this.dataDoc[this.props.fieldKey] = new WebField(new URL(this._url));
}
@action
setURL() {
const urlField: FieldResult<WebField> = Cast(this.dataDoc[this.props.fieldKey], WebField);
- if (urlField) this.url = urlField.url.toString();
- else this.url = "";
+ if (urlField) this._url = urlField.url.toString();
+ else this._url = "";
}
onValueKeyDown = async (e: React.KeyboardEvent) => {
@@ -98,47 +127,45 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
}
}
-
- switchToText = () => {
- let url: string = "";
- const field = Cast(this.props.Document[this.props.fieldKey], WebField);
- if (field) url = field.url.href;
-
- const newBox = Docs.Create.TextDocument(url, {
- x: NumCast(this.props.Document.x),
- y: NumCast(this.props.Document.y),
- title: url,
- _width: 200,
- _height: 70,
- });
-
- SelectionManager.SelectedDocuments().map(dv => {
- dv.props.addDocument && dv.props.addDocument(newBox);
- dv.props.removeDocument && dv.props.removeDocument(dv.props.Document);
- });
-
- Doc.BrushDoc(newBox);
+ toggleNativeDimensions = () => {
+ if (!this.layoutDoc.isAnnotating) {
+ //DocumentView.unfreezeNativeDimensions(this.layoutDoc);
+ this.layoutDoc.lockedTransform = false;
+ this.layoutDoc.isAnnotating = true;
+ }
+ else {
+ //Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
+ this.layoutDoc.lockedTransform = true;
+ this.layoutDoc.isAnnotating = false;
+ }
}
urlEditor() {
+ const frozen = this.layoutDoc._nativeWidth && this.layoutDoc.isAnnotating;
return (
- <div className="webView-urlEditor" style={{ top: this.collapsed ? -70 : 0 }}>
+ <div className="webBox-urlEditor" style={{ top: this._collapsed ? -70 : 0 }}>
<div className="urlEditor">
<div className="editorBase">
<button className="editor-collapse"
style={{
- top: this.collapsed ? 70 : 10,
- transform: `rotate(${this.collapsed ? 180 : 0}deg) scale(${this.collapsed ? 0.5 : 1}) translate(${this.collapsed ? "-100%, -100%" : "0, 0"})`,
- opacity: (this.collapsed && !this.props.isSelected()) ? 0 : 0.9,
- left: (this.collapsed ? 0 : "unset"),
+ top: this._collapsed ? 70 : 10,
+ transform: `rotate(${this._collapsed ? 180 : 0}deg) scale(${this._collapsed ? 0.5 : 1}) translate(${this._collapsed ? "-100%, -100%" : "0, 0"})`,
+ opacity: (this._collapsed && !this.props.isSelected()) ? 0 : 0.9,
+ left: (this._collapsed ? 0 : "unset"),
}}
title="Collapse Url Editor" onClick={this.toggleCollapse}>
<FontAwesomeIcon icon="caret-up" size="2x" />
</button>
- <div style={{ marginLeft: 54, width: "100%", display: this.collapsed ? "none" : "flex" }}>
+ <div className="webBox-buttons" style={{ display: this._collapsed ? "none" : "flex" }}>
+ <div className="webBox-freeze" title={"Annotate"} style={{ background: frozen ? "lightBlue" : "gray" }} onClick={this.toggleNativeDimensions} >
+ <FontAwesomeIcon icon={faPen} size={"2x"} />
+ </div>
+ <div className="webBox-freeze" title={"Select"} style={{ background: !frozen ? "lightBlue" : "gray" }} onClick={this.toggleNativeDimensions} >
+ <FontAwesomeIcon icon={faMousePointer} size={"2x"} />
+ </div>
<input className="webpage-urlInput"
placeholder="ENTER URL"
- value={this.url}
+ value={this._url}
onChange={this.onURLChange}
onKeyDown={this.onValueKeyDown}
/>
@@ -151,9 +178,6 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
<button className="submitUrl" onClick={this.submitURL}>
SUBMIT
</button>
- <div className="switchToText" title="Convert web to text doc" onClick={this.switchToText} style={{ display: "flex", alignItems: "center", justifyContent: "center" }} >
- <FontAwesomeIcon icon={faStickyNote} size={"lg"} />
- </div>
</div>
</div>
</div>
@@ -164,7 +188,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
@action
toggleCollapse = () => {
- this.collapsed = !this.collapsed;
+ this._collapsed = !this._collapsed;
}
_ignore = 0;
@@ -293,7 +317,8 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
if (field instanceof HtmlField) {
view = <span id="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
- view = <iframe ref={this._iframeRef} src={Utils.CorsProxy(field.url.href)} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
+ const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
+ view = <iframe ref={this._iframeRef} onLoad={this.iframeLoaded} src={url} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
} else {
view = <iframe ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} style={{ position: "absolute", width: "100%", height: "100%", top: 0 }} />;
}
@@ -303,56 +328,68 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
{view}
</div>;
- const decInteracting = DocumentDecorations.Instance && DocumentDecorations.Instance.Interacting;
+ const decInteracting = DocumentDecorations.Instance?.Interacting;
const frozen = !this.props.isSelected() || decInteracting;
- const classname = "webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !decInteracting ? "-interactive" : "");
- return (
- <>
- <div className={classname} >
- {content}
- </div>
- {!frozen ? (null) :
- <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
- <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
- <div className="indicator" ref={this._iframeIndicatorRef}></div>
- <div className="dragger" ref={this._iframeDragRef}></div>
- </div>
- </div>}
- </>);
+ return (<>
+ <div className={"webBox-cont" + (this.props.isSelected() && InkingControl.Instance.selectedTool === InkTool.None && !decInteracting ? "-interactive" : "")} >
+ {content}
+ </div>
+ {!frozen ? (null) :
+ <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
+ onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer}>
+ <div className="touch-iframe-overlay" onPointerDown={this.onLongPressDown} >
+ <div className="indicator" ref={this._iframeIndicatorRef}></div>
+ <div className="dragger" ref={this._iframeDragRef}></div>
+ </div>
+ </div>}
+ </>);
}
+ scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.Document.scrollTop));
render() {
- const dragging = "";//</div>!SelectionManager.GetIsDragging() ? "" : "-dragging";
- return (<div className={`webBox-container${dragging}`}
+ return (<div className={`webBox-container`}
style={{
transform: `scale(${this.props.ContentScaling()})`,
width: `${100 / this.props.ContentScaling()}%`,
height: `${100 / this.props.ContentScaling()}%`,
- pointerEvents: this.props.Document.isBackground ? "none" : undefined
+ pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
}} >
- <CollectionFreeFormView {...this.props}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.props.PanelWidth}
- annotationsKey={this.annotationKey}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- isAnnotationOverlay={true}
- select={emptyFunction}
- active={this.active}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
- {() => [this.content]}
- </CollectionFreeFormView>
+ {this.content}
+ <div className={"webBox-outerContent"} ref={this._outerRef}
+ style={{ pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none" }}
+ onWheel={e => e.stopPropagation()}
+ onScroll={e => {
+ if (this._iframeRef.current!.contentDocument!.children[0].scrollTop !== this._outerRef.current!.scrollTop) {
+ this._iframeRef.current!.contentDocument!.children[0].scrollTop = this._outerRef.current!.scrollTop;
+ }
+ //this._outerRef.current!.scrollTop !== this._scrollTop && (this._outerRef.current!.scrollTop = this._scrollTop)
+ }}>
+ <div className={"webBox-innerContent"} style={{ height: NumCast(this.layoutDoc.scrollHeight) }}>
+ <CollectionFreeFormView {...this.props}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.props.PanelWidth}
+ annotationsKey={this.annotationKey}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ focus={this.props.focus}
+ setPreviewCursor={this.setPreviewCursor}
+ isSelected={this.props.isSelected}
+ isAnnotationOverlay={true}
+ select={emptyFunction}
+ active={this.active}
+ ContentScaling={returnOne}
+ whenActiveChanged={this.whenActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocument}
+ CollectionView={undefined}
+ ScreenToLocalTransform={this.scrollXf}
+ renderDepth={this.props.renderDepth + 1}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+ </CollectionFreeFormView>
+ </div>
+ </div>
</div >);
}
} \ No newline at end of file