aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/AudioBox.tsx80
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx74
-rw-r--r--src/client/views/nodes/ColorBox.tsx7
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx2
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.scss3
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx79
-rw-r--r--src/client/views/nodes/DocHolderBox.tsx8
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx49
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx59
-rw-r--r--src/client/views/nodes/DocumentView.scss10
-rw-r--r--src/client/views/nodes/DocumentView.tsx403
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx2
-rw-r--r--src/client/views/nodes/FieldView.tsx17
-rw-r--r--src/client/views/nodes/FilterBox.scss55
-rw-r--r--src/client/views/nodes/FilterBox.tsx252
-rw-r--r--src/client/views/nodes/FontIconBox.scss24
-rw-r--r--src/client/views/nodes/FontIconBox.tsx31
-rw-r--r--src/client/views/nodes/ImageBox.tsx155
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx10
-rw-r--r--src/client/views/nodes/LabelBox.scss1
-rw-r--r--src/client/views/nodes/LinkAnchorBox.scss5
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx10
-rw-r--r--src/client/views/nodes/LinkBox.tsx4
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx31
-rw-r--r--src/client/views/nodes/MenuIconBox.scss49
-rw-r--r--src/client/views/nodes/MenuIconBox.tsx33
-rw-r--r--src/client/views/nodes/PDFBox.scss25
-rw-r--r--src/client/views/nodes/PDFBox.tsx49
-rw-r--r--src/client/views/nodes/PresBox.scss338
-rw-r--r--src/client/views/nodes/PresBox.tsx829
-rw-r--r--src/client/views/nodes/RadialMenuItem.tsx8
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx14
-rw-r--r--src/client/views/nodes/SliderBox.tsx5
-rw-r--r--src/client/views/nodes/VideoBox.tsx47
-rw-r--r--src/client/views/nodes/WebBox.scss88
-rw-r--r--src/client/views/nodes/WebBox.tsx164
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx19
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss4
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx41
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss62
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx474
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.scss5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx177
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts92
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx80
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts35
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx20
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts23
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts4
-rw-r--r--src/client/views/nodes/formattedText/prosemirrorPatches.js6
51 files changed, 2273 insertions, 1791 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 5c8cb5e35..cba65f663 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -26,7 +26,7 @@ import { Scripting } from "../../util/Scripting";
import Waveform from "react-audio-waveform";
import axios from "axios";
import { SnappingManager } from "../../util/SnappingManager";
-const _global = (window /* browser */ || global /* node */) as any;
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
declare class MediaRecorder {
// whatever MediaRecorder has
@@ -61,6 +61,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
_left: boolean = false;
_first: boolean = false;
_dragging = false;
+ _play: any = null;
_count: Array<any> = [];
_audioRef = React.createRef<HTMLDivElement>();
@@ -109,12 +110,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this._linkPlayDisposer?.();
this._scrubbingDisposer?.();
}
+
+ @action
componentDidMount() {
if (!this.dataDoc.markerAmount) {
this.dataDoc.markerAmount = 0;
}
- runInAction(() => this.audioState = this.path ? "paused" : undefined);
+ this.audioState = this.path ? "paused" : undefined;
this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
scrollLinkId => {
if (scrollLinkId) {
@@ -130,7 +133,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
selected => {
const sel = selected.length ? selected[0].props.Document : undefined;
- const link = sel && DocListCast(this.dataDoc.links).forEach(l => (l.anchor1 === sel || l.anchor2 === sel) && this.playLink(sel), false);
+ let link;
+ sel && DocListCast(this.dataDoc.links).forEach(l => {
+ if (l.anchor1 === sel || l.anchor2 === sel && !sel.audioStart) {
+ link = this.playLink(sel);
+ }
+ });
// for links created during recording
if (!link) {
this.layoutDoc.playOnSelect && this.recordingStart && sel && sel.creationDate && !Doc.AreProtosEqual(sel, this.props.Document) && this.playFromTime(DateCast(sel.creationDate).date.getTime());
@@ -155,10 +163,12 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
if (startTime) {
link = true;
- this.recordingStart && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
+ this.layoutDoc.playOnSelect && this.recordingStart && (endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime));
}
}
});
+
+ this.layoutDoc.playOnSelect && this.recordingStart && Doc.AreProtosEqual(doc, this.props.Document) && this.pause();
return link;
}
@@ -169,11 +179,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc.duration = htmlEle.duration);
DocListCast(this.dataDoc.links).map(l => {
const { la1, linkTime } = this.getLinkData(l);
- if (linkTime > NumCast(this.layoutDoc.currentTimecode) && linkTime < htmlEle.currentTime) {
+ if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < htmlEle.currentTime) {
Doc.linkFollowHighlight(la1);
}
});
- this.layoutDoc.currentTimecode = htmlEle.currentTime;
+ this.layoutDoc._currentTimecode = htmlEle.currentTime;
}
}
@@ -191,8 +201,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
// play back the audio from time
@action
playFrom = (seekTimeInSeconds: number, endTime: number = this.audioDuration) => {
- let play;
- clearTimeout(play);
+ clearTimeout(this._play);
this._duration = endTime - seekTimeInSeconds;
if (Number.isNaN(this._ele?.duration)) {
setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
@@ -208,7 +217,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this._ele.play();
runInAction(() => this.audioState = "playing");
if (endTime !== this.audioDuration) {
- play = setTimeout(() => this.pause(), (this._duration) * 1000); // use setTimeout to play a specific duration
+ this._play = setTimeout(() => this.pause(), (this._duration) * 1000); // use setTimeout to play a specific duration
}
} else {
this.pause();
@@ -223,7 +232,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
if (this._paused) {
this._pausedTime += (new Date().getTime() - this._recordStart) / 1000;
} else {
- this.layoutDoc.currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
+ this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000;
}
}
}
@@ -251,8 +260,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: (this.layoutDoc.playOnSelect ? "Don't play" : "Play") + " when link is selected", event: () => this.layoutDoc.playOnSelect = !this.layoutDoc.playOnSelect, icon: "expand-arrows-alt" });
- funcs.push({ description: (this.layoutDoc.hideMarkers ? "Don't hide" : "Hide") + " markers", event: () => this.layoutDoc.hideMarkers = !this.layoutDoc.hideMarkers, icon: "expand-arrows-alt" });
- funcs.push({ description: (this.layoutDoc.hideLabels ? "Don't hide" : "Hide") + " labels", event: () => this.layoutDoc.hideLabels = !this.layoutDoc.hideLabels, icon: "expand-arrows-alt" });
+ funcs.push({ description: (this.layoutDoc.hideMarkers ? "Don't hide" : "Hide") + " range markers", event: () => this.layoutDoc.hideMarkers = !this.layoutDoc.hideMarkers, icon: "expand-arrows-alt" });
+ funcs.push({ description: (this.layoutDoc.hideLabels ? "Don't hide" : "Hide") + " label markers", event: () => this.layoutDoc.hideLabels = !this.layoutDoc.hideLabels, icon: "expand-arrows-alt" });
funcs.push({ description: (this.layoutDoc.playOnClick ? "Don't play" : "Play") + " markers onClick", event: () => this.layoutDoc.playOnClick = !this.layoutDoc.playOnClick, icon: "expand-arrows-alt" });
ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
@@ -284,11 +293,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
// creates a text document for dictation
onFile = (e: any) => {
- const newDoc = Docs.Create.TextDocument("", {
- title: "", _chromeStatus: "disabled",
- x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
- _width: NumCast(this.props.Document._width), _height: 2 * NumCast(this.props.Document._height)
- });
+ const newDoc = CurrentUserUtils.GetNewTextDoc("", NumCast(this.props.Document.x), NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
+ NumCast(this.props.Document._width), 2 * NumCast(this.props.Document._height));
Doc.GetProto(newDoc).recordingSource = this.dataDoc;
Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("self.recordingSource.audioState");
@@ -303,6 +309,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this._ele = e;
}
+ // ref for timeline
+ timelineRef = (timeline: HTMLDivElement) => {
+ this._timeline = timeline;
+ }
+
// returns the path of the audio file
@computed get path() {
const field = Cast(this.props.Document[this.props.fieldKey], AudioField);
@@ -388,14 +399,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
onPointerDown = (e: React.PointerEvent, m: any, left: boolean): void => {
this._currMarker = m;
this._left = left;
- const rect = (e.target as any).getBoundingClientRect();
- const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration;
+ this._timeline?.setPointerCapture(e.pointerId);
+ const toTimeline = (screen_delta: number, width: number) => screen_delta / width * this.audioDuration;
setupMoveUpEvents(this, e,
- () => {
- this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x));
+ (e: PointerEvent) => {
+ const rect = (e.target as any).getBoundingClientRect();
+ this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x, rect.width));
return false;
},
- () => this._ele!.currentTime = this.layoutDoc.currentTimecode = toTimeline(e.clientX - rect.x),
+ (e: PointerEvent) => {
+ const rect = (e.target as any).getBoundingClientRect();
+ this._ele!.currentTime = this.layoutDoc._currentTimecode = toTimeline(e.clientX - rect.x, rect.width);
+ this._timeline?.releasePointerCapture(e.pointerId);
+ },
emptyFunction);
}
@@ -463,7 +479,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
color={"darkblue"}
height={this._waveHeight}
barWidth={0.1}
- // pos={this.layoutDoc.currentTimecode} need to correctly resize parent to make this work (not very necessary for function)
+ // pos={this.layoutDoc._currentTimecode} need to correctly resize parent to make this work (not very necessary for function)
pos={this.audioDuration}
duration={this.audioDuration}
peaks={this._buckets.length === 100 ? this._buckets : undefined}
@@ -517,9 +533,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
return <DocumentView {...this.props}
Document={mark}
focus={() => this.playLink(mark)}
- pointerEvents={true}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
+ pointerEvents={"all"}
rootSelected={returnFalse}
LayoutTemplate={undefined}
ContainingCollectionDoc={this.props.Document}
@@ -544,7 +558,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
<div className="buttons" onClick={this._paused ? this.recordPlay : this.recordPause}>
<FontAwesomeIcon style={{ width: "100%" }} icon={this._paused ? "play" : "pause"} size={this.props.PanelHeight() < 36 ? "1x" : "2x"} />
</div>
- <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}</div>
+ <div className="time">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
</div>
:
<button className={`audiobox-record${interactive}`} style={{ backgroundColor: "black" }}>
@@ -555,14 +569,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
<div className="audiobox-dictation"></div>
<div className="audiobox-player" >
<div className="audiobox-playhead" title={this.audioState === "paused" ? "play" : "pause"} onClick={this.onPlay}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.audioState === "paused" ? "play" : "pause"} size={"1x"} /></div>
- <div className="audiobox-timeline" onClick={e => { e.stopPropagation(); e.preventDefault(); }}
+ <div className="audiobox-timeline" ref={this.timelineRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }}
onPointerDown={e => {
if (e.button === 0 && !e.ctrlKey) {
const rect = (e.target as any).getBoundingClientRect();
if (e.target !== this._audioRef.current) {
const wasPaused = this.audioState === "paused";
- this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
+ this._ele!.currentTime = this.layoutDoc._currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration;
wasPaused && this.pause();
}
@@ -605,8 +619,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
<div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={l[Id]} style={{ left: `${startTime / this.audioDuration * 100}%` }} onClick={e => e.stopPropagation()}>
<DocumentView {...this.props}
Document={l}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
rootSelected={returnFalse}
ContainingCollectionDoc={this.props.Document}
parentActive={returnTrue}
@@ -614,7 +626,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
backgroundColor={returnTransparent}
ContentScaling={returnOne}
forcedBackgroundColor={returnTransparent}
- pointerEvents={false}
+ pointerEvents={"none"}
LayoutTemplate={undefined}
LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)}
/>
@@ -624,11 +636,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
})}
{this._visible ? this.selectionContainer : null}
- <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
+ <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc._currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} />
{this.audio}
</div>
<div className="current-time">
- {formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}
+ {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
</div>
<div className="total-time">
{formatTime(Math.round(this.audioDuration))}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 52f6a66c8..d6f8ce19c 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -11,7 +11,7 @@ import { Document } from "../../../fields/documentSchemas";
import { TraceMobx } from "../../../fields/util";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
import { List } from "../../../fields/List";
-import { numberRange, smoothScroll } from "../../../Utils";
+import { numberRange, smoothScroll, returnVal } from "../../../Utils";
import { ComputedField } from "../../../fields/ScriptField";
import { listSpec } from "../../../fields/Schema";
import { DocumentType } from "../../documents/DocumentTypes";
@@ -55,23 +55,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
@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)); }
-
- @computed get renderScriptDim() {
- if (this.Document.renderScript) {
- const someView = Cast(this.props.Document.someView, Doc);
- const minimap = Cast(this.props.Document.minimap, Doc);
- if (someView instanceof Doc && minimap instanceof Doc) {
- const x = (NumCast(someView._panX) - NumCast(someView._width) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap._width) - NumCast(minimap._width) / 2;
- const y = (NumCast(someView._panY) - NumCast(someView._height) / 2 / NumCast(someView._viewScale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap._height) - NumCast(minimap._height) / 2;
- const w = NumCast(someView._width) / NumCast(someView._viewScale) / NumCast(minimap.fitW) * NumCast(minimap.width);
- const h = NumCast(someView._height) / NumCast(someView._viewScale) / NumCast(minimap.fitH) * NumCast(minimap.height);
- return { x: x, y: y, width: w, height: h };
- }
- }
- return undefined;
- }
+ @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc._nativeWidth, this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
+ @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc._nativeHeight, this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
public static getValues(doc: Doc, time: number) {
const timecode = Math.round(time);
@@ -80,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
w: Cast(doc["w-indexed"], listSpec("number"), [NumCast(doc._width)]).reduce((p, w, i) => (i <= timecode && w !== undefined) || p === undefined ? w : p, undefined as any as number),
x: Cast(doc["x-indexed"], listSpec("number"), [NumCast(doc.x)]).reduce((p, x, i) => (i <= timecode && x !== undefined) || p === undefined ? x : p, undefined as any as number),
y: Cast(doc["y-indexed"], listSpec("number"), [NumCast(doc.y)]).reduce((p, y, i) => (i <= timecode && y !== undefined) || p === undefined ? y : p, undefined as any as number),
- scroll: Cast(doc["scroll-indexed"], listSpec("number"), [NumCast(doc._scrollTop, 0)]).reduce((p, s, i) => (i <= timecode && s !== undefined) || p === undefined ? s : p, undefined as any as number),
+ scroll: Cast(doc["scroll-indexed"], listSpec("number"), [NumCast(doc._scrollY, 0)]).reduce((p, s, i) => (i <= timecode && s !== undefined) || p === undefined ? s : p, undefined as any as number),
opacity: Cast(doc["opacity-indexed"], listSpec("number"), [NumCast(doc.opacity, 1)]).reduce((p, o, i) => i <= timecode || p === undefined ? o : p, undefined as any as number),
});
}
@@ -98,7 +83,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
hindexed[timecode] = h as any as number;
windexed[timecode] = w as any as number;
oindexed[timecode] = opacity as any as number;
- scrollIndexed[timecode] = scroll as any as number;
+ if (scroll) scrollIndexed[timecode] = scroll as any as number;
d["x-indexed"] = new List<number>(xindexed);
d["y-indexed"] = new List<number>(yindexed);
d["h-indexed"] = new List<number>(hindexed);
@@ -123,12 +108,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
setTimeout(() => doc.dataTransition = "inherit", 1010);
}
- public static setupScroll(doc: Doc, timecode: number, scrollProgressivize: boolean = false) {
+ public static setupScroll(doc: Doc, timecode: number) {
const scrollList = new List<number>();
- scrollList[timecode] = NumCast(doc._scrollTop);
+ scrollList[timecode] = NumCast(doc._scrollY);
doc["scroll-indexed"] = scrollList;
- doc.activeFrame = ComputedField.MakeFunction("self.currentFrame");
- doc._scrollTop = ComputedField.MakeInterpolated("scroll", "activeFrame");
+ doc.activeFrame = ComputedField.MakeFunction("self._currentFrame");
+ doc._scrollY = ComputedField.MakeInterpolated("scroll", "activeFrame");
}
@@ -165,7 +150,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
}
- public static setupZoom(doc: Doc, targDoc: Doc, zoomProgressivize: boolean = false) {
+ public static setupZoom(doc: Doc, targDoc: Doc) {
const width = new List<number>();
const height = new List<number>();
const top = new List<number>();
@@ -180,32 +165,25 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
doc["viewfinder-left-indexed"] = left;
}
- public static setupKeyframes(docs: Doc[], timecode: number, progressivize: boolean = false) {
- docs.forEach((doc, i) => {
- if (doc.appearFrame === undefined) doc.appearFrame = i;
- const curTimecode = progressivize ? i : timecode;
- const xlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const ylist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const wlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const hlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
- const olist = new List<number>(numberRange(timecode + 1).map(t => progressivize && t < (doc.appearFrame ? doc.appearFrame : i) ? 0 : 1));
- const oarray = olist;
- oarray.fill(0, 0, NumCast(doc.appearFrame) - 1);
- oarray.fill(1, NumCast(doc.appearFrame), timecode);
- // oarray.fill(0, 0, NumCast(doc.appearFrame) - 1);
- // oarray.fill(1, NumCast(doc.appearFrame), timecode);\
+ public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) {
+ docs.forEach(doc => {
+ if (doc.appearFrame === undefined) doc.appearFrame = currTimecode;
+ const curTimecode = currTimecode;
+ const xlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const ylist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const wlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const hlist = new List<number>(numberRange(currTimecode + 1).map(i => undefined) as any as number[]);
+ const olist = new List<number>(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1));
wlist[curTimecode] = NumCast(doc._width);
hlist[curTimecode] = NumCast(doc._height);
xlist[curTimecode] = NumCast(doc.x);
ylist[curTimecode] = NumCast(doc.y);
- doc.xArray = xlist;
- doc.yArray = ylist;
doc["x-indexed"] = xlist;
doc["y-indexed"] = ylist;
doc["w-indexed"] = wlist;
doc["h-indexed"] = hlist;
- doc["opacity-indexed"] = oarray;
- doc.activeFrame = ComputedField.MakeFunction("self.context?.currentFrame||0");
+ doc["opacity-indexed"] = olist;
+ doc.activeFrame = ComputedField.MakeFunction("self.context?._currentFrame||0");
doc._height = ComputedField.MakeInterpolated("h", "activeFrame");
doc._width = ComputedField.MakeInterpolated("w", "activeFrame");
doc.x = ComputedField.MakeInterpolated("x", "activeFrame");
@@ -269,15 +247,16 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
render() {
TraceMobx();
const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document, this.props.renderDepth);
+ const borderRounding = StrCast(Doc.Layout(this.layoutDoc).borderRounding) || StrCast(this.layoutDoc.borderRounding) || StrCast(this.Document.borderRounding) || undefined;
return <div className="collectionFreeFormDocumentView-container"
style={{
boxShadow:
this.Opacity === 0 ? undefined : // if it's not visible, then no shadow
this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
- this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
- this.layoutDoc.isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
+ this.props.backgroundHalo?.() && this.props.Document.type !== DocumentType.INK ? (`${this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc._isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ this.layoutDoc._isBackground ? undefined : // if it's a background & has a cluster color, make the shadow spread really big
StrCast(this.layoutDoc.boxShadow, ""),
- borderRadius: StrCast(Doc.Layout(this.layoutDoc).borderRounding),
+ borderRadius: borderRounding,
outline: this.Highlight ? "orange solid 2px" : "",
transform: this.transform,
transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
@@ -286,7 +265,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
zIndex: this.ZInd,
mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
display: this.ZInd === -99 ? "none" : undefined,
- pointerEvents: this.props.Document.isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
+ // @ts-ignore
+ pointerEvents: this.props.Document._isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents
}} >
{Doc.UserDoc().renderStyle !== "comic" ? (null) :
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 090cf015a..fcc9e50f5 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -15,6 +15,7 @@ import { ActiveInkPen, ActiveInkWidth, ActiveInkBezierApprox, SetActiveInkColor,
import "./ColorBox.scss";
import { FieldView, FieldViewProps } from './FieldView';
import { DocumentType } from "../../documents/DocumentTypes";
+import { RichTextMenu } from "./formattedText/RichTextMenu";
type ColorDocument = makeInterface<[typeof documentSchema]>;
const ColorDocument = makeInterface(documentSchema);
@@ -39,8 +40,8 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) { // this situation typically occurs when you have a link dot
targetDoc.backgroundColor = Doc.UserDoc().backgroundColor; // bcz: don't know how to change the color of an inline template...
}
- else if (StrCast(Doc.Layout(view.props.Document).layout).includes("FormattedTextBox") && window.getSelection()?.toString() !== "") {
- Doc.Layout(view.props.Document)[Doc.LayoutFieldKey(view.props.Document) + "-color"] = Doc.UserDoc().backgroundColor;
+ else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== "") {
+ Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + "-color"] = Doc.UserDoc().backgroundColor;
} else {
Doc.Layout(view.props.Document)._backgroundColor = Doc.UserDoc().backgroundColor; // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment
}
@@ -55,7 +56,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
render() {
const selDoc = SelectionManager.SelectedDocuments()?.[0]?.rootDoc;
return <div className={`colorBox-container${this.active() ? "-interactive" : ""}`}
- onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
+ onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} onClick={e => { (e.nativeEvent as any).stuff = true; e.stopPropagation(); }}
style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<SketchPicker onChange={ColorBox.switchColor} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 616cddfcf..2af5b3182 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -74,7 +74,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C
render() {
const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%";
- const childProps: DocumentViewProps = { ...this.props, pointerEvents: false, parentActive: this.props.active };
+ const childProps: DocumentViewProps = { ...this.props, pointerEvents: "none", parentActive: this.props.active };
const clearButton = (which: string) => {
return <div className={`clear-button ${which}`}
onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding
diff --git a/src/client/views/nodes/ContentFittingDocumentView.scss b/src/client/views/nodes/ContentFittingDocumentView.scss
index eb2d93b9a..679073d44 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.scss
+++ b/src/client/views/nodes/ContentFittingDocumentView.scss
@@ -3,7 +3,8 @@
.contentFittingDocumentView {
position: relative;
display: flex;
- align-items: center;
+ width: 100%;
+ height: 100%;
.contentFittingDocumentView-previewDoc {
position: relative;
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 6081def5d..09051da78 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -1,51 +1,19 @@
import React = require("react");
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Transform } from "nodemailer/lib/xoauth2";
-import { Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { ScriptField } from "../../../fields/ScriptField";
+import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction } from "../../../Utils";
-import { dropActionType } from "../../util/DragManager";
-import { CollectionView } from "../collections/CollectionView";
+import { emptyFunction, OmitKeys, returnVal } from "../../../Utils";
import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
import "./ContentFittingDocumentView.scss";
interface ContentFittingDocumentViewProps {
- Document: Doc;
- DataDocument?: Doc;
- LayoutDoc?: () => Opt<Doc>;
- NativeWidth?: () => number;
- NativeHeight?: () => number;
- FreezeDimensions?: boolean;
- LibraryPath: Doc[];
- renderDepth: number;
- fitToBox?: boolean;
- layoutKey?: string;
- dropAction?: dropActionType;
- PanelWidth: () => number;
- PanelHeight: () => number;
- focus?: (doc: Doc) => void;
- CollectionView?: CollectionView;
- CollectionDoc?: Doc;
- onClick?: ScriptField;
- backgroundColor?: (doc: Doc) => string | undefined;
- getTransform: () => Transform;
- addDocument?: (document: Doc) => boolean;
- moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
- removeDocument?: (document: Doc) => boolean;
- active: (outsideReaction: boolean) => boolean;
- whenActiveChanged: (isActive: boolean) => void;
- addDocTab: (document: Doc, where: string) => boolean;
- pinToPres: (document: Doc) => void;
- dontRegisterView?: boolean;
- rootSelected: (outsideReaction?: boolean) => boolean;
- Display?: string;
+ dontCenter?: boolean;
}
@observer
-export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
+export class ContentFittingDocumentView extends React.Component<DocumentViewProps & ContentFittingDocumentViewProps> {
public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
private get layoutDoc() {
return this.props.LayoutTemplate?.() ||
@@ -53,12 +21,18 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
Doc.Layout(this.props.Document);
}
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
- nativeWidth = () => NumCast(this.layoutDoc?._nativeWidth, this.props.NativeWidth?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth()));
- nativeHeight = () => NumCast(this.layoutDoc?._nativeHeight, this.props.NativeHeight?.() || (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight()));
+
+ nativeWidth = () => returnVal(this.props.NativeWidth?.(),
+ NumCast(this.layoutDoc?._nativeWidth || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeWidth"],
+ (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth())))
+ nativeHeight = () => returnVal(this.props.NativeHeight?.(),
+ NumCast(this.layoutDoc?._nativeHeight || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeHeight"],
+ (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())))
@computed get scaling() {
const wscale = this.props.PanelWidth() / this.nativeWidth();
+ const hscale = this.props.PanelHeight() / this.nativeHeight();
if (wscale * this.nativeHeight() > this.props.PanelHeight()) {
- return (this.props.PanelHeight() / this.nativeHeight()) || 1;
+ return hscale || 1;
}
return wscale || 1;
}
@@ -67,38 +41,37 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
private PanelWidth = () => this.panelWidth;
private PanelHeight = () => this.panelHeight;
- @computed get panelWidth() { return this.nativeWidth && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
- @computed get panelHeight() { return this.nativeHeight && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
+ @computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
+ @computed get panelHeight() { return this.nativeHeight() && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
- private getTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
- private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth && this.props.display !== "contents" ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
- private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 && this.props.display !== "contents" ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
+ @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); } // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply.
+ private getTransform = () => this.props.dontCenter ?
+ this.props.ScreenToLocalTransform().scale(this.childXf) :
+ this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(this.childXf)
+ private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
@computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); }
render() {
TraceMobx();
- return (<div className="contentFittingDocumentView" style={{
- width: Math.abs(this.centeringYOffset) > 0.001 ? "auto" : this.props.PanelWidth(),
- height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(),
- display: this.props.display /* just added for grid */
- }}>
+ return (<div className="contentFittingDocumentView">
{!this.props.Document || !this.props.PanelWidth ? (null) : (
<div className="contentFittingDocumentView-previewDoc"
style={{
- transform: `translate(${this.centeringOffset}px, 0px)`,
+ transform: !this.props.dontCenter ? `translate(${this.centeringOffset}px, ${this.centeringYOffset}px)` : undefined,
borderRadius: this.borderRounding,
height: Math.abs(this.centeringYOffset) > 0.001 ? `${100 * this.nativeHeight() / this.nativeWidth() * this.props.PanelWidth() / this.props.PanelHeight()}%` : this.props.PanelHeight(),
width: Math.abs(this.centeringOffset) > 0.001 ? `${100 * (this.props.PanelWidth() - this.centeringOffset * 2) / this.props.PanelWidth()}%` : this.props.PanelWidth()
}}>
- <DocumentView {...this.props}
+ <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
Document={this.props.Document}
DataDoc={this.props.DataDoc}
LayoutTemplate={this.props.LayoutTemplate}
LayoutTemplateString={this.props.LayoutTemplateString}
LibraryPath={this.props.LibraryPath}
- NativeWidth={this.nativeWidth}
- NativeHeight={this.nativeHeight}
+ // NativeWidth={this.nativeWidth}
+ // NativeHeight={this.nativeHeight}
PanelWidth={this.PanelWidth}
PanelHeight={this.PanelHeight}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 88eb48f51..dd254ae93 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -120,6 +120,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
DataDoc={undefined}
LibraryPath={emptyPath}
docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={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
ContainingCollectionDoc={undefined}
fitToBox={true}
@@ -134,8 +136,6 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
pinToPres={this.props.pinToPres}
ScreenToLocalTransform={this.getTransform}
renderDepth={containedDoc.type !== DocumentType.DOCHOLDER && !this.props.renderDepth ? 0 : this.props.renderDepth + 1}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
PanelWidth={this.pwidth}
PanelHeight={this.pheight}
focus={this.props.focus}
@@ -149,6 +149,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
DataDoc={undefined}
LibraryPath={emptyPath}
docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={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
ContainingCollectionDoc={undefined}
fitToBox={true}
@@ -164,8 +166,6 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
pinToPres={this.props.pinToPres}
ScreenToLocalTransform={this.getTransform}
renderDepth={containedDoc.type !== DocumentType.DOCHOLDER && !this.props.renderDepth ? 0 : this.props.renderDepth + 1}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
PanelWidth={this.pwidth}
PanelHeight={this.pheight}
focus={this.props.focus}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 2408b3906..e8c3662aa 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -18,14 +18,14 @@ import { DocHolderBox } from "./DocHolderBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FontIconBox } from "./FontIconBox";
-import { MenuIconBox } from "./MenuIconBox";
import { FieldView, FieldViewProps } from "./FieldView";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import { FormattedTextBox, FormattedTextBoxProps } from "./formattedText/FormattedTextBox";
import { ImageBox } from "./ImageBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
import { PresBox } from "./PresBox";
import { SearchBox } from "../search/SearchBox";
+import { FilterBox } from "./FilterBox";
import { ColorBox } from "./ColorBox";
import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
import { LinkAnchorBox } from "./LinkAnchorBox";
@@ -64,7 +64,19 @@ interface HTMLtagProps {
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
+
+//"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} overflow='hidden' position='absolute' width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'> <ImageBox {...props} fieldKey={'data'}/> <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' color='{this.bannerColor===`green`?`light`:`dark`}blue' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'> {this.title}</HTMLspan></HTMLdiv>"
+//"<HTMLdiv borderRadius='100px' overflow='hidden' position='absolute' width='100%' height='100%'
+// transform='rotate({2*this.x+this.y}deg)'
+// onClick = { this.bannerColor = this.bannerColor === 'red' ? 'green' : 'red' } >
+// <ImageBox {...props} fieldKey={'data'}/>
+// <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px'
+// transform='translate(-40px, 45px) rotate(-45deg)' position='absolute'
+// color='{this.bannerColor===`green`?`light`:`dark`}blue'
+// backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'>
+// {this.title}
+// </HTMLspan>
+// </HTMLdiv>"
@observer
export class HTMLtag extends React.Component<HTMLtagProps> {
click = (e: React.MouseEvent) => {
@@ -93,7 +105,7 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
}
@observer
-export class DocumentContentsView extends React.Component<DocumentViewProps & {
+export class DocumentContentsView extends React.Component<DocumentViewProps & FormattedTextBoxProps & {
isSelected: (outsideReaction: boolean) => boolean,
select: (ctrl: boolean) => void,
layoutKey: string,
@@ -102,22 +114,13 @@ 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"); // bcz: replaced this with below... is it right?
if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
+ if (!this.layoutDoc) return "<p>awaiting layout</p>";
+ if (this.props.layoutKey === "layout_keyValue") return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
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
- if (layout === undefined) {
- return this.props.Document.data ?
- "<FieldView {...props} fieldKey='data' />" :
- KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
- } else if (typeof layout === "string") {
- return layout;
- } else {
- return "<p>Loading layout</p>";
- }
+ if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
+ if (typeof layout === "string") return layout;
+ return "<p>Loading layout</p>";
}
get dataDoc() {
@@ -136,7 +139,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
const list = {
- ...OmitKeys(this.props, ['parentActive'], "", (obj: any) => obj.active = this.props.parentActive).omit,
+ ...OmitKeys(this.props, ['parentActive', 'NativeWidth', 'NativeHeight'], "", (obj: any) => obj.active = this.props.parentActive).omit,
RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
Document: this.layoutDoc,
DataDoc: this.dataDoc,
@@ -170,10 +173,10 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
// add onClick function to props
const makeFuncProp = (func: string) => {
- const splits = layoutFrame.split(`func=`);
+ 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);
+ layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1);
return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" });
}
return undefined;
@@ -190,9 +193,9 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
blacklistedAttrs={[]}
renderInWrapper={false}
components={{
- FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, MenuIconBox, LabelBox, SliderBox, FieldView,
+ FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
- PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox,
+ PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox,
ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
ScreenshotBox, HTMLtag, ComparisonBox
}}
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index cf8645e4c..ddfb3cc34 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -25,7 +25,7 @@ export const Flyout = higflyout.default;
interface DocumentLinksButtonProps {
View: DocumentView;
- Offset?: number[];
+ Offset?: (number | undefined)[];
AlwaysOn?: boolean;
InMenu?: boolean;
StartLink?: boolean;
@@ -36,6 +36,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
private _linkButton = React.createRef<HTMLDivElement>();
@observable public static StartLink: Doc | undefined;
+ @observable public static StartLinkView: DocumentView | undefined;
@observable public static AnnotationId: string | undefined;
@observable public static AnnotationUri: string | undefined;
@@ -79,8 +80,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
//action(() => Doc.BrushDoc(this.props.View.Document));
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
} else {
DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
}
} else if (!this.props.InMenu) {
DocumentLinksButton.EditLink = this.props.View;
@@ -95,8 +98,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
DocumentLinksButton.AnnotationUri = undefined;
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
} else {
DocumentLinksButton.StartLink = this.props.View.props.Document;
+ DocumentLinksButton.StartLinkView = this.props.View;
}
//action(() => Doc.BrushDoc(this.props.View.Document));
@@ -110,6 +115,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
if (doubleTap && !this.props.StartLink) {
if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
} else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) {
const sourceDoc = DocumentLinksButton.StartLink;
@@ -129,9 +135,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = e.screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
- LinkDescriptionPopup.popupX = e.screenX;
- LinkDescriptionPopup.popupY = e.screenY - 100;
- LinkDescriptionPopup.descriptionPopup = true;
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
}
@@ -144,15 +156,20 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
public static finishLinkClick = undoBatch(action((screenX: number, screenY: number, startLink: Doc, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView,) => {
if (startLink === endLink) {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
DocumentLinksButton.AnnotationId = undefined;
DocumentLinksButton.AnnotationUri = undefined;
//!this.props.StartLink
} else if (startLink !== endLink) {
- const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag");
+ const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag", undefined, undefined, true);
// this notifies any of the subviews that a document is made so that they can make finer-grained hyperlinks (). see note above in onLInkButtonMoved
if (endLinkView) {
- startLink._link = endLinkView._link = linkDoc;
- setTimeout(action(() => startLink._link = endLinkView._link = undefined), 0);
+ endLinkView._link = linkDoc;
+ DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView._link = linkDoc);
+ setTimeout(action(() => {
+ DocumentLinksButton.StartLinkView && (DocumentLinksButton.StartLinkView._link = undefined);
+ endLinkView._link = undefined;
+ }), 0);
}
LinkManager.currentLink = linkDoc;
@@ -176,6 +193,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
LinkDescriptionPopup.popupY = screenY - 100;
LinkDescriptionPopup.descriptionPopup = true;
}
+
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
+
setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
}
}
@@ -186,12 +214,13 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@action clearLinks() {
DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
}
@computed
get linkButton() {
TraceMobx();
- const links = this.props.links;
+ const links = DocUtils.FilterDocs(Array.from(new Set<Doc>(this.props.links)), this.props.View.props.docFilters(), []);
const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
const buttonTitle = "Tap to view links";
@@ -213,7 +242,10 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
id={"link-icon"}
src={`/assets/${"link.png"}`} />;
- const linkButton = <div ref={this._linkButton} style={{ minWidth: 20, minHeight: 20, position: "absolute", left: this.props.Offset?.[0] }}>
+ const linkButton = <div className="documentLinksButton-cont" ref={this._linkButton} style={{
+ minWidth: 20, minHeight: 20, position: "absolute",
+ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3]
+ }}>
<div className={"documentLinksButton"} style={{
backgroundColor: this.props.InMenu ? "" : "#add8e6",
color: this.props.InMenu ? "white" : "black",
@@ -233,7 +265,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
<FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length} */}
{this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
- link : links.length}
+ link : Array.from(links).length}
</div>
{this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ?
@@ -242,10 +274,11 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px",
backgroundColor: DocumentLinksButton.StartLink ? "" : "grey",
opacity: DocumentLinksButton.StartLink ? "" : "50%",
- border: DocumentLinksButton.StartLink ? "" : "none"
+ border: DocumentLinksButton.StartLink ? "" : "none",
+ cursor: DocumentLinksButton.StartLink ? "pointer" : "default"
}}
onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
- onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.screenX, e.screenY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
+ onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
}
{
DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
@@ -255,7 +288,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
</div >;
- return (!links.length) && !this.props.AlwaysOn ? (null) :
+ return (!Array.from(links).length) && !this.props.AlwaysOn ? (null) :
this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
<Tooltip title={<><div className="dash-tooltip">{title}</div></>}>
{linkButton}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index e6b8928d4..c31172e22 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -61,6 +61,7 @@
width: 100%;
height: 100%;
display: inline-block;
+ pointer-events: none;
}
.documentView-lock {
@@ -109,7 +110,7 @@
transform-origin: top left;
top: 0;
width: 100%;
- height: 25;
+ height: 14;
background: rgba(0, 0, 0, .4);
text-align: center;
text-overflow: ellipsis;
@@ -133,6 +134,8 @@
bottom: 0;
width: 100%;
transform-origin: bottom left;
+ opacity: 0.1;
+ transition: opacity 0.5s;
}
}
@@ -144,4 +147,9 @@
display:inline-block;
}
}
+ > .documentView-styleWrapper {
+ > .documentView-captionWrapper {
+ opacity: 1;
+ }
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b5d210b4d..5f99f27b1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,27 +1,29 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
+import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnOne, returnTransparent, returnVal, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { InteractionUtils } from '../../util/InteractionUtils';
import { LinkManager } from '../../util/LinkManager';
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from "../../util/SelectionManager";
-import SharingManager from '../../util/SharingManager';
+import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from "../../util/Transform";
import { undoBatch, UndoManager } from "../../util/UndoManager";
@@ -30,6 +32,7 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
+import { InkStrokeProperties } from '../InkStrokeProperties';
import { DocumentContentsView } from "./DocumentContentsView";
import { DocumentLinksButton } from './DocumentLinksButton';
import "./DocumentView.scss";
@@ -45,11 +48,14 @@ export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
docFilters: () => string[];
+ docRangeFilters: () => string[];
+ searchFilterDocs: () => Doc[];
FreezeDimensions?: boolean;
- NativeWidth: () => number;
- NativeHeight: () => number;
+ NativeWidth?: () => number;
+ NativeHeight?: () => number;
Document: Doc;
DataDoc?: Doc;
+ getView?: (view: DocumentView) => any;
LayoutTemplateString?: string;
LayoutTemplate?: () => Opt<Doc>;
LibraryPath: Doc[];
@@ -69,12 +75,13 @@ export interface DocumentViewProps {
removeDocument?: (doc: Doc | Doc[]) => boolean;
moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
- setupDragLines?: () => void;
+ setupDragLines?: (snapToDraggedDoc: boolean) => void;
renderDepth: number;
ContentScaling: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
- pointerEvents?: boolean;
+ pointerEvents?: string;
+ contentsPointerEvents?: string;
focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
parentActive: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
@@ -82,7 +89,7 @@ export interface DocumentViewProps {
addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean;
pinToPres: (document: Doc) => void;
backgroundHalo?: () => boolean;
- backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined;
+ backgroundColor?: (doc: Opt<Doc>, renderDepth: number) => string | undefined;
forcedBackgroundColor?: (doc: Doc) => string | undefined;
opacity?: () => number | undefined;
ChromeHeight?: () => number;
@@ -104,21 +111,26 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private _lastTap: number = 0;
private _doubleTap = false;
private _mainCont = React.createRef<HTMLDivElement>();
- private _dropDisposer?: DragManager.DragDropDisposer;
- private _showKPQuery: boolean = false;
- private _queries: string = "";
private _titleRef = React.createRef<EditableView>();
+ private _dropDisposer?: DragManager.DragDropDisposer;
private _gestureEventDisposer?: GestureUtils.GestureEventDisposer;
private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
public get ContentDiv() { return this._mainCont.current; }
- private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
+ public get LayoutFieldKey() { return this.props.layoutKey || Doc.LayoutFieldKey(this.layoutDoc); }
+ @computed get ShowTitle() {
+ return StrCast(this.layoutDoc._showTitle,
+ !Doc.IsSystem(this.layoutDoc) && this.rootDoc.type === DocumentType.RTF && !this.props.treeViewDoc ?
+ (this.dataDoc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") :
+ undefined);
+ }
@computed get topMost() { return this.props.renderDepth === 0; }
@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 nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
+ @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
@computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
@computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@@ -128,6 +140,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClickFunc = () => this.onClickHandler;
onDoubleClickFunc = () => this.onDoubleClickHandler;
+ constructor(props: any) {
+ super(props);
+ props.getView?.(this);
+ }
+
handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
this.removeMoveListeners();
this.removeEndListeners();
@@ -165,7 +182,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
me.touchEvent.preventDefault();
e.stopPropagation();
if (RadialMenu.Instance.used) {
- this.onContextMenu(me.touches[0]);
+ this.onContextMenu(undefined, me.touches[0].pageX, me.touches[0].pageY);
}
}
@@ -174,10 +191,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15);
- // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 });
+ // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 });
const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
(effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
- // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 });
+ // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
@@ -191,8 +208,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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)));
- if (!this.props.dontRegisterView) {
- DocumentManager.Instance.DocumentViews.push(this);
+ if (!BoolCast(this.rootDoc.dontRegisterView, this.props.dontRegisterView)) {
+ DocumentManager.Instance.AddView(this);
}
}
@@ -218,8 +235,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._holdDisposer?.();
Doc.UnBrushDoc(this.props.Document);
if (!this.props.dontRegisterView) {
- const index = DocumentManager.Instance.DocumentViews.indexOf(this);
- index !== -1 && DocumentManager.Instance.DocumentViews.splice(index, 1);
+ DocumentManager.Instance.RemoveView(this);
}
}
@@ -262,6 +278,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onKeyDown = (e: React.KeyboardEvent) => {
+ if (this.rootDoc._singleLine && ((e.key === "Backspace" && this.dataDoc.text && !(this.dataDoc.text as RichTextField)?.Text) || ["Tab", "Enter"].includes(e.key))) {
+ return;
+ }
if (e.altKey && !(e.nativeEvent as any).StopPropagationForReal) {
(e.nativeEvent as any).StopPropagationForReal = true; // e.stopPropagation() doesn't seem to work...
e.stopPropagation();
@@ -285,13 +304,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
+ _timeout: NodeJS.Timeout | undefined;
+
onClick = action((e: React.MouseEvent | React.PointerEvent) => {
if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
(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.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
+ !this.props.Document._isBackground && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+ if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !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
+ if (this._timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = undefined;
+ }
if (!(e.nativeEvent as any).formattedHandled) {
if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
const func = () => this.onDoubleClickHandler.script.run({
@@ -301,40 +326,53 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
shiftKey: e.shiftKey
}, console.log);
func();
- } else {
- UndoManager.RunInBatch(() => {
- let fullScreenDoc = this.props.Document;
- if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
- fullScreenDoc = Doc.MakeAlias(this.props.Document);
- fullScreenDoc.layoutKey = "layout_fullScreen";
- }
- this.props.addDocTab(fullScreenDoc, "inTab");
- }, "double tap");
- SelectionManager.DeselectAll();
+ } else if (!Doc.IsSystem(this.props.Document)) {
+ if (this.props.Document.type === DocumentType.INK) {
+ InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true);
+ } else {
+ UndoManager.RunInBatch(() => {
+ let fullScreenDoc = this.props.Document;
+ if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
+ fullScreenDoc = Doc.MakeAlias(this.props.Document);
+ fullScreenDoc.layoutKey = "layout_fullScreen";
+ }
+ this.props.addDocTab(fullScreenDoc, "add");
+ }, "double tap");
+ SelectionManager.DeselectAll();
+ }
Doc.UnBrushDoc(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
+ const shiftKey = e.shiftKey;
const func = () => this.onClickHandler.script.run({
this: this.layoutDoc,
self: this.rootDoc,
scriptContext: this.props.scriptContext,
thisContainer: this.props.ContainingCollectionDoc,
documentView: this,
- shiftKey: e.shiftKey
+ shiftKey
}, console.log);
- if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc)) {
- UndoManager.RunInBatch(func, "on click");
- } else func();
+ const clickFunc = () => {
+ if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) &&
+ !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc) &&
+ !this.onClickHandler.script.originalScript.includes("selectMainMenu")) {
+ UndoManager.RunInBatch(func, "on click");
+ } else func();
+ };
+ if (this.onDoubleClickHandler) {
+ this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500);
+ } else clickFunc();
} 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
- this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight");
+ this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right");
} else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
- this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey);
+ this.allLinks.length && DocumentView.followLinkClick(undefined, this.props.Document, this.props, e.shiftKey, e.altKey);
} else {
if ((this.layoutDoc.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 || e.shiftKey);
+ this.select(e.ctrlKey || e.shiftKey);
+ //SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey);
}
preventDefault = false;
}
@@ -346,27 +384,35 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// 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
// depending on the followLinkLocation property of the source (or the link itself as a fallback);
- followLinkClick = async (altKey: boolean, ctrlKey: boolean, shiftKey: boolean) => {
+ public static followLinkClick = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docView: {
+ focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void,
+ addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean,
+ ContainingCollectionDoc?: Doc
+ }, shiftKey: boolean, altKey: boolean) => {
const batch = UndoManager.StartBatch("follow link click");
// open up target if it's not already in view ...
const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => {
const targetFocusAfterDocFocus = () => {
- const where = StrCast(this.Document.followLinkLocation) || followLoc;
+ const where = StrCast(sourceDoc.followLinkLocation) || followLoc;
const hackToCallFinishAfterFocus = () => {
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, 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)
+ const addTab = docView.addDocTab(doc, where);
+ addTab && setTimeout(() => {
+ const targDocView = DocumentManager.Instance.getFirstDocumentView(doc);
+ targDocView?.props.focus(doc, BoolCast(sourceDoc.followLinkZoom, false), undefined, hackToCallFinishAfterFocus);
+ }); // add the target and focus on it.
+ return where !== "inPlace" || addTab; // 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)
};
- if (!this.Document.followLinkZoom) {
+ if (!sourceDoc.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);
+ docView.focus(sourceDoc, BoolCast(sourceDoc.followLinkZoom, true), 1, targetFocusAfterDocFocus);
}
};
- await DocumentManager.Instance.FollowLink(undefined, this.props.Document, createViewFunc, shiftKey, this.props.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+ await DocumentManager.Instance.FollowLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docView.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
}
handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
@@ -564,17 +610,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
deleteClicked = (): void => {
- if (Doc.UserDoc().activeWorkspace === this.props.Document) {
- alert("Can't delete the active workspace");
+ if (CurrentUserUtils.ActiveDashboard === this.props.Document) {
+ alert("Can't delete the active dashboard");
} else {
- const selected = SelectionManager.SelectedDocuments().slice();
- SelectionManager.DeselectAll();
- selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
this.props.removeDocument?.(this.props.Document);
}
}
@undoBatch @action
+ toggleRaiseWhenDragged = () => {
+ this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined;
+ }
+
+ @undoBatch @action
toggleFollowLink = (location: Opt<string>, zoom: boolean, setPushpin: boolean): void => {
this.Document.ignoreClick = false;
this.Document.isLinkButton = !this.Document.isLinkButton;
@@ -586,6 +634,27 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.Document.onClick = this.layoutDoc.onClick = undefined;
}
}
+ @undoBatch @action
+ toggleTargetOnClick = (): void => {
+ this.Document.ignoreClick = false;
+ this.Document.isLinkButton = true;
+ this.Document.isPushpin = true;
+ }
+ @undoBatch @action
+ followLinkOnClick = (location: Opt<string>, zoom: boolean,): void => {
+ this.Document.ignoreClick = false;
+ this.Document.isLinkButton = true;
+ this.Document.isPushpin = false;
+ this.Document.followLinkZoom = zoom;
+ this.Document.followLinkLocation = location;
+ }
+ @undoBatch @action
+ selectOnClick = (): void => {
+ this.Document.ignoreClick = false;
+ this.Document.isLinkButton = false;
+ this.Document.isPushpin = false;
+ this.Document.onClick = this.layoutDoc.onClick = undefined;
+ }
@undoBatch
@@ -601,8 +670,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
drop = async (e: Event, de: DragManager.DropEvent) => {
- if (this.props.Document === Doc.UserDoc().activeWorkspace) {
- alert("linking to document tabs not yet supported. Drop link on document content.");
+ if (this.props.LayoutTemplateString) return;
+ if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
+ if ((e.target as any)?.closest?.("*.lm_content")) {
+ alert("You can't perform this move most likely because you don't have permission to modify the destination.");
+ }
+ else alert("linking to document tabs not yet supported. Drop link on document content.");
return;
}
const makeLink = action((linkDoc: Doc) => {
@@ -617,15 +690,23 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
LinkDescriptionPopup.popupY = de.y;
LinkDescriptionPopup.descriptionPopup = true;
+ const rect = document.body.getBoundingClientRect();
+ if (LinkDescriptionPopup.popupX + 200 > rect.width) {
+ LinkDescriptionPopup.popupX -= 190;
+ TaskCompletionBox.popupX -= 40;
+ }
+ if (LinkDescriptionPopup.popupY + 100 > rect.height) {
+ LinkDescriptionPopup.popupY -= 40;
+ TaskCompletionBox.popupY -= 40;
+ }
+
setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
});
if (de.complete.annoDragData) {
/// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner
e.stopPropagation();
- de.complete.annoDragData.linkedToDoc = true;
-
- const linkDoc = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link");
- linkDoc && makeLink(linkDoc);
+ de.complete.annoDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link");
+ de.complete.annoDragData.linkDocument && makeLink(de.complete.annoDragData.linkDocument);
}
if (de.complete.linkDragData) {
e.stopPropagation();
@@ -666,12 +747,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch
@action
toggleBackground = () => {
- this.Document.isBackground = (this.Document.isBackground ? undefined : true);
- this.Document._overflow = this.Document.isBackground ? "visible" : undefined;
- if (this.Document.isBackground) {
+ this.Document._isBackground = (this.Document._isBackground ? undefined : true);
+ this.Document._overflow = this.Document._isBackground ? "visible" : undefined;
+ if (this.Document._isBackground) {
this.props.bringToFront(this.props.Document, true);
- this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = this.Document[WidthSym]();
- this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeHeight"] = this.Document[HeightSym]();
+ const wid = this.Document[WidthSym](); // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
+ const hgt = this.Document[HeightSym]();
+ this.props.Document[DataSym][this.LayoutFieldKey + "-nativeWidth"] = wid;
+ this.props.Document[DataSym][this.LayoutFieldKey + "-nativeHeight"] = hgt;
}
}
@@ -684,60 +767,82 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
- onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => {
+ onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
+ if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
+ e.preventDefault();
+ e.stopPropagation();
+ !this.isSelected(true) && SelectionManager.SelectDoc(this, false);
+ }
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
- if (!(e instanceof Touch)) {
- if (e.button === 0 && !e.ctrlKey) {
+ if (e) {
+ if (e.button === 0 && !e.ctrlKey || e.isDefaultPrevented()) {
e.preventDefault();
return;
}
- e.persist();
e.stopPropagation();
+ e.persist();
- if (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3 ||
- e?.isDefaultPrevented()) {
- e?.preventDefault();
- return;
+ if (!navigator.userAgent.includes("Mozilla")) {
+ if (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3) {
+ e?.preventDefault();
+ return;
+ }
}
e.preventDefault();
}
const cm = ContextMenu.Instance;
- if (!cm) return;
+ if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
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" }));
+ item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" }));
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
const appearance = cm.findByDescription("UI Controls...");
const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
- templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
- //DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
+ !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+ DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
!appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
- const options = cm.findByDescription("Options...");
- const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
- optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
- !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
-
-
- 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(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
- 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.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
- !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" });
- onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
- onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
- onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
- !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+ if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+ const existingOnClick = cm.findByDescription("OnClick...");
+ const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+
+ const zorders = cm.findByDescription("ZOrder...");
+ const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
+ zorderItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" });
+ zorderItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" });
+ zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: this.toggleRaiseWhenDragged, icon: "expand-arrows-alt" });
+ !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
+
+ if (!this.Document.annotationOn) {
+ const options = cm.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+ !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+ !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+
+ onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+ onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" });
+ 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.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
+ !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
+ onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
+ onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
+ onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+ } else if (DocListCast(this.Document.links).length) {
+ onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
+ onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
+ onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+ }
+ }
const funcs: ContextMenuProps[] = [];
- if (this.layoutDoc.onDragStart) {
+ if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
@@ -746,42 +851,43 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const more = cm.findByDescription("More...");
const moreItems = more && "subitems" in more ? more.subitems : [];
- moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) });
- moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" });
- //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
- //moreItems.push({ description: "Create an Alias", event: () => this.onCopy(), icon: "copy" });
- if (!Doc.UserDoc().noviceMode) {
- 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._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
-
- if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
- moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
- moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
- moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+ if (!Doc.IsSystem(this.rootDoc)) {
+ (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" });
+ //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
+ if (!Doc.UserDoc().noviceMode) {
+ 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._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+
+ if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
+ moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
+ moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
+ moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+ }
+ moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
}
- moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
- Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()) && moreItems.push({ description: "Toggle Always Show Link End", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
}
const collectionAcl = GetEffectiveAcl(this.props.ContainingCollectionDoc?.[DataSym]);
- if (collectionAcl === AclAdmin || collectionAcl === AclEdit) moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+ if (this.props.removeDocument && !this.props.Document._stayInCollection) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
+ moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+ }
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!);
const help = cm.findByDescription("Help...");
const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
- !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
- helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
- helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+ !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+ helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+ !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
runInAction(() => {
- if (!this.topMost && !(e instanceof Touch)) {
- e.stopPropagation(); // DocumentViews should stop propagation of this event
+ if (!this.topMost) {
+ e?.stopPropagation(); // DocumentViews should stop propagation of this event
}
- cm.displayMenu(e.pageX - 15, e.pageY - 15);
- !SelectionManager.IsSelected(this, true) && SelectionManager.SelectDoc(this, false);
+ cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
+ !this.isSelected(true) && SelectionManager.SelectDoc(this, false);
});
}
@@ -795,11 +901,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
- chromeHeight = () => {
- const showTitle = StrCast(this.layoutDoc._showTitle);
- const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
- return showTextTitle ? 25 : 1;
+ @computed get showOverlappingTitle() {
+ const excluded = ["PresBox", /* "FormattedTextBox", */ "FontIconBox"]; // bcz: shifting the title for texst causes problems with collaborative use when some people see titles, and others don't
+ return !excluded.includes(StrCast(this.layoutDoc.layout));
}
+ chromeHeight = () => this.showOverlappingTitle ? 1 : 25;
@computed get finalLayoutKey() {
if (typeof this.props.layoutKey === "string") {
@@ -812,13 +918,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
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());
- @computed.struct get linkOffset() { return [-15, 0]; }
+ @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, undefined]; }
@computed get contents() {
- const pos = this.props.relative ? "relative " : "absolute";
TraceMobx();
- return (<div className="documentView-contentsView" style={{ borderRadius: "inherit", width: "100%", height: "100%" }}>
+ return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionView={this.props.ContainingCollectionView}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
NativeWidth={this.NativeWidth}
@@ -857,7 +964,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
{/* {this.allAnchors} */}
- {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) :
+ {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || (!this.isSelected() && (this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton)) || this.props.dontRegisterView ? (null) :
<DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />}
</div>
);
@@ -880,9 +987,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
anchorPanelWidth = () => this.props.PanelWidth() || 1;
anchorPanelHeight = () => this.props.PanelHeight() || 1;
- @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
- @computed.struct get allLinks() { return DocListCast(this.Document.links); }
- @computed.struct get allAnchors() {
+ @computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
+ @computed get allLinks() { TraceMobx(); return DocListCast(this.Document.links); }
+ @computed get allAnchors() {
TraceMobx();
if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null;
if ((this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots
@@ -904,7 +1011,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
dontRegisterView={false}
forcedBackgroundColor={returnTransparent}
removeDocument={this.hideLinkAnchor}
- pointerEvents={false}
+ pointerEvents={"none"}
LayoutTemplate={undefined}
LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(d, this.props.Document)}`)} />
</div >);
@@ -922,14 +1029,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
</div>;
}
- const showTitle = StrCast(this.layoutDoc._showTitle);
const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
const showCaption = StrCast(this.layoutDoc._showCaption);
- const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
const captionView = (!showCaption ? (null) :
<div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
+ yMargin={10}
+ xMargin={10}
hideOnLeave={true}
+ dontRegisterView={true}
LayoutTemplateString={`<FormattedTextBox {...props} fieldKey={'${showCaption}'}/>`}
ContentScaling={returnOne}
ChromeHeight={this.chromeHeight}
@@ -938,28 +1046,32 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClick={this.onClickFunc}
layoutKey={this.finalLayoutKey} />
</div>);
- const titleView = (!showTitle ? (null) :
+ const titleView = (!this.ShowTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
- position: showTextTitle ? "relative" : "absolute",
+ position: this.showOverlappingTitle ? "absolute" : "relative",
+ background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.UserDoc().userColor) : "rgba(0,0,0,0.4)"),
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
- contents={(this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
- display={"block"} height={72} fontSize={12}
- GetValue={() => (this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)}
+ contents={this.ShowTitle === "title" ? StrCast((this.dataDoc || this.props.Document).title) : this.ShowTitle.split(";").map(field => field + ":" + (this.dataDoc || this.props.Document)[field]?.toString()).join(" ")}
+ display={"block"}
+ fontSize={10}
+ GetValue={() => Field.toString((this.dataDoc || this.props.Document)[this.ShowTitle.split(";")[0]] as any as Field)}
+ SetValue={undoBatch((value: string) => {
+ this.ShowTitle.includes("Date") ? true : (Doc.GetProto(this.dataDoc || this.props.Document)[this.ShowTitle] = value) ? true : true;
+ })}
/>
</div>);
- return !showTitle && !showCaption ?
+ return !this.ShowTitle && !showCaption ?
this.contents :
<div className="documentView-styleWrapper" >
- {this.Document.type !== DocumentType.RTF ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
+ {this.showOverlappingTitle ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
{captionView}
</div>;
}
@computed get ignorePointerEvents() {
- return this.props.pointerEvents === false ||
- (this.Document.isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
+ return this.props.pointerEvents === "none" ||
+ (this.Document._isBackground && !this.isSelected() && !SnappingManager.GetIsDragging()) ||
(this.Document.type === DocumentType.INK && Doc.GetSelectedTool() !== InkTool.None);
}
@undoBatch
@@ -981,16 +1093,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
renderLock() {
- return (this.Document.isBackground !== undefined || this.isSelected(false)) &&
+ return (this.Document._isBackground !== undefined || this.isSelected(false)) &&
((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) &&
this.props.renderDepth > 0 && !this.props.treeViewDoc ?
<div className="documentView-lock" onClick={this.toggleBackground}>
- <FontAwesomeIcon icon={this.Document.isBackground ? "unlock" : "lock"} style={{ color: this.Document.isBackground ? "red" : undefined }} size="lg" />
+ <FontAwesomeIcon icon={this.Document._isBackground ? "unlock" : "lock"} style={{ color: this.Document._isBackground ? "red" : undefined }} size="lg" />
</div>
: (null);
}
render() {
+ TraceMobx();
if (!(this.props.Document instanceof Doc)) return (null);
if (GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate) return (null);
if (this.props.Document.hidden) return (null);
@@ -998,10 +1111,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
- const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+ const fullDegree = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
const borderRounding = this.layoutDoc.borderRounding;
const localScale = fullDegree;
- const highlightColors = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ?
+ const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ?
["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] :
["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"];
const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"];
@@ -1012,8 +1125,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
id={this.props.Document[Id]}
ref={this._mainCont} onKeyDown={this.onKeyDown}
onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={action(() => { Doc.BrushDoc(this.props.Document); })}
- onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
+ onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+ onPointerLeave={action(e => {
let entered = false;
const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
for (let child: any = target; child; child = child?.parentElement) {
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 92ca276cb..0d1e063af 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -17,7 +17,7 @@ export interface RectangleTemplate {
}
@observer
-export default class FaceRectangles extends React.Component<FaceRectanglesProps> {
+export class FaceRectangles extends React.Component<FaceRectanglesProps> {
render() {
const faces = DocListCast(this.props.document.faces);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 9d61ec6d1..d65b43704 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -29,6 +29,8 @@ export interface FieldViewProps {
dropAction: dropActionType;
backgroundHalo?: () => boolean;
docFilters: () => string[];
+ docRangeFilters: () => string[];
+ searchFilterDocs: () => Doc[];
isSelected: (outsideReaction?: boolean) => boolean;
select: (isCtrlPressed: boolean) => void;
rootSelected: (outsideReaction?: boolean) => boolean;
@@ -38,11 +40,12 @@ export interface FieldViewProps {
pinToPres: (document: Doc) => void;
removeDocument?: (document: Doc | Doc[]) => boolean;
moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
- backgroundColor?: (document: Doc, renderDepth: number) => string | undefined;
+ backgroundColor?: (document: Opt<Doc>, renderDepth: number) => string | undefined;
ScreenToLocalTransform: () => Transform;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
active: (outsideReaction?: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
+ LayoutTemplateString?: string;
dontRegisterView?: boolean;
focus: (doc: Doc) => void;
presMultiSelect?: (doc: Doc) => void; //added for selecting multiple documents in a presentation
@@ -51,17 +54,15 @@ export interface FieldViewProps {
PanelHeight: () => number;
PanelPosition?: string;
overflow?: boolean;
- NativeHeight: () => number;
- NativeWidth: () => number;
+ NativeHeight?: () => number;
+ NativeWidth?: () => number;
setVideoBox?: (player: VideoBox) => void;
ContentScaling: () => number;
-
ChromeHeight?: () => number;
childLayoutTemplate?: () => Opt<Doc>;
- highlighting?: string[];
- lines?: string[];
- doc?: Doc;
// properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
+ fontSize?: number;
+ pointerEvents?: string;
height?: number;
width?: number;
background?: string;
@@ -133,7 +134,7 @@ export class FieldView extends React.Component<FieldViewProps> {
// );
}
else if (field instanceof List) {
- return <div> {field.map(f => Field.toString(f)).join(", ")} </div>;
+ return <div> {field.length ? field.map(f => Field.toString(f)).join(", ") : "[]"} </div>;
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
else if (field instanceof WebField) {
diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss
new file mode 100644
index 000000000..d32cc0d2b
--- /dev/null
+++ b/src/client/views/nodes/FilterBox.scss
@@ -0,0 +1,55 @@
+
+
+.filterBox-flyout {
+ width: 400px;
+ display: block;
+ text-align: left;
+ .filterBox-flyout-facet {
+ background-color: lightgray;
+ text-align: left;
+ display: inline-block;
+ position: relative;
+ width: 100%;
+ }
+}
+.filterBox-treeView {
+ display: flex;
+ flex-direction: column;
+ width: 200px;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ border-left: solid 1px;
+ z-index: 1;
+
+ .filterBox-addfacet {
+ display: inline-block;
+ width: 200px;
+ height: 30px;
+ background: darkGray;
+ text-align: left;
+
+ .filterBox-addFacetButton {
+ display: flex;
+ margin: auto;
+ cursor: pointer;
+
+ .filterBox-span {
+ margin-right: 15px;
+ }
+ }
+
+ >div,
+ >div>div {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ .filterBox-tree {
+ display: inline-block;
+ width: 100%;
+ height: calc(100% - 30px);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
new file mode 100644
index 000000000..d3c0bfba3
--- /dev/null
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -0,0 +1,252 @@
+import React = require("react");
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { computed } from "mobx";
+import { observer } from "mobx-react";
+import { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { List } from "../../../fields/List";
+import { RichTextField } from "../../../fields/RichTextField";
+import { listSpec, makeInterface } from "../../../fields/Schema";
+import { ComputedField, ScriptField } from "../../../fields/ScriptField";
+import { Cast } from "../../../fields/Types";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionTreeView } from "../collections/CollectionTreeView";
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { SearchBox } from "../search/SearchBox";
+import { FieldView, FieldViewProps } from './FieldView';
+import './FilterBox.scss';
+import { Scripting } from "../../util/Scripting";
+import { values } from "lodash";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+type FilterBoxDocument = makeInterface<[typeof documentSchema]>;
+const FilterBoxDocument = makeInterface(documentSchema);
+
+@observer
+export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
+
+ @computed get allDocs() {
+ const allDocs = new Set<Doc>();
+ if (CollectionDockingView.Instance) {
+ const activeTabs = DocListCast(CollectionDockingView.Instance.props.Document.data);
+ SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allDocs.add(doc));
+ setTimeout(() => CollectionDockingView.Instance.props.Document.allDocuments = new List<Doc>(Array.from(allDocs)));
+ }
+ return allDocs;
+ }
+
+ @computed get _allFacets() {
+ const noviceReqFields = ["author", "tags", "text", "type"];
+ const noviceLayoutFields: string[] = [];//["_curPage"];
+ const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
+
+ const keys = new Set<string>(noviceFields);
+ this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
+ return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort();
+ }
+
+ gatherFieldValues(dashboard: Doc, facetKey: string) {
+ const childDocs = DocListCast((dashboard.data as any)[0].data);
+ const valueSet = new Set<string>();
+ let rtFields = 0;
+ childDocs.forEach((d) => {
+ const facetVal = d[facetKey];
+ if (facetVal instanceof RichTextField) rtFields++;
+ valueSet.add(Field.toString(facetVal as Field));
+ const fieldKey = Doc.LayoutFieldKey(d);
+ const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+ const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+ if (data !== undefined) {
+ let subDocs = DocListCast(data);
+ if (subDocs.length > 0) {
+ let newarray: Doc[] = [];
+ while (subDocs.length > 0) {
+ newarray = [];
+ subDocs.forEach((t) => {
+ const facetVal = t[facetKey];
+ if (facetVal instanceof RichTextField) rtFields++;
+ valueSet.add(Field.toString(facetVal as Field));
+ const fieldKey = Doc.LayoutFieldKey(t);
+ const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
+ DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+ });
+ subDocs = newarray;
+ }
+ }
+ }
+ });
+ return { strings: Array.from(valueSet.keys()), rtFields };
+ }
+ /**
+ * Responds to clicking the check box in the flyout menu
+ */
+ facetClick = (facetHeader: string) => {
+ const targetDoc = CollectionDockingView.Instance.props.Document;
+ const found = DocListCast(this.dataDoc[this.props.fieldKey]).findIndex(doc => doc.title === facetHeader);
+ if (found !== -1) {
+ (this.dataDoc[this.props.fieldKey] as List<Doc>).splice(found, 1);
+ const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
+ if (docFilter) {
+ let index: number;
+ while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) {
+ docFilter.splice(index, 3);
+ }
+ }
+ const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+ if (docRangeFilters) {
+ let index: number;
+ while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) {
+ docRangeFilters.splice(index, 3);
+ }
+ }
+ } else {
+ const allCollectionDocs = DocListCast((targetDoc.data as any)[0].data);
+ const facetValues = this.gatherFieldValues(targetDoc, facetHeader);
+
+ let nonNumbers = 0;
+ let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+ facetValues.strings.map(val => {
+ const num = Number(val);
+ if (Number.isNaN(num)) {
+ nonNumbers++;
+ } else {
+ minVal = Math.min(num, minVal);
+ maxVal = Math.max(num, maxVal);
+ }
+ });
+ let newFacet: Opt<Doc>;
+ if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) {
+ newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true });
+ Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
+ newFacet._textBoxPadding = 4;
+ const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
+ newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
+ } else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) {
+ newFacet = Docs.Create.SliderDocument({ title: facetHeader, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", treeViewOpen: true });
+ const newFacetField = Doc.LayoutFieldKey(newFacet);
+ const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
+ Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
+ const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05);
+ const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05);
+ newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
+ newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
+ Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
+ Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
+ const scriptText = `setDocFilterRange(this?.target, "${facetHeader}", range)`;
+ newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
+ } else {
+ newFacet = new Doc();
+ newFacet.sytem = true;
+ newFacet.title = facetHeader;
+ newFacet.treeViewOpen = true;
+ newFacet.type = DocumentType.COL;
+ const capturedVariables = { layoutDoc: targetDoc, _stayInCollection: true, _hideContextMenu: true, dataDoc: (targetDoc.data as any)[0][DataSym] };
+ newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, "${facetHeader}")`, {}, capturedVariables);
+ }
+ newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
+ }
+ }
+ filterBackground = () => "rgba(105, 105, 105, 0.432)";
+ get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves)
+ @computed get scriptField() {
+ const scriptText = "setDocFilter(this?.target, heading, this.title, checked)";
+ const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+ return script ? () => script : undefined;
+ }
+
+ render() {
+ const facetCollection = this.props.Document.proto as Doc;
+ const flyout = <div className="filterBox-flyout" style={{ width: `100%`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
+ {this._allFacets.map(facet => <label className="filterBox-flyout-facet" key={`${facet}`} onClick={e => this.facetClick(facet)}>
+ <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey]).some(d => d.title === facet)} />
+ <span className="checkmark" />
+ {facet}
+ </label>)}
+ </div>;
+
+ return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}>
+ <div className="filterBox-addFacet" style={{ width: "100%" }} onPointerDown={e => e.stopPropagation()}>
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}>
+ <div className="filterBox-addFacetButton">
+ <FontAwesomeIcon icon={"edit"} size={"lg"} />
+ <span className="filterBox-span">Choose Facets</span>
+ </div>
+ </Flyout>
+ </div>
+ <div className="filterBox-tree" key="tree">
+ <CollectionTreeView
+ PanelPosition={""}
+ Document={facetCollection}
+ DataDoc={Doc.GetProto(facetCollection)}
+ fieldKey={`${this.props.fieldKey}`}
+ CollectionView={undefined}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
+ LibraryPath={emptyPath}
+ rootSelected={this.props.rootSelected}
+ renderDepth={1}
+ dropAction={this.props.dropAction}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ isSelected={returnFalse}
+ select={returnFalse}
+ bringToFront={emptyFunction}
+ active={this.props.active}
+ whenActiveChanged={returnFalse}
+ treeViewHideTitle={true}
+ ContentScaling={returnOne}
+ focus={returnFalse}
+ treeViewHideHeaderFields={true}
+ onCheckedClick={this.scriptField}
+ ignoreFields={this.ignoreFields}
+ annotationsKey={""}
+ dontRegisterView={true}
+ backgroundColor={this.filterBackground}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ addDocument={returnFalse} />
+ </div>
+ </div>;
+ }
+}
+
+Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
+ const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
+ for (let i = 0; i < docFilters.length; i += 3) {
+ const [header, value, state] = docFilters.slice(i, i + 3);
+ if (header === facetHeader && value === facetValue) {
+ return state;
+ }
+ }
+ return undefined;
+});
+Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) {
+ const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments);
+ const set = new Set<string>();
+ if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+ else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
+ const facetValues = Array.from(set).filter(v => v);
+
+ let nonNumbers = 0;
+ facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
+ const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+ const doc = new Doc();
+ doc.system = true;
+ doc.title = facetValue.toString();
+ doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue });
+ return doc;
+ });
+ return new List<Doc>(facetValueDocSet);
+}); \ No newline at end of file
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 75bc90d7a..33ac85a0e 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -34,12 +34,14 @@
}
}
+.menuButton-circle,
.menuButton-round {
border-radius: 100%;
background-color: black;
+ padding: 0;
.fontIconBox-label {
- margin-left: -10px; // button padding is 10px;
+ //margin-left: -10px; // button padding is 10px;
bottom: 0;
position: absolute;
}
@@ -52,7 +54,6 @@
.menuButton-square {
padding-top: 3px;
padding-bottom: 3px;
- padding-left: 5px;
.fontIconBox-label {
border-radius: 0px;
@@ -62,9 +63,11 @@
}
.menuButton,
+.menuButton-circle,
.menuButton-round,
.menuButton-square {
- width: 100%;
+ margin-left: -5%;
+ width: 110%;
height: 100%;
pointer-events: all;
touch-action: none;
@@ -72,11 +75,7 @@
.menuButton-wrap {
touch-action: none;
border-radius: 8px;
-
- // &:hover {
- // background: rgb(61, 61, 61);
- // cursor: pointer;
- // }
+ width: 100%;
}
.menuButton-icon-square {
@@ -89,4 +88,13 @@
width: 95% !important;
height: 95%;
}
+}
+.menuButton-round {
+ width: 100%;
+ svg {
+ width: 50% !important;
+ height: 50%;
+ position: relative;
+ bottom: 2px;
+ }
} \ No newline at end of file
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index fd71876b0..6e96513c7 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -39,20 +39,18 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
showTemplate = (): void => {
const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
- dragFactory && this.props.addDocTab(dragFactory, "onRight");
- }
- dragAsTemplate = (): void => {
- this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
- }
- useAsPrototype = (): void => {
- this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)');
+ dragFactory && this.props.addDocTab(dragFactory, "add:right");
}
+ dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); };
+ useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); };
specificContextMenu = (): void => {
- const cm = ContextMenu.Instance;
- cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
- cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
- cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" });
+ if (!Doc.UserDoc().noviceMode) {
+ const cm = ContextMenu.Instance;
+ cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
+ cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
+ cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" });
+ }
}
componentWillUnmount() {
@@ -63,16 +61,19 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
const color = StrCast(this.layoutDoc.color, this._foregroundColor);
const backgroundColor = StrCast(this.layoutDoc._backgroundColor, StrCast(this.rootDoc.backgroundColor, this.props.backgroundColor?.(this.rootDoc, this.props.renderDepth)));
- const shape = StrCast(this.layoutDoc.iconShape, "round");
-
+ const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle");
+ const icon = StrCast(this.dataDoc.icon, "user") as any;
+ const presSize = shape === 'round' ? 25 : 30;
+ const presTrailsIcon = <img src={`/assets/${"presTrails.png"}`}
+ style={{ width: presSize, height: presSize, filter: `invert(${color === "white" ? "100%" : "0%"})`, marginBottom: "5px" }} />;
const button = <button className={`menuButton-${shape}`} ref={this._ref} onContextMenu={this.specificContextMenu}
style={{
boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined,
backgroundColor: this.layoutDoc.iconShape === "square" ? backgroundColor : "",
}}>
<div className="menuButton-wrap">
- {<FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={StrCast(this.dataDoc.icon, "user") as any} color={color}
- size={this.layoutDoc.iconShape === "square" ? "sm" : "lg"} />}
+ {icon === 'pres-trail' ? presTrailsIcon : <FontAwesomeIcon className={`menuButton-icon-${shape}`} icon={icon} color={color}
+ size={this.layoutDoc.iconShape === "square" ? "sm" : "sm"} />}
{!label ? (null) : <div className="fontIconBox-label" style={{ color, backgroundColor }}> {label} </div>}
{this.props.Document.watchedDocuments ? <FontIconBadge collection={Cast(this.props.Document.watchedDocuments, Doc, null)} /> : (null)}
</div>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 216c5f39e..164456cfb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,8 +1,5 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEye } from '@fortawesome/free-regular-svg-icons';
-import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, observable, runInAction, reaction, IReactionDisposer } from 'mobx';
import { observer } from "mobx-react";
import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
import { documentSchema } from '../../../fields/documentSchemas';
@@ -14,7 +11,8 @@ import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { AudioField, ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils';
+import { emptyFunction, returnOne, returnZero, Utils, OmitKeys } from '../../../Utils';
+import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs } from '../../documents/Documents';
import { Networking } from '../../Network';
@@ -24,22 +22,16 @@ import { ContextMenu } from "../../views/ContextMenu";
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import FaceRectangles from './FaceRectangles';
+import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
-import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-const requestImageSize = require('../../util/request-image-size');
const path = require('path');
const { Howl } = require('howler');
-library.add(faImage, faEye as any, faPaintBrush, faBrain);
-library.add(faFileAudio, faAsterisk);
-
-
export const pageSchema = createSchema({
- curPage: "number",
+ _curPage: "number",
fitWidth: "boolean",
googlePhotosUrl: "string",
googlePhotosTags: "string"
@@ -70,15 +62,33 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
private _dropDisposer?: DragManager.DragDropDisposer;
+ private _pathDisposer?: IReactionDisposer;
@observable private _audioState = 0;
@observable static _showControls: boolean;
@observable uploadIcon = uploadIcons.idle;
protected createDropTarget = (ele: HTMLDivElement) => {
- this._dropDisposer && this._dropDisposer();
+ this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
}
+ componentDidMount() {
+ this._pathDisposer = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
+ ({ nativeSize, width }) => {
+ if (this.props.NativeWidth?.() !== 0 || !this.layoutDoc._height) {
+ this.layoutDoc._nativeWidth = nativeSize.nativeWidth;
+ this.layoutDoc._nativeHeight = nativeSize.nativeHeight;
+ this.layoutDoc._nativeOrientation = nativeSize.nativeOrientation;
+ this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
+ }
+ },
+ { fireImmediately: true });
+ }
+
+ componentWillUnmount() {
+ this._pathDisposer?.();
+ }
+
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
@@ -111,23 +121,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}).then(function (stream) {
gumStream = stream;
recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async function (e: any) {
- const formData = new FormData();
- formData.append("file", e.data);
- const res = await fetch(Utils.prepend("/uploadFormData"), {
- method: 'POST',
- body: formData
- });
- const files = await res.json();
- const url = Utils.prepend(files[0].path);
- // upload to server with known URL
- const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = "layout";
- const audioAnnos = Cast(this.dataDoc[this.fieldKey + "-audioAnnotations"], listSpec(Doc));
- if (audioAnnos === undefined) {
- this.dataDoc[this.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
- } else {
- audioAnnos.push(audioDoc);
+ recorder.ondataavailable = async (e: any) => {
+ const [{ result }] = await Networking.UploadFilesToServer(e.data);
+ if (!(result instanceof Error)) {
+ const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
+ audioDoc.treeViewExpandedView = "layout";
+ const audioAnnos = Cast(self.dataDoc[self.fieldKey + "-audioAnnotations"], listSpec(Doc));
+ if (audioAnnos === undefined) {
+ self.dataDoc[self.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
+ } else {
+ audioAnnos.push(audioDoc);
+ }
}
};
runInAction(() => self._audioState = 2);
@@ -158,23 +162,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
if (field) {
const funcs: ContextMenuProps[] = [];
funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
- funcs.push({ description: "Make Background", event: () => { this.layoutDoc.isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
+ funcs.push({ description: "Make Background", event: () => { this.layoutDoc._isBackground = true; this.props.bringToFront?.(this.rootDoc); }, icon: "expand-arrows-alt" });
if (!Doc.UserDoc().noviceMode) {
funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
- // funcs.push({
- // description: "Reset Native Dimensions", event: action(async () => {
- // const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
- // const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
- // if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) {
- // this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH;
- // this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight();
- // } else {
- // this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth();
- // this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW;
- // }
- // }), icon: "expand-arrows-alt"
- // });
const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : [];
@@ -219,15 +210,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
choosePath(url: URL) {
const lower = url.href.toLowerCase();
- if (url.protocol === "data") {
- return url.href;
- } else if (url.href.indexOf(window.location.origin) === -1) {
- return Utils.CorsProxy(url.href);
- } else if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) {
- return url.href;//Why is this here
- }
+ if (url.protocol === "data") return url.href;
+ if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
+ if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href; //Why is this here
+
const ext = path.extname(url.href);
- this._curSuffix = this.props.renderDepth < 1 ? "_o" : this.layoutDoc[WidthSym]() < 100 ? "_s" : "_m";
+ this._curSuffix = this.props.renderDepth < 1 ? "_o" : this.props.PanelWidth() < 100 ? "_s" : "_m";
return url.href.replace(ext, this._curSuffix + ext);
}
@@ -253,45 +241,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}
_curSuffix = "_m";
- resize = (imgPath: string) => {
- const basePath = imgPath.replace(/_[oms]./, "");
- const cachedNativeSize = {
- width: basePath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]) : 0,
- height: basePath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]) : 0,
- };
- 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(action((inquiredSize: any) => {
- const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180;
- const rotatedNativeSize = { width: inquiredSize.width, height: inquiredSize.height };
- if (inquiredSize.orientation === 6 || rotation === 90 || rotation === 270) {
- rotatedNativeSize.width = inquiredSize.height;
- rotatedNativeSize.height = inquiredSize.width;
- }
- const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width;
- 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"] = basePath;
- }
- })).catch(console.log);
- } else if (Math.abs(1 - docAspect / cachedAspect) > 0.1) {
- this.layoutDoc._width = this.layoutDoc[WidthSym]() || cachedNativeSize.width;
- this.layoutDoc._height = this.layoutDoc[WidthSym]() * cachedAspect;
- }
- } 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);
- }
- }
-
@action
onPointerEnter = () => {
const self = this;
@@ -371,10 +320,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
@computed get nativeSize() {
TraceMobx();
- const pw = this.props.PanelWidth?.() || 50;
- const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], pw);
+ const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], 500);
const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], 1);
- return { nativeWidth, nativeHeight };
+ const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + "-nativeOrientation"], 1);
+ return { nativeWidth, nativeHeight, nativeOrientation };
}
// this._curSuffix = "";
@@ -395,11 +344,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
const srcpath = this.paths[0];
const fadepath = this.paths[Math.min(1, this.paths.length - 1)];
- const { nativeWidth, nativeHeight } = this.nativeSize;
+ const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
- const aspect = (rotation % 180) ? nativeHeight / nativeWidth : 1;
- const shift = (rotation % 180) ? (nativeHeight - nativeWidth) * (1 - 1 / aspect) : 0;
- this.resize(srcpath);
+ const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
let transformOrigin = "center center";
let transform = `translate(0%, 0%) rotate(${rotation}deg) scale(${aspect})`;
if (rotation === 90 || rotation === -270) {
@@ -437,7 +384,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
>
<FontAwesomeIcon className="imageBox-audioFont"
style={{ color: [DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }}
- icon={!DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "microphone" : faFileAudio} size="sm" />
+ icon={!DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
</div>}
{this.considerDownloadIcon}
{this.considerGooglePhotosLink()}
@@ -465,15 +412,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
transform: this.props.PanelWidth() ? undefined : `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,
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
}} >
- <CollectionFreeFormView {...this.props}
+ <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
forceScaling={true}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
annotationsKey={this.annotationKey}
isAnnotationOverlay={true}
focus={this.props.focus}
@@ -489,6 +434,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
ScreenToLocalTransform={this.screenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index b732f5f83..c5ff42a1a 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -230,7 +230,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
openItems.push({
description: "Default Perspective", event: () => {
this.props.addDocTab(this.props.Document, "close");
- this.props.addDocTab(this.fieldDocToLayout, "onRight");
+ this.props.addDocTab(this.fieldDocToLayout, "add:right");
}, icon: "image"
});
!open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" });
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 4568a6b16..2e2319447 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import { Doc, Field, Opt } from '../../../fields/Doc';
-import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
@@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
if (value instanceof Doc) {
e.stopPropagation();
e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
+ ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
}
}
@@ -56,7 +56,9 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
Document: this.props.doc,
DataDoc: this.props.doc,
LibraryPath: [],
- docFilters:returnEmptyFilter,
+ docFilters: returnEmptyFilter,
+ docRangeFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
ContainingCollectionView: undefined,
ContainingCollectionDoc: undefined,
fieldKey: this.props.keyName,
@@ -70,8 +72,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
whenActiveChanged: emptyFunction,
ScreenToLocalTransform: Transform.Identity,
focus: emptyFunction,
- NativeHeight: returnZero,
- NativeWidth: returnZero,
PanelWidth: this.props.PanelWidth,
PanelHeight: this.props.PanelHeight,
addDocTab: returnFalse,
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index b605df262..109a02df4 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -8,6 +8,7 @@
}
.labelBox-mainButton {
+ max-width: 100%;
width: fit-content;
height: max-content;
border-radius: inherit;
diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss
index 42ef2958e..62ee9513c 100644
--- a/src/client/views/nodes/LinkAnchorBox.scss
+++ b/src/client/views/nodes/LinkAnchorBox.scss
@@ -22,6 +22,11 @@
padding-left: 2px;
padding-top: 1px;
}
+ .linkAnchorBox-button {
+ pointer-events: all;
+ position: relative;
+ display: inline-block;
+ }
}
.linkAnchorBox-cont-small {
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 50b2af0d7..ec8c43ab4 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -73,7 +73,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
this._timeout = setTimeout(action(() => {
- DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, e.altKey ? "inTab" : "onRight")), false);
+ DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, (doc, where) => this.props.addDocTab(doc, where), false);
this._editing = false;
}), 300 - (Date.now() - this._lastTap));
}
@@ -87,14 +87,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
}
openLinkDocOnRight = (e: React.MouseEvent) => {
- this.props.addDocTab(this.rootDoc, "onRight");
+ this.props.addDocTab(this.rootDoc, "add:right");
}
openLinkTargetOnRight = (e: React.MouseEvent) => {
const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
alias.isLinkButton = undefined;
- alias.isBackground = undefined;
+ alias._isBackground = undefined;
alias.layoutKey = "layout";
- this.props.addDocTab(alias, "onRight");
+ this.props.addDocTab(alias, "add:right");
}
@action
openLinkEditor = action((e: React.MouseEvent) => {
@@ -148,7 +148,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
}} >
{!this._editing && !this._forceOpen ? (null) :
<Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => this._isOpen = true} onClose={action(() => this._isOpen = this._forceOpen = this._editing = false)}>
- <span className="parentDocumentSelector-button" >
+ <span className="linkAnchorBox-button" >
<FontAwesomeIcon icon={"eye"} size={"lg"} />
</span>
</Flyout>}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 532e7dc15..64ae1051b 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -22,10 +22,10 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(
<CollectionTreeView {...this.props}
ChromeHeight={returnZero}
overrideDocuments={[this.dataDoc]}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
annotationsKey={""}
+ dontRegisterView={true}
+ renderDepth={this.props.renderDepth + 1}
CollectionView={undefined}
addDocument={returnFalse}
removeDocument={returnFalse}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c4481b213..d14157c09 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import wiki from "wikijs";
import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Cast, FieldValue, NumCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { Transform } from "../../util/Transform";
@@ -16,12 +16,13 @@ interface Props {
linkDoc?: Doc;
linkSrc?: Doc;
href?: string;
- backgroundColor: (doc: Doc, renderDepth: number) => string;
+ backgroundColor: (doc: Opt<Doc>, renderDepth: number) => string;
addDocTab: (document: Doc, where: string) => boolean;
location: number[];
}
@observer
export class LinkDocPreview extends React.Component<Props> {
+ static TargetDoc: Doc | undefined;
@observable public static LinkInfo: Opt<{ linkDoc?: Doc; addDocTab: (document: Doc, where: string) => boolean, linkSrc: Doc; href?: string; Location: number[] }>;
@observable _targetDoc: Opt<Doc>;
@observable _toolTipText = "";
@@ -40,14 +41,16 @@ export class LinkDocPreview extends React.Component<Props> {
async followDefault() {
DocumentLinksButton.EditLink = undefined;
LinkDocPreview.LinkInfo = undefined;
- this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "onRight"), false) : null;
+ this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, (doc, where) => this.props.addDocTab(doc, where), false) : null;
}
+ componentWillUnmount() { LinkDocPreview.TargetDoc = undefined; }
componentDidUpdate() { this.updatePreview(); }
componentDidMount() { this.updatePreview(); }
async updatePreview() {
const linkDoc = this.props.linkDoc;
const linkSrc = this.props.linkSrc;
+ LinkDocPreview.TargetDoc = undefined;
if (this.props.href) {
if (this.props.href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(this.props.href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
@@ -59,7 +62,7 @@ export class LinkDocPreview extends React.Component<Props> {
const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
runInAction(() => {
this._toolTipText = "";
- this._targetDoc = target;
+ LinkDocPreview.TargetDoc = this._targetDoc = target;
if (anchor !== this._targetDoc && anchor && this._targetDoc) {
this._targetDoc._scrollY = NumCast(anchor?.y);
}
@@ -69,13 +72,13 @@ export class LinkDocPreview extends React.Component<Props> {
pointerDown = (e: React.PointerEvent) => {
if (this.props.linkDoc && this.props.linkSrc) {
DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
} else if (this.props.href) {
- this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight");
+ this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { _fitWidth: true, title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
}
}
- width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
- height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
+ width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)) - 16;
+ height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)) - 16;
@computed get targetDocView() {
return !this._targetDoc ?
<div style={{
@@ -101,17 +104,17 @@ export class LinkDocPreview extends React.Component<Props> {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
- renderDepth={0}
- PanelWidth={() => this.width() - 16} //Math.min(350, NumCast(target._width, 350))}
- PanelHeight={() => this.height() - 16} //Math.min(250, NumCast(target._height, 250))}
+ renderDepth={-1}
+ PanelWidth={this.width} //Math.min(350, NumCast(target._width, 350))}
+ PanelHeight={this.height} //Math.min(250, NumCast(target._height, 250))}
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
ContentScaling={returnOne}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
backgroundColor={this.props.backgroundColor} />;
}
@@ -119,7 +122,7 @@ export class LinkDocPreview extends React.Component<Props> {
return <div className="linkDocPreview"
style={{
position: "absolute", left: this.props.location[0],
- top: this.props.location[1], width: this.width(), height: this.height(),
+ top: this.props.location[1], width: this.width() + 16, height: this.height() + 16,
zIndex: 1000,
border: "8px solid white", borderRadius: "7px",
boxShadow: "3px 3px 1.5px grey",
diff --git a/src/client/views/nodes/MenuIconBox.scss b/src/client/views/nodes/MenuIconBox.scss
deleted file mode 100644
index 1b72f5a8f..000000000
--- a/src/client/views/nodes/MenuIconBox.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-.menuButton {
- //padding: 7px;
- padding-left: 7px;
- width: 100%;
- width: 60px;
- height: 70px;
-
- .menuButton-wrap {
- width: 45px;
- /* padding: 5px; */
- touch-action: none;
- background: black;
- transform-origin: top left;
- /* margin-bottom: 5px; */
- margin-top: 5px;
- margin-right: 25px;
- border-radius: 8px;
-
- &:hover {
- background: rgb(61, 61, 61);
- cursor: pointer;
- }
- }
-
- .menuButton-label {
- color: white;
- margin-right: 4px;
- border-radius: 8px;
- width: 42px;
- position: relative;
- text-align: center;
- font-size: 8px;
- margin-top: 1px;
- letter-spacing: normal;
- padding: 3px;
- background-color: inherit;
- }
-
- .menuButton-icon {
- width: auto;
- height: 35px;
- padding: 5px;
- }
-
- svg {
- width: 95% !important;
- height: 95%;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/MenuIconBox.tsx b/src/client/views/nodes/MenuIconBox.tsx
deleted file mode 100644
index 5ed8a9b78..000000000
--- a/src/client/views/nodes/MenuIconBox.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { createSchema, makeInterface } from '../../../fields/Schema';
-import { StrCast } from '../../../fields/Types';
-import { DocComponent } from '../DocComponent';
-import { FieldView, FieldViewProps } from './FieldView';
-import './MenuIconBox.scss';
-const MenuIconSchema = createSchema({
- icon: "string"
-});
-
-type MenuIconDocument = makeInterface<[typeof MenuIconSchema]>;
-const MenuIconDocument = makeInterface(MenuIconSchema);
-@observer
-export class MenuIconBox extends DocComponent<FieldViewProps, MenuIconDocument>(MenuIconDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MenuIconBox, fieldKey); }
- _ref: React.RefObject<HTMLButtonElement> = React.createRef();
-
- render() {
-
- const color = this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) === "lightgrey" ? "black" : "white";
- const menuBTN = <div className="menuButton" style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }}>
- <div className="menuButton-wrap"
- style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document, this.props.renderDepth) }} >
- <FontAwesomeIcon className="menuButton-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={color} size="lg" />
- <div className="menuButton-label" style={{ color: color }}> {this.dataDoc.title} </div>
- </div>
- </div>;
-
- return menuBTN;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index f2ab37984..e2bdd98cc 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -15,6 +15,8 @@
height: 100%;
z-index: 1;
pointer-events: none;
+ top: 0;
+ left: 0;
.pdfBox-pageNums {
display: flex;
@@ -175,7 +177,7 @@
}
.pdfBox-title-outer {
- width: 150%;
+ width: 100%;
height: 100%;
position: relative;
display: grid;
@@ -187,14 +189,13 @@
color: lightgray;
margin-top: auto;
margin-bottom: auto;
- transform-origin: 42% 15%;
- width: 100%;
- transform: rotate(55deg);
- font-size: 200;
+ transform-origin: 40% 40%;
+ width: 125%;
+ transform: rotate(40deg) scale(0.8);
+ font-size: 80;
padding: 5%;
overflow: hidden;
display: inline-block;
- white-space: pre;
text-overflow: ellipsis;
text-align: center;
}
@@ -202,6 +203,9 @@
}
.pdfBox {
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
.pdfViewerDash-text {
.textLayer {
span {
@@ -211,8 +215,17 @@
}
}
+.pdfBox-background {
+ width: 100%;
+ height: 100%;
+ background: lightGray;
+}
+
.pdfBox-interactive {
+ width: 100%;
+ height: 100%;
pointer-events: all;
+ z-index: -1;
.pdfViewerDash-text {
.textLayer {
span {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 255a1b2d0..74b431bea 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -21,6 +21,8 @@ import { KeyCodes } from '../../util/KeyCodes';
import "./PDFBox.scss";
import React = require("react");
import { documentSchema } from '../../../fields/documentSchemas';
+import { CollectionViewType } from '../collections/CollectionView';
+import { TraceMobx } from '../../../fields/util';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@@ -99,20 +101,28 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
public search = (string: string, fwd: boolean) => { this._pdfViewer?.search(string, fwd); };
public prevAnnotation = () => { this._pdfViewer?.prevAnnotation(); };
public nextAnnotation = () => { this._pdfViewer?.nextAnnotation(); };
- public backPage = () => { this._pdfViewer!.gotoPage((this.Document.curPage || 1) - 1); };
- public forwardPage = () => { this._pdfViewer!.gotoPage((this.Document.curPage || 1) + 1); };
- public gotoPage = (p: number) => { this._pdfViewer!.gotoPage(p); };
+ public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; };
+ public forwardPage = () => { this.Document._curPage = (this.Document._curPage || 1) + 1; return true; };
+ public gotoPage = (p: number) => { this.Document._curPage = p; };
@undoBatch
onKeyDown = action((e: KeyboardEvent) => {
+ let processed = false;
if (e.key === "f" && e.ctrlKey) {
this._searching = true;
setTimeout(() => this._searchRef.current && this._searchRef.current.focus(), 100);
+ processed = true;
+ }
+ if (e.key === "PageDown") processed = this.forwardPage();
+ if (e.key === "PageUp") processed = this.backPage();
+ if (e.target instanceof HTMLInputElement || this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform) {
+ if (e.key === "ArrowDown" || e.key === "ArrowRight") processed = this.forwardPage();
+ if (e.key === "ArrowUp" || e.key === "ArrowLeft") processed = this.backPage();
+ }
+ if (processed) {
e.stopImmediatePropagation();
e.preventDefault();
}
- if (e.key === "PageDown" || e.key === "ArrowDown" || e.key === "ArrowRight") this.forwardPage();
- if (e.key === "PageUp" || e.key === "ArrowUp" || e.key === "ArrowLeft") this.backPage();
});
@undoBatch
@@ -150,7 +160,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
</button>
</>;
const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
- const curPage = this.Document.curPage || 1;
+ const curPage = this.Document._curPage || 1;
return !this.active() ? (null) :
(<div className="pdfBox-ui" onKeyDown={e => e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true}
onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none" }}>
@@ -177,12 +187,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
<div className="pdfBox-pageNums">
<input value={curPage}
- onChange={e => this.gotoPage(Number(e.currentTarget.value))}
+ onChange={e => this.Document._curPage = Number(e.currentTarget.value)}
style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: "all" }}
onClick={action(() => this._pageControls = !this._pageControls)} />
{this._pageControls ? pageBtns : (null)}
</div>
- <div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}>
+ {/* <div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}>
<button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" >
<div className="pdfBox-settingsButton-arrow" style={{ transform: `scaleX(${this._flyout ? -1 : 1})` }} />
<div className="pdfBox-settingsButton-iconCont">
@@ -211,7 +221,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
</button>
</div>
</div>
- </div>
+ </div> */}
</div>);
}
@@ -227,12 +237,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
@computed get contentScaling() { return this.props.ContentScaling(); }
@computed get renderTitleBox() {
const classname = "pdfBox" + (this.active() ? "-interactive" : "");
- return <div className={classname} style={{
- width: !this.props.Document._fitWidth ? this.Document._nativeWidth || 0 : `${100 / this.contentScaling}%`,
- //height adjusted for mobile (window.screen.width > 600)
- height: !this.props.Document._fitWidth && (window.screen.width > 600) ? this.Document._nativeHeight || 0 : `${100 / this.contentScaling}%`,
- transform: `scale(${this.contentScaling})`
- }} >
+ return <div className={classname} >
<div className="pdfBox-title-outer">
<strong className="pdfBox-title" >{this.props.Document.title}</strong>
</div>
@@ -241,27 +246,33 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
isChildActive = (outsideReaction?: boolean) => this._isChildActive;
@computed get renderPdfView() {
+ TraceMobx();
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
+ <div className="pdfBox-background"></div>
<PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
- addDocTab={this.props.addDocTab} focus={this.props.focus} docFilters={this.props.docFilters}
+ addDocTab={this.props.addDocTab} focus={this.props.focus} searchFilterDocs={this.props.searchFilterDocs}
+ docFilters={this.props.docFilters} docRangeFilters={this.props.docRangeFilters}
pinToPres={this.props.pinToPres} addDocument={this.addDocument}
Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
isChildActive={this.isChildActive}
- fieldKey={this.props.fieldKey} startupLive={this._initialScale < 2.5 || this.props.Document._scrollTop ? true : false} />
+ fieldKey={this.props.fieldKey} startupLive={true} />
{this.settingsPanel()}
</div>;
}
_pdfjsRequested = false;
render() {
+ TraceMobx();
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null);
- if (this.props.isSelected() || this.props.renderDepth === 0 || 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 (true) {//this.props.isSelected() || (this.props.active() && this.props.renderDepth === 0) || this.props.Document._scrollY !== undefined) {
+ this._everActive = true;
+ }
+ if (pdfUrl && this._everActive) {
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 c4d8f1a4f..7bc6c1dfd 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -144,6 +144,21 @@ $light-background: #ececec;
grid-template-columns: repeat(auto-fit, 70px);
}
+ .ribbon-colorBox {
+ cursor: pointer;
+ border: solid 1px black;
+ display: flex;
+ margin-left: 5px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ justify-content: center;
+ align-items: center;
+ height: 15px;
+ width: 15px;
+ padding: 0px;
+ transform: translate(2px, 3px);
+ }
+
.ribbon-property {
font-size: 11;
font-weight: 200;
@@ -415,10 +430,38 @@ $light-background: #ececec;
.ribbon-button {
cursor: pointer;
font-size: 10.5;
+ font-weight: 300;
+ height: 20;
+ background-color: #979797;
+ color: white;
+ display: flex;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ border-radius: 5px;
+ margin-right: 5px;
+ width: max-content;
+ justify-content: center;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+
+ .ribbon-button:hover {
+ transform: scale(1.03);
+ transition: all 0.4s;
+ font-weight: 400;
+ opacity: 1;
+ color: white;
+ background-color: black;
+ }
+
+ .ribbon-toggle {
+ cursor: pointer;
+ font-size: 10.5;
font-weight: 200;
height: 20;
background-color: $light-background;
- border: solid 1px black;
+ border: solid 1px rgba(0, 0, 0, 0.5);
display: flex;
margin-top: 5px;
margin-bottom: 5px;
@@ -431,12 +474,14 @@ $light-background: #ececec;
padding-left: 10px;
}
- .ribbon-button.active {
+ .ribbon-toggle.active {
background-color: #aedef8;
}
- .ribbon-button:hover {
- background-color: lightgrey;
+ .ribbon-toggle:hover {
+ transform: scale(1.03);
+ transition: all 0.4s;
+ border: solid 1px rgba(0, 0, 0, 0.75);
}
svg.svg-inline--fa.fa-thumbtack.fa-w-12.toolbar-thumbtack {
@@ -488,7 +533,7 @@ $light-background: #ececec;
max-width: 200px;
overflow: visible;
-
+
.presBox-dropdownOption {
cursor: pointer;
font-size: 11;
@@ -801,82 +846,6 @@ $light-background: #ececec;
}
- .presPanelOverlay {
- background-color: #323232;
- color: white;
- border-radius: 5px;
- grid-template-rows: 100%;
- height: 25;
- width: max-content;
- min-width: max-content;
- justify-content: space-evenly;
- align-items: center;
- display: flex;
- position: absolute;
- right: 10px;
- transition: all 0.2s;
-
- .presPanel-button-text {
- display: flex;
- height: 20;
- width: max-content;
- font-family: Roboto;
- font-weight: 400;
- margin-left: 3px;
- margin-right: 3px;
- padding-right: 3px;
- padding-left: 3px;
- letter-spacing: normal;
- border-radius: 5px;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
-
- .presPanel-divider {
- width: 0.5px;
- height: 80%;
- border-right: solid 1px #5a5a5a;
- }
-
- .presPanel-button-frame {
- justify-self: center;
- align-self: center;
- align-items: center;
- display: grid;
- grid-template-columns: auto auto auto;
- justify-content: space-around;
- font-size: 11;
- margin-left: 7;
- width: 30;
- height: 85%;
- background-color: rgba(91, 157, 221, 0.4);
- border-radius: 5px;
- }
-
- .presPanel-button {
- cursor: pointer;
- display: flex;
- height: 20;
- min-width: 20;
- margin-left: 3px;
- margin-right: 3px;
- border-radius: 100%;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
-
- .presPanel-button:hover {
- background-color: #5a5a5a;
- }
-
- .presPanel-button-text:hover {
- background-color: #5a5a5a;
- }
- }
-
-
.collectionViewBaseChrome-viewPicker {
min-width: 50;
@@ -952,80 +921,165 @@ $light-background: #ececec;
}
}
-.miniPres:hover {
- opacity: 1;
-}
-
.miniPres {
cursor: grab;
position: absolute;
- overflow: hidden;
right: 10;
top: 10;
opacity: 0.1;
transition: all 0.4s;
- /* border: solid 1px; */
color: white;
- /* box-shadow: black 0.4vw 0.4vw 0.8vw; */
-
- .miniPresOverlay {
- display: grid;
- grid-template-columns: auto auto auto auto auto auto auto auto;
- grid-template-rows: 100%;
- height: 100%;
- justify-items: center;
- align-items: center;
+}
- .miniPres-button-text {
- cursor: pointer;
- display: flex;
- height: 20;
- font-weight: 400;
- min-width: 100%;
- border-radius: 5px;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
+.miniPres:hover {
+ opacity: 1;
+}
- .miniPres-button-frame {
- justify-self: center;
- align-self: center;
- align-items: center;
- display: grid;
- grid-template-columns: auto auto auto;
- justify-content: space-around;
- font-size: 11;
- margin-left: 7;
- width: 30;
- height: 85%;
- background-color: rgba(91, 157, 221, 0.4);
- border-radius: 5px;
- }
+.presPanelOverlay {
+ background-color: #323232;
+ color: white;
+ border-radius: 5px;
+ grid-template-rows: 100%;
+ height: 25;
+ width: max-content;
+ min-width: max-content;
+ justify-content: space-evenly;
+ align-items: center;
+ display: flex;
+ position: absolute;
+ right: 10px;
+ transition: all 0.2s;
- .miniPres-divider {
- width: 0.5px;
- height: 80%;
- border-right: solid 2px #5a5a5a;
- }
+ .presPanel-button-text {
+ display: flex;
+ height: 20;
+ width: max-content;
+ font-family: Roboto;
+ font-weight: 400;
+ margin-left: 3px;
+ margin-right: 3px;
+ padding-right: 3px;
+ padding-left: 3px;
+ letter-spacing: normal;
+ border-radius: 5px;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+ }
- .miniPres-button {
- cursor: pointer;
- display: flex;
- height: 20;
- min-width: 20;
- border-radius: 100%;
- align-items: center;
- justify-content: center;
- transition: all 0.3s;
- }
+ .presPanel-divider {
+ width: 0.5px;
+ height: 80%;
+ border-right: solid 1px #5a5a5a;
+ }
- .miniPres-button:hover {
- background-color: #5a5a5a;
- }
+ .presPanel-button-frame {
+ justify-self: center;
+ align-self: center;
+ align-items: center;
+ display: grid;
+ grid-template-columns: auto auto auto;
+ justify-content: space-around;
+ font-size: 11;
+ margin-left: 7;
+ width: 30;
+ height: 85%;
+ background-color: rgba(91, 157, 221, 0.4);
+ border-radius: 5px;
+ }
- .miniPres-button-text:hover {
- background-color: #5a5a5a;
- }
+ .presPanel-button {
+ cursor: pointer;
+ display: flex;
+ height: 20;
+ min-width: 20;
+ margin-left: 3px;
+ margin-right: 3px;
+ border-radius: 100%;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s;
+ }
+
+ .presPanel-button:hover {
+ background-color: #5a5a5a;
+ }
+
+ .presPanel-button-text:hover {
+ background-color: #5a5a5a;
}
}
+
+// .miniPres {
+// cursor: grab;
+// position: absolute;
+// overflow: hidden;
+// right: 10;
+// top: 10;
+// opacity: 0.1;
+// transition: all 0.4s;
+// /* border: solid 1px; */
+// color: white;
+// /* box-shadow: black 0.4vw 0.4vw 0.8vw; */
+
+// .miniPresOverlay {
+// display: grid;
+// grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
+// grid-template-rows: 100%;
+// height: 100%;
+// justify-items: center;
+// align-items: center;
+
+// .miniPres-button-text {
+// cursor: pointer;
+// display: flex;
+// height: 20;
+// font-weight: 400;
+// min-width: 100%;
+// border-radius: 5px;
+// align-items: center;
+// justify-content: center;
+// transition: all 0.3s;
+// }
+
+// .miniPres-button-frame {
+// justify-self: center;
+// align-self: center;
+// align-items: center;
+// display: grid;
+// grid-template-columns: auto auto auto;
+// justify-content: space-around;
+// font-size: 11;
+// margin-left: 7;
+// width: 30;
+// height: 85%;
+// background-color: rgba(91, 157, 221, 0.4);
+// border-radius: 5px;
+// }
+
+// .miniPres-divider {
+// width: 0.5px;
+// height: 80%;
+// border-right: solid 2px #5a5a5a;
+// }
+
+// .miniPres-button {
+// cursor: pointer;
+// display: flex;
+// height: 20;
+// min-width: 20;
+// border-radius: 100%;
+// align-items: center;
+// justify-content: center;
+// transition: all 0.3s;
+// }
+
+// .miniPres-button:hover {
+// background-color: #5a5a5a;
+// }
+
+// .miniPres-button-text:hover {
+// background-color: #5a5a5a;
+// }
+// }
+// } \ No newline at end of file
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 5fc76223d..7eca09f8c 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1,34 +1,50 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, DocCastAsync, WidthSym } from "../../../fields/Doc";
-import { InkTool } from "../../../fields/InkField";
-import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types";
-import { returnFalse, returnOne, numberRange, returnTrue } from "../../../Utils";
+import { ColorState, SketchPicker } from "react-color";
+import { Doc, DocCastAsync, DocListCast, DocListCastAsync } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
-import { DocumentManager } from "../../util/DocumentManager";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView";
-import { CollectionView, CollectionViewType } from "../collections/CollectionView";
-import { FieldView, FieldViewProps } from './FieldView';
-import { DocumentType } from "../../documents/DocumentTypes";
-import "./PresBox.scss";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { InkTool } from "../../../fields/InkField";
+import { List } from "../../../fields/List";
import { PrefetchProxy } from "../../../fields/Proxy";
+import { listSpec, makeInterface } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { returnFalse, returnOne, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { DocumentManager } from "../../util/DocumentManager";
import { Scripting } from "../../util/Scripting";
-import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
-import { List } from "../../../fields/List";
-import { Tooltip } from "@material-ui/core";
-import { actionAsync } from "mobx-utils";
import { SelectionManager } from "../../util/SelectionManager";
+import { undoBatch } from "../../util/UndoManager";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionView, CollectionViewType } from "../collections/CollectionView";
+import { TabDocView } from "../collections/TabDocView";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { AudioBox } from "./AudioBox";
-import { DocumentView } from "./DocumentView";
-import { SketchPicker, ColorState } from "react-color";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./PresBox.scss";
+import { VideoBox } from "./VideoBox";
+
+export enum PresMovement {
+ Zoom = "zoom",
+ Pan = "pan",
+ Jump = "jump",
+ None = "none",
+}
+
+export enum PresEffect {
+ Fade = "fade",
+ Flip = "flip",
+ Rotate = "rotate",
+ Bounce = "bounce",
+ Roll = "roll",
+ None = "none",
+}
type PresBoxSchema = makeInterface<[typeof documentSchema]>;
const PresBoxDocument = makeInterface(documentSchema);
@@ -51,16 +67,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@observable private transitionTools: boolean = false;
@observable private newDocumentTools: boolean = false;
@observable private progressivizeTools: boolean = false;
- @observable private moreInfoTools: boolean = false;
- @observable private playTools: boolean = false;
@observable private presentTools: boolean = false;
@observable private pathBoolean: boolean = false;
- @observable private expandBoolean: boolean = false;
@observable private openMovementDropdown: boolean = false;
@observable private openEffectDropdown: boolean = false;
-
@computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
@computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); }
+ @computed get activeItem() { return Cast(this.childDocs[this.itemIndex], Doc, null); }
+ @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
@computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
constructor(props: any) {
super(props);
@@ -79,35 +93,37 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
}
@computed get selectedDocumentView() {
- if (SelectionManager.SelectedDocuments().length) {
- return SelectionManager.SelectedDocuments()[0];
- } else if (PresBox.Instance._selectedArray.length) {
- return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
- } else { return undefined; }
+ if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
+ if (PresBox.Instance && PresBox.Instance._selectedArray) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
+ return undefined;
}
@computed get isPres(): boolean {
+ document.removeEventListener("keydown", this.keyEvents, true);
if (this.selectedDoc?.type === DocumentType.PRES) {
- document.removeEventListener("keydown", this.keyEvents, true);
document.addEventListener("keydown", this.keyEvents, true);
return true;
- } else {
- document.removeEventListener("keydown", this.keyEvents, true);
- return false;
}
+ return false;
}
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
componentWillUnmount() {
document.removeEventListener("keydown", this.keyEvents, true);
- this.turnOffEdit();
+ // Turn of progressivize editors
+ this.turnOffEdit(true);
}
- componentDidMount() {
+ componentDidMount = async () => {
this.rootDoc.presBox = this.rootDoc;
this.rootDoc._forceRenderEngine = "timeline";
this.rootDoc._replacedChrome = "replaced";
this.layoutDoc.presStatus = "edit";
this.layoutDoc._gridGap = 5;
+ this.turnOffEdit(true);
+ DocListCastAsync((Doc.UserDoc().myPresentations as Doc).data).then(async pres => {
+ await Promise.all(pres!);
+ if (!DocListCast((Doc.UserDoc().myPresentations as Doc).data).includes(this.rootDoc)) Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc);
+ });
}
updateCurrentPresentation = () => {
@@ -123,37 +139,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
next = () => {
this.updateCurrentPresentation();
const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null);
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const presTargetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- const childDocs = DocListCast(presTargetDoc[Doc.LayoutFieldKey(presTargetDoc)]);
- const currentFrame = Cast(presTargetDoc.currentFrame, "number", null);
- const lastFrame = Cast(presTargetDoc.lastFrame, "number", null);
- const curFrame = NumCast(presTargetDoc.currentFrame);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
+ const currentFrame = Cast(targetDoc._currentFrame, "number", null);
+ const lastFrame = Cast(targetDoc.lastFrame, "number", null);
+ const curFrame = NumCast(targetDoc._currentFrame);
let internalFrames: boolean = false;
- if (presTargetDoc.presProgressivize || activeItem.zoomProgressivize || presTargetDoc.scrollProgressivize) internalFrames = true;
+ if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
// Case 1: There are still other frames and should go through all frames before going to next slide
if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
- presTargetDoc._viewTransition = "all 1s";
- setTimeout(() => presTargetDoc._viewTransition = undefined, 1010);
- presTargetDoc.currentFrame = curFrame + 1;
- if (presTargetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(presTargetDoc, currentFrame);
- if (presTargetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, presTargetDoc);
- else presTargetDoc.editing = true;
- if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(presTargetDoc);
- // Case 2: Audio or video therefore wait to play the audio or video before moving on
- } else if ((presTargetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio && this.layoutDoc.presStatus !== 'auto') {
- AudioBox.Instance.playFrom(0);
- this._moveOnFromAudio = true;
- // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
+ targetDoc._viewTransition = "all 1s";
+ setTimeout(() => targetDoc._viewTransition = undefined, 1010);
+ // targetDoc._currentFrame = curFrame + 1;
+ this.nextKeyframe(targetDoc, activeItem);
+ // if (targetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(targetDoc, currentFrame);
+ if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
+ else targetDoc.editing = true;
+ // if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(targetDoc);
+ // Case 2: 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
+ } else if ((targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && !activeItem.playAuto && activeItem.playNow && this.layoutDoc.presStatus !== 'auto') {
+ if (targetDoc.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeItem.presStartTime));
+ // if (targetDoc.type === DocumentType.VID) { VideoBox.Instance.Play() };
+ activeItem.playNow = false;
+ // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
} else if (this.childDocs[this.itemIndex + 1] !== undefined) {
+ if (activeNext.presPinView) setTimeout(() => this.selectPres(), 0);
+ else this.selectPres();
const nextSelected = this.itemIndex + 1;
- if (presTargetDoc.type === DocumentType.AUDIO) AudioBox.Instance.pause();
- this.gotoDocument(nextSelected, this.itemIndex);
+ if (targetDoc.type === DocumentType.AUDIO) { if (AudioBox.Instance._ele) AudioBox.Instance.pause(); }
+ // if (targetDoc.type === DocumentType.VID) { if (AudioBox.Instance._ele) VideoBox.Instance.Pause(); }
const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
- if (activeNext && targetNext.type === DocumentType.AUDIO && activeNext.playAuto) {
- AudioBox.Instance.playFrom(0);
- this._moveOnFromAudio = true;
- } else this._moveOnFromAudio = false;
+ // If next slide is audio / video 'Play automatically' then the next slide should be played
+ if (activeNext && (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) && activeNext.playAuto) {
+ console.log('play next automatically');
+ if (targetNext.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeNext.presStartTime));
+ // if (targetNext.type === DocumentType.VID) { VideoBox.Instance.Play() };
+ } else if (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) { activeNext.playNow = true; console.log('play next after it is navigated to'); }
+ this.gotoDocument(nextSelected, this.itemIndex);
+ } else if (this.childDocs[this.itemIndex + 1] === undefined && this.layoutDoc.presLoop) {
+ this.gotoDocument(0, this.itemIndex);
}
}
@@ -166,19 +191,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
back = () => {
this.updateCurrentPresentation();
- const docAtCurrent = this.childDocs[this.itemIndex];
- const targetDoc = Cast(docAtCurrent.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
const lastFrame = Cast(targetDoc.lastFrame, "number", null);
- const curFrame = NumCast(targetDoc.currentFrame);
+ const curFrame = NumCast(targetDoc._currentFrame);
+ if (prevItem.presPinView) setTimeout(() => this.selectPres(), 0);
+ else this.selectPres();
if (lastFrame !== undefined && curFrame >= 1) {
- this.prevKeyframe(targetDoc, docAtCurrent);
- } else if (docAtCurrent) {
+ this.prevKeyframe(targetDoc, activeItem);
+ } else if (activeItem) {
let prevSelected = this.itemIndex;
prevSelected = Math.max(0, prevSelected - 1);
this.gotoDocument(prevSelected, this.itemIndex);
- if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc.currentFrame = NumCast(prevTargetDoc.lastFrame);
+ if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame);
}
}
@@ -189,9 +216,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
Doc.UnBrushAllDocs();
if (index >= 0 && index < this.childDocs.length) {
this.rootDoc._itemIndex = index;
+ const activeItem: Doc = this.activeItem;
const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null);
+ if (activeItem.presPinView) {
+ const bestTarget = DocumentManager.Instance.getFirstDocumentView(presTargetDoc)?.props.Document;
+ bestTarget && runInAction(() => {
+ if (activeItem.presMovement === PresMovement.Jump) {
+ bestTarget._viewTransition = '0s';
+ } else {
+ bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s';
+ setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010);
+ }
+ });
+ } else {
+ presTargetDoc && runInAction(() => {
+ if (activeItem.presMovement === PresMovement.Jump) presTargetDoc.focusSpeed = 0;
+ else presTargetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+ });
+ setTimeout(() => presTargetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+ }
if (presTargetDoc?.lastFrame !== undefined) {
- presTargetDoc.currentFrame = 0;
+ presTargetDoc._currentFrame = 0;
}
this._selectedArray = [this.childDocs[index]]; //Update selected array
//Handles movement to element
@@ -209,40 +254,39 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* on the right.
*/
navigateToElement = async (curDoc: Doc) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const srcContext = await DocCastAsync(targetDoc.context);
const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
const collectionDocView = presCollection ? await DocumentManager.Instance.getDocumentView(presCollection) : undefined;
this.turnOffEdit();
-
if (this.itemIndex >= 0) {
if (srcContext && targetDoc) {
this.layoutDoc.presCollection = srcContext;
} else if (targetDoc) this.layoutDoc.presCollection = targetDoc;
}
- if (collectionDocView) {
- if (srcContext && srcContext !== presCollection) {
- // Case 1: new srcContext inside of current collection so add a new tab to the current pres collection
- collectionDocView.props.addDocTab(srcContext, "inPlace");
- }
- }
+ // if (collectionDocView) {
+ // if (srcContext && srcContext !== presCollection) {
+ // // Case 1: new srcContext inside of current collection so add a new tab to the current pres collection
+ // collectionDocView.props.addDocTab(srcContext, "inPlace");
+ // }
+ // }
this.updateCurrentPresentation();
const docToJump = curDoc;
const willZoom = false;
// If openDocument is selected then it should open the document for the user
if (activeItem.openDocument) {
- this.props.addDocTab(activeItem, "replace");
+ collectionDocView ? collectionDocView.props.addDocTab(activeItem, "inPlace") : this.props.addDocTab(activeItem, "replace:right");
} else
//docToJump stayed same meaning, it was not in the group or was the last element in the group
if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') {
this.zoomProgressivizeNext(targetDoc);
} else if (docToJump === curDoc) {
//checking if curDoc has navigation open
- if (curDoc.presNavButton && targetDoc) {
+ if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext);
- } else if (curDoc.presZoomButton && targetDoc) {
+ } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
//awaiting jump so that new scale can be found, since jumping is async
await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext);
}
@@ -254,9 +298,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// adjust the pan and scale to that of the pinView when it was added.
// TODO: Add option to remove presPinView
if (activeItem.presPinView) {
- targetDoc._panX = activeItem.presPinViewX;
- targetDoc._panY = activeItem.presPinViewY;
- targetDoc._viewScale = activeItem.presPinViewScale;
+ // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
+ const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+ bestTarget && runInAction(() => {
+ bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+ bestTarget._panX = activeItem.presPinViewX;
+ bestTarget._panY = activeItem.presPinViewY;
+ bestTarget._viewScale = activeItem.presPinViewScale;
+ });
+ //setTimeout(() => targetDoc._viewTransition = undefined, 1010);
}
// If website and has presWebsite data associated then on click it should
// go back to that specific website
@@ -271,7 +321,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* @param presTargetDoc: document for which internal zoom is used
*/
zoomProgressivizeNext = (activeItem: Doc) => {
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
const srcContext = Cast(targetDoc?.context, Doc, null);
const docView = DocumentManager.Instance.getDocumentView(targetDoc);
const vfLeft = this.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
@@ -346,15 +396,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
startAutoPres = (startSlide: number) => {
this.updateCurrentPresentation();
- let activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- let targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- let duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
+ let activeItem: Doc = this.activeItem;
+ let targetDoc: Doc = this.targetDoc;
+ let duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
const load = async () => { // Wrap the loop into an async function for this to work
for (var i = startSlide; i < this.childDocs.length; i++) {
activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- duration = NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition);
+ duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
if (duration <= 100) { duration = 2500; }
if (NumCast(targetDoc.lastFrame) > 0) {
for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) {
@@ -363,17 +413,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}
await timer(duration); this.next(); // then the created Promise can be awaited
- if (i === this.childDocs.length - 1) setTimeout(() => { clearTimeout(this._presTimer); if (this.layoutDoc.presStatus === 'auto') this.layoutDoc.presStatus = "manual"; }, duration);
+ if (i === this.childDocs.length - 1) {
+ setTimeout(() => {
+ clearTimeout(this._presTimer);
+ if (this.layoutDoc.presStatus === 'auto' && !this.layoutDoc.presLoop) this.layoutDoc.presStatus = "manual";
+ else if (this.layoutDoc.presLoop) this.startAutoPres(0);
+ }, duration);
+ }
}
};
+ this.layoutDoc.presStatus = "auto";
+ this.startPresentation(startSlide);
+ this.gotoDocument(startSlide, this.itemIndex);
+ load();
+ }
+
+ @action
+ pauseAutoPres = () => {
if (this.layoutDoc.presStatus === "auto") {
if (this._presTimer) clearTimeout(this._presTimer);
this.layoutDoc.presStatus = "manual";
- } else {
- this.layoutDoc.presStatus = "auto";
- this.startPresentation(startSlide);
- this.gotoDocument(startSlide, this.itemIndex);
- load();
+ this.layoutDoc.presLoop = false;
}
}
@@ -384,7 +444,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.rootDoc._itemIndex = 0;
}
- @action togglePath = () => this.pathBoolean = !this.pathBoolean;
+ @action togglePath = (srcContext: Doc, off?: boolean) => {
+ if (off) {
+ this.pathBoolean = false;
+ srcContext.presPathView = false;
+ } else {
+ this.pathBoolean = !this.pathBoolean;
+ srcContext.presPathView = this.pathBoolean;
+ }
+ }
@action toggleExpandMode = () => {
this.rootDoc.expandBoolean = !this.rootDoc.expandBoolean;
this.childDocs.forEach((doc) => {
@@ -401,12 +469,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
startPresentation = (startIndex: number) => {
this.updateCurrentPresentation();
this.childDocs.map(doc => {
- const presTargetDoc = doc.presentationTargetDoc as Doc;
+ const tagDoc = doc.presentationTargetDoc as Doc;
if (doc.presHideTillShownButton && this.childDocs.indexOf(doc) > startIndex) {
- presTargetDoc.opacity = 0;
+ tagDoc.opacity = 0;
}
if (doc.presHideAfterButton && this.childDocs.indexOf(doc) < startIndex) {
- presTargetDoc.opacity = 0;
+ tagDoc.opacity = 0;
}
});
}
@@ -418,20 +486,32 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
updateMinimize = () => {
+ const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
if (this.layoutDoc.inOverlay) {
this.layoutDoc.presStatus = 'edit';
- Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
- CollectionDockingView.AddRightSplit(this.rootDoc);
+ Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+ CollectionDockingView.AddSplit(this.rootDoc, "right");
this.layoutDoc.inOverlay = false;
+ } else if (this.layoutDoc.context && docView) {
+ this.layoutDoc.presStatus = 'edit';
+ clearTimeout(this._presTimer);
+ const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+ this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
+ this.rootDoc.y = pt[1] + 10;
+ this.rootDoc._height = 35;
+ this.rootDoc._width = 250;
+ docView.props.removeDocument?.(this.layoutDoc);
+ Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
} else {
- this.layoutDoc.presStatus = 'manual';
+ this.layoutDoc.presStatus = 'edit';
+ clearTimeout(this._presTimer);
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
this.rootDoc.y = pt[1] + 10;
this.rootDoc._height = 35;
this.rootDoc._width = 250;
this.props.addDocTab?.(this.rootDoc, "close");
- Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc);
+ Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
}
}
@@ -453,38 +533,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* When the movement dropdown is changes
*/
@undoBatch
- movementChanged = action((movement: string) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ updateMovement = action((movement: any, activeItem: Doc, targetDoc: Doc) => {
switch (movement) {
- case 'zoom': //Pan and zoom
- activeItem.presZoomButton = !activeItem.presZoomButton;
- if (activeItem.presZoomButton) activeItem.presMovement = 'Zoom';
- else activeItem.presMovement = 'None';
- activeItem.presNavButton = false;
+ case PresMovement.Zoom: //Pan and zoom
+ activeItem.presMovement = PresMovement.Zoom;
break;
- case 'pan': //Pan
- activeItem.presZoomButton = false;
- activeItem.presNavButton = !activeItem.presNavButton;
- if (activeItem.presNavButton) activeItem.presMovement = 'Pan';
- else activeItem.presMovement = 'None';
+ case PresMovement.Pan: //Pan
+ activeItem.presMovement = PresMovement.Pan;
break;
- case 'jump': //Jump Cut
- targetDoc.presTransition = 0;
- activeItem.presZoomButton = true;
- activeItem.presSwitchButton = !activeItem.presSwitchButton;
- if (activeItem.presSwitchButton) activeItem.presMovement = 'Jump cut';
- else activeItem.presMovement = 'None';
+ case PresMovement.Jump: //Jump Cut
+ activeItem.presJump = true;
+ activeItem.presMovement = PresMovement.Jump;
break;
- case 'none': default:
- activeItem.presMovement = 'None';
- activeItem.presZoomButton = false;
- activeItem.presNavButton = false;
- activeItem.presSwitchButton = false;
+ case PresMovement.None: default:
+ activeItem.presMovement = PresMovement.None;
break;
}
});
+ setMovementName = action((movement: any, activeItem: Doc): string => {
+ let output: string = 'none';
+ switch (movement) {
+ case PresMovement.Zoom: output = 'Zoom'; break; //Pan and zoom
+ case PresMovement.Pan: output = 'Pan'; break; //Pan
+ case PresMovement.Jump: output = 'Jump cut'; break; //Jump Cut
+ case PresMovement.None: output = 'None'; break; //None
+ default: output = 'Zoom'; activeItem.presMovement = 'zoom'; break; //default set as zoom
+ }
+ return output;
+ });
+
whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
// For dragging documents into the presentation trail
addDocumentFilter = (doc: Doc | Doc[]) => {
@@ -492,19 +570,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
docs.forEach((doc, i) => {
if (this.childDocs.includes(doc)) {
if (docs.length === i + 1) return false;
+ } else if (doc.type === DocumentType.LABEL) {
+ const audio = Cast(doc.annotationOn, Doc, null);
+ if (audio) {
+ audio.aliasOf instanceof Doc;
+ audio.presStartTime = NumCast(doc.audioStart);
+ audio.presEndTime = NumCast(doc.audioEnd);
+ audio.presDuration = NumCast(doc.audioEnd) - NumCast(doc.audioStart);
+ TabDocView.PinDoc(audio, false, true);
+ setTimeout(() => this.removeDocument(doc), 1);
+ return false;
+ }
} else {
doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
- !this.childDocs.includes(doc) && (doc.presZoomButton = true);
+ !this.childDocs.includes(doc) && (doc.presMovement = PresMovement.Zoom);
if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true;
}
});
return true;
}
childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
- removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
+ removeDocument = (doc: Doc) => { Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); };
getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
panelHeight = () => this.props.PanelHeight() - 40;
- active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground) &&
+ active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
(this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
/**
@@ -526,18 +615,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
*/
@computed get listOfSelected() {
const list = this._selectedArray.map((doc: Doc, index: any) => {
- const activeItem = Cast(doc, Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc!, Doc, null);
- return (
- <div className="selectedList-items">{index + 1}. {targetDoc.title}</div>
- );
+ const curDoc = Cast(doc, Doc, null);
+ const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
+ if (tagDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>;
+ else if (curDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>;
});
return list;
}
@action
selectPres = () => {
- const presDocView = DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc)!;
+ const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc)!;
SelectionManager.SelectDoc(presDocView, false);
}
@@ -545,7 +633,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
selectElement = (doc: Doc) => {
this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex));
- this.selectPres();
+ if (doc.presPinView) setTimeout(() => this.selectPres(), 0);
+ else this.selectPres();
}
//Command click
@@ -563,8 +652,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
this._selectedArray = [];
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- if (activeItem) {
+ // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ if (this.activeItem) {
for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) {
this._selectedArray.push(this.childDocs[i]);
this._eleArray.push(ref);
@@ -575,8 +664,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
// Key for when the presentaiton is active
- @action
- keyEvents = (e: KeyboardEvent) => {
+ @undoBatch
+ keyEvents = action((e: KeyboardEvent) => {
+ if (e.target instanceof HTMLInputElement) return;
let handled = false;
const anchorNode = document.activeElement as HTMLDivElement;
if (anchorNode && anchorNode.className?.includes("lm_title")) return;
@@ -592,43 +682,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
handled = true;
}
} if (e.keyCode === 37 || e.keyCode === 38) { // left(37) / a(65) / up(38) to go back
- this.back(); if (this._presTimer) clearTimeout(this._presTimer);
+ this.back();
+ if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if (e.keyCode === 39 || e.keyCode === 40) { // right (39) / d(68) / down(40) to go to next
- this.next(); if (this._presTimer) clearTimeout(this._presTimer);
+ this.next();
+ if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
} if (e.keyCode === 32) { // spacebar to 'present' or autoplay
if (this.layoutDoc.presStatus !== "edit") this.startAutoPres(0);
- else this.layoutDoc.presStatus = "manual"; if (this._presTimer) clearTimeout(this._presTimer);
+ else this.next();
handled = true;
- }
- if (e.keyCode === 8) { // delete selected items
+ } if (e.keyCode === 8) { // delete selected items
if (this.layoutDoc.presStatus === "edit") {
- this._selectedArray.forEach((doc, i) => {
- this.removeDocument(doc);
- });
+ this._selectedArray.forEach((doc, i) => this.removeDocument(doc));
+ this._selectedArray = [];
+ this._eleArray = [];
+ this._dragArray = [];
handled = true;
}
- }
- if (handled) {
+ } if (handled) {
e.stopPropagation();
e.preventDefault();
+ } if ((e.keyCode === 37 || e.keyCode === 38) && e.shiftKey) { // left(37) / a(65) / up(38) to go back
+ if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) {
+ const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]);
+ if ((index - 1) > 0) this._selectedArray.push(this.childDocs[index - 1]);
+ }
+ handled = true;
+ } if ((e.keyCode === 39 || e.keyCode === 40) && e.shiftKey) { // left(37) / a(65) / up(38) to go back
+ if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) {
+ const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]);
+ if ((index - 1) > 0) {
+ this._selectedArray.push(this.childDocs[index - 1]);
+ }
+ }
+ handled = true;
}
- }
+ });
/**
*
*/
@undoBatch
@action
- viewPaths = async () => {
+ viewPaths = () => {
const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
- if (this.pathBoolean && srcContext) {
- this.togglePath();
- srcContext.presPathView = false;
- } else if (srcContext) {
- this.togglePath();
- srcContext.presPathView = true;
+ if (srcContext) {
+ this.togglePath(srcContext);
}
}
@@ -636,12 +737,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get order() {
const order: JSX.Element[] = [];
this.childDocs.forEach((doc, index) => {
- const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(targetDoc?.context, Doc, null);
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ const srcContext = Cast(tagDoc?.context, Doc, null);
+ const width = NumCast(tagDoc._width) / 10;
+ const height = Math.max(NumCast(tagDoc._height) / 10, 15);
+ const edge = Math.max(width, height);
+ const fontSize = edge * 0.8;
// Case A: Document is contained within the colleciton
if (this.rootDoc.presCollection === srcContext) {
order.push(
- <div className="pathOrder" style={{ top: NumCast(targetDoc.y), left: NumCast(targetDoc.x) }}>
+ <div className="pathOrder" style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}>
<div className="pathOrder-frame">{index + 1}</div>
</div>);
// Case B: Document is not inside of the collection
@@ -666,11 +771,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get paths() {
let pathPoints = "";
this.childDocs.forEach((doc, index) => {
- const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(targetDoc?.context, Doc, null);
- if (targetDoc && this.rootDoc.presCollection === srcContext) {
- const n1x = NumCast(targetDoc.x) + (NumCast(targetDoc._width) / 2);
- const n1y = NumCast(targetDoc.y) + (NumCast(targetDoc._height) / 2);
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ const srcContext = Cast(tagDoc?.context, Doc, null);
+ if (tagDoc && this.rootDoc.presCollection === srcContext) {
+ const n1x = NumCast(tagDoc.x) + (NumCast(tagDoc._width) / 2);
+ const n1y = NumCast(tagDoc.y) + (NumCast(tagDoc._height) / 2);
if (index = 0) pathPoints = n1x + "," + n1y;
else pathPoints = pathPoints + " " + n1x + "," + n1y;
} else {
@@ -685,11 +790,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
stroke: "#69a6db",
strokeWidth: 5,
strokeDasharray: '10 5',
+ boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
}}
fill="none"
- markerStart="url(#markerSquare)"
+ markerStart="url(#markerArrow)"
markerMid="url(#markerSquare)"
- markerEnd="url(#markerArrow)"
+ markerEnd="url(#markerSquareFilled)"
/>);
}
@@ -701,8 +807,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
activeItem.presFadeButton = !activeItem.presFadeButton;
if (!activeItem.presFadeButton) {
if (targetDoc) {
@@ -722,9 +828,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 10000) timeInMS = 10000;
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- if (targetDoc) targetDoc.presTransition = timeInMS;
+ if (this.activeItem) this.activeItem.presTransition = timeInMS;
}
// Converts seconds to ms and updates presDuration
@@ -733,19 +837,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 20000) timeInMS = 20000;
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- if (targetDoc) targetDoc.presDuration = timeInMS;
+ if (this.activeItem) this.activeItem.presDuration = timeInMS;
}
@computed get transitionDropdown() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
- const transitionSpeed = targetDoc.presTransition ? NumCast(targetDoc.presTransition) / 1000 : 0.5;
- let duration = targetDoc.presDuration ? NumCast(targetDoc.presDuration) / 1000 : 2;
- if (targetDoc.type === DocumentType.AUDIO) duration = NumCast(targetDoc.duration);
+ const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
+ let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2;
+ if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
const effect = targetDoc.presEffect ? targetDoc.presEffect : 'None';
activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
return (
@@ -753,21 +855,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Movement
<div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? 'solid 2px #5B9FDD' : 'solid 1px black' }}>
- {activeItem.presMovement}
+ {this.setMovementName(activeItem.presMovement, activeItem)}
<FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'None' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('none')}>None</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Zoom' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('zoom')}>Pan and Zoom</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Pan' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('pan')}>Pan</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Jump cut' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('jump')}>Jump cut</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None, activeItem, targetDoc)}>None</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom, activeItem, targetDoc)}>Pan and Zoom</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan, activeItem, targetDoc)}>Pan</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump, activeItem, targetDoc)}>Jump cut</div>
</div>
</div>
- <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "inline-flex" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Transition Speed</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={transitionSpeed}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
@@ -779,8 +880,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
</div>
- <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
- <div className={`slider-headers ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`}>
+ <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
+ <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
<div className="slider-text">Fast</div>
<div className="slider-text">Medium</div>
<div className="slider-text">Slow</div>
@@ -789,15 +890,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Visibility {"&"} Duration
<div className="ribbon-doubleButton">
- <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-button ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton}>Hide before</div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-button ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton}>Hide after</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton}>Hide before</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton}>Hide after</div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={() => activeItem.openDocument = !activeItem.openDocument}>Open</div></Tooltip>
</div>
<div className="ribbon-doubleButton" >
<div className="presBox-subheading">Slide Duration</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={duration}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
+ // onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e) => this.setDurationTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
@@ -837,11 +939,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
<div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "left" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'left'}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "right" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'right'}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === "top" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'top'}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === "bottom" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'bottom'}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection ? "solid 2px black" : "solid 2px #5a9edd", borderRadius: "100%" }} onClick={() => targetDoc.presEffectDirection = false}></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "left" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={() => targetDoc.presEffectDirection = 'left'}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "right" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={() => targetDoc.presEffectDirection = 'right'}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === "top" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={() => targetDoc.presEffectDirection = 'top'}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === "bottom" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={() => targetDoc.presEffectDirection = 'bottom'}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection ? "solid 2px black" : "solid 2px #5a9edd", borderRadius: "100%", cursor: "pointer" }} onClick={() => targetDoc.presEffectDirection = false}></div></Tooltip>
</div>
</div>
<div className="ribbon-final-box">
@@ -858,10 +960,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get effectDirection(): string {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
let effect = '';
- switch (targetDoc.presEffectDirection) {
+ switch (this.targetDoc.presEffectDirection) {
case 'left': effect = "Enter from left"; break;
case 'right': effect = "Enter from right"; break;
case 'top': effect = "Enter from top"; break;
@@ -874,41 +974,62 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
applyTo = (array: Doc[]) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- array.forEach((doc, index) => {
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ array.forEach((doc) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
if (tagDoc && targetDoc) {
- tagDoc.presTransition = targetDoc.presTransition;
- tagDoc.presDuration = targetDoc.presDuration;
+ curDoc.presTransition = activeItem.presTransition;
+ curDoc.presDuration = activeItem.presDuration;
tagDoc.presEffect = targetDoc.presEffect;
tagDoc.presEffectDirection = targetDoc.presEffectDirection;
+ tagDoc.presMovement = targetDoc.presMovement;
curDoc.presMovement = activeItem.presMovement;
+ this.updateMovement(activeItem.presMovement, curDoc, tagDoc);
curDoc.presHideTillShownButton = activeItem.presHideTillShownButton;
curDoc.presHideAfterButton = activeItem.presHideAfterButton;
}
});
}
@computed get optionsDropdown() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 16, filter: 'invert(1)' }} />;
if (activeItem && targetDoc) {
return (
<div>
<div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
<div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.playAuto ? "#aedef8" : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div>
- <div className="ribbon-button" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : "#aedef8" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div>
- </div>
- <div className="ribbon-doubleButton" style={{ display: "flex" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={() => activeItem.openDocument = !activeItem.openDocument}>Open document</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.playAuto ? "#aedef8" : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div>
+ <div className="ribbon-toggle" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : "#aedef8" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div>
</div>
+ {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? "#aedef8" : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)}
+ {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Start time</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presStartTime)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })} />
+ </div>
+ </div> : (null)}
+ {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">End time</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presEndTime)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presEndTime = Number(val); })} />
+ </div>
+ </div> : (null)}
+ {targetDoc.type === DocumentType.COL ? 'Presentation Pin View' : (null)}
<div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.presPinView ? "#aedef8" : "" }}
+ <div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? "#aedef8" : "" }}
onClick={() => {
activeItem.presPinView = !activeItem.presPinView;
+ targetDoc.presPinView = activeItem.presPinView;
if (activeItem.presPinView) {
const x = targetDoc._panX;
const y = targetDoc._panY;
@@ -917,7 +1038,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
activeItem.presPinViewY = y;
activeItem.presPinViewScale = scale;
}
- }}>Presentation pin view</div>
+ }}>{presPinWithViewIcon}</div>
+ {activeItem.presPinView ? <div className="ribbon-button"
+ onClick={() => {
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ activeItem.presPinViewX = x;
+ activeItem.presPinViewY = y;
+ activeItem.presPinViewScale = scale;
+ }}>Update</div> : (null)}
</div>
<div style={{ display: activeItem.presPinView ? "block" : "none" }}>
<div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
@@ -926,7 +1056,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewX)}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
</div>
</div>
@@ -936,7 +1065,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewY)}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
</div>
</div>
@@ -946,13 +1074,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<input className="presBox-input"
style={{ textAlign: 'left', width: 50 }}
type="number" value={NumCast(activeItem.presPinViewScale)}
- onFocus={() => { document.removeEventListener("keydown", this.keyEvents, true); }}
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
</div>
</div>
</div>
{/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}>
- <div className="ribbon-button" onClick={this.progressivizeText}>Store original website</div>
+ <div className="ribbon-toggle" onClick={this.progressivizeText}>Store original website</div>
</div> */}
</div>
</div>
@@ -996,20 +1123,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Slide Title: <br></br>
<input className="ribbon-textInput" placeholder="..." type="text" name="fname"
- onFocus={() => {
- document.removeEventListener("keydown", this.keyEvents, true);
- }}
onChange={(e) => {
e.stopPropagation();
e.preventDefault();
runInAction(() => this.title = e.target.value);
- }}></input>
+ }}>
+ </input>
</div>
<div className="ribbon-box">
Choose type:
<div className="ribbon-doubleButton">
- <div title="Text" className={'ribbon-button'} style={{ background: this.addFreeform ? "" : "#aedef8" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
- <div title="Freeform" className={'ribbon-button'} style={{ background: this.addFreeform ? "#aedef8" : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
+ <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : "#aedef8" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
+ <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "#aedef8" : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
</div>
</div>
<div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}>
@@ -1052,21 +1177,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (layout) doc = this.createTemplate(layout);
if (freeform && layout) doc = this.createTemplate(layout, title);
if (!freeform && !layout) doc = Docs.Create.TextDocument("", { _nativeWidth: 400, _width: 225, title: title });
- const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
- const data = Cast(presCollection?.data, listSpec(Doc));
- const presData = Cast(this.rootDoc.data, listSpec(Doc));
- if (data && doc && presData) {
- data.push(doc);
- DockedFrameRenderer.PinDoc(doc, false);
- this.gotoDocument(this.childDocs.length, this.itemIndex);
- } else {
- this.props.addDocTab(doc as Doc, "onRight");
+ if (doc) {
+ const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
+ const data = Cast(presCollection?.data, listSpec(Doc));
+ const presData = Cast(this.rootDoc.data, listSpec(Doc));
+ if (data && presData) {
+ data.push(doc);
+ TabDocView.PinDoc(doc, false);
+ this.gotoDocument(this.childDocs.length, this.itemIndex);
+ } else {
+ this.props.addDocTab(doc, "add:right");
+ }
}
}
createTemplate = (layout: string, input?: string) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
let x = 0;
let y = 0;
if (activeItem && targetDoc) {
@@ -1107,10 +1234,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get presentDropdown() {
return (
<div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
- <div className="dropdown-play-button" onClick={this.updateMinimize}>
+ <div className="dropdown-play-button" onClick={(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}>
Minimize
</div>
- <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(); }))}>
+ <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}>
Sidebar view
</div>
</div>
@@ -1120,57 +1247,57 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// Case in which the document has keyframes to navigate to next key frame
@undoBatch
@action
- nextKeyframe = (tagDoc: Doc, activeItem: Doc): void => {
+ nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
- const currentFrame = Cast(tagDoc.currentFrame, "number", null);
+ const currentFrame = Cast(tagDoc._currentFrame, "number", null);
if (currentFrame === undefined) {
- tagDoc.currentFrame = 0;
+ tagDoc._currentFrame = 0;
CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
}
CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame);
CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
- tagDoc.currentFrame = Math.max(0, (currentFrame || 0) + 1);
- tagDoc.lastFrame = Math.max(NumCast(tagDoc.currentFrame), NumCast(tagDoc.lastFrame));
- if (activeItem.zoomProgressivize) {
- const resize = document.getElementById('resizable');
- if (resize) {
- resize.style.width = this.checkList(tagDoc, activeItem["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, activeItem["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, activeItem["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, activeItem["viewfinder-left-indexed"]) + 'px';
- }
- }
+ tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
+ tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
+ // if (curDoc.zoomProgressivize) {
+ // const resize = document.getElementById('resizable');
+ // if (resize) {
+ // resize.style.width = this.checkList(tagDoc, curDoc["viewfinder-width-indexed"]) + 'px';
+ // resize.style.height = this.checkList(tagDoc, curDoc["viewfinder-height-indexed"]) + 'px';
+ // resize.style.top = this.checkList(tagDoc, curDoc["viewfinder-top-indexed"]) + 'px';
+ // resize.style.left = this.checkList(tagDoc, curDoc["viewfinder-left-indexed"]) + 'px';
+ // }
+ // }
}
@undoBatch
@action
- prevKeyframe = (tagDoc: Doc, activeItem: Doc): void => {
+ prevKeyframe = (tagDoc: Doc, actItem: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
- const currentFrame = Cast(tagDoc.currentFrame, "number", null);
+ const currentFrame = Cast(tagDoc._currentFrame, "number", null);
if (currentFrame === undefined) {
- tagDoc.currentFrame = 0;
+ tagDoc._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
}
CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
- tagDoc.currentFrame = Math.max(0, (currentFrame || 0) - 1);
- if (activeItem.zoomProgressivize) {
- const resize = document.getElementById('resizable');
- if (resize) {
- resize.style.width = this.checkList(tagDoc, activeItem["viewfinder-width-indexed"]) + 'px';
- resize.style.height = this.checkList(tagDoc, activeItem["viewfinder-height-indexed"]) + 'px';
- resize.style.top = this.checkList(tagDoc, activeItem["viewfinder-top-indexed"]) + 'px';
- resize.style.left = this.checkList(tagDoc, activeItem["viewfinder-left-indexed"]) + 'px';
- }
- }
+ tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
+ // if (actItem.zoomProgressivize) {
+ // const resize = document.getElementById('resizable');
+ // if (resize) {
+ // resize.style.width = this.checkList(tagDoc, actItem["viewfinder-width-indexed"]) + 'px';
+ // resize.style.height = this.checkList(tagDoc, actItem["viewfinder-height-indexed"]) + 'px';
+ // resize.style.top = this.checkList(tagDoc, actItem["viewfinder-top-indexed"]) + 'px';
+ // resize.style.left = this.checkList(tagDoc, actItem["viewfinder-left-indexed"]) + 'px';
+ // }
+ // }
}
/**
* Returns the collection type as a string for headers
*/
@computed get stringType(): string {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
let type: string = '';
if (activeItem) {
switch (targetDoc.type) {
@@ -1193,39 +1320,39 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get progressivizeDropdown() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
- const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
- const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (activeItem && targetDoc) {
+ const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
+ const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
return (
<div>
<div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
{this.stringType} selected
- <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
- <div className="ribbon-button" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Active text color</div>
- <div className="ribbon-property" style={{ backgroundColor: activeFontColor }} onClick={action(() => { console.log("hi"); this.openActiveColorPicker = !this.openActiveColorPicker; })}>
+ <div className="ribbon-colorBox" style={{ backgroundColor: activeFontColor, height: 15, width: 15 }} onClick={action(() => { this.openActiveColorPicker = !this.openActiveColorPicker; })}>
</div>
</div>
{this.activeColorPicker}
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Viewed font color</div>
- <div className="ribbon-property" style={{ backgroundColor: viewedFontColor }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
+ <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
</div>
</div>
{this.viewedColorPicker}
{/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
- <div className="ribbon-button" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
</div> */}
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
- <div className="ribbon-button" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
</div>
</div>
<div className="ribbon-final-box" style={{ display: activeItem.zoomProgressivize || activeItem.scrollProgressivize || activeItem.presProgressivize || activeItem.textProgressivize ? "grid" : "none" }}>
@@ -1237,7 +1364,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.editing ? "white" : "black", backgroundColor: targetDoc.editing ? "#5B9FDD" : "#AEDDF8" }}
onClick={action(() => targetDoc.editing = !targetDoc.editing)} >
- {NumCast(targetDoc.currentFrame)}
+ {NumCast(targetDoc._currentFrame)}
</div>
<div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc, activeItem); }}>
<FontAwesomeIcon icon={"caret-right"} size={"lg"} />
@@ -1245,7 +1372,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
<Tooltip title={<><div className="dash-tooltip">{"Last frame"}</div></>}><div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div></Tooltip>
</div>
- <div className="ribbon-button" style={{ height: 20, backgroundColor: "#AEDDF8" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+ <div className="ribbon-toggle" style={{ height: 20, backgroundColor: "#AEDDF8" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
</div>
</div>
</div>
@@ -1256,8 +1383,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
switchActive = (color: ColorState) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const val = String(color.hex);
targetDoc["pres-text-color"] = val;
return true;
@@ -1265,16 +1392,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
switchPresented = (color: ColorState) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const val = String(color.hex);
targetDoc["pres-text-viewed-color"] = val;
return true;
}
@computed get activeColorPicker() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
return !this.openActiveColorPicker ? (null) : <SketchPicker onChange={this.switchActive}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -1283,8 +1410,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get viewedColorPicker() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
return !this.openViewedColorPicker ? (null) : <SketchPicker onChange={this.switchPresented}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -1292,15 +1419,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
color={StrCast(targetDoc["pres-text-viewed-color"])} />;
}
- turnOffEdit = () => {
+ @action
+ turnOffEdit = (paths?: boolean) => {
+ if (paths) {
+ // Turn off paths
+ const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
+ if (srcContext) this.togglePath(srcContext, true);
+ }
+ // Turn off the progressivize editors for each
this.childDocs.forEach((doc) => {
doc.editSnapZoomProgressivize = false;
doc.editZoomProgressivize = false;
- doc.editScrollProgressivize = false;
const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
if (targetDoc) {
targetDoc.editZoomProgressivize = false;
- targetDoc.editScrollProgressivize = false;
+ // targetDoc.editScrollProgressivize = false;
}
});
}
@@ -1308,8 +1441,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//Toggle whether the user edits or not
@action
editZoomProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (!targetDoc.editZoomProgressivize) {
if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true; targetDoc.zoomProgressivize = true;
targetDoc.editZoomProgressivize = true;
@@ -1323,8 +1456,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//Toggle whether the user edits or not
@action
editScrollProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
if (!targetDoc.editScrollProgressivize) {
if (!targetDoc.scrollProgressivize) { targetDoc.scrollProgressivize = true; activeItem.scrollProgressivize = true; }
targetDoc.editScrollProgressivize = true;
@@ -1337,14 +1470,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
progressivizeScroll = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const activeItem: Doc = this.activeItem;
activeItem.scrollProgressivize = !activeItem.scrollProgressivize;
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize;
- CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc.currentFrame), true);
+ CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame));
if (targetDoc.editScrollProgressivize) {
targetDoc.editScrollProgressivize = false;
- targetDoc.currentFrame = 0;
+ targetDoc._currentFrame = 0;
targetDoc.lastFrame = 0;
}
}
@@ -1353,14 +1486,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
progressivizeZoom = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ const activeItem: Doc = this.activeItem;
activeItem.zoomProgressivize = !activeItem.zoomProgressivize;
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize;
- CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc, true);
+ CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc);
if (activeItem.editZoomProgressivize) {
activeItem.editZoomProgressivize = false;
- targetDoc.currentFrame = 0;
+ targetDoc._currentFrame = 0;
targetDoc.lastFrame = 0;
}
}
@@ -1368,9 +1501,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//Progressivize Child Docs
@action
editProgressivize = (e: React.MouseEvent) => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
- targetDoc.currentFrame = targetDoc.lastFrame;
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ targetDoc._currentFrame = targetDoc.lastFrame;
if (!targetDoc.editProgressivize) {
if (!activeItem.presProgressivize) { activeItem.presProgressivize = true; targetDoc.presProgressivize = true; }
targetDoc.editProgressivize = true;
@@ -1382,22 +1515,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
progressivizeChild = (e: React.MouseEvent) => {
e.stopPropagation();
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
if (!activeItem.presProgressivize) {
targetDoc.editing = false;
activeItem.presProgressivize = true;
targetDoc.presProgressivize = true;
- targetDoc.currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true);
- targetDoc.lastFrame = docs.length - 1;
+ targetDoc._currentFrame = 0;
+ docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true));
+ targetDoc.lastFrame = targetDoc.lastFrame ? NumCast(targetDoc.lastFrame) : docs.length - 1;
} else {
targetDoc.editProgressivize = false;
activeItem.presProgressivize = false;
targetDoc.presProgressivize = false;
- targetDoc.currentFrame = 0;
- targetDoc.lastFrame = 0;
+ targetDoc._currentFrame = 0;
targetDoc.editing = true;
}
}
@@ -1438,18 +1570,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
checkList = (doc: Doc, list: any): number => {
const x: List<number> = list;
- if (x && x.length >= NumCast(doc.currentFrame) + 1) {
- return x[NumCast(doc.currentFrame)];
+ if (x && x.length >= NumCast(doc._currentFrame) + 1) {
+ return x[NumCast(doc._currentFrame)];
} else if (x) {
- x.length = NumCast(doc.currentFrame) + 1;
- x[NumCast(doc.currentFrame)] = x[NumCast(doc.currentFrame) - 1];
- return x[NumCast(doc.currentFrame)];
+ x.length = NumCast(doc._currentFrame) + 1;
+ x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1];
+ return x[NumCast(doc._currentFrame)];
} else return 100;
}
@computed get progressivizeChildDocs() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
const tags: JSX.Element[] = [];
docs.forEach((doc, index) => {
@@ -1457,7 +1588,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
}
tags.push(
- <div className="progressivizeButton" onPointerLeave={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
+ <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
<div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
<div className="progressivizeButton-frame">{doc.appearFrame}</div>
<div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
@@ -1469,8 +1600,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
nextAppearFrame = (doc: Doc, i: number): void => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
const appearFrame = Cast(doc.appearFrame, "number", null);
if (appearFrame === undefined) {
doc.appearFrame = 0;
@@ -1482,8 +1613,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@undoBatch
@action
prevAppearFrame = (doc: Doc, i: number): void => {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
+ // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
const appearFrame = Cast(doc.appearFrame, "number", null);
if (appearFrame === undefined) {
doc.appearFrame = 0;
@@ -1604,13 +1735,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get playButtonFrames() {
- const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
- const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
+ const targetDoc: Doc = this.targetDoc;
return (
<>
- {targetDoc ? <div className="miniPres-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
- <div>{targetDoc.currentFrame}</div>
- <div className="miniPres-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
+ {this.targetDoc ? <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
+ <div>{targetDoc._currentFrame}</div>
+ <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
<div>{targetDoc.lastFrame}</div>
</div> : null}
</>
@@ -1620,8 +1750,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get playButtons() {
// Case 1: There are still other frames and should go through all frames before going to next slide
return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="presPanel-divider"></div>
<div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <div className="presPanel-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
<div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
<div className="presPanel-divider"></div>
<div className="presPanel-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
@@ -1629,13 +1761,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{this.playButtonFrames}
</div>
<div className="presPanel-divider"></div>
- {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => this.layoutDoc.presStatus = "edit"}>EXIT</div>
+ {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div>
: <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}>
<FontAwesomeIcon icon={"times"} />
</div>}
</div>);
}
+ @action
+ startOrPause = () => {
+ if (this.layoutDoc.presStatus === "manual" || this.layoutDoc.presStatus === "edit") this.startAutoPres(this.itemIndex);
+ else this.pauseAutoPres();
+ }
+
render() {
// calling this method for keyEvents
this.isPres;
@@ -1643,19 +1781,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
return this.layoutDoc.inOverlay ?
- <div className="miniPres" style={{ width: 250, height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
- {<div className="miniPresOverlay">
- <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <div className="miniPres-button" onClick={() => this.startAutoPres(this.itemIndex)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div>
- <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text">
+ <div className="miniPres">
+ <div className="presPanelOverlay" style={{ display: "inline-flex", height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
+ <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button-text">
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
</div>
- <div className="miniPres-divider"></div>
- <div className="miniPres-button-text" onClick={this.updateMinimize}>EXIT</div>
- </div>}
+ <div className="presPanel-divider"></div>
+ <div className="presPanel-button-text" onClick={this.updateMinimize}>EXIT</div>
+ </div>
</div>
:
<div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
@@ -1668,6 +1808,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
ContainingCollectionDoc={this.props.Document}
PanelWidth={this.props.PanelWidth}
PanelHeight={this.panelHeight}
+ childIgnoreNativeSize={true}
moveDocument={returnFalse}
childOpacity={returnOne}
childLayoutTemplate={this.childLayoutTemplate}
diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx
index bd5b3bff4..8876b4879 100644
--- a/src/client/views/nodes/RadialMenuItem.tsx
+++ b/src/client/views/nodes/RadialMenuItem.tsx
@@ -1,13 +1,9 @@
import React = require("react");
-import { observable, action } from "mobx";
-import { observer } from "mobx-react";
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { observer } from "mobx-react";
import { UndoManager } from "../../util/UndoManager";
-library.add(faAngleRight);
-
export interface RadialMenuProps {
description: string;
event: (stuff?: any) => void;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 1cd29d795..16ce749bc 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,16 +1,15 @@
import React = require("react");
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { faVideo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from 'request-promise';
import { Doc } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
+import { InkTool } from "../../../fields/InkField";
import { listSpec, makeInterface } from "../../../fields/Schema";
import { Cast, NumCast } from "../../../fields/Types";
import { VideoField } from "../../../fields/URLField";
-import { emptyFunction, returnFalse, returnOne, returnZero, Utils } from "../../../Utils";
+import { emptyFunction, returnFalse, returnOne, returnZero, Utils, OmitKeys } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { ContextMenu } from "../ContextMenu";
@@ -18,14 +17,11 @@ import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from './FieldView';
import "./ScreenshotBox.scss";
-import { InkTool } from "../../../fields/InkField";
const path = require('path');
type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
const ScreenshotDocument = makeInterface(documentSchema);
-library.add(faVideo);
-
@observer
export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
private _reactionDisposer?: IReactionDisposer;
@@ -77,7 +73,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
const spt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
imageSummary.x = spt[0];
imageSummary.y = spt[1];
- Cast(Cast(Doc.UserDoc().myOverlayDocuments, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
+ Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null)?.data, listSpec(Doc), []).push(imageSummary);
} else {
this.props.addDocument?.(imageSummary);
}
@@ -173,11 +169,9 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
return (<div className="videoBox" onContextMenu={this.specificContextMenu}
style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<div className="videoBox-viewer" >
- <CollectionFreeFormView {...this.props}
+ <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
annotationsKey={""}
focus={this.props.focus}
isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 45cdfc5ad..d13680046 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -1,5 +1,3 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit } from '@fortawesome/free-regular-svg-icons';
import { runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -16,9 +14,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
import './SliderBox.scss';
-
-library.add(faEdit as any);
-
const SliderSchema = createSchema({
_sliderMin: "number",
_sliderMax: "number",
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ee92e517c..998ddde9a 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -8,7 +8,7 @@ import { InkTool } from "../../../fields/InkField";
import { createSchema, makeInterface } from "../../../fields/Schema";
import { Cast, StrCast } from "../../../fields/Types";
import { VideoField } from "../../../fields/URLField";
-import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils";
+import { Utils, emptyFunction, returnOne, returnZero, OmitKeys } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { ContextMenu } from "../ContextMenu";
@@ -23,7 +23,7 @@ import { SnappingManager } from "../../util/SnappingManager";
const path = require('path');
export const timeSchema = createSchema({
- currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
+ _currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first
});
type VideoDocument = makeInterface<[typeof documentSchema, typeof timeSchema]>;
const VideoDocument = makeInterface(documentSchema, timeSchema);
@@ -82,7 +82,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@action public FullScreen() {
this._fullScreen = true;
this.player && this.player.requestFullscreen();
- this._youtubePlayer && this.props.addDocTab(this.rootDoc, "inTab");
+ this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
}
choosePath(url: string) {
@@ -109,14 +109,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
if (!this._videoRef) {
const b = Docs.Create.LabelDocument({
x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
- _width: 150, _height: 50, title: (this.layoutDoc.currentTimecode || 0).toString(),
+ _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
});
b.isLinkButton = true;
this.props.addDocument?.(b);
DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
Networking.PostToServer("/youtubeScreenshot", {
id: this.youtubeVideoId,
- timecode: this.layoutDoc.currentTimecode
+ timecode: this.layoutDoc._currentTimecode
}).then(response => {
const resolved = response?.accessPaths?.agnostic?.client;
if (resolved) {
@@ -128,7 +128,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
//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.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc.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: string) => {
if (returnedFilename) {
this.createRealSummaryLink(returnedFilename);
@@ -144,7 +144,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const imageSummary = Docs.Create.ImageDocument(url, {
_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-"
+ _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
});
Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
@@ -155,8 +155,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@action
updateTimecode = () => {
- this.player && (this.layoutDoc.currentTimecode = this.player.currentTime);
- this._youtubePlayer && (this.layoutDoc.currentTimecode = this._youtubePlayer.getCurrentTime());
+ this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+ this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime());
}
componentDidMount() {
@@ -185,9 +185,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
this._videoRef = vref;
if (vref) {
this._videoRef!.ontimeupdate = this.updateTimecode;
+ // @ts-ignore
vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
- this._reactionDisposer && this._reactionDisposer();
- this._reactionDisposer = reaction(() => (this.layoutDoc.currentTimecode || 0),
+ this._reactionDisposer?.();
+ this._reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
}
}
@@ -272,21 +273,21 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const onYoutubePlayerReady = (event: any) => {
this._reactionDisposer?.();
this._youtubeReactionDisposer?.();
- this._reactionDisposer = reaction(() => this.layoutDoc.currentTimecode, () => !this._playing && this.Seek((this.layoutDoc.currentTimecode || 0)));
+ this._reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
this._youtubeReactionDisposer = reaction(
- () => Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+ () => !this.props.Document.isAnnotating && Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
(interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
};
this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
events: {
- 'onReady': onYoutubePlayerReady,
- 'onStateChange': onYoutubePlayerStateChange,
+ 'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+ 'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
}
});
}
private get uIButtons() {
- const curTime = (this.layoutDoc.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>
@@ -328,7 +329,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
onResetMove = (e: PointerEvent) => {
this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY);
- this.Seek(Math.max(0, (this.layoutDoc.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();
}
@@ -336,14 +337,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
onResetUp = (e: PointerEvent) => {
document.removeEventListener("pointermove", this.onResetMove, true);
document.removeEventListener("pointerup", this.onResetUp, true);
- this._isResetClick < 10 && (this.layoutDoc.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.layoutDoc.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.layoutDoc._nativeWidth || 640)} height={(this.layoutDoc._nativeHeight || 390)}
src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
@@ -353,7 +354,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
const docs = doc instanceof Doc ? [doc] : doc;
docs.forEach(doc => {
- const curTime = (this.layoutDoc.currentTimecode || -1);
+ const curTime = (this.layoutDoc._currentTimecode || -1);
curTime !== -1 && (doc.displayTimecode = curTime);
});
return this.addDocument(doc);
@@ -364,11 +365,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
return (<div className="videoBox" onContextMenu={this.specificContextMenu}
style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<div className="videoBox-viewer" >
- <CollectionFreeFormView {...this.props}
+ <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
annotationsKey={this.annotationKey}
focus={this.props.focus}
isSelected={this.props.isSelected}
@@ -384,6 +383,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 875142169..ea822f553 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -2,15 +2,16 @@
.webBox {
- height:100%;
+ height: 100%;
position: relative;
display: flex;
.pdfViewerDash-dragAnnotationBox {
- position:absolute;
+ position: absolute;
background-color: transparent;
opacity: 0.1;
}
+
.webBox-annotationLayer {
position: absolute;
transform-origin: left top;
@@ -19,10 +20,12 @@
pointer-events: none;
mix-blend-mode: multiply; // bcz: makes text fuzzy!
}
+
.webBox-annotationBox {
position: absolute;
background-color: rgba(245, 230, 95, 0.616);
}
+
.webBox-container {
transform-origin: top left;
width: 100%;
@@ -33,10 +36,13 @@
top: 0;
left: 0;
}
+
.webBox-cont {
pointer-events: none;
}
- .webBox-cont, .webBox-cont-interactive {
+
+ .webBox-cont,
+ .webBox-cont-interactive {
padding: 0vw;
position: absolute;
top: 0;
@@ -45,18 +51,21 @@
height: 100%;
transform-origin: top left;
overflow: auto;
+
.webBox-iframe {
width: 100%;
height: 100%;
position: absolute;
- top:0;
+ top: 0;
}
}
+
.webBox-cont-interactive {
span {
user-select: text !important;
}
}
+
.webBox-outerContent {
width: 100%;
height: 100%;
@@ -65,12 +74,12 @@
left: 0;
overflow: auto;
}
+
div.webBox-outerContent::-webkit-scrollbar-thumb {
- display:none;
+ display: none;
}
}
-
.webBox-overlay {
width: 100%;
height: 100%;
@@ -79,64 +88,31 @@
.webBox-buttons {
margin-left: 44;
- background:lightGray;
+ background: lightGray;
width: 100%;
}
- .webBox-freeze {
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 5px;
- width: 30px;
- }
- .webBox-urlEditor {
- position: relative;
- opacity: 0.9;
+ .webBox-annotationToggle {
z-index: 9001;
- transition: top .5s;
-
- .urlEditor {
- display: grid;
- grid-template-columns: 1fr auto;
- padding-bottom: 10px;
- overflow: hidden;
-
- .editorBase {
- display: flex;
-
- .editor-collapse {
- transition: all .5s, opacity 0.3s;
- position: absolute;
- width: 40px;
- transform-origin: top left;
- }
-
- .switchToText {
- color: $main-accent;
- }
-
- .switchToText:hover {
- color: $dark-color;
- }
- }
+ position: absolute;
+ top: 2;
+ left: 2;
+ cursor: pointer;
+ box-shadow: black 0.3em 0.3em 1em;
+ border-radius: 5px;
+ display: flex;
+ opacity: 0.3;
+ width: 25px;
+ height: 25px;
+ align-items: center;
- button:hover {
- transform: scale(1);
- }
+ >svg {
+ margin: auto;
}
}
- .webpage-urlInput {
- padding: 12px 10px 11px 10px;
- border: 0px;
- color: grey;
- letter-spacing: 2px;
- outline-color: black;
- background: rgb(238, 238, 238);
- width: 100%;
- margin-right: 10px;
- height: 100%;
+ .webBox-annotationToggle:hover {
+ opacity: 1;
}
.touch-iframe-overlay {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 1393e7868..421aac69f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
import { observer } from "mobx-react";
import { Dictionary } from "typescript-collections";
import * as WebRequest from 'web-request';
-import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin, DataSym } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin, DataSym, HeightSym, WidthSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { HtmlField } from "../../../fields/HtmlField";
@@ -14,7 +14,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { WebField } from "../../../fields/URLField";
import { TraceMobx, GetEffectiveAcl } from "../../../fields/util";
-import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils, returnTrue } from "../../../Utils";
+import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils, returnTrue, OmitKeys } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DragManager } from "../../util/DragManager";
import { ImageUtils } from "../../util/Import & Export/ImageUtils";
@@ -24,13 +24,16 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
-import Annotation from "../pdf/Annotation";
-import PDFMenu from "../pdf/PDFMenu";
+import { Annotation } from "../pdf/Annotation";
+import { PDFMenu } from "../pdf/PDFMenu";
import { PdfViewerMarquee } from "../pdf/PDFViewer";
import { FieldView, FieldViewProps } from './FieldView';
import "./WebBox.scss";
import "../pdf/PDFViewer.scss";
import React = require("react");
+import { Tooltip } from '@material-ui/core';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
const htmlToText = require("html-to-text");
type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -67,6 +70,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
private _iframeDragRef = React.createRef<HTMLDivElement>();
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+ constructor(props: any) {
+ super(props);
+ this.dataDoc[this.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 850));
+ this.dataDoc[this.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, this.Document[HeightSym]() / this.Document[WidthSym]() * 850));
+ }
+
iframeLoaded = action((e: any) => {
const iframe = this._iframeRef.current;
if (iframe && iframe.contentDocument) {
@@ -100,10 +109,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this._setPreviewCursor?.(e.screenX, e.screenY, false);
}
iframeScrolled = (e: any) => {
- const scrollTop = e.target?.children?.[0].scrollTop;
- const scrollLeft = e.target?.children?.[0].scrollLeft;
- this.layoutDoc._scrollTop = this._outerRef.current!.scrollTop = scrollTop;
- this.layoutDoc._scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft;
+ if (e.target?.children) {
+ e.target.children[0].scrollLeft = 0;
+ const scrollTop = e.target.children[0].scrollTop;
+ const scrollLeft = e.target.children[0].scrollLeft;
+ this.layoutDoc._scrollTop = this._outerRef.current!.scrollTop = scrollTop;
+ this.layoutDoc._scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft;
+ }
}
async componentDidMount() {
const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField);
@@ -235,81 +247,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
e.stopPropagation();
}
- toggleAnnotationMode = () => {
- this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating;
- }
-
- urlEditor() {
- return (
- <div className="webBox-urlEditor"
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover} style={{ top: this._collapsed ? -70 : 0 }}>
- <div className="urlEditor">
- <div className="editorBase">
- <button className="editor-collapse"
- style={{
- top: this._collapsed ? 70 : 0,
- 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 className="webBox-buttons"
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover} style={{ display: this._collapsed ? "none" : "flex" }}>
- <div className="webBox-freeze" title={"Annotate"} style={{ background: this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
- <FontAwesomeIcon icon={faPen} size={"2x"} />
- </div>
- <div className="webBox-freeze" title={"Select"} style={{ background: !this.layoutDoc.isAnnotating ? "lightBlue" : "gray" }} onClick={this.toggleAnnotationMode} >
- <FontAwesomeIcon icon={faMousePointer} size={"2x"} />
- </div>
- <input className="webpage-urlInput"
- placeholder="ENTER URL"
- value={this._url}
- onDrop={this.onUrlDrop}
- onDragOver={this.onUrlDragover}
- onChange={this.onURLChange}
- onKeyDown={this.onValueKeyDown}
- onClick={(e) => {
- this._keyInput.current!.select();
- e.stopPropagation();
- }}
- ref={this._keyInput}
- />
- <div style={{
- display: "flex",
- flexDirection: "row",
- justifyContent: "space-between",
- maxWidth: "120px",
- }}>
- <button className="submitUrl" onClick={this.submitURL}
- onDragOver={this.onUrlDragover} onDrop={this.onUrlDrop}>
- GO
- </button>
- <button className="submitUrl" onClick={this.back}>
- <FontAwesomeIcon icon="caret-left" size="lg"></FontAwesomeIcon>
- </button>
- <button className="submitUrl" onClick={this.forward}>
- <FontAwesomeIcon icon="caret-right" size="lg"></FontAwesomeIcon>
- </button>
- </div>
- </div>
- </div>
- </div>
+ editToggleBtn() {
+ return <Tooltip title={<div className="dash-tooltip" >{`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}</div>}>
+ <div className="webBox-annotationToggle"
+ style={{ color: this.props.Document.isAnnotating ? "black" : "white", backgroundColor: this.props.Document.isAnnotating ? "white" : "black" }}
+ onClick={action(() => this.layoutDoc.isAnnotating = !this.layoutDoc.isAnnotating)}>
+ <FontAwesomeIcon icon="edit" size="sm" />
</div>
- );
+ </Tooltip>;
}
-
- @action
- toggleCollapse = () => {
- this._collapsed = !this._collapsed;
- }
-
-
-
_ignore = 0;
onPreWheel = (e: React.WheelEvent) => {
this._ignore = e.timeStamp;
@@ -433,12 +380,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
@undoBatch
@action
toggleNativeDimensions = () => {
- Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+ Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
}
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
const funcs: ContextMenuProps[] = [];
- funcs.push({ description: (this.layoutDoc.UseCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.UseCors = !this.layoutDoc.UseCors, icon: "snowflake" });
+ funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+ funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : NumCast(this.layoutDoc._nativeWidth), icon: "snowflake" });
cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
@@ -453,7 +401,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
if (field instanceof HtmlField) {
view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
- const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
+ const url = this.layoutDoc.useCors ? Utils.CorsProxy(field.url.href) : field.url.href;
+ // view = <iframe className="webBox-iframe" src={url} onLoad={e => { e.currentTarget.before((e.currentTarget.contentDocument?.body || e.currentTarget.contentDocument)?.children[0]!); e.currentTarget.remove(); }}
+
view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded}
// the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
@@ -465,25 +415,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
@computed
get content() {
const view = this.urlContent;
- const decInteracting = DocumentDecorations.Instance?.Interacting;
- const frozen = !this.props.isSelected() || decInteracting;
+ const frozen = !this.props.isSelected() || DocumentDecorations.Instance?.Interacting;
return (<>
- <div className={"webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !decInteracting ? "-interactive" : "")}
- style={{ width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%" }}
+ <div className={"webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
+ style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || (Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%") }}
onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
{view}
- </div>;
+ </div>
{!frozen ? (null) :
- <div className="webBox-overlay" style={{ pointerEvents: this.layoutDoc.isBackground ? undefined : "all" }}
+ <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>}
- {this.urlEditor()}
</>);
}
@@ -510,7 +458,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
TraceMobx();
return <div className="webBox-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight) }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
- <Annotation {...this.props} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
+ <Annotation {...this.props} showInfo={emptyFunction} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
}
</div>;
}
@@ -538,7 +486,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
highlight = (color: string) => {
// creates annotation documents for current highlights
const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
- const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) ? this.makeAnnotationDocument(color) : undefined;
+ const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) ? this.makeAnnotationDocument(color.replace(/[0-9.]*\)/, "0.3)")) : undefined;
annotationDoc && this.addDocument?.(annotationDoc);
return annotationDoc ?? undefined;
}
@@ -551,22 +499,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
e.preventDefault();
e.stopPropagation();
- const clipDoc = Doc.MakeAlias(this.dataDoc);
- clipDoc._fitWidth = true;
- clipDoc._width = this.marqueeWidth();
- clipDoc._height = this.marqueeHeight();
- clipDoc._scrollTop = this.marqueeY();
- const targetDoc = Docs.Create.TextDocument("", { _width: 125, _height: 125, title: "Note linked to " + this.props.Document.title });
- Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
- clipDoc.rootDocument = targetDoc;
- targetDoc.layoutKey = "layout";
- const annotationDoc = this.highlight("rgba(146, 245, 95, 0.467)"); // yellowish highlight color when dragging out a text selection
+ const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 125, 125);
+ FormattedTextBox.SelectOnLoad = targetDoc[Id];
+ const annotationDoc = this.highlight("rgba(173, 216, 230, 0.35)"); // hyperlink color
if (annotationDoc) {
DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
dragComplete: e => {
- if (!e.aborted && e.annoDragData && !e.annoDragData.linkedToDoc) {
- DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
+ if (!e.aborted && e.annoDragData && !e.annoDragData.linkDocument) {
+ e.annoDragData.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation");
annotationDoc.isLinkButton = true;
+ annotationDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.Document;
}
}
});
@@ -682,7 +624,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
transform: `scale(${this.props.ContentScaling()})`,
width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
height: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
- pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
}}
onContextMenu={this.specificContextMenu}>
<base target="_blank" />
@@ -690,7 +632,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{
width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%",
- pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc.isBackground ? "all" : "none"
+ pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc._isBackground ? "all" : "none"
}}
onWheel={e => e.stopPropagation()}
onPointerDown={this.onMarqueeDown}
@@ -709,14 +651,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}}>
<div className={"webBox-innerContent"} style={{
height: NumCast(this.layoutDoc.scrollHeight),
- pointerEvents: this.layoutDoc.isBackground ? "none" : undefined
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
}}>
- <CollectionFreeFormView {...this.props}
+ <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
annotationsKey={this.annotationKey}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
VisibleHeight={this.visibleHeiht}
focus={this.props.focus}
setPreviewCursor={this.setPreviewCursor}
@@ -733,6 +673,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
ScreenToLocalTransform={this.scrollXf}
renderDepth={this.props.renderDepth + 1}
docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
</CollectionFreeFormView>
</div>
@@ -740,6 +682,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
{this.annotationLayer}
<PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} />
</div >
+
+ {this.props.isSelected() ? this.editToggleBtn() : null}
</div>);
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 145ee8c2e..123946dea 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -5,13 +5,14 @@ import { Id } from "../../../../fields/FieldSymbols";
import { ObjectField } from "../../../../fields/ObjectField";
import { ComputedField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import { Transform } from "../../../util/Transform";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
interface IDashDocView {
node: any;
@@ -131,14 +132,6 @@ export class DashDocView extends React.Component<IDashDocView> {
if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
finalLayout.rootDocument = dashDoc.aliasOf;
}
- const layoutKey = StrCast(finalLayout.layoutKey);
- const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
- if (finalLayout !== dashDoc && finalKey) {
- const finalLayoutField = finalLayout[finalKey];
- if (finalLayoutField instanceof ObjectField) {
- finalLayout[finalKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${finalKey})`, { this: Doc.name });
- }
- }
this._finalLayout = finalLayout;
this._resolvedDataDoc = Cast(finalLayout.resolvedDataDoc, Doc, null);
return { finalLayout, resolvedDataDoc: Cast(finalLayout.resolvedDataDoc, Doc, null) };
@@ -175,7 +168,7 @@ export class DashDocView extends React.Component<IDashDocView> {
const outerStyle = {
position: "relative" as "relative",
textIndent: "0",
- border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray")),
+ border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")),
width: this.props.node.props.width,
height: this.props.node.props.height,
display: this.props.node.props.hidden ? "none" : "inline-block",
@@ -202,7 +195,7 @@ export class DashDocView extends React.Component<IDashDocView> {
({ dim, color }) => {
spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px";
spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px";
- outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
@@ -244,8 +237,6 @@ export class DashDocView extends React.Component<IDashDocView> {
addDocTab={this._textBox.props.addDocTab}
pinToPres={returnFalse}
renderDepth={self._textBox.props.renderDepth + 1}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
PanelWidth={finalLayout[WidthSym]}
PanelHeight={finalLayout[HeightSym]}
focus={this.outerFocus}
@@ -255,6 +246,8 @@ export class DashDocView extends React.Component<IDashDocView> {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this.props.tbox?.props.docFilters || returnEmptyFilter}
+ docRangeFilters={this.props.tbox?.props.docRangeFilters || returnEmptyFilter}
+ searchFilterDocs={this.props.tbox?.props.searchFilterDocs || returnEmptyDoclist}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 23cf1e79b..e16036000 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -18,7 +18,8 @@
.dashFieldView-labelSpan {
position: relative;
display: inline-block;
- font-size: small;
+ font-weight: normal;
+ background: rgba(0,0,0,0.1);
}
.dashFieldView-fieldSpan {
min-width: 20px;
@@ -27,6 +28,7 @@
position: relative;
display: inline;
background-color: rgba(155, 155, 155, 0.24);
+ font-weight: bold;
span {
min-width: 100%;
display: inline-block;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 8ae71c035..b39a845db 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,5 +1,5 @@
-import { IReactionDisposer, observable, runInAction, computed, action } from "mobx";
-import { Doc, DocListCast, Field } from "../../../../fields/Doc";
+import { IReactionDisposer, observable, computed, action } from "mobx";
+import { Doc, DocListCast, Field, DataSym } from "../../../../fields/Doc";
import { List } from "../../../../fields/List";
import { listSpec } from "../../../../fields/Schema";
import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
@@ -70,7 +70,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
DocServer.GetRefField(this.props.docid).
then(action(async dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
} else {
- this._dashDoc = this.props.tbox.props.DataDoc || this.props.tbox.dataDoc;
+ this._dashDoc = this.props.tbox.rootDoc;
}
}
componentWillUnmount() {
@@ -82,7 +82,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
if (this._dashDoc) {
- const dashVal = this._dashDoc[this._fieldKey] || (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
+ const dashVal = this._dashDoc[DataSym][this._fieldKey] ?? this._dashDoc[this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
const boolVal = Cast(fval, "boolean", null);
const strVal = Field.toString(fval as Field) || "";
@@ -92,7 +92,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
return <input
className="dashFieldView-fieldCheck"
type="checkbox" checked={boolVal}
- onChange={e => this._dashDoc![this._fieldKey] = e.target.checked}
+ onChange={e => {
+ if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = e.target.checked;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, e.target.checked, true);
+ }}
/>;
}
else // field value is a string, so display it as an editable span
@@ -106,7 +109,10 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
ref={r => {
r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
- r?.addEventListener("pointerdown", action((e) => this._showEnumerables = true));
+ r?.addEventListener("pointerdown", action((e) => {
+ this._showEnumerables = true;
+ e.stopPropagation();
+ }));
}} >
{strVal}
</span>;
@@ -149,16 +155,23 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
if (modText) {
// elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []);
- this._dashDoc![this._fieldKey] = modText;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true);
} // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
else if (nodeText.startsWith(":=")) {
- this._dashDoc![this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
+ this._dashDoc![DataSym][this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(2));
} else if (nodeText.startsWith("=:=")) {
Doc.Layout(this._textBoxDoc)[this._fieldKey] = ComputedField.MakeFunction(nodeText.substring(3));
} else {
- const splits = newText.split(this.multiValueDelimeter);
- if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
- this._dashDoc![this._fieldKey] = splits.length > 1 ? new List<string>(splits) : newText;
+ if (Number(newText).toString() === newText) {
+ if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
+ } else {
+ const splits = newText.split(this.multiValueDelimeter);
+ if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
+ const strVal = splits.length > 1 ? new List<string>(splits) : newText;
+ if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
+ Doc.SetInPlace(this._dashDoc!, this._fieldKey, strVal, true);
+ }
}
}
});
@@ -169,7 +182,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
onPointerDownEnumerables = async (e: any) => {
e.stopPropagation();
const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
- collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight");
+ collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right");
}
@@ -183,7 +196,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
}
if (container) {
const alias = Doc.MakeAlias(container.props.Document);
- alias.viewType = CollectionViewType.Time;
+ alias._viewType = CollectionViewType.Time;
let list = Cast(alias._columnHeaders, listSpec(SchemaHeaderField));
if (!list) {
alias._columnHeaders = list = new List<SchemaHeaderField>();
@@ -191,7 +204,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
list.map(c => c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb"));
list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb"));
alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey;
- this.props.tbox.props.addDocTab(alias, "onRight");
+ this.props.tbox.props.addDocTab(alias, "add:right");
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index afdd8fea2..dbf98a5e9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -32,8 +32,8 @@
.formattedTextBox-dictation {
height: 12px;
width: 10px;
- top: 0px;
- left: 0px;
+ bottom: 5px;
+ right: 8px;
position: absolute;
}
}
@@ -43,24 +43,21 @@
overflow: auto;
display: inline-block;
width: 100%;
- height: 100%;
+ height: unset;
}
-
+
.formattedTextBox-sidebar-handle {
position: absolute;
- top: calc(50% - 17.5px);
+ top: 0;
+ //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
width: 10px;
- height: 35px;
+ height: 100%;
+ max-height: 35px;
background: lightgray;
border-radius: 20px;
cursor:grabbing;
}
-.formattedTextBox-cont>.formattedTextBox-sidebar-handle {
- right: 0;
- left: unset;
-}
-
.formattedTextBox-sidebar,
.formattedTextBox-sidebar-inking {
border-left: dashed 1px black;
@@ -73,11 +70,6 @@
.collectionfreeformview-container {
position: relative;
}
-
- >.formattedTextBox-sidebar-handle {
- right: unset;
- left: -5;
- }
}
.formattedTextBox-sidebar-inking {
@@ -94,7 +86,9 @@
}
.formattedTextBox-inner-rounded, .formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner, .formattedTextBox-inner-selected {
+.formattedTextBox-inner,
+.formattedTextBox-inner-minimal,
+.formattedTextBox-inner-selected {
height: 100%;
white-space: pre-wrap;
.ProseMirror:hover {
@@ -112,6 +106,15 @@
border-width: 1px;
}
}
+.formattedTextBox-inner-rounded-selected,
+.formattedTextBox-inner-selected {
+ .ProseMirror {
+ padding:10px;
+ }
+ .ProseMirror:hover {
+ background: unset;
+ }
+}
// .menuicon {
// display: inline-block;
@@ -338,15 +341,6 @@ footnote::after {
.multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; vertical-align: top; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
}
-.formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner-selected {
- .ProseMirror {
- padding:10px;
- }
- .ProseMirror:hover {
- background: unset;
- }
-}
@media only screen and (max-width: 1000px) {
@import "../../globalCssVariables";
@@ -396,10 +390,11 @@ footnote::after {
width: 100%;
height: 100%;
}
-
+
.formattedTextBox-sidebar-handle {
position: absolute;
- top: calc(50% - 17.5px);
+ top: 0;
+ //top: calc(50% - 17.5px); // use this to center vertically -- make sure it looks okay for slide views
width: 10px;
height: 35px;
background: lightgray;
@@ -407,11 +402,6 @@ footnote::after {
cursor:grabbing;
}
- .formattedTextBox-cont>.formattedTextBox-sidebar-handle {
- right: 0;
- left: unset;
- }
-
.formattedTextBox-sidebar,
.formattedTextBox-sidebar-inking {
border-left: dashed 1px black;
@@ -423,11 +413,6 @@ footnote::after {
.collectionfreeformview-container {
position: relative;
}
-
- >.formattedTextBox-sidebar-handle {
- right: unset;
- left: -5;
- }
}
.formattedTextBox-sidebar-inking {
@@ -443,6 +428,7 @@ footnote::after {
left: 10%;
}
+ .formattedTextBox-inner-minimal,
.formattedTextBox-inner-rounded,
.formattedTextBox-inner {
height: 100%;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d4c9f74d5..a998386d8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,7 +1,5 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit, faSmile, faTextHeight, faUpload } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { isEqual } from "lodash";
+import { isEqual, max } from "lodash";
import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap, selectAll } from "prosemirror-commands";
@@ -17,15 +15,15 @@ import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, Wid
import { documentSchema } from '../../../../fields/documentSchemas';
import applyDevTools = require("prosemirror-dev-tools");
import { removeMarkWithAttrs } from "./prosemirrorPatches";
-import { Id } from '../../../../fields/FieldSymbols';
+import { Id, Copy } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { createSchema, makeInterface } from "../../../../fields/Schema";
+import { makeInterface } from "../../../../fields/Schema";
import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types";
-import { TraceMobx, OVERRIDE_ACL, GetEffectiveAcl } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
+import { TraceMobx, OVERRIDE_acl, GetEffectiveAcl } from '../../../../fields/util';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents, OmitKeys } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from '../../../documents/Documents';
@@ -33,8 +31,8 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { DictationManager } from '../../../util/DictationManager';
import { DragManager } from "../../../util/DragManager";
import { makeTemplate } from '../../../util/DropConverter';
-import buildKeymap, { updateBullets } from "./ProsemirrorExampleTransfer";
-import RichTextMenu, { RichTextMenuPlugin } from './RichTextMenu';
+import { buildKeymap, updateBullets } from "./ProsemirrorExampleTransfer";
+import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from "./RichTextRules";
//import { DashDocView } from "./DashDocView";
@@ -60,15 +58,16 @@ import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark } from './FormattedTextBoxComment';
import React = require("react");
import { DocumentManager } from '../../../util/DocumentManager';
-
-library.add(faEdit);
-library.add(faSmile, faTextHeight, faUpload);
+import { CollectionStackingView } from '../../collections/CollectionStackingView';
+import { CollectionViewType } from '../../collections/CollectionView';
+import { SnappingManager } from '../../../util/SnappingManager';
export interface FormattedTextBoxProps {
makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
xMargin?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
yMargin?: number;
+ dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded
}
export const GoogleRef = "googleDocId";
@@ -84,6 +83,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
public static Instance: FormattedTextBox;
public ProseRef?: HTMLDivElement;
public get EditorView() { return this._editorView; }
+ private _boxRef: React.RefObject<HTMLDivElement> = React.createRef();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
@@ -99,8 +99,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private _linkTime: number | null = null;
private _pause: boolean = false;
- @computed get _recording() { return this.dataDoc.audioState === "recording"; }
- set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; }
+ @computed get _recording() { return this.dataDoc?.audioState === "recording"; }
+ set _recording(value) {
+ this.dataDoc.audioState = value ? "recording" : undefined;
+ }
@observable private _entered = false;
@@ -225,7 +227,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
- const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value });
+ const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value });
const mval = this._editorView.state.schema.marks.metadataVal.create();
const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -239,20 +241,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
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");
- const curTemp = Cast(this.layoutDoc[this.props.fieldKey + "-textTemplate"], RichTextField); // the actual text in the text box
+ const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined; // the actual text in the text box
const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
const json = JSON.stringify(state.toJSON());
let unchanged = true;
const effectiveAcl = GetEffectiveAcl(this.dataDoc);
+ const removeSelection = (json: string | undefined) => {
+ return json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
+ };
if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) {
- if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
+ if (!this._applyingChange && removeSelection(json) !== removeSelection(curProto?.Data)) {
this._applyingChange = true;
(curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())));
- if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
- if (json.replace(/"selection":.*/, "") !== curLayout?.Data.replace(/"selection":.*/, "")) {
+ if ((!curTemp && !curProto) || curText || json.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
+ if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
if (!this._pause && !this.layoutDoc._timeStampOnEnter) {
timeStamp = setTimeout(() => this.pause(), 10 * 1000); // 10 seconds delay for time stamp
}
@@ -265,7 +270,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
!curText && tx.storedMarks?.map(m => m.type.name === "pFontSize" && (Doc.UserDoc().fontSize = this.layoutDoc._fontSize = m.attrs.fontSize));
!curText && tx.storedMarks?.map(m => m.type.name === "pFontFamily" && (Doc.UserDoc().fontFamily = this.layoutDoc._fontFamily = m.attrs.fontFamily));
this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText);
- this.dataDoc[this.props.fieldKey + "-noTemplate"] = (curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+ this.dataDoc[this.props.fieldKey + "-noTemplate"] = true;//(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
unchanged = false;
@@ -349,8 +354,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
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.rootDoc.customTitle) {
+ if (!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+ StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] &&
+ (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) {
let node = this._editorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild;
const str = node.textContent;
@@ -374,10 +380,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
}
}
- public highlightSearchTerms = (terms: string[], alt: boolean) => {
+ public highlightSearchTerms = (terms: string[], backward: boolean) => {
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);
const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true });
const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term));
@@ -385,24 +389,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let tr = this._editorView.state.tr;
const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
- if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) {
- this._searchIndex = 0;
- Doc.GetProto(this.dataDoc).resetSearch = undefined;
- }
- else {
- this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
- if (alt === true) {
- if (this._searchIndex > 1) {
- this._searchIndex += -2;
- }
- else if (this._searchIndex === 1) {
- this._searchIndex = length - 1;
- }
- else if (this._searchIndex === 0 && length !== 1) {
- this._searchIndex = length - 2;
- }
-
+ this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ if (backward === true) {
+ if (this._searchIndex > 1) {
+ this._searchIndex += -2;
+ }
+ else if (this._searchIndex === 1) {
+ this._searchIndex = length - 1;
+ }
+ else if (this._searchIndex === 0 && length !== 1) {
+ this._searchIndex = length - 2;
}
+
}
const lastSel = Math.min(flattened.length - 1, this._searchIndex);
@@ -470,12 +468,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
} else if (de.complete.linkDragData) {
de.complete.linkDragData.linkDropCallback = this.linkDrop;
}
+ else if (de.complete.annoDragData) {
+ de.complete.annoDragData.linkDropCallback = this.linkDrop;
+ }
}
- linkDrop = (data: DragManager.LinkDragData) => {
+ linkDrop = (data: { linkDocument?: Doc }) => {
const linkDoc = data.linkDocument!;
const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-";
const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : "";
- this.makeLinkToSelection(linkDoc[Id], anchor1Title, "onRight", anchor1Id);
+ this.makeLinkToSelection(linkDoc[Id], anchor1Title, "add:right", anchor1Id);
}
getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
@@ -509,10 +510,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (node.isTextblock) {
let index = 0, foundAt;
const ep = this.getNodeEndpoints(pm.state.doc, node);
- while (ep && (foundAt = node.textContent.slice(index).search(RegExp(find, "i"))) > -1) {
- const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
- ret.push(sel);
- index = index + foundAt + find.length;
+ const regexp = new RegExp(find.replace("*", ""), "i");
+ if (regexp) {
+ while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) {
+ const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1));
+ ret.push(sel);
+ index = index + foundAt + find.length;
+ }
}
} else {
node.content.forEach((child, i) => ret = ret.concat(this.findInNode(pm, child, find)));
@@ -556,17 +560,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
sidebarDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction,
- () => (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%"));
+ () => setTimeout(action(() => {
+ const prevWidth = this.sidebarWidth();
+ this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
+ this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+ })), false);
}
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
const bounds = this.CurrentDiv.getBoundingClientRect();
- this.layoutDoc._sidebarWidthPercent = "" + 100 * (1 - (e.clientX - bounds.left) / bounds.width) + "%";
+ this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
+ this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== "0%";
return false;
}
@undoBatch
@action
toggleNativeDimensions = () => {
- Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
+ Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
}
public static get DefaultLayout(): Doc | string | undefined {
@@ -576,6 +585,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const cm = ContextMenu.Instance;
const changeItems: ContextMenuProps[] = [];
+ changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
DocListCast(noteTypesDoc?.data).forEach(note => {
changeItems.push({
@@ -585,9 +595,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}), icon: "eye"
});
});
- changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
+ !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
const highlighting: ContextMenuProps[] = [];
- ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
+ const noviceHighlighting = ["My Text", "Text from Others"];
+ const expertHighlighting = ["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
+ (Doc.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
highlighting.push({
description: (FormattedTextBox._highlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
e.stopPropagation();
@@ -602,10 +614,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const uicontrols: ContextMenuProps[] = [];
- uicontrols.push({ description: `${this.layoutDoc._showSidebar ? "Hide" : "Show"} Sidebar`, event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" });
uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
- uicontrols.push({ description: `Create TimeStamp When ${this.layoutDoc._timeStampOnEnter ? "Pause" : "Enter"}`, event: () => this.layoutDoc._timeStampOnEnter = !this.layoutDoc._timeStampOnEnter, icon: "expand-arrows-alt" });
+ !Doc.UserDoc().noviceMode && uicontrols.push({ description: `Create TimeStamp When ${this.layoutDoc._timeStampOnEnter ? "Pause" : "Enter"}`, event: () => this.layoutDoc._timeStampOnEnter = !this.layoutDoc._timeStampOnEnter, icon: "expand-arrows-alt" });
!Doc.UserDoc().noviceMode && uicontrols.push({
description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto =>
proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
@@ -615,15 +626,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const appearance = cm.findByDescription("Appearance...");
const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
- this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
- Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
- appearanceItems.push({
- description: "Convert to be a template style", event: () => {
+ // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
+ !Doc.UserDoc().noviceMode && appearanceItems.push({
+ description: "Make Default Layout", event: () => {
if (!this.layoutDoc.isTemplateDoc) {
const title = StrCast(this.rootDoc.title);
this.rootDoc.title = "text";
this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title);
- } else {
+ } else if (!this.rootDoc.isTemplateDoc) {
const title = StrCast(this.rootDoc.title);
this.rootDoc.title = "text";
this.rootDoc.layout = (this.layoutDoc as Doc).layout as string;
@@ -637,12 +647,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields
this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
+ this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, "string", null);
}, 10);
}
+ Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc);
}, icon: "eye"
});
- appearanceItems.push({ description: "Create progressivized slide...", event: this.progressivizeText, icon: "desktop" });
+ !Doc.UserDoc().noviceMode && appearanceItems.push({ description: "Create progressivized slide...", event: this.progressivizeText, icon: "desktop" });
cm.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
const options = cm.findByDescription("Options...");
@@ -662,7 +674,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const newBullets: Doc[] = this.recursiveProgressivize(1, list)[0];
mainBulletList.push.apply(mainBulletList, newBullets);
}
- console.log(mainBulletList.length);
const title = Docs.Create.TextDocument(StrCast(this.rootDoc.title), { title: "Title", _width: 800, _height: 70, x: 20, y: -10, _fontSize: '20pt', backgroundColor: "rgba(0,0,0,0)", appearFrame: 0, _fontWeight: 700 });
mainBulletList.push(title);
const doc = Docs.Create.FreeformDocument(mainBulletList, {
@@ -717,7 +728,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
recordDictation = () => {
DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
+ interimHandler: this.setDictationContent,
continuous: { indefinite: false },
}).then(results => {
if (results && [DictationManager.Controls.Infringed].includes(results)) {
@@ -728,22 +739,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
stopDictation = (abort: boolean) => { DictationManager.Controls.stop(!abort); };
- recordBullet = async () => {
- const completedCue = "end session";
- const results = await DictationManager.Controls.listen({
- interimHandler: this.setCurrentBulletContent,
- continuous: { indefinite: false },
- terminators: [completedCue, "bullet", "next"]
- });
- if (results && [DictationManager.Controls.Infringed, completedCue].includes(results)) {
- DictationManager.Controls.stop();
- return;
- }
- this.nextBullet(this._editorView!.state.selection.to);
- setTimeout(this.recordBullet, 2000);
- }
-
- setCurrentBulletContent = (value: string) => {
+ setDictationContent = (value: string) => {
if (this._editorView) {
const state = this._editorView.state;
const now = Date.now();
@@ -758,33 +754,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
}
- const recordingStart = DateCast(this.props.Document.recordingStart).date.getTime();
- this._break = false;
- value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
const from = state.selection.from;
- const inserted = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
- this._editorView.dispatch(inserted.setSelection(TextSelection.create(inserted.doc, from, from + value.length + 1)));
- }
- }
-
- nextBullet = (pos: number) => {
- if (this._editorView) {
- const frag = Fragment.fromArray(this.newListItems(2));
- if (this._editorView.state.doc.resolve(pos).depth >= 2) {
- const slice = new Slice(frag, 2, 2);
- let state = this._editorView.state;
- this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice)));
- pos += 4;
- state = this._editorView.state;
- this._editorView.dispatch(state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos, pos)));
+ this._break = false;
+ if (this.props.Document.recordingStart) {
+ const recordingStart = DateCast(this.props.Document.recordingStart)?.date.getTime();
+ value = "" + (mark.attrs.modified * 1000 - recordingStart) / 1000 + value;
}
+ const tr = state.tr.insertText(value).addMark(from, from + value.length + 1, mark);
+ this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, from + value.length + 1)));
}
}
- private newListItems = (count: number) => {
- return numberRange(count).map(x => schema.nodes.list_item.create(undefined, schema.nodes.paragraph.create()));
- }
-
_keymap: any = undefined;
_rules: RichTextRules | undefined;
@computed get config() {
@@ -823,9 +803,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
tr = tr.addMark(pos, pos + node.nodeSize, link);
}
});
- OVERRIDE_ACL(true);
+ OVERRIDE_acl(true);
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
- OVERRIDE_ACL(false);
+ OVERRIDE_acl(false);
}
}
componentDidMount() {
@@ -848,19 +828,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
() => this.props.makeLink?.(),
(linkDoc: Opt<Doc>) => {
if (linkDoc) {
- const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-";
- const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : "";
- this.makeLinkToSelection(linkDoc[Id], anchor2Title, "onRight", anchor2Id);
+ const a1 = Cast(linkDoc.anchor1, Doc, null);
+ const a2 = Cast(linkDoc.anchor2, Doc, null);
+ const otherAnchor = Doc.AreProtosEqual(a1, this.rootDoc) ? a2 : a1;
+ const anchor2Title = StrCast(otherAnchor.title, "-untitled-");
+ const anchor2Id = otherAnchor?.[Id] || "";
+ this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id);
}
},
{ fireImmediately: true }
);
this._disposers.editorState = reaction(
() => {
- if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey + "-textTemplate"]) {
+ if (!this.dataDoc || !this.layoutDoc) return undefined;
+ if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey]) {
return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data;
}
- return Cast(this.layoutDoc[this.props.fieldKey + "-textTemplate"], RichTextField, null)?.Data;
+ return Cast(this.layoutDoc[this.props.fieldKey], RichTextField, null)?.Data;
},
incomingValue => {
if (incomingValue !== undefined && this._editorView && !this._applyingChange) {
@@ -870,7 +854,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.tryUpdateHeight();
}
}
- }
+ },
);
this._disposers.pullDoc = reaction(
() => this.props.Document[Pulls],
@@ -892,13 +876,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
);
this._disposers.autoHeight = reaction(
- () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight],
- () => setTimeout(() => this.tryUpdateHeight(), 0)
+ () => ({
+ width: NumCast(this.layoutDoc._width),
+ autoHeight: this.layoutDoc?._autoHeight
+ }),
+ ({ width, autoHeight }) => width !== undefined && setTimeout(() => this.tryUpdateHeight(), 0)
);
this._disposers.height = reaction(
- () => this.layoutDoc[HeightSym](),
+ () => NumCast(this.layoutDoc._height),
action(height => {
- if (height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
+ if (height !== undefined && height <= 20 && height < NumCast(this.layoutDoc._delayAutoHeight, 20)) {
this.layoutDoc._delayAutoHeight = height;
}
})
@@ -906,23 +893,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.setupEditor(this.config, this.props.fieldKey);
- this._disposers.searchAlt = reaction(() => this.rootDoc.searchMatchAlt,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], false) : this.unhighlightSearchTerms(),
- { fireImmediately: true });
- this._disposers.search = reaction(() => this.rootDoc.searchMatch,
- search => search ? this.highlightSearchTerms([Doc.SearchQuery()], true) : this.unhighlightSearchTerms(),
- { fireImmediately: this.rootDoc.searchMatch ? true : false });
-
- this._disposers.record = reaction(() => this._recording,
- () => {
- if (this._recording) {
- setTimeout(action(() => {
- this.stopDictation(true);
- setTimeout(() => this.recordDictation(), 500);
- }), 500);
- } else setTimeout(() => this.stopDictation(true), 0);
- }
- );
+ this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc),
+ search => search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(),
+ { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
+
+ this._disposers.selected = reaction(() => this.props.isSelected(), action(() => this._recording = false));
+
+ if (!this.props.dontRegisterView) {
+ this._disposers.record = reaction(() => this._recording,
+ () => {
+ if (this._recording) {
+ setTimeout(action(() => {
+ this.stopDictation(true);
+ setTimeout(() => this.recordDictation(), 500);
+ }), 500);
+ } else setTimeout(() => this.stopDictation(true), 0);
+ }
+ );
+ }
this._disposers.scrollToRegion = reaction(
() => StrCast(this.layoutDoc.scrollToLinkID),
async (scrollToLinkID) => {
@@ -932,10 +920,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const examinedNode = findLinkNode(node, editor);
if (examinedNode?.textContent) {
nodes.push(examinedNode);
- start += index;
+ !start && (start = index);
}
});
- return { frag: Fragment.fromArray(nodes), start: start };
+ return { frag: Fragment.fromArray(nodes), start };
};
const findLinkNode = (node: Node, editor: EditorView) => {
if (!node.isText) {
@@ -972,7 +960,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true }
);
- setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight, 0)));
+ setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight)));
}
pushToGoogleDoc = async () => {
@@ -1018,7 +1006,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (documentId) {
exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
}
- UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
+ exportState && UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
}
updateState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
@@ -1034,7 +1022,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}, 0);
dataDoc.title = exportState.title;
- this.rootDoc.customTitle = true;
+ this.dataDoc["title-custom"] = true;
dataDoc.unchanged = true;
} else {
delete dataDoc[GoogleRef];
@@ -1129,7 +1117,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type.name === "link");
const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }];
- const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true });
+ const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true });
marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
return node.mark(marks);
}
@@ -1137,8 +1125,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
private setupEditor(config: any, fieldKey: string) {
const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null);
- const useTemplate = !curText?.Text && this.layoutDoc[this.props.fieldKey + "-textTemplate"];
- const rtfField = Cast((useTemplate && this.layoutDoc[this.props.fieldKey + "-textTemplate"]) || this.dataDoc[fieldKey], RichTextField);
+ const rtfField = Cast((!curText?.Text && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
if (this.ProseRef) {
const self = this;
this._editorView?.destroy();
@@ -1174,11 +1161,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad;
- if (selectOnLoad && !this.props.dontRegisterView) {
+ if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
- FormattedTextBox.SelectOnLoadChar && this._editorView!.dispatch(this._editorView!.state.tr.insertText(FormattedTextBox.SelectOnLoadChar));
- FormattedTextBox.SelectOnLoadChar = "";
+ if (FormattedTextBox.SelectOnLoadChar && this._editorView) {
+ const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+ const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
+ const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+ const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
+ const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
+ this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
+ FormattedTextBox.SelectOnLoadChar = "";
+ } else if (curText?.Text) {
+ selectAll(this._editorView!.state, this._editorView?.dispatch);
+ this.startUndoTypingBatch();
+ }
}
selectOnLoad && this._editorView!.focus();
@@ -1201,8 +1198,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
componentWillUnmount() {
+ this.endUndoTypingBatch();
Object.values(this._disposers).forEach(disposer => disposer?.());
this._editorView?.destroy();
+ FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
}
_downEvent: any;
@@ -1230,7 +1229,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (this.props.onClick && e.button === 0 && !this.props.isSelected(false)) {
e.preventDefault();
}
- if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar
e.stopPropagation(); // if the text box is selected, then it consumes all down events
}
@@ -1248,7 +1247,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBoxComment.textBox = this;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
!this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
- FormattedTextBoxComment.update(editor, undefined, (e.target as any)?.className === "prosemirror-dropdownlink" ? (e.target as any).href : "");
+ const target = (e.target as any).parentElement; // hrefs are store don the database of the <a> node that wraps the hyerlink <span>
+ FormattedTextBoxComment.update(editor, undefined, target?.dataset?.targethrefs);
}
(e.nativeEvent as any).formattedHandled = true;
@@ -1276,10 +1276,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBoxComment.Hide();
if (FormattedTextBoxComment.linkDoc) {
if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
}
}
@@ -1316,7 +1316,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
onPointerWheel = (e: React.WheelEvent): void => {
// if a text note is not selected and scrollable, this prevents us from being able to scroll and zoom out at the same time
- if (this.props.isSelected(true) || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
+ if ((this.props.rootSelected(true) || this.props.isSelected(true)) || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
e.stopPropagation();
}
}
@@ -1339,7 +1339,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
if (!node && this.ProseRef) {
const lastNode = this.ProseRef.children[this.ProseRef.children.length - 1].children[this.ProseRef.children[this.ProseRef.children.length - 1].children.length - 1]; // get the last prosemirror div
- if (e.clientY > lastNode?.getBoundingClientRect().bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+ const boundsRect = lastNode?.getBoundingClientRect();
+ if (e.clientX > boundsRect.left && e.clientX < boundsRect.right &&
+ e.clientY > boundsRect.bottom) { // if we clicked below the last prosemirror div, then set the selection to be the end of the document
+ this._editorView?.focus();
this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, this._editorView!.state.doc.content.size)));
}
} else if ([this._editorView!.state.schema.nodes.ordered_list, this._editorView!.state.schema.nodes.listItem].includes(node?.type) &&
@@ -1415,14 +1418,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const self = this;
return new Plugin({
view(newView) {
- self.props.isSelected(true) && (RichTextMenu.Instance.view = newView);
+ self.props.isSelected(true) && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView);
return self.menuPlugin = new RichTextMenuPlugin({ editorProps: this.props });
}
});
}
public startUndoTypingBatch() {
- this._undoTyping = UndoManager.StartBatch("undoTyping");
+ !this._undoTyping && (this._undoTyping = UndoManager.StartBatch("undoTyping"));
}
public endUndoTypingBatch() {
@@ -1433,19 +1436,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
return wasUndoing;
}
+ public static LiveTextUndo: UndoManager.Batch | undefined;
public static HadSelection: boolean = false;
onBlur = (e: any) => {
FormattedTextBox.HadSelection = window.getSelection()?.toString() !== "";
- //DictationManager.Controls.stop(false);
this.endUndoTypingBatch();
this.doLinkOnDeselect();
+ FormattedTextBox.LiveTextUndo?.end();
+ FormattedTextBox.LiveTextUndo = undefined;
// move the richtextmenu offscreen
//if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
_lastTimedMark: Mark | undefined = undefined;
- onKeyPress = (e: React.KeyboardEvent) => {
+ onKeyDown = (e: React.KeyboardEvent) => {
+ // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container)
+ if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) {
+ return;
+ }
if (e.altKey) {
e.preventDefault();
return;
@@ -1478,9 +1487,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
}
- if (!this._undoTyping) {
- this.startUndoTypingBatch();
- }
+ this.startUndoTypingBatch();
}
ondrop = (eve: React.DragEvent) => {
@@ -1492,8 +1499,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
@action
tryUpdateHeight(limitHeight?: number) {
- let scrollHeight = this._ref.current?.scrollHeight;
- if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+ let scrollHeight = this.ProseRef?.scrollHeight || 0;
+ this.rootDoc[this.fieldKey + "-height"] = 0;
+ if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
scrollHeight = scrollHeight * NumCast(this.layoutDoc._viewScale, 1);
if (limitHeight && scrollHeight > limitHeight) {
scrollHeight = limitHeight;
@@ -1503,7 +1511,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight, 0);
const dh = NumCast(this.rootDoc._height, 0);
const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0));
- if (this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
+ if (!this.props.LayoutTemplateString && this.rootDoc !== this.layoutDoc.doc && !this.layoutDoc.resolvedDataDoc) {
// if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived...
console.log("Delayed height adjustment...");
setTimeout(() => {
@@ -1511,52 +1519,115 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
}, 10);
} else {
- this.layoutDoc._height = newHeight;
- this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
+ try {
+ const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+ const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
+ const finalHeight = newHeight + Math.max(0, outer);
+ const maxsidebar = !this.sidebarWidth() ? 0 : Array.from(this._boxRef.current!.getElementsByClassName("collectionStackingViewFieldColumn")).reduce((prev, ele) => Math.max(prev, Number(getComputedStyle(ele).height.replace("px", ""))), 0);
+ if (this.rootDoc._height !== finalHeight && finalHeight > maxsidebar) {
+ this.rootDoc._height = finalHeight;
+ this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
+ }
+ this.rootDoc[this.fieldKey + "-height"] = finalHeight;
+ } catch (e) { console.log("Error in tryUpdateHeight"); }
}
}
}
+ @computed get audioHandle() {
+ return !this.layoutDoc._showAudio ? (null) :
+ <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
+ <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" />
+ </div>;
+ }
+
+ @computed get sidebarHandle() {
+ const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.title).length;
+ return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) :
+ <div className="formattedTextBox-sidebar-handle"
+ style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightBlue" : undefined }}
+ onPointerDown={this.sidebarDown}
+ onClick={e => {
+ console.log(e);
+ }}
+ />;
+ }
+
+ @computed get sidebarCollection() {
+ const fitToBox = this.props.Document._fitToBox;
+ const collectionProps = {
+ ...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit,
+ PanelHeight: this.props.PanelHeight,
+ PanelWidth: this.sidebarWidth,
+ xMargin: 0,
+ yMargin: 0,
+ chromeStatus: "enabled",
+ scaleField: this.annotationKey + "-scale",
+ annotationsKey: this.annotationKey,
+ isAnnotationOverlay: true,
+ fitToBox: fitToBox,
+ focus: this.props.focus,
+ isSelected: this.props.isSelected,
+ select: emptyFunction,
+ active: this.annotationsActive,
+ ContentScaling: returnOne,
+ whenActiveChanged: this.whenActiveChanged,
+ removeDocument: this.removeDocument,
+ moveDocument: this.moveDocument,
+ addDocument: this.addDocument,
+ CollectionView: undefined,
+ ScreenToLocalTransform: this.sidebarScreenToLocal,
+ renderDepth: this.props.renderDepth + 1,
+ ContainingCollectionDoc: this.props.ContainingCollectionDoc,
+ };
+ return !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
+ <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
+ style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+ {this.layoutDoc.sidebarViewType === CollectionViewType.Freeform ? <CollectionFreeFormView {...collectionProps} /> : <CollectionStackingView {...collectionProps} />}
+ </div>;
+ }
+
@computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
- @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); }
+ @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
render() {
TraceMobx();
+ const selected = this.props.isSelected();
+ const active = this.active();
const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
- const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
- this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected
- if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
- setTimeout(() => FormattedTextBoxComment.Hide(), 0);
- }
- const selPad = this.props.isSelected() ? -10 : 0;
- const selclass = this.props.isSelected() ? "-selected" : "";
+ const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && !this.layoutDoc._isBackground;
+ selected && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props)); // need to make sure that we update a text box that is selected after updating the one that was deselected
+ if (!selected && FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); }
+ const minimal = this.props.ignoreAutoHeight;
+ const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
+ const selPad = Math.min(margins, 10);
+ const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
+ const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
return (
- <div className={"formattedTextBox-cont"}
+ <div className={"formattedTextBox-cont"} ref={this._boxRef}
style={{
transform: `scale(${scale})`,
transformOrigin: "top left",
width: `${100 / scale}%`,
height: `calc(${100 / scale}% - ${this.props.ChromeHeight?.() || 0}px)`,
- ...this.styleFromLayoutString(scale)
+ ...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
}}>
<div className={`formattedTextBox-cont`} ref={this._ref}
style={{
overflow: this.layoutDoc._autoHeight ? "hidden" : undefined,
width: "100%",
- height: this.props.height ? this.props.height : this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined,
+ height: this.props.height || (this.layoutDoc._autoHeight && this.props.renderDepth ? "max-content" : undefined),
background: Doc.UserDoc().renderStyle === "comic" ? "transparent" : this.props.background ? this.props.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.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
pointerEvents: interactive ? undefined : "none",
- fontSize: Cast(this.layoutDoc._fontSize, "string", null),
+ fontSize: this.props.fontSize || Cast(this.layoutDoc._fontSize, "string", null),
fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
- transition: "opacity 1s"
}}
onContextMenu={this.specificContextMenu}
- onKeyDown={this.onKeyPress}
+ onKeyDown={this.onKeyDown}
onFocus={this.onFocused}
onClick={this.onClick}
onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)}
@@ -1566,66 +1637,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
onMouseUp={this.onMouseUp}
onWheel={this.onPointerWheel}
onPointerEnter={action(() => this._entered = true)}
- onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
+ onPointerLeave={action(e => {
this._entered = false;
const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
for (let child: any = target; child; child = child?.parentElement) {
- if (child === this._ref.current!) {
- this._entered = true;
- }
+ child === this._ref.current! && (this._entered = true);
}
})}
onDoubleClick={this.onDoubleClick}
>
<div className={`formattedTextBox-outer`} ref={this._scrollRef}
- style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !this.props.active() ? "none" : undefined }}
+ style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined }}
onScroll={this.onscrolled} onDrop={this.ondrop} >
- <div className={`formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
+ <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget}
style={{
overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
- padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`,
- pointerEvents: !this.props.active() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
+ padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
+ pointerEvents: !active && !SnappingManager.GetIsDragging() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined
}}
/>
</div>
- {!this.layoutDoc._showSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
- <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} /> :
- <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")}
- style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <CollectionFreeFormView {...this.props}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.sidebarWidth}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
- scaleField={this.annotationKey + "-scale"}
- annotationsKey={this.annotationKey}
- isAnnotationOverlay={false}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- select={emptyFunction}
- active={this.annotationsActive}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={this.sidebarScreenToLocal}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
- </CollectionFreeFormView>
- <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} />
- </div>}
- {!this.layoutDoc._showAudio ? (null) :
- <div className="formattedTextBox-dictation"
- onPointerDown={e => {
- runInAction(() => this._recording = !this._recording);
- setTimeout(() => this._editorView!.focus(), 500);
- e.stopPropagation();
- }} >
- <FontAwesomeIcon className="formattedTextBox-audioFont"
- style={{ color: this._recording ? "red" : "blue", opacity: this._recording ? 1 : 0.5, display: this.props.isSelected() ? "" : "none" }} icon={"microphone"} size="sm" />
- </div>}
+ {this.sidebarCollection}
+ {this.sidebarHandle}
+ {this.audioHandle}
</div>
</div>
);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
index 6a403cb17..582ada6ce 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
@@ -13,11 +13,12 @@
.FormattedTextBoxComment {
background-color: white;
border: 8px solid white;
+ //width: 200px;
//display: flex;
.FormattedTextBoxComment-info {
- margin-bottom: 9px;
+ margin-bottom: 37px;
.FormattedTextBoxComment-title {
padding-right: 4px;
@@ -61,7 +62,7 @@
}
.FormattedTextBoxComment-preview-wrapper {
- width: 170px;
+ //width: 170px;
height: 170px;
overflow: hidden;
//padding-top: 5px;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 6f3984f39..5183e193b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -1,28 +1,28 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
+import { action, observable } from "mobx";
import { Mark, ResolvedPos } from "prosemirror-model";
import { EditorState, Plugin } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
+import wiki from "wikijs";
import { Doc, DocCastAsync, Opt } from "../../../../fields/Doc";
import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne, returnEmptyFilter } from "../../../../Utils";
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { schema } from "./schema_rts";
+import { Docs } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { LinkManager } from "../../../util/LinkManager";
import { Transform } from "../../../util/Transform";
+import { undoBatch } from "../../../util/UndoManager";
import { ContentFittingDocumentView } from "../ContentFittingDocumentView";
+import { DocumentLinksButton } from "../DocumentLinksButton";
+import { DocumentView } from "../DocumentView";
+import { LinkDocPreview } from "../LinkDocPreview";
import { FormattedTextBox } from "./FormattedTextBox";
import './FormattedTextBoxComment.scss';
+import { schema } from "./schema_rts";
import React = require("react");
-import { Docs } from "../../../documents/Documents";
-import wiki from "wikijs";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
-import { LinkManager } from "../../../util/LinkManager";
-import { LinkDocPreview } from "../LinkDocPreview";
-import { DocumentLinksButton } from "../DocumentLinksButton";
-import { Tooltip } from "@material-ui/core";
-import { undoBatch } from "../../../util/UndoManager";
export let formattedTextBoxCommentPlugin = new Plugin({
view(editorView) { return new FormattedTextBoxComment(editorView); }
@@ -60,6 +60,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
// this view appears when clicking on text that has a hyperlink which is configured to show a preview of its target.
// this will also display metadata information about text when the view is configured to display things like other people who authored text.
//
+
export class FormattedTextBoxComment {
static tooltip: HTMLElement;
static tooltipText: HTMLElement;
@@ -72,6 +73,13 @@ export class FormattedTextBoxComment {
static _deleteRef: Opt<HTMLDivElement | null>;
static _followRef: Opt<HTMLDivElement | null>;
+ static _nextRef: Opt<HTMLDivElement | null>;
+
+ static _lastState?: EditorState;
+ static _lastView?: EditorView;
+
+ @observable static _hrefInd = 0;
+ static _hrefs: string[] | undefined = [];
constructor(view: any) {
if (!FormattedTextBoxComment.tooltip) {
@@ -80,84 +88,67 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipInput.type = "checkbox";
FormattedTextBoxComment.tooltip = document.createElement("div");
FormattedTextBoxComment.tooltipText = document.createElement("div");
- FormattedTextBoxComment.tooltipText.style.width = "100%";
+ //FormattedTextBoxComment.tooltipText.style.width = "100%";
FormattedTextBoxComment.tooltipText.style.height = "100%";
FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
- FormattedTextBoxComment.tooltip.style.maxWidth = "200px";
+ FormattedTextBoxComment.tooltip.style.maxWidth = "400px";
FormattedTextBoxComment.tooltip.style.maxHeight = "235px";
- FormattedTextBoxComment.tooltip.style.width = "100%";
+ //FormattedTextBoxComment.tooltip.style.width = "100%";
FormattedTextBoxComment.tooltip.style.height = "100%";
FormattedTextBoxComment.tooltip.style.overflow = "hidden";
FormattedTextBoxComment.tooltip.style.display = "none";
- FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
+ // FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipInput);
FormattedTextBoxComment.tooltip.onpointerdown = async (e: PointerEvent) => {
const keep = e.target && (e.target as any).type === "checkbox" ? true : false;
const textBox = FormattedTextBoxComment.textBox;
- if (FormattedTextBoxComment.linkDoc && !keep && textBox) {
- if (FormattedTextBoxComment.linkDoc.author) {
-
- if (FormattedTextBoxComment._deleteRef && FormattedTextBoxComment._deleteRef.contains(e.target as any)) {
+ const linkDoc = FormattedTextBoxComment.linkDoc;
+ if (linkDoc && !keep && textBox) {
+ if (linkDoc.author) {
+ if (FormattedTextBoxComment._deleteRef?.contains(e.target as any)) {
this.deleteLink();
- } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) {
- if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
- } else {
- const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)), textBox.dataDoc) ?
- Cast(FormattedTextBoxComment.linkDoc.anchor2, Doc) : (Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc))
- || FormattedTextBoxComment.linkDoc);
- const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
-
- if (FormattedTextBoxComment.linkDoc.follow) {
- if (FormattedTextBoxComment.linkDoc.follow === "Default") {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
- } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in right tab") {
- if (target) { textBox.props.addDocTab(target, "onRight"); }
- } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in new tab") {
- if (target) { textBox.props.addDocTab(target, "inTab"); }
- }
- } else {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false);
- }
- }
+ } else if (FormattedTextBoxComment._nextRef?.contains(e.target as any)) {
+ FormattedTextBoxComment.showPreview(FormattedTextBoxComment._lastView!, FormattedTextBoxComment._lastState, FormattedTextBoxComment._hrefs?.[(++FormattedTextBoxComment._hrefInd) % FormattedTextBoxComment._hrefs?.length]);
} else {
- if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
- textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight");
+ FormattedTextBoxComment.linkDoc = undefined;
+ if (linkDoc.type !== DocumentType.LINK) {
+ textBox.props.addDocTab(linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document,
- (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation));
+ const target = LinkManager.getOppositeAnchor(linkDoc, textBox.dataDoc);
+ target && DocumentView.followLinkClick(linkDoc, textBox.dataDoc, textBox.props, e.shiftKey, e.altKey);
}
}
-
}
} 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, UseCors: true }), "onRight");
+ textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _fitWidth: true, _width: 200, _height: 400, useCors: true }), "add:right");
}
keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation(
FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark);
e.stopPropagation();
e.preventDefault();
};
- root && root.appendChild(FormattedTextBoxComment.tooltip);
+ root?.appendChild(FormattedTextBoxComment.tooltip);
}
}
@undoBatch
- @action
- deleteLink = () => {
+ deleteLink = action(() => {
FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null;
LinkDocPreview.LinkInfo = undefined;
DocumentLinksButton.EditLink = undefined;
- //FormattedTextBoxComment.tooltipText = undefined;
FormattedTextBoxComment.Hide();
- }
+ });
public static Hide() {
FormattedTextBoxComment.textBox = undefined;
+ FormattedTextBoxComment.linkDoc = undefined;
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
+ } catch (e) { }
}
public static SetState(textBox: any, start: number, end: number, mark: Mark) {
FormattedTextBoxComment.textBox = textBox;
@@ -185,14 +176,20 @@ export class FormattedTextBoxComment {
}
static update(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
- const state = view.state;
// Don't do anything if the document/selection didn't change
- if (lastState && lastState.doc.eq(state.doc) &&
- lastState.selection.eq(state.selection)) {
+ if (!forceUrl && lastState?.doc.eq(view.state.doc) && lastState?.selection.eq(view.state.selection)) {
return;
}
+ FormattedTextBoxComment._lastState = lastState;
+ FormattedTextBoxComment._lastView = view;
+ FormattedTextBoxComment._hrefs = forceUrl ? forceUrl.trim().split(" ") : undefined;
+ FormattedTextBoxComment._hrefInd = 0;
FormattedTextBoxComment.linkDoc = undefined;
+ FormattedTextBoxComment.showPreview(view, lastState, FormattedTextBoxComment._hrefs?.[FormattedTextBoxComment._hrefInd]);
+ }
+ static showPreview(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
+ const state = view.state;
const textBox = FormattedTextBoxComment.textBox;
if (!textBox || !textBox.props) {
return;
@@ -217,7 +214,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark);
}
if (mark && child && ((nbef && naft) || !noselection)) {
- FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString();
+ FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " on " + (new Date(mark.attrs.modified * 1000)).toLocaleString();
set = "";
FormattedTextBoxComment.tooltipInput.style.display = "";
}
@@ -230,10 +227,22 @@ export class FormattedTextBoxComment {
state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
child = child || (nbef && state.selection.$from.nodeBefore);
const mark = child ? findLinkMark(child.marks) : undefined;
- const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl;
+ const href = forceUrl || (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href;
if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
- FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
+ try {
+ ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
+ FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText);
+ } catch (e) { }
+ FormattedTextBoxComment.tooltipText = document.createElement("div");
+ FormattedTextBoxComment.tooltipText.className = "FormattedTextBoxComment-toolTipText";
+ FormattedTextBoxComment.tooltipText.style.width = "100%";
+ FormattedTextBoxComment.tooltipText.style.height = "100%";
+ FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
+ FormattedTextBoxComment.tooltipText.style.cursor = "pointer";
+ FormattedTextBoxComment.tooltipText.textContent = "URL: " + href;
(FormattedTextBoxComment.tooltipText as any).href = href;
+ FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+
if (href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
} else {
@@ -241,12 +250,9 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipText.style.overflow = "hidden";
}
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
FormattedTextBoxComment.tooltipText.textContent = "target not found...";
(FormattedTextBoxComment.tooltipText as any).href = "";
- const docTarget = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
- try {
- ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
- } catch (e) { }
docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = href;
@@ -259,34 +265,35 @@ export class FormattedTextBoxComment {
if (target?.author) {
FormattedTextBoxComment.showCommentbox("", view, nbef);
- const title = StrCast(target.title).length > 16 ?
- StrCast(target.title).substr(0, 16) + "..." : target.title;
-
+ const title = StrCast(target.title).length > 16 ? StrCast(target.title).substr(0, 16) + "..." : target.title;
const docPreview = <div className="FormattedTextBoxComment">
<div className="FormattedTextBoxComment-info">
<div className="FormattedTextBoxComment-title">
{title}
- {FormattedTextBoxComment.linkDoc.description !== "" ? <p className="FormattedTextBoxComment-description">
- {StrCast(FormattedTextBoxComment.linkDoc.description)}</p> : null}
+ {FormattedTextBoxComment.linkDoc.description === "" ? (null) :
+ <p className="FormattedTextBoxComment-description"> {StrCast(FormattedTextBoxComment.linkDoc.description)}</p>}
</div>
<div className="wrapper" style={{ float: "right" }}>
+ {(FormattedTextBoxComment._hrefs?.length || 0) <= 1 ? (null) : <Tooltip title={<><div className="dash-tooltip">Next Link</div></>} placement="top">
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._nextRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="chevron-right" color="white" size="sm" />
+ </div>
+ </Tooltip>}
<Tooltip title={<><div className="dash-tooltip">Delete Link</div></>} placement="top">
- <div className="FormattedTextBoxComment-button"
- ref={(r) => this._deleteRef = r}>
- <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white"
- size="sm" /></div>
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._deleteRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white" size="sm" />
+ </div>
</Tooltip>
<Tooltip title={<><div className="dash-tooltip">Follow Link</div></>} placement="top">
- <div className="FormattedTextBoxComment-button"
- ref={(r) => this._followRef = r}>
- <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white"
- size="sm" />
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._followRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white" size="sm" />
</div>
</Tooltip>
- </div> </div>
+ </div>
+ </div>
<div className="FormattedTextBoxComment-preview-wrapper">
<ContentFittingDocumentView
Document={target}
@@ -302,29 +309,29 @@ export class FormattedTextBoxComment {
pinToPres={returnFalse}
dontRegisterView={true}
docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
- renderDepth={0}
+ renderDepth={-1}
PanelWidth={() => 175} //Math.min(350, NumCast(target._width, 350))}
PanelHeight={() => 175} //Math.min(250, NumCast(target._height, 250))}
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
ContentScaling={returnOne}
- NativeWidth={() => target._nativeWidth ? NumCast(target._nativeWidth) : 0}
- NativeHeight={() => target._nativeHeight ? NumCast(target._nativeHeight) : 0}
+ NativeWidth={target._nativeWidth ? (() => NumCast(target._nativeWidth)) : undefined}
+ NativeHeight={target._natvieHeight ? (() => NumCast(target._nativeHeight)) : undefined}
/>
</div>
</div>;
-
-
FormattedTextBoxComment.showCommentbox("", view, nbef);
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltip.style.width = NumCast(target._width) ? `${NumCast(target._width)}` : "100%";
- FormattedTextBoxComment.tooltip.style.height = NumCast(target._height) ? `${NumCast(target._height)}` : "100%";
+ //FormattedTextBoxComment.tooltip.style.width = "100%";
+ FormattedTextBoxComment.tooltip.style.height = "100%";
}
}
});
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 8faf752b4..cb5823e86 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,11 +7,13 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { SelectionManager } from "../../../util/SelectionManager";
import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym } from "../../../../fields/Doc";
+import { Doc, DataSym, DocListCast } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
import { Id } from "../../../../fields/FieldSymbols";
import { Docs } from "../../../documents/Documents";
import { Utils } from "../../../../Utils";
+import { listSpec } from "../../../../fields/Schema";
+import { List } from "../../../../fields/List";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -33,7 +35,7 @@ export let updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?:
return tx2;
};
-export default function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap {
+export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap {
const keys: { [key: string]: any } = {};
function bind(key: string, cmd: any) {
@@ -45,6 +47,29 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
keys[key] = cmd;
}
+ /// bcz; Argh!! replace with an onEnter func that conditionally handles Enter
+ const addTextBox = (below: boolean, force?: boolean) => {
+ if (props.Document.treeViewOutlineMode) return true; // bcz: Arghh .. need to determine if this is an treeViewOutlineBox in which case Enter's are ignored..
+ const layoutDoc = props.Document;
+ const originalDoc = layoutDoc.rootDocument || layoutDoc;
+ if (force || props.Document._singleLine) {
+ const layoutKey = StrCast(originalDoc.layoutKey);
+ const newDoc = Doc.MakeCopy(originalDoc, true);
+ const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)];
+ newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+ if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
+ else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
+ if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
+ newDoc[layoutKey] = originalDoc[layoutKey];
+ }
+ Doc.GetProto(newDoc).text = undefined;
+ FormattedTextBox.SelectOnLoad = newDoc[Id];
+ props.addDocument(newDoc);
+ return true;
+ }
+ return false;
+ };
+
//History commands
bind("Mod-z", undo);
bind("Shift-Mod-z", redo);
@@ -66,6 +91,11 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
bind("Ctrl-i", wrapInList(schema.nodes.ordered_list));
bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
+ if (props.Document._singleLine) {
+ if (!props.LayoutTemplateString) return addTextBox(false, true);
+ return true;
+ }
const ref = state.selection;
const range = ref.$from.blockRange(ref.$to);
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -89,6 +119,8 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
});
bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab);
+ if (props.Document._singleLine) return true;
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
@@ -104,7 +136,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//Command to create a new Tab with a PDF of all the command shortcuts
bind("Mod-/", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _fitWidth: true, _width: 300, _height: 300 });
- props.addDocTab(newDoc, "onRight");
+ props.addDocTab(newDoc, "add:right");
});
//Commands to modify BlockType
@@ -136,47 +168,11 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
return tx;
};
- const addTextOnRight = (force: boolean) => {
- const layoutDoc = props.Document;
- const originalDoc = layoutDoc.rootDocument || layoutDoc;
- if (force || props.Document._singleLine) {
- const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Doc.MakeCopy(originalDoc, true);
- newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
- if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
- newDoc[layoutKey] = originalDoc[layoutKey];
- }
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
- props.addDocument(newDoc);
- return true;
- }
- return false;
- };
-
//Command to create a text document to the right of the selected textbox
- bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
- return addTextOnRight(true);
- });
+ bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => addTextBox(false, true));
//Command to create a text document to the bottom of the selected textbox
- bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- const layoutDoc = props.Document;
- const originalDoc = layoutDoc.rootDocument || layoutDoc;
- if (originalDoc instanceof Doc) {
- const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Doc.MakeCopy(originalDoc, true);
- newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
- if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
- newDoc[layoutKey] = originalDoc[layoutKey];
- }
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
- props.addDocument(newDoc);
- }
- });
+ bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => addTextBox(true, true));
// backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
@@ -199,7 +195,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
//newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
//command to break line
bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
- if (addTextOnRight(false)) return true;
+ if (addTextBox(true, false)) return true;
const trange = state.selection.$from.blockRange(state.selection.$to);
const path = (state.selection.$from as any).path;
const depth = trange ? liftTarget(trange) : undefined;
@@ -221,11 +217,13 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
const fromattrs = state.selection.$from.node().attrs;
if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
const tonode = tx3.selection.$to.node();
- const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
- splitMetadata(marks, tx4);
- if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
- dispatch(tx4);
- }
+ if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
+ const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks);
+ splitMetadata(marks, tx4);
+ if (!liftListItem(schema.nodes.list_item)(tx4, dispatch as ((tx: Transaction<Schema<any, any>>) => void))) {
+ dispatch(tx4);
+ }
+ } else dispatch(tx3.insertText("\r\n"));
})) {
return false;
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 213b341e8..68239a8f1 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,8 +1,8 @@
import React = require("react");
-import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
-import { faBold, faCaretDown, faChevronLeft, faEyeDropper, faHighlighter, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faItalic, faLink, faPaintRoller, faPalette, faStrikethrough, faSubscript, faSuperscript, faUnderline } from "@fortawesome/free-solid-svg-icons";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, IReactionDisposer, reaction } from "mobx";
+import { Tooltip } from "@material-ui/core";
+import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { lift, wrapIn } from "prosemirror-commands";
import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model";
@@ -11,27 +11,24 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Doc } from "../../../../fields/Doc";
import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast, BoolCast, NumCast } from "../../../../fields/Types";
+import { Cast, StrCast } from "../../../../fields/Types";
+import { TraceMobx } from "../../../../fields/util";
import { unimplementedFunction, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { LinkManager } from "../../../util/LinkManager";
import { SelectionManager } from "../../../util/SelectionManager";
-import AntimodeMenu, { AntimodeMenuProps } from "../../AntimodeMenu";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
import { FieldViewProps } from "../FieldView";
import { FormattedTextBox, FormattedTextBoxProps } from "./FormattedTextBox";
import { updateBullets } from "./ProsemirrorExampleTransfer";
import "./RichTextMenu.scss";
import { schema } from "./schema_rts";
-import { TraceMobx } from "../../../../fields/util";
-import { UndoManager, undoBatch } from "../../../util/UndoManager";
-import { Tooltip } from "@material-ui/core";
const { toggleMark } = require("prosemirror-commands");
-library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faOutdent, faIndent, faHandPointLeft, faHandPointRight, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller);
-
@observer
-export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
+export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: RichTextMenu;
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
@@ -78,24 +75,24 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
RichTextMenu.Instance = this;
this._canFade = false;
//this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
- this.Pinned = true;
+ runInAction(() => this.Pinned = true);
this.fontSizeOptions = [
- { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72pt", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize },
{ mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true },
- { mark: null, title: "", label: "13pt", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
+ { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
];
this.fontFamilyOptions = [
@@ -180,7 +177,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.activeListType = this.getActiveListStyle();
this.activeAlignment = this.getActiveAlignment();
this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
- this.activeFontSize = !activeSizes.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
+ this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "...";
this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
@@ -258,7 +255,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
marks.forEach(m => {
m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
- m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
+ m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px");
m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
});
}
@@ -387,7 +384,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
if (!self.TextView.props.isSelected(true)) {
switch (mark.type) {
case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break;
- case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "pt"); break;
+ case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
}
}
else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown");
@@ -428,7 +425,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
changeFontSize = (mark: Mark, view: EditorView) => {
if ((this.view?.state.selection.$from.pos || 0) < 2) {
- this.TextView.layoutDoc._fontSize = mark.attrs.fontSize;
+ this.TextView.layoutDoc._fontSize = mark.attrs.fontSize === Number(mark.attrs.fontSize) ? `${mark.attrs.fontSize}pt` : mark.attrs.fontSize;
}
const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
@@ -678,6 +675,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; }
@action setActiveColor(color: string) { this.activeFontColor = color; }
get TextView() { return (this.view as any)?.TextView as FormattedTextBox; }
+ get TextViewFieldKey() { return this.TextView?.props.fieldKey; }
createColorButton() {
const self = this;
@@ -813,7 +811,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
<div className="dropdown link-menu">
<p>Linked to:</p>
<input value={link} placeholder="Enter URL" onChange={onLinkChange} />
- <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "onRight")}>Apply hyperlink</button>
+ <button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "add:right")}>Apply hyperlink</button>
<div className="divider"></div>
<button className="remove-button" onPointerDown={e => this.deleteLink()}>Remove link</button>
</div>;
@@ -860,7 +858,7 @@ export default class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// TODO: should check for valid URL
@undoBatch
makeLinkToURL = (target: string, lcoation: string) => {
- ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target);
+ ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRadd:rightight", "", target);
}
@undoBatch
@@ -1021,6 +1019,7 @@ interface ButtonDropdownProps {
dropdownContent: JSX.Element;
openDropdownOnButton?: boolean;
link?: boolean;
+ pdf?: boolean;
}
@observer
@@ -1060,13 +1059,22 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> {
}, 0);
}
+
render() {
return (
<div className="button-dropdown-wrapper" ref={node => this.ref = node}>
- <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
- {this.props.button}
- <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
- </div>
+ {!this.props.pdf ?
+ <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.onDropdownClick}>
+ {this.props.button}
+ <div style={{ marginTop: "-8.5" }}><FontAwesomeIcon icon="caret-down" size="sm" /></div>
+ </div>
+ :
+ <>
+ {this.props.button}
+ <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}>
+ <FontAwesomeIcon icon="caret-down" size="sm" />
+ </button>
+ </>}
{this.showDropdown ? this.props.dropdownContent : (null)}
</div>
);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index dc1d8a2c8..921c0e128 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -3,13 +3,13 @@ import { NodeSelection, TextSelection } from "prosemirror-state";
import { DataSym, Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../../fields/Types";
import { returnFalse, Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
import { FormattedTextBox } from "./FormattedTextBox";
import { wrappingInputRule } from "./prosemirrorPatches";
-import RichTextMenu from "./RichTextMenu";
+import { RichTextMenu } from "./RichTextMenu";
import { schema } from "./schema_rts";
import { List } from "../../../../fields/List";
@@ -90,9 +90,9 @@ export class RichTextRules {
textDoc.inlineTextCount = numInlines + 1;
const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to
const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
- const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" });
+ const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" });
textDocInline.title = inlineFieldKey; // give the annotation its own title
- textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
+ textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
textDocInline._textContext = ComputedField.MakeFunction(`copyField(self.${inlineFieldKey})`);
@@ -267,19 +267,21 @@ export class RichTextRules {
// [[fieldKey]] => show field
// [[fieldKey:Doc]] => show field of doc
new InputRule(
- new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@\? \-0-9]+)?\]\]$/),
+ new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@\.\? \-0-9]+)?\]\]$/),
(state, match, start, end) => {
const fieldKey = match[1];
- const docid = match[3]?.substring(1);
+ const rawdocid = match[3]?.substring(1);
+ const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
const value = match[2]?.substring(1);
if (!fieldKey) {
+ const linkId = Utils.GenerateGuid();
if (docid) {
DocServer.GetRefField(docid).then(docx => {
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, }, docid);
- DocUtils.Publish(target, docid, returnFalse, returnFalse);
- DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500, }, docid);
+ DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to", undefined, linkId);
});
- const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
+ const allLinks = [{ href: Utils.prepend("/doc/" + docid), title: docid, targetId: docid, linkId }];
+ const link = state.schema.marks.linkAnchor.create({ allLinks, title: rawdocid, location: "add:right" });
return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
}
return state.tr;
@@ -297,15 +299,16 @@ export class RichTextRules {
// {{<layout>}} => show layout for this doc
// {{<layout> : Doc}} => show layout for another doc
new InputRule(
- new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_ \-0-9]+)?\}\}$/),
+ new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/),
(state, match, start, end) => {
const fieldKey = match[1] || "";
const fieldParam = match[2]?.replace("…", "...") || "";
- const docid = match[3]?.substring(1);
+ const rawdocid = match[3]?.substring(1);
+ const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
- const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
+ const docx = Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docid);
DocUtils.Publish(docx, docid, returnFalse, returnFalse);
}
});
@@ -321,7 +324,11 @@ export class RichTextRules {
(state, match, start, end) => {
const tag = match[1];
if (!tag) return state.tr;
- this.Document[DataSym]["#" + tag] = ".";
+ this.Document[DataSym]["#" + tag] = "#" + tag;
+ const tags = StrCast(this.Document[DataSym].tags, ":");
+ if (!tags.includes(`#${tag}:`)) {
+ this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`;
+ }
const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
return state.tr.deleteRange(start, end).insert(start, fieldView);
}),
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 33a080fe4..1767a04de 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import React = require("react");
+import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
export class DashDocView {
@@ -43,7 +44,7 @@ export class DashDocView {
this._outer = document.createElement("span");
this._outer.style.position = "relative";
this._outer.style.textIndent = "0";
- this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
this._outer.style.width = node.attrs.width;
this._outer.style.height = node.attrs.height;
this._outer.style.display = node.attrs.hidden ? "none" : "inline-block";
@@ -126,7 +127,7 @@ export class DashDocView {
this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => {
this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
- this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeWorkspace, Doc, null).darkScheme ? "dimGray" : "lightGray"));
+ this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray"));
}, { fireImmediately: true });
const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => {
@@ -144,8 +145,6 @@ export class DashDocView {
addDocTab={this._textBox.props.addDocTab}
pinToPres={returnFalse}
renderDepth={self._textBox.props.renderDepth + 1}
- NativeHeight={returnZero}
- NativeWidth={returnZero}
PanelWidth={finalLayout[WidthSym]}
PanelHeight={finalLayout[HeightSym]}
focus={this.outerFocus}
@@ -155,6 +154,8 @@ export class DashDocView {
bringToFront={emptyFunction}
dontRegisterView={false}
docFilters={this._textBox.props.docFilters}
+ docRangeFilters={this._textBox.props.docRangeFilters}
+ searchFilterDocs={this._textBox.props.searchFilterDocs}
ContainingCollectionView={this._textBox.props.ContainingCollectionView}
ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
@@ -177,17 +178,6 @@ export class DashDocView {
this._renderDisposer?.();
this._renderDisposer = reaction(() => {
- // if (!Doc.AreProtosEqual(finalLayout, dashDoc)) {
- // finalLayout.rootDocument = dashDoc.aliasOf; // bcz: check on this ... why is it here?
- // }
- const layoutKey = StrCast(finalLayout.layoutKey);
- const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1];
- if (finalLayout !== dashDoc && finalKey) {
- const finalLayoutField = finalLayout[finalKey];
- if (finalLayoutField instanceof ObjectField) {
- finalLayout[finalKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${finalKey})`, { this: Doc.name });
- }
- }
return { finalLayout, resolvedDataDoc: Cast(finalLayout.resolvedDataDoc, Doc, null) };
},
(res) => doReactRender(res.finalLayout, res.resolvedDataDoc),
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index ce784c3d9..cca7ea013 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -36,20 +36,21 @@ export const marks: { [index: string]: MarkSpec } = {
}],
toDOM(node: any) {
const targetids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, "");
+ const targethrefs = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.href, "");
const linkids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, "");
return node.attrs.docref && node.attrs.title ?
["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allLinks[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
- node.attrs.allLinks.length === 1 ?
- ["a", { ...node.attrs, class: linkids, dataTargetids: targetids, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0] :
- ["div", { class: "prosemirror-anchor" },
- ["span", { class: "prosemirror-linkBtn" },
- ["a", { ...node.attrs, class: linkids, dataTargetids: targetids, title: `${node.attrs.title}` }, 0],
- ["input", { class: "prosemirror-hrefoptions" }],
- ],
- ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
- ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
- )]
- ];
+ //node.attrs.allLinks.length === 1 ?
+ ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, "data-targethrefs": targethrefs, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0];
+ // ["div", { class: "prosemirror-anchor" },
+ // ["span", { class: "prosemirror-linkBtn" },
+ // ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
+ // ["input", { class: "prosemirror-hrefoptions" }],
+ // ],
+ // ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
+ // ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
+ // )]
+ // ];
}
},
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 1616500f6..64f7d27e5 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -148,7 +148,7 @@ export const nodes: { [index: string]: NodeSpec } = {
alt: { default: null },
title: { default: null },
float: { default: "left" },
- location: { default: "onRight" },
+ location: { default: "add:right" },
docid: { default: "" }
},
group: "inline",
@@ -177,7 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = {
height: { default: 100 },
title: { default: null },
float: { default: "right" },
- location: { default: "onRight" },
+ location: { default: "add:right" },
hidden: { default: false },
fieldKey: { default: "" },
docid: { default: "" },
diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js
index 0969ea4ef..746c93868 100644
--- a/src/client/views/nodes/formattedText/prosemirrorPatches.js
+++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js
@@ -146,7 +146,9 @@ function isInSetWithAttrs(mark, set, attrs) {
for (var i = 0; i < set.length; i++) {
if (set[i].type == mark) {
if (Array.from(Object.keys(attrs)).reduce((p, akey) => {
- return p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey]);
+ if (p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey])) return true;
+ set[i].attrs.allLinks = Array.from(set[i].attrs.allLinks).filter(a => !Array.from(attrs.allLinks.map(al => al.targetId)).includes(a.targetId) || !Array.from(attrs.allLinks.map(al => al.linkId).includes(a.linkId)))
+ return false;
}, true)) {
return set[i];
}
@@ -178,7 +180,7 @@ function removeMarkWithAttrs(tr, from, to, mark, attrs) {
var style = toRemove[i], found$1 = (void 0);
for (var j = 0; j < matched.length; j++) {
var m = matched[j];
- if (m.step == step - 1 && style.eq(matched[j].style)) { found$1 = m; }
+ if (m.step == step - 1 /*&& style.eq(matched[j].style)*/) { found$1 = m; } // bcz: not sure what this is even trying to do, but style.eq doesn't seem to exist anymore
}
if (found$1) {
found$1.to = end;