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.tsx82
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx1
-rw-r--r--src/client/views/nodes/ComparisonBox.scss2
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx31
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.scss7
-rw-r--r--src/client/views/nodes/DataVizBox/components/Chart.scss25
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.scss22
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/nodes/DocumentIcon.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx77
-rw-r--r--src/client/views/nodes/FieldView.tsx1
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx115
-rw-r--r--src/client/views/nodes/ImageBox.tsx19
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx2
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx12
-rw-r--r--src/client/views/nodes/LoadingBox.scss40
-rw-r--r--src/client/views/nodes/LoadingBox.tsx14
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx115
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx85
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx4
-rw-r--r--src/client/views/nodes/PDFBox.tsx16
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx221
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingView.tsx1
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx19
-rw-r--r--src/client/views/nodes/SliderBox.scss19
-rw-r--r--src/client/views/nodes/SliderBox.tsx103
-rw-r--r--src/client/views/nodes/VideoBox.scss2
-rw-r--r--src/client/views/nodes/VideoBox.tsx29
-rw-r--r--src/client/views/nodes/WebBox.scss1
-rw-r--r--src/client/views/nodes/WebBox.tsx148
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx147
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx29
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts7
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/nodes/trails/PresBox.scss15
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx270
-rw-r--r--src/client/views/nodes/trails/PresElementBox.scss4
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx60
39 files changed, 1093 insertions, 661 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 7c409c38c..8d80f1364 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,14 +1,15 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { DateField } from '../../../fields/DateField';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc } from '../../../fields/Doc';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, DateCast, NumCast } from '../../../fields/Types';
import { AudioField, nullAudio } from '../../../fields/URLField';
import { emptyFunction, formatTime, returnFalse, setupMoveUpEvents } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
+import { Docs, DocUtils } from '../../documents/Documents';
import { Networking } from '../../Network';
import { DragManager } from '../../util/DragManager';
import { LinkManager } from '../../util/LinkManager';
@@ -18,6 +19,7 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import './AudioBox.scss';
+import { DocFocusOptions } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { PinProps, PresBox } from './trails';
@@ -66,7 +68,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
_stream: MediaStream | undefined; // passed to MediaRecorder, records device input audio
_play: any = null; // timeout for playback
- @observable _stackedTimeline: any; // CollectionStackedTimeline ref
+ @observable _stackedTimeline: CollectionStackedTimeline | null | undefined; // CollectionStackedTimeline ref
@observable _finished: boolean = false; // has playback reached end of clip
@observable _volume: number = 1;
@observable _muted: boolean = false;
@@ -134,16 +136,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
- const anchor =
- CollectionStackedTimeline.createAnchor(
- this.rootDoc,
- this.dataDoc,
- this.annotationKey,
- this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
- undefined,
- undefined,
- addAsAnnotation
- ) || this.rootDoc;
+ const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
+ const anchor = addAsAnnotation
+ ? CollectionStackedTimeline.createAnchor(
+ this.rootDoc,
+ this.dataDoc,
+ this.annotationKey,
+ this._ele?.currentTime || Cast(this.props.Document._layout_currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined),
+ undefined,
+ undefined,
+ addAsAnnotation
+ ) || this.rootDoc
+ : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc });
+
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
return anchor;
};
@@ -418,7 +423,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
};
// plays link
- playLink = (link: Doc) => {
+ playLink = (link: Doc, options: DocFocusOptions) => {
if (link.annotationOn === this.rootDoc) {
if (!this.layoutDoc.dontAutoPlayFollowedLinks) {
this.playFrom(this.timeline?.anchorStart(link) || 0, this.timeline?.anchorEnd(link));
@@ -473,8 +478,34 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
};
// for trim button, double click displays full clip, single displays curr trim bounds
+ onResetPointerDown = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ this.timeline &&
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ returnFalse,
+ action(e => {
+ if (this.timeline?.IsTrimming !== TrimScope.None) {
+ this.timeline?.CancelTrimming();
+ } else {
+ this.beginEndtime = this.timeline?.trimEnd;
+ this.beginStarttime = this.timeline?.trimStart;
+ this.startTrim(TrimScope.All);
+ }
+ })
+ );
+ };
+
+ beginEndtime: number | undefined;
+ beginStarttime: number | undefined;
+
+ // for trim button, double click displays full clip, single displays curr trim bounds
onClipPointerDown = (e: React.PointerEvent) => {
e.stopPropagation();
+ this.beginEndtime = this.timeline?.trimEnd;
+ this.beginStarttime = this.timeline?.trimStart;
this.timeline &&
setupMoveUpEvents(
this,
@@ -525,7 +556,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
r,
(e, de) => {
const [xp, yp] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
- de.complete.docDragData && this.timeline.internalDocDrop(e, de, de.complete.docDragData, xp);
+ de.complete.docDragData && this.timeline?.internalDocDrop(e, de, de.complete.docDragData, xp);
},
this.layoutDoc,
undefined
@@ -587,9 +618,22 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
</div>
{!this.miniPlayer && (
- <div className="audiobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish' : 'trim'} onPointerDown={this.onClipPointerDown}>
- <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} size={'1x'} />
- </div>
+ <>
+ <Tooltip title={<>trim audio</>}>
+ <div className="audiobox-button" onPointerDown={this.onClipPointerDown}>
+ <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} size={'1x'} />
+ </div>
+ </Tooltip>
+ {this.timeline?.IsTrimming == TrimScope.None && !NumCast(this.layoutDoc.clipStart) && NumCast(this.layoutDoc.clipEnd) === this.rawDuration ? (
+ <></>
+ ) : (
+ <Tooltip title={<>{this.timeline?.IsTrimming !== TrimScope.None ? 'Cancel trimming' : 'Edit original timeline'}</>}>
+ <div className="audiobox-button" onPointerDown={this.onResetPointerDown}>
+ <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'cancel' : 'arrows-left-right'} size={'1x'} />
+ </div>
+ </Tooltip>
+ )}
+ </>
)}
</div>
<div className="controls-right">
@@ -654,7 +698,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed get renderTimeline() {
return (
<CollectionStackedTimeline
- ref={action((r: any) => (this._stackedTimeline = r))}
+ ref={action((r: CollectionStackedTimeline | null) => (this._stackedTimeline = r))}
{...this.props}
CollectionFreeFormDocumentView={undefined}
dataFieldKey={this.fieldKey}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 6710cee63..4a438f826 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -40,7 +40,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
{ key: 'opacity', val: 1 },
{ key: '_currentFrame' },
{ key: 'freeform_scale', val: 1 },
- { key: 'freeform_scale', val: 1 },
{ key: 'freeform_panX' },
{ key: 'freeform_panY' },
]; // fields that are configured to be animatable using animation frames
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index a12f1c12b..39c864b2b 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -60,6 +60,8 @@
opacity: 0.8;
pointer-events: all;
cursor: pointer;
+ width: 15px;
+ height: 15px;
}
.clear-button.before {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index a334e75f1..b09fcd882 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -109,12 +109,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
};
@undoBatch
- clearDoc = (e: React.MouseEvent, fieldKey: string) => {
- e.stopPropagation; // prevent click event action (slider movement) in registerSliding
- delete this.dataDoc[fieldKey];
- };
+ clearDoc = (fieldKey: string) => delete this.dataDoc[fieldKey];
+
moveDoc = (doc: Doc, addDocument: (document: Doc | Doc[]) => boolean, which: string) => this.remDoc(doc, which) && addDocument(doc);
addDoc = (doc: Doc, which: string) => {
+ if (this.dataDoc[which]) return false;
this.dataDoc[which] = doc;
return true;
};
@@ -128,6 +127,24 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
whenChildContentsActiveChanged = action((isActive: boolean) => (this._isAnyChildContentActive = isActive));
+ closeDown = (e: React.PointerEvent, which: string) => {
+ setupMoveUpEvents(
+ this,
+ e,
+ e => {
+ const de = new DragManager.DocumentDragData([DocCast(this.dataDoc[which])], 'move');
+ de.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
+ this.clearDoc(which);
+ return addDocument(doc);
+ };
+ de.canEmbed = true;
+ DragManager.StartDocumentDrag([this._closeRef.current!], de, e.clientX, e.clientY);
+ return true;
+ },
+ emptyFunction,
+ e => this.clearDoc(which)
+ );
+ };
docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
if (property === StyleProp.PointerEvents) return 'none';
return this.props.styleProvider?.(doc, props, property);
@@ -136,13 +153,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true);
remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true);
remDoc2 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_2'), true);
+ _closeRef = React.createRef<HTMLDivElement>();
render() {
const clearButton = (which: string) => {
return (
<div
+ ref={this._closeRef}
className={`clear-button ${which}`}
- onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding
- onClick={e => this.clearDoc(e, which)}>
+ onPointerDown={e => this.closeDown(e, which)} // prevent triggering slider movement in registerSliding
+ >
<FontAwesomeIcon className={`clear-button ${which}`} icon="times" size="sm" />
</div>
);
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss
index a69881b7c..430446c06 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.scss
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss
@@ -1,12 +1,15 @@
.dataviz {
- overflow: scroll;
+ overflow: auto;
height: 100%;
width: 100%;
- .datatype-button{
+ .datatype-button {
display: flex;
flex-direction: row;
}
+ .button-container {
+ pointer-events: unset;
+ }
}
.start-message {
margin: 10px;
diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss
index 50dfe7f05..3072d0907 100644
--- a/src/client/views/nodes/DataVizBox/components/Chart.scss
+++ b/src/client/views/nodes/DataVizBox/components/Chart.scss
@@ -6,10 +6,10 @@
margin-top: 10px;
overflow-y: visible;
- .graph{
+ .graph {
overflow: visible;
}
- .graph-title{
+ .graph-title {
align-items: center;
font-size: larger;
display: flex;
@@ -29,7 +29,7 @@
position: relative;
margin-bottom: -35px;
}
- .selected-data{
+ .selected-data {
align-items: center;
text-align: center;
display: flex;
@@ -44,10 +44,10 @@
stroke-width: 2px;
}
}
-
- .histogram-bar{
+
+ .histogram-bar {
outline: thin solid black;
- &.hover{
+ &.hover {
outline: 3px solid black;
outline-offset: -3px;
}
@@ -91,13 +91,22 @@
.tableBox {
display: flex;
flex-direction: column;
+ height: calc(100% - 40px); // bcz: hack 40px is the size of the button rows
+ .table {
+ height: 100%;
+ }
}
-.table-container{
+.table-container {
overflow: scroll;
margin: 5px;
margin-left: 25px;
margin-right: 10px;
margin-bottom: 0;
+ tr td {
+ height: 40px !important; // bcz: hack. you can't set a <tr> height directly, but you can set the height of all of it's <td>s. So this is the height of a tableBox row.
+ padding: 0 !important;
+ vertical-align: middle !important;
+ }
}
.selectAll-buttons {
display: flex;
@@ -106,4 +115,4 @@
margin-top: 5px;
margin-right: 10px;
float: right;
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.scss b/src/client/views/nodes/DataVizBox/components/TableBox.scss
deleted file mode 100644
index 1264d6a46..000000000
--- a/src/client/views/nodes/DataVizBox/components/TableBox.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-.table {
- margin-top: 10px;
- margin-bottom: 10px;
- margin-left: 10px;
- margin-right: 10px;
-}
-
-.table-row {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- padding: 5px;
- border-bottom: 1px solid #ccc;
-}
-
-.table-container {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 8ac9d6804..e1de2fa76 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -41,7 +41,6 @@ import { PhysicsSimulationBox } from './PhysicsBox/PhysicsSimulationBox';
import { RecordingBox } from './RecordingBox';
import { ScreenshotBox } from './ScreenshotBox';
import { ScriptingBox } from './ScriptingBox';
-import { SliderBox } from './SliderBox';
import { PresBox } from './trails/PresBox';
import { VideoBox } from './VideoBox';
import { WebBox } from './WebBox';
@@ -240,7 +239,6 @@ export class DocumentContentsView extends React.Component<
FontIconBox,
LabelBox,
EquationBox,
- SliderBox,
FieldView,
CollectionFreeFormView,
CollectionDockingView,
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 6e2ed72b8..bccbd66e8 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -9,6 +9,7 @@ import { action, observable } from 'mobx';
import { Id } from '../../../fields/FieldSymbols';
import { factory } from 'typescript';
import { LightboxView } from '../LightboxView';
+import { SettingsManager } from '../../util/SettingsManager';
@observer
export class DocumentIcon extends React.Component<{ view: DocumentView; index: number }> {
@@ -29,6 +30,7 @@ export class DocumentIcon extends React.Component<{ view: DocumentView; index: n
pointerEvents: 'all',
opacity: this._hovered ? 0.3 : 1,
position: 'absolute',
+ background: SettingsManager.userBackgroundColor,
transform: `translate(${(left + right) / 2}px, ${top}px)`,
}}>
<Tooltip title={<>{this.props.view.rootDoc.title}</>}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 74eee49e0..e4fc6c4a2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
-import { AclPrivate, Animation, DocData, Width } from '../../../fields/DocSymbols';
+import { AclPrivate, Animation, AudioPlay, DocData, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -100,6 +100,7 @@ export interface DocFocusOptions {
effect?: Doc; // animation effect for focus
noSelect?: boolean; // whether target should be selected after focusing
playAudio?: boolean; // whether to play audio annotation on focus
+ playMedia?: boolean; // whether to play start target videos
openLocation?: OpenWhere; // where to open a missing document
zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections
toggleTarget?: boolean; // whether to toggle target on and off
@@ -120,6 +121,7 @@ export interface DocComponentView {
reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views
select?: (ctrlKey: boolean, shiftKey: boolean) => void;
+ focus?: (textAnchor: Doc, options: DocFocusOptions) => Opt<number>;
menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
isAnyChildContentActive?: () => boolean; // is any child content of the document active
onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
@@ -392,18 +394,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dragData.treeViewDoc = this.props.treeViewDoc;
dragData.removeDocument = this.props.removeDocument;
dragData.moveDocument = this.props.moveDocument;
+ dragData.draggedViews = [this.props.DocumentView()];
dragData.canEmbed = this.rootDoc.dragAction ?? this.props.dragAction ? true : false;
- const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
- ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
DragManager.StartDocumentDrag(
selected.map(dv => dv.docView!._mainCont.current!),
dragData,
x,
y,
- { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) },
- () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))
+ { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart && !this.props.dontHideOnDrag) }
); // this needs to happen after the drop event is processed.
- ffview?.setupDragLines(false);
}
}
@@ -493,11 +492,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const sendToBack = e.altKey;
this._singleClickFunc =
- clickFunc ??
- (() =>
- sendToBack
- ? this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.bringToFront(this.rootDoc, true)
- : this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ?? this.props.select(e.ctrlKey || e.metaKey || e.shiftKey));
+ // prettier-ignore
+ clickFunc ?? (() => (sendToBack ? this.props.DocumentView().props.bringToFront(this.rootDoc, true) :
+ this._componentView?.select?.(e.ctrlKey || e.metaKey, e.shiftKey) ??
+ this.props.select(e.ctrlKey || e.metaKey || e.shiftKey)));
const waitFordblclick = this.props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
if ((clickFunc && waitFordblclick !== 'never') || waitFordblclick === 'always') {
this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
@@ -629,7 +627,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
if (linkdrag) {
linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
- if (linkdrag.linkSourceDoc) {
+ if (linkdrag.linkSourceDoc && linkdrag.linkSourceDoc !== this.rootDoc) {
if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
}
@@ -700,7 +698,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const cm = ContextMenu.Instance;
- if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
+ if (!cm || (e as any)?.nativeEvent?.SchemaHandled || DocumentView.ExploreMode) return;
if (e && !(e.nativeEvent as any).dash) {
const onDisplay = () => {
@@ -761,10 +759,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
onClicks.push({ description: this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' });
- onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
+ !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
!existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
} else if (LinkManager.Links(this.Document).length) {
- onClicks.push({ description: 'Select on Click', event: () => this.noOnClick(), icon: 'link' });
+ onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' });
onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' });
!existingOnClick && cm.addItem({ description: 'OnClick...', subitems: onClicks, icon: 'mouse-pointer' });
}
@@ -798,7 +796,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
!more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
}
const constantItems: ContextMenuProps[] = [];
- if (!Doc.IsSystem(this.rootDoc)) {
+ if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._type_collection !== CollectionViewType.Docking) {
constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
(this.rootDoc._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) {
@@ -965,6 +963,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
case StyleProp.ShowTitle: return '';
case StyleProp.PointerEvents: return 'none';
case StyleProp.Highlighting: return undefined;
+ case StyleProp.Opacity: {
+ const filtered = DocUtils.FilterDocs(this.directLinks, this.props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines);
+ return filtered.some(link => link._link_displayArrow) ? 0 : undefined;
+ }
}
return this.props.styleProvider?.(doc, props, property);
};
@@ -1052,7 +1054,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
}
};
- runInAction(() => (dataDoc.audioAnnoState = 'recording'));
+ //runInAction(() => (dataDoc.audioAnnoState = 'recording'));
recorder.start();
const stopFunc = () => {
recorder.stop();
@@ -1069,23 +1071,30 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const audioAnnoState = this.dataDoc.audioAnnoState ?? 'stopped';
const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '_audioAnnotations'], listSpec(AudioField), null);
const anno = audioAnnos?.lastElement();
- if (anno instanceof AudioField && audioAnnoState === 'stopped') {
- new Howl({
- src: [anno.url.href],
- format: ['mp3'],
- autoplay: true,
- loop: false,
- volume: 0.5,
- onend: action(() => (self.dataDoc.audioAnnoState = 'stopped')),
- });
- this.dataDoc.audioAnnoState = 'playing';
+ if (anno instanceof AudioField) {
+ switch (audioAnnoState) {
+ case 'stopped':
+ this.dataDoc[AudioPlay] = new Howl({
+ src: [anno.url.href],
+ format: ['mp3'],
+ autoplay: true,
+ loop: false,
+ volume: 0.5,
+ onend: action(() => (self.dataDoc.audioAnnoState = 'stopped')),
+ });
+ this.dataDoc.audioAnnoState = 'playing';
+ break;
+ case 'playing':
+ this.dataDoc[AudioPlay]?.stop();
+ this.dataDoc.audioAnnoState = 'stopped';
+ break;
+ }
}
};
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
@computed get innards() {
TraceMobx();
- const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
const showTitle = this.layout_showTitle?.split(':')[0];
const showTitleHover = this.layout_showTitle?.includes(':hover');
const captionView = !this.layout_showCaption ? null : (
@@ -1093,8 +1102,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
className="documentView-captionWrapper"
style={{
pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
- minWidth: 50 * ffscale(),
- maxHeight: `max(100%, ${20 * ffscale()}px)`,
background: StrCast(this.layoutDoc._backgroundColor, 'rgba(0,0,0,0.2)'),
color: lightOrDark(StrCast(this.layoutDoc._backgroundColor, 'black')),
}}>
@@ -1103,7 +1110,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
yPadding={10}
xPadding={10}
fieldKey={this.layout_showCaption}
- fontSize={12 * Math.max(1, (2 * ffscale()) / 3)}
styleProvider={this.captionStyleProvider}
dontRegisterView={true}
noSidebar={true}
@@ -1114,7 +1120,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div>
);
const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
- const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userVariantColor));
+ const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor));
const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', '');
const titleView = !showTitle ? null : (
<div
@@ -1124,7 +1130,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
position: this.headerMargin ? 'relative' : 'absolute',
height: this.titleHeight,
width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button
- color: lightOrDark(background),
+ color: background === 'transparent' ? SettingsManager.userColor : lightOrDark(background),
background,
pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
}}>
@@ -1138,14 +1144,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
display={'block'}
fontSize={10}
GetValue={() => {
- this.props.select(false);
return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle;
}}
SetValue={undoBatch((input: string) => {
if (input?.startsWith('#')) {
- if (this.props.layout_showTitle) {
+ if (this.rootDoc.layout_showTitle) {
this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined;
- } else {
+ } else if (!this.props.layout_showTitle) {
Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'author_date';
}
} else {
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4ebf22ddf..f014f842e 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -34,7 +34,6 @@ export interface FieldViewProps extends DocumentViewSharedProps {
backgroundColor?: string;
treeViewDoc?: Doc;
color?: string;
- fontSize?: number;
height?: number;
width?: number;
noSidebar?: boolean;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 64f289cf7..d2e1293da 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -6,8 +6,8 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { Utils } from '../../../../Utils';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { SelectionManager } from '../../../util/SelectionManager';
import { SettingsManager } from '../../../util/SettingsManager';
@@ -19,7 +19,6 @@ import { SelectedDocView } from '../../selectedDoc';
import { StyleProp } from '../../StyleProvider';
import { OpenWhere } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
-import { RichTextMenu } from '../formattedText/RichTextMenu';
import './FontIconBox.scss';
import TrailsIcon from './TrailsIcon';
@@ -176,86 +175,88 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
);
}
+ dropdownItemDown = (e: React.PointerEvent, value: string | number) => {
+ setupMoveUpEvents(
+ this,
+ e,
+ (e: PointerEvent) => {
+ return ScriptCast(this.rootDoc.onDragScript)?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: { doc: value, e } }).result;
+ },
+ emptyFunction,
+ emptyFunction
+ );
+ return false;
+ };
+
/**
* Dropdown list
*/
@computed get dropdownListButton() {
- const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const script = ScriptCast(this.rootDoc.script);
let noviceList: string[] = [];
let text: string | undefined;
- let dropdown = true;
let getStyle: (val: string) => any = () => {};
let icon: IconProp = 'caret-down';
- let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView');
- try {
- if (isViewDropdown) {
- const selectedDocs: Doc[] = SelectionManager.Docs();
- const selected = SelectionManager.Docs().lastElement();
- if (selected) {
- if (StrCast(selected.type) === DocumentType.COL) {
- text = StrCast(selected._type_collection);
+ const isViewDropdown = script?.script.originalScript.startsWith('setView');
+ if (isViewDropdown) {
+ const selected = SelectionManager.Docs();
+ if (selected.lastElement()) {
+ if (StrCast(selected.lastElement().type) === DocumentType.COL) {
+ text = StrCast(selected.lastElement()._type_collection);
+ } else {
+ if (selected.length > 1) {
+ text = selected.length + ' selected';
} else {
- dropdown = false;
- if (selectedDocs.length > 1) {
- text = selectedDocs.length + ' selected';
- } else {
- text = Utils.cleanDocumentType(StrCast(selected.type) as DocumentType);
- icon = Doc.toIcon(selected);
- }
- return (
- <Popup
- icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
- text={text}
- type={Type.TERT}
- color={SettingsManager.userColor}
- background={SettingsManager.userVariantColor}
- popup={<SelectedDocView selectedDocs={selectedDocs} />}
- fillWidth
- />
- );
+ text = Utils.cleanDocumentType(StrCast(selected.lastElement().type) as DocumentType);
+ icon = Doc.toIcon(selected.lastElement());
}
- } else {
- dropdown = false;
- return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
+ return (
+ <Popup
+ icon={<FontAwesomeIcon size={'1x'} icon={icon} />}
+ text={text}
+ type={Type.TERT}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ popup={<SelectedDocView selectedDocs={selected} />}
+ fillWidth
+ />
+ );
}
- noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
} else {
- text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- getStyle = (val: string) => {
- return { fontFamily: val };
- };
+ return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
}
- } catch (e) {
- console.log(e);
+ noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+ } else {
+ text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
+ // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+ getStyle = (val: string) => ({ fontFamily: val });
}
// Get items to place into the list
const list: IListItemProps[] = this.buttonList
.filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
.map(value => ({
- text: value.charAt(0).toUpperCase() + value.slice(1),
+ text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
val: value,
style: getStyle(value),
- onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value),
// shortcut: '#',
}));
return (
- <div style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}>
- <Dropdown
- selectedVal={text}
- setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)}
- color={SettingsManager.userColor}
- background={isViewDropdown ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
- type={Type.TERT}
- dropdownType={DropdownType.SELECT}
- items={list}
- tooltip={this.label}
- fillWidth
- />
- </div>
+ <Dropdown
+ selectedVal={text}
+ setSelectedVal={undoable(value => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), `dropdown select ${this.label}`)}
+ color={SettingsManager.userColor}
+ background={SettingsManager.userVariantColor}
+ type={Type.TERT}
+ closeOnSelect={false}
+ dropdownType={DropdownType.SELECT}
+ onItemDown={this.dropdownItemDown}
+ items={list}
+ tooltip={this.label}
+ fillWidth
+ />
);
}
@@ -343,7 +344,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
toggleStatus={toggleStatus}
text={buttonText}
color={color}
- background={SettingsManager.userBackgroundColor}
+ //background={SettingsManager.userBackgroundColor}
icon={this.Icon(color)!}
label={this.label}
onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f5c6a9273..1ab120af5 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -85,8 +85,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
};
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
+ const visibleAnchor = this._getAnchor?.(this._savedAnnotations, false); // use marquee anchor, otherwise, save zoom/pan as anchor
const anchor =
- this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
+ visibleAnchor ??
Docs.Create.ConfigDocument({
title: 'ImgAnchor:' + this.rootDoc.title,
config_panX: NumCast(this.layoutDoc._freeform_panX),
@@ -96,8 +97,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
});
if (anchor) {
if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
- /* addAsAnnotation &&*/ this.addDocument(anchor);
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true } }, this.rootDoc);
+ addAsAnnotation && this.addDocument(anchor);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: visibleAnchor ? false : true } }, this.rootDoc);
return anchor;
}
return this.rootDoc;
@@ -230,13 +231,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
croppingProto['data_nativeWidth'] = anchw;
croppingProto['data_nativeHeight'] = anchh;
croppingProto.freeform_scale = viewScale;
- croppingProto.freeform_scaleMin = viewScale;
+ croppingProto.freeform_scale_min = viewScale;
croppingProto.freeform_panX = anchx / viewScale;
croppingProto.freeform_panY = anchy / viewScale;
- croppingProto.freeform_panXMin = anchx / viewScale;
- croppingProto.freeform_panXMax = anchw / viewScale;
- croppingProto.freeform_panYMin = anchy / viewScale;
- croppingProto.freeform_panYMax = anchh / viewScale;
+ croppingProto.freeform_panX_min = anchx / viewScale;
+ croppingProto.freeform_panX_max = anchw / viewScale;
+ croppingProto.freeform_panY_min = anchy / viewScale;
+ croppingProto.freeform_panY_max = anchh / viewScale;
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[Width]();
@@ -502,7 +503,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._marqueeing = undefined;
this.props.select(false);
};
- focus = (anchor: Doc, options: DocFocusOptions) => this._ffref.current?.focus(anchor, options);
+ focus = (anchor: Doc, options: DocFocusOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
_ffref = React.createRef<CollectionFreeFormView>();
savedAnnotations = () => this._savedAnnotations;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index b0d041bdd..f22cb195f 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -111,7 +111,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
X
</button>
<input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
- <Tooltip title={Object.entries(new DocumentOptions()).find((pair: [string, FInfo]) => pair[0].replace(/^_/, '') === props.fieldKey)?.[1].description}>
+ <Tooltip title={Object.entries(new DocumentOptions()).find((pair: [string, FInfo]) => pair[0].replace(/^_/, '') === props.fieldKey)?.[1].description ?? ''}>
<div className="keyValuePair-keyField" style={{ marginLeft: 20 * (props.fieldKey.match(/_/g)?.length || 0), color: keyStyle }}>
{'('.repeat(parenCount)}
{props.fieldKey}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 42c8ea6a4..198cbe851 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -19,6 +19,7 @@ import { Transform } from '../../util/Transform';
import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
import './LinkDocPreview.scss';
import React = require('react');
+import { DocumentManager } from '../../util/DocumentManager';
interface LinkDocPreviewProps {
linkDoc?: Doc;
@@ -176,7 +177,12 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
const webDoc =
Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this.props.hrefs[0]).keys()).lastElement() ??
Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
- this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
+ DocumentManager.Instance.showDocument(webDoc, {
+ openLocation: OpenWhere.lightbox,
+ willPan: true,
+ zoomTime: 500,
+ });
+ //this.props.docProps?.addDocTab(webDoc, OpenWhere.lightbox);
}
};
@@ -198,7 +204,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
};
@computed get previewHeader() {
return !this._linkDoc || !this._markerTargetDoc || !this._targetDoc || !this._linkSrc ? null : (
- <div className="linkDocPreview-info">
+ <div className="linkDocPreview-info" style={{ background: SettingsManager.userBackgroundColor }}>
<div className="linkDocPreview-buttonBar" style={{ float: 'left' }}>
<Tooltip title={<div className="dash-tooltip">Edit Link</div>} placement="top">
<div className="linkDocPreview-button" onPointerDown={this.editLink}>
@@ -296,7 +302,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
className="linkDocPreview"
ref={this._linkDocRef}
onPointerDown={this.followLinkPointerDown}
- style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+ style={{ borderColor: SettingsManager.userColor, left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
{this.docPreview}
</div>
);
diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss
index d4a7e18f2..cabd4de05 100644
--- a/src/client/views/nodes/LoadingBox.scss
+++ b/src/client/views/nodes/LoadingBox.scss
@@ -5,34 +5,28 @@
justify-content: center;
background-color: #fdfdfd;
height: 100%;
+ width: 100%;
align-items: center;
- .textContainer,
- .text {
- overflow: hidden;
+ .loadingBox-textContainer,
+ .loadingBox-title {
text-overflow: ellipsis;
max-width: 80%;
text-align: center;
- display: flex;
- flex-direction: column;
+ display: block;
gap: 8px;
align-items: center;
+ white-space: normal;
+ word-break: all;
+ .loadingBox-headerText {
+ text-align: center;
+ font-weight: bold;
+ height: auto;
+ width: 100%;
+ }
+ }
+ .loadingBox-spinner {
+ position: absolute;
+ top: 0;
+ left: 0;
}
-}
-
-.textContainer {
- margin: 5px;
-}
-
-.textContainer {
- justify-content: center;
- align-content: center;
-}
-
-.headerText {
- text-align: center;
- font-weight: bold;
-}
-
-.spinner {
- text-align: center;
}
diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx
index 0b02626e9..bdc074e0c 100644
--- a/src/client/views/nodes/LoadingBox.tsx
+++ b/src/client/views/nodes/LoadingBox.tsx
@@ -46,7 +46,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const updateFunc = async () => {
const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc[Id])); // We use the guid of the overwriteDoc to track file uploads.
runInAction(() => (this.progress = result.progress));
- this._timer = setTimeout(updateFunc, 1000);
+ !this.rootDoc.loadingError && (this._timer = setTimeout(updateFunc, 1000));
};
this._timer = setTimeout(updateFunc, 1000);
}
@@ -58,10 +58,14 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
render() {
return (
<div className="loadingBoxContainer" style={{ background: !this.rootDoc.loadingError ? '' : 'red' }}>
- <div className="textContainer">
- <p className="headerText">{StrCast(this.rootDoc.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p>
- <span className="text">{StrCast(this.rootDoc.title)}</span>
- {this.rootDoc.loadingError ? null : <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />}
+ <div className="loadingBox-textContainer">
+ <span className="loadingBox-title">{StrCast(this.rootDoc.title)}</span>
+ <p className="loadingBox-headerText">{StrCast(this.rootDoc.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}</p>
+ {this.rootDoc.loadingError ? null : (
+ <div className="loadingBox-spinner">
+ <ReactLoading type="spinningBubbles" color="blue" height={100} width={100} />
+ </div>
+ )}
</div>
</div>
);
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index f731763af..7af4d9b59 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,43 +1,32 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
+import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { ColorState } from 'react-color';
import { Doc, Opt } from '../../../../fields/Doc';
-import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../../Utils';
+import { returnFalse, setupMoveUpEvents, unimplementedFunction } from '../../../../Utils';
import { SelectionManager } from '../../../util/SelectionManager';
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu"
-import { LinkPopup } from '../../linking/LinkPopup';
-import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
// import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
-import { EditorView } from 'prosemirror-view';
+import { IconButton } from 'browndash-components';
+import { SettingsManager } from '../../../util/SettingsManager';
import './MapAnchorMenu.scss';
-import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
-import { StrCast } from '../../../../fields/Types';
-import { DocumentType } from '../../../documents/DocumentTypes';
@observer
export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: MapAnchorMenu;
private _disposer: IReactionDisposer | undefined;
- private _disposer2: IReactionDisposer | undefined;
- private _commentCont = React.createRef<HTMLButtonElement>();
-
-
-
+ private _commentRef = React.createRef<HTMLDivElement>();
public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
public Center: () => void = unimplementedFunction;
- // public OnClick: (e: PointerEvent) => void = unimplementedFunction;
+ public OnClick: (e: PointerEvent) => void = unimplementedFunction;
// public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
- // public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
- // public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+ public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
public Delete: () => void = unimplementedFunction;
- public LinkNote: () => void = unimplementedFunction;
// public MakeTargetToggle: () => void = unimplementedFunction;
// public ShowTargetTrail: () => void = unimplementedFunction;
public IsTargetToggler: () => boolean = returnFalse;
@@ -54,23 +43,12 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentWillUnmount() {
this._disposer?.();
- this._disposer2?.();
}
componentDidMount() {
- this._disposer2 = reaction(
- () => this._opacity,
- opacity => {
- if (!opacity) {
- }
- },
- { fireImmediately: true }
- );
this._disposer = reaction(
() => SelectionManager.Views().slice(),
- selected => {
- MapAnchorMenu.Instance.fadeOut(true);
- }
+ selected => MapAnchorMenu.Instance.fadeOut(true)
);
}
// audioDown = (e: React.PointerEvent) => {
@@ -89,42 +67,54 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
// e => this.OnCrop?.(e)
// );
// };
-
+ notePointerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(
+ this,
+ e,
+ (e: PointerEvent) => {
+ this.StartDrag(e, this._commentRef.current!);
+ return true;
+ },
+ returnFalse,
+ e => this.OnClick(e)
+ );
+ };
static top = React.createRef<HTMLDivElement>();
// public get Top(){
// return this.top
// }
-
render() {
- const buttons =(
- <>
- {(
- <IconButton
- tooltip="Delete Pin" //
- onPointerDown={this.Delete}
- icon={<FontAwesomeIcon icon="trash-alt" />}
- color={StrCast(Doc.UserDoc().userColor)}
- />
- )}
- {(
+ const buttons = (
+ <>
+ {
+ <IconButton
+ tooltip="Delete Pin" //
+ onPointerDown={this.Delete}
+ icon={<FontAwesomeIcon icon="trash-alt" />}
+ color={SettingsManager.userColor}
+ />
+ }
+ {
+ <div ref={this._commentRef}>
<IconButton
tooltip="Link Note to Pin" //
- onPointerDown={this.LinkNote}
+ onPointerDown={this.notePointerDown}
icon={<FontAwesomeIcon icon="sticky-note" />}
- color={StrCast(Doc.UserDoc().userColor)}
+ color={SettingsManager.userColor}
/>
- )}
- {(
- <IconButton
- tooltip="Center on pin" //
- onPointerDown={this.Center}
- icon={<FontAwesomeIcon icon="compress-arrows-alt" />}
- color={StrCast(Doc.UserDoc().userColor)}
- />
- )}
- {/* {this.IsTargetToggler !== returnFalse && (
+ </div>
+ }
+ {
+ <IconButton
+ tooltip="Center on pin" //
+ onPointerDown={this.Center}
+ icon={<FontAwesomeIcon icon="compress-arrows-alt" />}
+ color={SettingsManager.userColor}
+ />
+ }
+ {/* {this.IsTargetToggler !== returnFalse && (
<Toggle
tooltip={'Make target visibility toggle on click'}
type={Type.PRIM}
@@ -132,15 +122,16 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
toggleStatus={this.IsTargetToggler()}
onClick={this.MakeTargetToggle}
icon={<FontAwesomeIcon icon="thumbtack" />}
- color={StrCast(Doc.UserDoc().userColor)}
+ color={SettingsManager.userColor}
/>
)} */}
- </>
- );
+ </>
+ );
- return this.getElement(<div ref={MapAnchorMenu.top} style={{width:"100%", display:"flex"}}>
- {buttons}
- </div>
+ return this.getElement(
+ <div ref={MapAnchorMenu.top} style={{ width: '100%', display: 'flex' }}>
+ {buttons}
+ </div>
);
}
}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index f0106dbbb..65c138975 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -6,9 +6,10 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols';
+import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
@@ -24,6 +25,7 @@ import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { SidebarAnnos } from '../../SidebarAnnos';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
+import { FormattedTextBox } from '../formattedText/FormattedTextBox';
import { PinProps, PresBox } from '../trails';
import { MapAnchorMenu } from './MapAnchorMenu';
import './MapBox.scss';
@@ -70,7 +72,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
private _sidebarRef = React.createRef<SidebarAnnos>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _disposers: { [key: string]: IReactionDisposer } = {};
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
+ private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
@observable private _marqueeing: number[] | undefined;
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@@ -106,6 +108,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
componentWillUnmount(): void {
this._unmounting = true;
this.deselectPin();
+ this._rerenderTimeout && clearTimeout(this._rerenderTimeout);
Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
}
@@ -139,13 +142,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return this.addDocument(doc, sidebarKey); // add to sidebar list
};
+ removeMapDocument = (doc: Doc | Doc[], annotationKey?: string) => {
+ const docs = doc instanceof Doc ? [doc] : doc;
+ this.allAnnotations.filter(anno => docs.includes(DocCast(anno.mapPin))).forEach(anno => (anno.mapPin = undefined));
+ return this.removeDocument(doc, annotationKey, undefined);
+ };
+
/**
* Removing documents from the sidebar
* @param doc
* @param sidebarKey
* @returns
*/
- sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeDocument(doc, sidebarKey);
+ sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeMapDocument(doc, sidebarKey);
/**
* Toggle sidebar onclick the tiny comment button on the top right corner
@@ -208,10 +217,41 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
};
+ startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const sourceAnchorCreator = action(() => {
+ const note = this.getAnchor(true);
+ if (note && this.selectedPin) {
+ note.latitude = this.selectedPin.latitude;
+ note.longitude = this.selectedPin.longitude;
+ note.map = this.selectedPin.map;
+ }
+ return note as Doc;
+ });
+
+ const targetCreator = (annotationOn: Doc | undefined) => {
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+ FormattedTextBox.SelectOnLoad = target[Id];
+ return target;
+ };
+ const docView = this.props.DocumentView?.();
+ docView &&
+ DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+ dragComplete: e => {
+ if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
+ e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document;
+ e.annoDragData.linkSourceDoc.followLinkZoom = false;
+ }
+ },
+ });
+ };
+
createNoteAnnotation = () => {
const createFunc = undoable(
action(() => {
- const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']);
+ const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', '-linkedTo']);
if (note && this.selectedPin) {
note.latitude = this.selectedPin.latitude;
note.longitude = this.selectedPin.longitude;
@@ -236,7 +276,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return false;
};
- setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
+ setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
@action
onMarqueeDown = (e: React.PointerEvent) => {
@@ -257,7 +297,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@action finishMarquee = (x?: number, y?: number) => {
this._marqueeing = undefined;
- x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
+ x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false, this.rootDoc);
};
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);
@@ -326,7 +366,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
NumCast(longitude),
false,
[],
- { map: map }
+ { title: map ?? `lat=${latitude},lng=${longitude}`, map: map }
// ,'pushpinIDamongus'+ this.incrementer++
);
this.addDocument(pushpin, this.annotationKey);
@@ -343,7 +383,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// Removes filter
Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
- Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toString(DocCast(this.selectedPin.mapPin)), 'removeAll');
+ Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
const temp = this.selectedPin;
if (!this._unmounting) {
@@ -375,13 +415,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
// Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
- Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toScriptString(this.selectedPin), 'mapPin' as any);
+ Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
this.recolorPin(this.selectedPin, 'green');
MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
- MapAnchorMenu.Instance.LinkNote = this.createNoteAnnotation;
+ MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
+ MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
@@ -451,11 +492,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
config_map_type: StrCast(this.dataDoc.map_type),
config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map),
layout_unrendered: true,
+ mapPin: existingPin ?? this.selectedPin,
+ annotationOn: this.rootDoc,
});
if (anchor) {
- anchor.mapPin = existingPin ?? this.selectedPin;
if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
- /* addAsAnnotation &&*/ this.addDocument(anchor);
+ addAsAnnotation && this.addDocument(anchor);
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
return anchor;
}
@@ -489,7 +531,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
* Removes pin from annotations
*/
@action
- removePushpin = (pinDoc: Doc) => this.removeDocument(pinDoc, this.annotationKey);
+ removePushpin = (pinDoc: Doc) => this.removeMapDocument(pinDoc, this.annotationKey);
/*
* Removes pushpin from map render
@@ -508,7 +550,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// Removes filter
Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
- Doc.setDocFilter(this.rootDoc, '-linkedTo', Field.toString(DocCast(this.selectedPin.mapPin)), 'removeAll');
+ Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
this.removePushpin(this.selectedPin);
}
@@ -683,20 +725,20 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch();
static _firstRender = true;
- static _rerenderDelay = 0;
+ static _rerenderDelay = 500;
_rerenderTimeout: any;
render() {
// bcz: no idea what's going on here, but bings maps have some kind of bug
// such that we need to delay rendering a second map on startup until the first map is rendered.
this.rootDoc[DocCss];
- if (MapBox._firstRender) {
- MapBox._firstRender = false;
- MapBox._rerenderDelay = 500;
- } else if (MapBox._rerenderDelay) {
+ if (MapBox._rerenderDelay) {
// prettier-ignore
this._rerenderTimeout = this._rerenderTimeout ??
setTimeout(action(() => {
- MapBox._rerenderDelay = 0;
+ if ((window as any).Microsoft?.Maps?.Internal._WorkDispatcher) {
+ MapBox._rerenderDelay = 0;
+ }
+ this._rerenderTimeout = undefined;
this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1;
}), MapBox._rerenderDelay);
return null;
@@ -753,8 +795,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
? null
: this.allAnnotations
.filter(anno => !anno.layout_unrendered)
- .map(pushpin => (
+ .map((pushpin, i) => (
<DocumentView
+ key={i}
{...this.props}
renderDepth={this.props.renderDepth + 1}
Document={pushpin}
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index a54bdcd5e..407a91dd0 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -98,7 +98,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
public get SidebarKey() {
return this.fieldKey + '_sidebar';
}
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
+ private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
@computed get inlineTextAnnotations() {
return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
}
@@ -502,7 +502,7 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action finishMarquee = (x?: number, y?: number) => {
this._marqueeing = undefined;
this._isAnnotating = false;
- x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
+ x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false, this.props.Document);
};
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 758b49655..cf44649a2 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -24,7 +24,6 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { Colors } from '../global/globalEnums';
-import { LightboxView } from '../LightboxView';
import { CreateImage } from '../nodes/WebBoxRenderer';
import { PDFViewer } from '../pdf/PDFViewer';
import { SidebarAnnos } from '../SidebarAnnos';
@@ -243,14 +242,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._layout_scrollTop)?.toFixed(0)),
annotationOn: this.rootDoc,
});
- const annoAnchor = this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations(), true);
- const anchor = annoAnchor ?? docAnchor();
+ const visibleAnchor = this._pdfViewer?._getAnchor?.(this._pdfViewer.savedAnnotations(), true);
+ const anchor = visibleAnchor ?? docAnchor();
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.rootDoc);
anchor.text = ele?.textContent ?? '';
anchor.text_html = ele?.innerHTML;
- if (addAsAnnotation || annoAnchor) {
- this.addDocument(anchor);
- }
+ addAsAnnotation && this.addDocument(anchor);
+
return anchor;
};
@@ -439,12 +437,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return PDFBox.sidebarResizerWidth + nativeDiff * (this.props.NativeDimScaling?.() || 1);
};
@undoBatch
- toggleSidebarType = () => (this.rootDoc.sidebar_collectionType = this.rootDoc.sidebar_collectionType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
+ toggleSidebarType = () => (this.rootDoc[this.SidebarKey + '_type_collection'] = this.rootDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform);
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
const options = cm.findByDescription('Options...');
const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
- optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
+ !Doc.noviceMode && optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
!Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
//optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
!options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' });
@@ -555,7 +553,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
return (
<div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: '100%', right: 0, backgroundColor: `white` }}>
- {renderComponent(StrCast(this.layoutDoc.sidebar_collectionType))}
+ {renderComponent(StrCast(this.layoutDoc[this.SidebarKey + '_type_collection']))}
</div>
);
}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 8fa2861b6..481e43feb 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -1,20 +1,27 @@
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { DateField } from '../../../../fields/DateField';
+import { Doc, DocListCast } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { BoolCast, DocCast } from '../../../../fields/Types';
import { VideoField } from '../../../../fields/URLField';
import { Upload } from '../../../../server/SharedMediaTypes';
+import { Docs } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager } from '../../../util/DragManager';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { Presentation } from '../../../util/TrackMovements';
+import { undoBatch } from '../../../util/UndoManager';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
import { ViewBoxBaseComponent } from '../../DocComponent';
+import { media_state } from '../AudioBox';
import { FieldView, FieldViewProps } from '../FieldView';
import { VideoBox } from '../VideoBox';
import { RecordingView } from './RecordingView';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { Presentation } from '../../../util/TrackMovements';
-import { Doc } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { BoolCast, DocCast } from '../../../../fields/Types';
-import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { Docs } from '../../../documents/Documents';
@observer
export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -34,9 +41,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable videoDuration: number | undefined = undefined;
@action
- setVideoDuration = (duration: number) => {
- this.videoDuration = duration;
- };
+ setVideoDuration = (duration: number) => (this.videoDuration = duration);
@action
setResult = (info: Upload.AccessPathInfo, presentation?: Presentation) => {
@@ -54,6 +59,128 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.dataDoc[this.fieldKey + '_presentation'] = JSON.stringify(presCopy);
}
};
+ @undoBatch
+ @action
+ public static WorkspaceStopRecording() {
+ const remDoc = RecordingBox.screengrabber?.rootDoc;
+ if (remDoc) {
+ //if recordingbox is true; when we press the stop button. changed vals temporarily to see if changes happening
+ RecordingBox.screengrabber?.Pause?.();
+ setTimeout(() => {
+ RecordingBox.screengrabber?.Finish?.();
+ remDoc.overlayX = 70; //was 100
+ remDoc.overlayY = 590;
+ RecordingBox.screengrabber = undefined;
+ }, 100);
+ //could break if recording takes too long to turn into videobox. If so, either increase time on setTimeout below or find diff place to do this
+ setTimeout(() => Doc.RemFromMyOverlay(remDoc), 1000);
+ Doc.UserDoc().workspaceRecordingState = media_state.Paused;
+ Doc.AddDocToList(Doc.UserDoc(), 'workspaceRecordings', remDoc);
+ }
+ }
+
+ /**
+ * This method toggles whether or not we are currently using the RecordingBox to record with the topbar button
+ * @param _readOnly_
+ * @returns
+ */
+ @undoBatch
+ @action
+ public static WorkspaceStartRecording(value: string) {
+ const screengrabber =
+ value === 'Record Workspace'
+ ? Docs.Create.ScreenshotDocument({
+ title: `${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`,
+ _width: 205,
+ _height: 115,
+ })
+ : Docs.Create.WebCamDocument(`${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`, {
+ title: `${new DateField()}-${Doc.ActiveDashboard?.title ?? ''}`,
+ _width: 205,
+ _height: 115,
+ });
+ screengrabber.overlayX = 70; //was -400
+ screengrabber.overlayY = 590; //was 0
+ Doc.GetProto(screengrabber)[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+ Doc.AddToMyOverlay(screengrabber); //just adds doc to overlay
+ DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
+ RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
+ RecordingBox.screengrabber.Record?.();
+ });
+ Doc.UserDoc().workspaceRecordingState = media_state.Recording;
+ }
+
+ /**
+ * This method changes the menu depending on whether or not we are in playback mode
+ * @param value RecordingBox rootdoc
+ */
+ @undoBatch
+ @action
+ public static replayWorkspace(value: Doc) {
+ Doc.UserDoc().currentRecording = value;
+ value.overlayX = 70;
+ value.overlayY = window.innerHeight - 180;
+ Doc.AddToMyOverlay(value);
+ DocumentManager.Instance.AddViewRenderedCb(value, docView => {
+ Doc.UserDoc().currentRecording = docView.rootDoc;
+ SelectionManager.SelectSchemaViewDoc(value);
+ RecordingBox.resumeWorkspaceReplaying(value);
+ });
+ }
+
+ /**
+ * Adds the recording box to the canvas
+ * @param value current recordingbox
+ */
+ @undoBatch
+ @action
+ public static addRecToWorkspace(value: RecordingBox) {
+ let ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView);
+ (ffView?.ComponentView as CollectionFreeFormView).props.addDocument?.(value.rootDoc);
+ Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.rootDoc);
+ Doc.RemFromMyOverlay(value.rootDoc);
+ Doc.UserDoc().currentRecording = undefined;
+ Doc.UserDoc().workspaceReplayingState = undefined;
+ Doc.UserDoc().workspaceRecordingState = undefined;
+ }
+
+ @action
+ public static resumeWorkspaceReplaying(doc: Doc) {
+ const docView = DocumentManager.Instance.getDocumentView(doc);
+ if (docView?.ComponentView instanceof VideoBox) {
+ docView.ComponentView.Play();
+ }
+ Doc.UserDoc().workspaceReplayingState = media_state.Playing;
+ }
+
+ @action
+ public static pauseWorkspaceReplaying(doc: Doc) {
+ const docView = DocumentManager.Instance.getDocumentView(doc);
+ const videoBox = docView?.ComponentView as VideoBox;
+ if (videoBox) {
+ videoBox.Pause();
+ }
+ Doc.UserDoc().workspaceReplayingState = media_state.Paused;
+ }
+
+ @action
+ public static stopWorkspaceReplaying(value: Doc) {
+ Doc.RemFromMyOverlay(value);
+ Doc.UserDoc().currentRecording = undefined;
+ Doc.UserDoc().workspaceReplayingState = undefined;
+ Doc.UserDoc().workspaceRecordingState = undefined;
+ Doc.RemFromMyOverlay(value);
+ }
+
+ @undoBatch
+ @action
+ public static removeWorkspaceReplaying(value: Doc) {
+ Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value);
+ Doc.RemFromMyOverlay(value);
+ Doc.UserDoc().currentRecording = undefined;
+ Doc.UserDoc().workspaceReplayingState = undefined;
+ Doc.UserDoc().workspaceRecordingState = undefined;
+ }
Record: undefined | (() => void);
Pause: undefined | (() => void);
@@ -81,28 +208,52 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
static screengrabber: RecordingBox | undefined;
}
-ScriptingGlobals.add(function toggleRecording(_readOnly_: boolean) {
- if (_readOnly_) return RecordingBox.screengrabber ? true : false;
- if (RecordingBox.screengrabber) {
- RecordingBox.screengrabber.Pause?.();
- setTimeout(() => {
- RecordingBox.screengrabber?.Finish?.();
- RecordingBox.screengrabber!.rootDoc.overlayX = 100;
- RecordingBox.screengrabber!.rootDoc.overlayY = 100;
- RecordingBox.screengrabber = undefined;
- }, 100);
- } else {
- const screengrabber = Docs.Create.WebCamDocument('', {
- _width: 384,
- _height: 216,
- });
- screengrabber.overlayX = -400;
- screengrabber.overlayY = 0;
- screengrabber[Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
- Doc.AddToMyOverlay(screengrabber);
- DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => {
- RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
- RecordingBox.screengrabber.Record?.();
- });
+
+ScriptingGlobals.add(function stopWorkspaceRecording() {
+ RecordingBox.WorkspaceStopRecording();
+});
+
+ScriptingGlobals.add(function stopWorkspaceReplaying(value: Doc) {
+ RecordingBox.stopWorkspaceReplaying(value);
+});
+ScriptingGlobals.add(function removeWorkspaceReplaying(value: Doc) {
+ RecordingBox.removeWorkspaceReplaying(value);
+});
+
+ScriptingGlobals.add(function getCurrentRecording() {
+ return Doc.UserDoc().currentRecording;
+});
+ScriptingGlobals.add(function getWorkspaceRecordings() {
+ return new List<any>(['Record Workspace', `Record Webcam`, ...DocListCast(Doc.UserDoc().workspaceRecordings)]);
+});
+ScriptingGlobals.add(function isWorkspaceRecording() {
+ return Doc.UserDoc().workspaceRecordingState === media_state.Recording;
+});
+ScriptingGlobals.add(function isWorkspaceReplaying() {
+ return Doc.UserDoc().workspaceReplayingState;
+});
+ScriptingGlobals.add(function replayWorkspace(value: Doc | string, _readOnly_: boolean) {
+ if (_readOnly_) return DocCast(Doc.UserDoc().currentRecording) ?? 'Record Workspace';
+ if (typeof value === 'string') RecordingBox.WorkspaceStartRecording(value);
+ else RecordingBox.replayWorkspace(value);
+});
+ScriptingGlobals.add(function pauseWorkspaceReplaying(value: Doc, _readOnly_: boolean) {
+ RecordingBox.pauseWorkspaceReplaying(value);
+});
+ScriptingGlobals.add(function resumeWorkspaceReplaying(value: Doc, _readOnly_: boolean) {
+ RecordingBox.resumeWorkspaceReplaying(value);
+});
+
+ScriptingGlobals.add(function startRecordingDrag(value: { doc: Doc | string; e: React.PointerEvent }) {
+ if (DocCast(value.doc)) {
+ DragManager.StartDocumentDrag([value.e.target as HTMLElement], new DragManager.DocumentDragData([DocCast(value.doc)], 'embed'), value.e.clientX, value.e.clientY);
+ value.e.preventDefault();
+ return true;
+ }
+});
+ScriptingGlobals.add(function renderDropdown() {
+ if (!Doc.UserDoc().workspaceRecordings || DocListCast(Doc.UserDoc().workspaceRecordings).length === 0) {
+ return true;
}
-}, 'toggle recording');
+ return false;
+});
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 0e386b093..f7ed82643 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -164,7 +164,6 @@ export function RecordingView(props: IRecordingViewProps) {
const finish = () => {
// call stop on the video recorder if active
videoRecorder.current?.state !== 'inactive' && videoRecorder.current?.stop();
-
// end the streams (audio/video) to remove recording icon
const stream = videoElementRef.current!.srcObject;
stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index fa19caae1..ebb8a3374 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -26,6 +26,8 @@ import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import './ScreenshotBox.scss';
import { VideoBox } from './VideoBox';
+import { TrackMovements } from '../../util/TrackMovements';
+import { media_state } from './AudioBox';
declare class MediaRecorder {
constructor(e: any, options?: any); // whatever MediaRecorder has
@@ -180,7 +182,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
ref={r => {
this._videoRef = r;
setTimeout(() => {
- if (this.rootDoc.mediaState === 'pendingRecording' && this._videoRef) {
+ if (this.rootDoc.mediaState === media_state.PendingRecording && this._videoRef) {
this.toggleRecording();
}
}, 100);
@@ -219,6 +221,9 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
// }
return null;
}
+ Record = () => !this._screenCapture && this.toggleRecording();
+ Pause = () => this._screenCapture && this.toggleRecording();
+
toggleRecording = async () => {
if (!this._screenCapture) {
this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
@@ -233,9 +238,19 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
const vid_chunks: any = [];
- this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '_recordingStart'] = new DateField(new Date()));
+ this._videoRec.onstart = () => {
+ if (this.dataDoc[this.props.fieldKey + '_trackScreen']) TrackMovements.Instance.start();
+ this.dataDoc[this.props.fieldKey + '_recordingStart'] = new DateField(new Date());
+ };
this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
this._videoRec.onstop = async (e: any) => {
+ const presentation = TrackMovements.Instance.yieldPresentation();
+ if (presentation?.movements) {
+ const presCopy = { ...presentation };
+ presCopy.movements = presentation.movements.map(movement => ({ ...movement, doc: movement.doc[Id] })) as any;
+ this.dataDoc[this.fieldKey + '_presentation'] = JSON.stringify(presCopy);
+ }
+ TrackMovements.Instance.finish();
const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
const [{ result }] = await Networking.UploadFilesToServer({ file });
this.dataDoc[this.fieldKey + '_duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss
deleted file mode 100644
index 4206a368d..000000000
--- a/src/client/views/nodes/SliderBox.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.sliderBox-outerDiv {
- width: calc(100% - 14px); // 14px accounts for handles that are at the max value of the slider that would extend outside the box
- height: 100%;
- border-radius: inherit;
- display: flex;
- flex-direction: column;
- position: relative;
- .slider-tracks {
- top: 7px;
- position: relative;
- }
- .slider-ticks {
- position: relative;
- }
- .slider-handles {
- top: 7px;
- position: relative;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
deleted file mode 100644
index 430b20eb5..000000000
--- a/src/client/views/nodes/SliderBox.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxBaseComponent } from '../DocComponent';
-import { ScriptBox } from '../ScriptBox';
-import { StyleProp } from '../StyleProvider';
-import { FieldView, FieldViewProps } from './FieldView';
-import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
-import './SliderBox.scss';
-
-@observer
-export class SliderBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(SliderBox, fieldKey);
- }
-
- get minThumbKey() {
- return this.fieldKey + '-minThumb';
- }
- get maxThumbKey() {
- return this.fieldKey + '-maxThumb';
- }
- get minKey() {
- return this.fieldKey + '-min';
- }
- get maxKey() {
- return this.fieldKey + '-max';
- }
- specificContextMenu = (e: React.MouseEvent): void => {
- const funcs: ContextMenuProps[] = [];
- funcs.push({ description: 'Edit Thumb Change Script', icon: 'edit', event: (obj: any) => ScriptBox.EditButtonScript('On Thumb Change ...', this.props.Document, 'onThumbChange', obj.x, obj.y) });
- ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
- };
- onChange = (values: readonly number[]) =>
- runInAction(() => {
- this.dataDoc[this.minThumbKey] = values[0];
- this.dataDoc[this.maxThumbKey] = values[1];
- ScriptCast(this.layoutDoc.onThumbChanged, null)?.script.run({
- self: this.rootDoc,
- scriptContext: this.props.scriptContext,
- range: values,
- this: this.layoutDoc,
- });
- });
-
- render() {
- const domain = [NumCast(this.layoutDoc[this.minKey]), NumCast(this.layoutDoc[this.maxKey])];
- const defaultValues = [NumCast(this.dataDoc[this.minThumbKey]), NumCast(this.dataDoc[this.maxThumbKey])];
- return domain[1] <= domain[0] ? null : (
- <div className="sliderBox-outerDiv" onContextMenu={this.specificContextMenu} onPointerDown={e => e.stopPropagation()} style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
- <div
- className="sliderBox-mainButton"
- onContextMenu={this.specificContextMenu}
- style={{
- background: StrCast(this.layoutDoc.backgroundColor),
- color: StrCast(this.layoutDoc.color, 'black'),
- fontSize: StrCast(this.layoutDoc._text_fontSize),
- letterSpacing: StrCast(this.layoutDoc.letterSpacing),
- }}>
- <Slider mode={2} step={Math.min(1, 0.1 * (domain[1] - domain[0]))} domain={domain} rootStyle={{ position: 'relative', width: '100%' }} onChange={this.onChange} values={defaultValues}>
- <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
- <Handles>
- {({ handles, activeHandleID, getHandleProps }) => (
- <div className="slider-handles">
- {handles.map((handle, i) => {
- const value = i === 0 ? defaultValues[0] : defaultValues[1];
- return (
- <div title={String(value)}>
- <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
- </div>
- );
- })}
- </div>
- )}
- </Handles>
- <Tracks left={false} right={false}>
- {({ tracks, getTrackProps }) => (
- <div className="slider-tracks">
- {tracks.map(({ id, source, target }) => (
- <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
- ))}
- </div>
- )}
- </Tracks>
- <Ticks count={5}>
- {({ ticks }) => (
- <div className="slider-ticks">
- {ticks.map(tick => (
- <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
- ))}
- </div>
- )}
- </Ticks>
- </Slider>
- </div>
- </div>
- );
- }
-}
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 5e1359441..f803715ad 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -84,7 +84,7 @@
width: 0;
height: 0;
position: relative;
- z-index: 100001;
+ z-index: 2000;
}
.videoBox-ui {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 48716b867..d7f7c9b73 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -29,7 +29,7 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp
import { MarqueeAnnotator } from '../MarqueeAnnotator';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { StyleProp } from '../StyleProvider';
-import { DocumentView, OpenWhere } from './DocumentView';
+import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { RecordingBox } from './RecordingBox';
import { PinProps, PresBox } from './trails';
@@ -57,6 +57,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
static _youtubeIframeCounter: number = 0;
static heightPercent = 80; // height of video relative to videoBox when timeline is open
static numThumbnails = 20;
+ private unmounting = false;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _youtubePlayer: YT.Player | undefined = undefined;
private _videoRef: HTMLVideoElement | null = null; // <video> ref
@@ -124,6 +125,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
componentDidMount() {
+ this.unmounting = false;
this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
if (this.youtubeVideoId) {
const youtubeaspect = 400 / 315;
@@ -135,7 +137,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
}
}
- this.player && this.setPlayheadTime(0);
+ this.player && this.setPlayheadTime(this.timeline.clipStart || 0);
document.addEventListener('keydown', this.keyEvents, true);
if (this.presentation) {
@@ -144,6 +146,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
componentWillUnmount() {
+ this.unmounting = true;
this.removeCurrentlyPlaying();
this.Pause();
Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
@@ -191,7 +194,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._finished = false;
start = this.timeline.trimStart;
}
-
try {
this._audioPlayer && this.player && (this._audioPlayer.currentTime = this.player?.currentTime);
update && this.player && this.playFrom(start, undefined, true);
@@ -386,7 +388,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
- const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc;
+ const anchor = addAsAnnotation
+ ? CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.rootDoc
+ : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.rootDoc });
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.rootDoc);
return anchor;
};
@@ -408,7 +412,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// updates video time
@action
updateTimecode = () => {
- this.player && (this.layoutDoc._layout_currentTimecode = this.player.currentTime);
+ !this.unmounting && this.player && (this.layoutDoc._layout_currentTimecode = this.player.currentTime);
try {
this._youtubePlayer && (this.layoutDoc._layout_currentTimecode = this._youtubePlayer.getCurrentTime?.());
} catch (e) {
@@ -847,11 +851,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
zoom = (zoom: number) => this.timeline?.setZoom(zoom);
// plays link
- playLink = (doc: Doc) => {
- const startTime = Math.max(0, this._stackedTimeline?.anchorStart(doc) || 0);
+ playLink = (doc: Doc, options: DocFocusOptions) => {
+ const startTime = Math.max(0, NumCast(doc.config_clipStart, this._stackedTimeline?.anchorStart(doc) || 0));
const endTime = this.timeline?.anchorEnd(doc);
if (startTime !== undefined) {
- if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
+ if (options.playMedia) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
else this.Seek(startTime);
}
};
@@ -1039,7 +1043,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.props.bringToFront(cropping);
return cropping;
};
-
savedAnnotations = () => this._savedAnnotations;
render() {
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
@@ -1077,8 +1080,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
fieldKey={this.annotationKey}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
isAnyChildContentActive={returnFalse}
ScreenToLocalTransform={this.screenToLocalTransform}
childFilters={this.timelineDocFilter}
@@ -1119,7 +1122,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed get UIButtons() {
const bounds = this.props.docViewPath().lastElement().getBounds();
const width = (bounds?.right || 0) - (bounds?.left || 0);
- const curTime = NumCast(this.layoutDoc._layout_currentTimecode) - (this.timeline?.clipStart || 0);
+ const curTime = NumCast(this.layoutDoc._layout_currentTimecode);
return (
<>
<div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
@@ -1128,7 +1131,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{this.timeline && width > 150 && (
<div className="timecode-controls">
- <div className="timecode-current">{formatTime(curTime)}</div>
+ <div className="timecode-current">{formatTime(curTime - (this.timeline?.clipStart || 0))}</div>
{this._fullScreen || (this.heightPercent === 100 && width > 200) ? (
<div className="timeline-slider">
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 75847c100..511c91da0 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -176,7 +176,6 @@
width: 100%;
height: 100%;
transform-origin: top left;
- overflow: auto;
.webBox-iframe {
width: 100%;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index febf8341e..af20ff061 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -13,7 +13,7 @@ import { listSpec } from '../../../fields/Schema';
import { Cast, ImageCast, NumCast, StrCast, WebCast } from '../../../fields/Types';
import { ImageField, WebField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
@@ -50,7 +50,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
public static openSidebarWidth = 250;
public static sidebarResizerWidth = 5;
static webStyleSheet = addStyleSheet();
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
+ private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void);
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -62,10 +62,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
private _searchRef = React.createRef<HTMLInputElement>();
private _searchString = '';
private _scrollTimer: any;
+ private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
- private get _getAnchor() {
- return AnchorMenu.Instance?.GetAnchor;
- }
@observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't want the src parameter to also change as that would cause an unnecessary re-render.
@observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe. so thisforces the ifrmae to be rebuilt when allowScripts is toggled
@observable private _searching: boolean = false;
@@ -186,18 +184,25 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document. this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
runInAction(() => {
- this._annotationKeySuffix = () => this._urlHash + '_annotations';
- const reqdFuncs: { [key: string]: string } = {};
+ this._annotationKeySuffix = () => (this._urlHash ? this._urlHash + '_' : '') + 'annotations';
// bcz: need to make sure that doc.data_annotations points to the currently active web page's annotations (this could/should be when the doc is created)
- reqdFuncs[this.fieldKey + '_annotations'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"])`;
- reqdFuncs[this.fieldKey + '_annotations-setter'] = `this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_annotations"] = value`;
- reqdFuncs[this.fieldKey + '_sidebar'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"_sidebar"])`;
- DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
+ if (this._url) {
+ const reqdFuncs: { [key: string]: string } = {};
+ reqdFuncs[this.fieldKey + '_annotations'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"annotations"])`;
+ reqdFuncs[this.fieldKey + '_annotations-setter'] = `this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"annotations"] = value`;
+ reqdFuncs[this.fieldKey + '_sidebar'] = `copyField(this["${this.fieldKey}_"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"sidebar"])`;
+ DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
+ }
});
this._disposers.urlchange = reaction(
() => WebCast(this.rootDoc.data),
+ url => this.submitURL(false, false)
+ );
+ this._disposers.titling = reaction(
+ () => StrCast(this.rootDoc.title),
url => {
- this.submitURL(url.url.href, false, false);
+ url.startsWith('www') && this.setData('http://' + url);
+ url.startsWith('http') && this.setData(url);
}
);
@@ -259,14 +264,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
const rect = clientRects.item(i);
+ const mainrect = this._url ? { translateX: 0, translateY: 0, scale: 1 } : Utils.GetScreenTransform(this._mainCont.current);
if (rect && rect.width !== this._mainCont.current.clientWidth) {
const annoBox = document.createElement('div');
annoBox.className = 'marqueeAnnotator-annotationBox';
+ const scale = this._url ? 1 : this.props.ScreenToLocalTransform().Scale;
// transforms the positions from screen onto the pdf div
- annoBox.style.top = (rect.top + this._mainCont.current.scrollTop).toString();
- annoBox.style.left = rect.left.toString();
- annoBox.style.width = rect.width.toString();
- annoBox.style.height = rect.height.toString();
+ annoBox.style.top = ((rect.top - mainrect.translateY) * scale + (this._url ? this._mainCont.current.scrollTop : NumCast(this.layoutDoc.layout_scrollTop))).toString();
+ annoBox.style.left = ((rect.left - mainrect.translateX) * scale).toString();
+ annoBox.style.width = (rect.width * scale).toString();
+ annoBox.style.height = (rect.height * scale).toString();
this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
}
}
@@ -325,8 +332,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
ele.append(contents);
}
} catch (e) {}
+ const visibleAnchor = this._getAnchor(this._savedAnnotations, false);
const anchor =
- this._getAnchor(this._savedAnnotations, false) ??
+ visibleAnchor ??
Docs.Create.ConfigDocument({
title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._layout_scrollTop),
y: NumCast(this.layoutDoc._layout_scrollTop),
@@ -334,9 +342,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
});
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: pinProps?.pinData ? true : false, pannable: true } }, this.rootDoc);
anchor.text = ele?.textContent ?? '';
- anchor.text_html = ele?.innerHTML;
- //addAsAnnotation &&
- this.addDocumentWrapper(anchor);
+ anchor.text_html = ele?.innerHTML ?? this._selectionText;
+ addAsAnnotation && this.addDocumentWrapper(anchor);
return anchor;
};
@@ -357,21 +364,57 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale);
// Changing which document to add the annotation to (the currently selected WebBox)
- GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash}_sidebar`);
+ GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
}
}
};
@action
+ webClipDown = (e: React.PointerEvent) => {
+ const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
+ const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
+ const word = getWordAtPoint(e.target, e.clientX, e.clientY);
+ this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
+ MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+ e.button !== 2 && (this._marqueeing = [e.clientX, e.clientY]);
+ if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
+ e.stopPropagation();
+ setTimeout(
+ action(() => (this._marqueeing = undefined)),
+ 100
+ ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+ } else {
+ this._isAnnotating = true;
+ this.props.select(false);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ document.addEventListener('pointerup', this.webClipUp);
+ };
+ webClipUp = (e: PointerEvent) => {
+ document.removeEventListener('pointerup', this.webClipUp);
+ this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
+ const sel = window.getSelection();
+ if (sel && !sel.isCollapsed) {
+ const selRange = sel.getRangeAt(0);
+ this._selectionText = sel.toString();
+ AnchorMenu.Instance.setSelectedText(sel.toString());
+ this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
+ AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
+ // Changing which document to add the annotation to (the currently selected WebBox)
+ GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
+ GPTPopup.Instance.addDoc = this.sidebarAddDocument;
+ }
+ };
+ @action
iframeDown = (e: PointerEvent) => {
- const sel = this._iframe?.contentWindow?.getSelection?.();
const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
const word = getWordAtPoint(e.target, e.clientX, e.clientY);
- this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
+ this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale]);
- if (word || ((e.target as any) || '').className.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
+ if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
setTimeout(
action(() => (this._marqueeing = undefined)),
100
@@ -392,9 +435,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
ContextMenu.Instance.setIgnoreEvents(true);
}
};
- isFirefox = () => {
- return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
- };
+ isFirefox = () => 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
iframeClick = () => this._iframeClick;
iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
@@ -417,6 +458,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
_iframetimeout: any = undefined;
+ @observable _warning = 0;
@action
iframeLoaded = (e: any) => {
const iframe = this._iframe;
@@ -430,6 +472,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
try {
href = iframe?.contentWindow?.location.href;
} catch (e) {
+ runInAction(() => this._warning++);
href = undefined;
}
let requrlraw = decodeURIComponent(href?.replace(Utils.prepend('') + '/corsProxy/', '') ?? this._url.toString());
@@ -462,12 +505,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
// { passive: false }
// );
const initHeights = () => {
- this._scrollHeight = Math.max(this._scrollHeight, (iframeContent.body.children[0] as any)?.scrollHeight || 0);
+ this._scrollHeight = Math.max(this._scrollHeight, iframeContent.body.scrollHeight || 0);
if (this._scrollHeight) {
this.rootDoc.nativeHeight = Math.min(NumCast(this.rootDoc.nativeHeight), this._scrollHeight);
this.layoutDoc.height = Math.min(this.layoutDoc[Height](), (this.layoutDoc[Width]() * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth));
}
};
+ const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
+ if (swidth) {
+ const aspectResize = swidth / NumCast(this.rootDoc.nativeWidth);
+ this.rootDoc.nativeWidth = swidth;
+ this.rootDoc.nativeHeight = NumCast(this.rootDoc.nativeHeight) * aspectResize;
+ this.layoutDoc.height = this.layoutDoc[Height]() * aspectResize;
+ }
initHeights();
this._iframetimeout && clearTimeout(this._iframetimeout);
this._iframetimeout = setTimeout(
@@ -545,7 +595,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
goTo = (scrollTop: number, duration: number, easeFunc: 'linear' | 'ease' | undefined) => {
if (this._outerRef.current) {
- const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
if (duration) {
smoothScroll(duration, [this._outerRef.current], scrollTop, easeFunc);
this.setDashScrollTop(scrollTop, duration);
@@ -610,9 +659,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
};
@action
- submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => {
- if (!newUrl) return;
- if (!newUrl.startsWith('http')) newUrl = 'http://' + newUrl;
+ submitURL = (preview?: boolean, dontUpdateIframe?: boolean) => {
try {
if (!preview) {
if (this._webPageHasBeenRendered) {
@@ -668,9 +715,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
!Doc.noviceMode &&
funcs.push({ description: (this.layoutDoc[this.fieldKey + '_useCors'] ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc[this.fieldKey + '_useCors'] = !this.layoutDoc[this.fieldKey + '_useCors']), icon: 'snowflake' });
funcs.push({
- description: (this.layoutDoc.allowScripts ? 'Prevent' : 'Allow') + ' Scripts',
+ description: (this.dataDoc[this.fieldKey + '_allowScripts'] ? 'Prevent' : 'Allow') + ' Scripts',
event: () => {
- this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
+ this.dataDoc[this.fieldKey + '_allowScripts'] = !this.dataDoc[this.fieldKey + '_allowScripts'];
if (this._iframe) {
runInAction(() => (this._hackHide = true));
setTimeout(action(() => (this._hackHide = false)));
@@ -712,14 +759,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
@action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
+ this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this._marqueeing = undefined;
this._isAnnotating = false;
this._iframeClick = undefined;
- const sel = this._iframe?.contentDocument?.getSelection();
+ const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
if (sel?.empty) sel.empty(); // Chrome
else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
if (x !== undefined && y !== undefined) {
- this._setPreviewCursor?.(x, y, false, false);
+ this._setPreviewCursor?.(x, y, false, false, this.rootDoc);
ContextMenu.Instance.closeMenu();
ContextMenu.Instance.setIgnoreEvents(false);
if (e?.button === 2 || e?.altKey) {
@@ -729,6 +777,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
+ @observable lighttext = false;
+
@computed get urlContent() {
setTimeout(
action(() => {
@@ -740,20 +790,37 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
const field = this.rootDoc[this.props.fieldKey];
if (field instanceof HtmlField) {
- return <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
+ return (
+ <span
+ className="webBox-htmlSpan"
+ ref={action((r: any) => {
+ if (r) {
+ this._scrollHeight = Number(getComputedStyle(r).height.replace('px', ''));
+ this.lighttext = Array.from(r.children).some((c: any) => c instanceof HTMLElement && lightOrDark(getComputedStyle(c).color) !== Colors.WHITE);
+ }
+ })}
+ contentEditable
+ onPointerDown={this.webClipDown}
+ dangerouslySetInnerHTML={{ __html: field.html }}
+ />
+ );
}
if (field instanceof WebField) {
const url = this.layoutDoc[this.fieldKey + '_useCors'] ? Utils.CorsProxy(this._webUrl) : this._webUrl;
+ const scripts = this.dataDoc[this.fieldKey + '_allowScripts'] || this._webUrl.includes('wikipedia.org') || this._webUrl.includes('google.com') || this._webUrl.startsWith('https://bing');
+ //if (!scripts) console.log('No scripts for: ' + url);
return (
<iframe
+ key={this._warning}
className="webBox-iframe"
ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
+ style={{ pointerEvents: this._isAnyChildContentActive || DocumentView.Interacting ? 'none' : undefined }}
src={url}
onLoad={this.iframeLoaded}
scrolling="no" // ugh.. on windows, I get an inner scroll bar for the iframe's body even though the scrollHeight should be set to the full height of the document.
// 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"} />;
- sandbox={`${this.layoutDoc.allowScripts ? 'allow-scripts' : ''} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`}
+ sandbox={`${scripts ? 'allow-scripts' : ''} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`}
/>
);
}
@@ -761,7 +828,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
- (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url)));
+ this._url && (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.config_data = new WebField(this._url)));
return this.addDocument(doc, annotationKey);
};
@@ -886,6 +953,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
style={{
transform: `scale(${this.zoomScaling()}) translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
height: Doc.NativeHeight(this.Document) || undefined,
+ mixBlendMode: this._url || !this.lighttext ? 'multiply' : 'hard-light',
}}
ref={this._annotationLayer}>
{this.inlineTextAnnotations
@@ -993,7 +1061,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
- setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
+ setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 48f4c2afd..c5167461b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -8,7 +8,6 @@ import { NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
-import { ColorScheme } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { DocFocusOptions, DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
@@ -22,7 +21,7 @@ export class DashDocView {
this.dom = document.createElement('span');
this.dom.style.position = 'relative';
this.dom.style.textIndent = '0';
- this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
+ this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, 'lightGray');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index bd969b527..90ebf5206 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -38,7 +38,7 @@ import { LinkManager } from '../../../util/LinkManager';
import { RTFMarkup } from '../../../util/RTFMarkup';
import { SelectionManager } from '../../../util/SelectionManager';
import { SnappingManager } from '../../../util/SnappingManager';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
import { CollectionStackingView } from '../../collections/CollectionStackingView';
import { CollectionTreeView } from '../../collections/CollectionTreeView';
@@ -71,13 +71,14 @@ import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
import applyDevTools = require('prosemirror-dev-tools');
import React = require('react');
-const translateGoogleApi = require('translate-google-api');
+import { media_state } from '../AudioBox';
+import { setCORS } from 'google-translate-api-browser';
+// setting up cors-anywhere server address
+const translate = setCORS('http://cors-anywhere.herokuapp.com/');
export const GoogleRef = 'googleDocId';
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
-export interface FormattedTextBoxProps {
- allowScroll?: boolean;
-}
+export interface FormattedTextBoxProps {}
@observer
export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() {
public static LayoutString(fieldStr: string) {
@@ -93,6 +94,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
static _userStyleSheet: any = addStyleSheet();
static _hadSelection: boolean = false;
private _sidebarRef = React.createRef<SidebarAnnos>();
+ private _sidebarTagRef = React.createRef<React.Component>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
@@ -153,11 +155,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@computed get layout_autoHeightMargins() {
return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins);
}
- @computed get _recording() {
- return this.dataDoc?.mediaState === 'recording';
+ @computed get _recordingDictation() {
+ return this.dataDoc?.mediaState === media_state.Recording;
}
- set _recording(value) {
- !this.dataDoc[`${this.fieldKey}_recordingSource`] && (this.dataDoc.mediaState = value ? 'recording' : undefined);
+ set _recordingDictation(value) {
+ !this.dataDoc[`${this.fieldKey}_recordingSource`] && (this.dataDoc.mediaState = value ? media_state.Recording : undefined);
}
@computed get config() {
this._keymap = buildKeymap(schema, this.props);
@@ -265,19 +267,36 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
!this.layoutDoc.layout_showSidebar && this.toggleSidebar();
const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true);
+
setTimeout(() => {
const target = this._sidebarRef.current?.anchorMenuClick(anchor);
if (target) {
anchor.followLinkAudio = true;
- DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
+ let stopFunc: any;
+ Doc.GetProto(target).mediaState = media_state.Recording;
+ Doc.GetProto(target).audioAnnoState = 'recording';
+ DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target), stop => (stopFunc = stop));
+ let reactionDisposer = reaction(
+ () => target.mediaState,
+ action(dictation => {
+ if (!dictation) {
+ Doc.GetProto(target).audioAnnoState = 'stopped';
+ stopFunc();
+ reactionDisposer();
+ }
+ })
+ );
target.title = ComputedField.MakeFunction(`self["text_audioAnnotations_text"].lastElement()`);
}
});
};
- AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
- this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
- return undefined;
- });
+ AnchorMenu.Instance.Highlight = undoable(
+ action((color: string, isLinkButton: boolean) => {
+ this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
+ return undefined;
+ }),
+ 'highlght text'
+ );
AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
/**
@@ -372,7 +391,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let link;
LinkManager.Links(this.dataDoc).forEach((l, i) => {
const anchor = (l.link_anchor_1 as Doc).annotationOn ? (l.link_anchor_1 as Doc) : (l.link_anchor_2 as Doc).annotationOn ? (l.link_anchor_2 as Doc) : undefined;
- if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') {
+ if (anchor && (anchor.annotationOn as Doc).mediaState === media_state.Recording) {
linkTime = NumCast(anchor._timecodeToShow /* audioStart */);
linkAnchor = anchor;
link = l;
@@ -439,7 +458,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
if (!(cfield instanceof ComputedField)) {
- this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '');
+ this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
if (str.startsWith('@') && str.length > 1) {
Doc.AddDocToList(Doc.MyPublishedDocs, undefined, this.rootDoc);
}
@@ -541,7 +560,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
drop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.annoDragData) {
de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
- e.stopPropagation();
return true;
}
const dragData = de.complete.docDragData;
@@ -648,7 +666,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-remote', { background: 'yellow' });
}
if (highlights.includes('My Text')) {
- addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { background: 'moccasin' });
+ addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace(/\./g, '').replace(/@/g, ''), { background: 'moccasin' });
}
if (highlights.includes('Todo Items')) {
addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-todo', { outline: 'black solid 1px' });
@@ -688,7 +706,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
toggleSidebar = (preview: boolean = false) => {
const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
if (preview) this._showSidebar = true;
- else this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
+ else {
+ this.layoutDoc[this.SidebarKey + '_freeform_scale_max'] = 1;
+ this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
+ }
this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
};
@@ -844,7 +865,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
event: () => (this.layoutDoc._layout_enableAltContentUI = !this.layoutDoc._layout_enableAltContentUI),
icon: !this.Document._layout_enableAltContentUI ? 'eye-slash' : 'eye',
});
- uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
+ !Doc.noviceMode && uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
!Doc.noviceMode &&
uicontrols.push({
description: 'Broadcast Message',
@@ -945,14 +966,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
breakupDictation = () => {
- if (this._editorView && this._recording) {
+ if (this._editorView && this._recordingDictation) {
this.stopDictation(true);
this._break = true;
const state = this._editorView.state;
const to = state.selection.to;
const updated = TextSelection.create(state.doc, to, to);
this._editorView.dispatch(state.tr.setSelection(updated).insertText('\n', to));
- if (this._recording) {
+ if (this._recordingDictation) {
this.recordDictation();
}
}
@@ -1242,13 +1263,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!this.props.dontRegisterView) {
this._disposers.record = reaction(
- () => this._recording,
+ () => this._recordingDictation,
() => {
this.stopDictation(true);
- this._recording && this.recordDictation();
- }
+ this._recordingDictation && this.recordDictation();
+ },
+ { fireImmediately: true }
);
- if (this._recording) setTimeout(this.recordDictation);
+ if (this._recordingDictation) setTimeout(this.recordDictation);
}
var quickScroll: string | undefined = '';
this._disposers.scroll = reaction(
@@ -1546,8 +1568,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@action
componentWillUnmount() {
- if (this._recording) {
- this._recording = !this._recording;
+ if (this._recordingDictation) {
+ this._recordingDictation = !this._recordingDictation;
}
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
@@ -1561,7 +1583,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onPointerDown = (e: React.PointerEvent): void => {
if ((e.nativeEvent as any).handledByInnerReactInstance) {
- return e.stopPropagation();
+ return; //e.stopPropagation();
} else (e.nativeEvent as any).handledByInnerReactInstance = true;
if (this.Document.forceActive) e.stopPropagation();
@@ -1585,7 +1607,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
});
}
- if (this._recording && !e.ctrlKey && e.button === 0) {
+ if (this._recordingDictation && !e.ctrlKey && e.button === 0) {
this.breakupDictation();
}
this._downX = e.clientX;
@@ -1593,10 +1615,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
FormattedTextBoxComment.textBox = this;
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
- // bcz: Change. drag selecting requires that preventDefault is NOT called. This used to happen in DocumentView,
- // but that's changed, so this shouldn't be needed.
- //e.stopPropagation(); // if the text box is selected, then it consumes all down events
+ // stop propagation if not in sidebar, otherwise nested boxes will lose focus to outer boxes.
+ e.stopPropagation(); // if the text box's content is active, then it consumes all down events
document.addEventListener('pointerup', this.onSelectEnd);
}
}
@@ -1614,8 +1634,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
else if (this.props.isContentActive(true)) {
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- // !this.props.isSelected(true) &&
- editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0))));
+ let xpos = pcords?.pos || 0;
+ while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
+ xpos = xpos - 1;
+ }
+ editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(xpos))));
let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
while (target && !target.dataset?.targethrefs) target = target.parentElement;
FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
@@ -1758,13 +1781,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const state = this._editorView!.state;
const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
- if (this.layoutDoc.sidebar_collectionType === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
+ if (this.layoutDoc[this.SidebarKey + '_type_collection'] === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
try {
- translateGoogleApi(curText, { from: 'en', to: 'es' }).then((result1: any) => {
+ translate(curText, { from: 'en', to: 'es' }).then((result1: any) => {
setTimeout(
() =>
- translateGoogleApi(result1[0], { from: 'es', to: 'en' }).then((result: any) => {
- this.dataDoc[this.fieldKey + '_translation'] = result1 + '\r\n\r\n' + result[0];
+ translate(result1.text, { from: 'es', to: 'en' }).then((result: any) => {
+ const tb = this._sidebarTagRef.current as FormattedTextBox;
+ tb._editorView?.dispatch(tb._editorView!.state.tr.insertText(result1.text + '\r\n\r\n' + result.text));
}),
1000
);
@@ -1802,7 +1826,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (state.selection.empty || !this._rules!.EnteringStyle) {
this._rules!.EnteringStyle = false;
}
- e.stopPropagation();
+ let stopPropagation = true;
for (var i = state.selection.from; i <= state.selection.to; i++) {
const node = state.doc.resolve(i);
if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
@@ -1821,6 +1845,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
case 'Tab':
e.preventDefault();
break;
+ case 'c':
+ this._editorView?.state.selection.empty && (stopPropagation = false);
+ break;
default:
if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
case ' ':
@@ -1830,6 +1857,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
break;
}
+ if (stopPropagation) e.stopPropagation();
this.startUndoTypingBatch();
};
ondrop = (e: React.DragEvent) => {
@@ -1887,7 +1915,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
.scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this.props.NativeDimScaling?.() || 1));
@computed get audioHandle() {
- return !this._recording ? null : (
+ return !this._recordingDictation ? null : (
<div
className="formattedTextBox-dictation"
onPointerDown={e =>
@@ -1896,7 +1924,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
e,
returnFalse,
emptyFunction,
- action(e => (this._recording = !this._recording))
+ action(e => (this._recordingDictation = !this._recordingDictation))
)
}>
<FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: 'red' }} icon={'microphone'} size="sm" />
@@ -1948,6 +1976,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
<div onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}>
<ComponentTag
{...this.props}
+ ref={this._sidebarTagRef as any}
setContentView={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
@@ -1970,14 +1999,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
fitContentsToBox={this.fitContentsToBox}
noSidebar={true}
treeViewHideTitle={true}
- fieldKey={this.layoutDoc.sidebar_collectionType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
+ fieldKey={this.layoutDoc[this.SidebarKey + '_type_collection'] === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
/>
</div>
);
};
return (
<div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- {renderComponent(StrCast(this.layoutDoc.sidebar_collectionType))}
+ {renderComponent(StrCast(this.layoutDoc[this.SidebarKey + '_type_collection']))}
</div>
);
}
@@ -2025,9 +2054,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@observable _isHovering = false;
onPassiveWheel = (e: WheelEvent) => {
+ if (e.clientX > this.ProseRef!.getBoundingClientRect().right) {
+ if (this.rootDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) {
+ // if the scrolled freeform is a child of the sidebar component, we need to let the event go through
+ // so react can let the freeform view handle it. We prevent default to stop any containing views from scrolling
+ e.preventDefault();
+ }
+ return;
+ }
+
// if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
- if (this.props.isContentActive() && !this.props.allowScroll) {
- if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault();
+ if (this.props.isContentActive()) {
+ // prevent default if selected || child is active but this doc isn't scrollable
+ if (
+ (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil(this.props.PanelHeight()) && //
+ (this.props.isSelected() || this.isAnyChildContentActive())
+ ) {
+ e.preventDefault();
+ }
e.stopPropagation();
}
};
@@ -2084,7 +2128,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
ref={this._ref}
style={{
cursor: this.props.isContentActive() ? 'text' : undefined,
- overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none',
}}
@@ -2098,10 +2141,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onPointerDown={this.onPointerDown}
onDoubleClick={this.onDoubleClick}>
<div
- className={`formattedTextBox-outer`}
+ className="formattedTextBox-outer"
ref={this._scrollRef}
style={{
- width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
+ width: this.props.dontSelectOnLoad || this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
@@ -2119,9 +2162,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
/>
</div>
{this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
- {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR ? null : this.sidebarHandle}
+ {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR || this.layoutDoc._chromeHidden ? null : this.sidebarHandle}
{this.audioHandle}
- {this.layoutDoc._layout_enableAltContentUI ? this.overlayAlternateIcon : null}
+ {this.layoutDoc._layout_enableAltContentUI && !this.layoutDoc._chromeHidden ? this.overlayAlternateIcon : null}
</div>
</div>
);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9c46459b0..e3ac4fb9d 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -21,6 +21,7 @@ import { updateBullets } from './ProsemirrorExampleTransfer';
import './RichTextMenu.scss';
import { schema } from './schema_rts';
import { EquationBox } from '../EquationBox';
+import { numberRange } from '../../../../Utils';
const { toggleMark } = require('prosemirror-commands');
@observer
@@ -149,19 +150,31 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
setMark = (mark: Mark, state: EditorState, dispatch: any, dontToggle: boolean = false) => {
if (mark) {
- const node = (state.selection as NodeSelection).node;
+ const liFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.list_item);
+ const liTo = numberRange(state.selection.$to.depth + 1).find(i => state.selection.$to.node(i)?.type === state.schema.nodes.list_item);
+ const olFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.ordered_list);
+ const nodeOl = (liFirst && liTo && state.selection.$from.node(liFirst) !== state.selection.$to.node(liTo) && olFirst) || (!liFirst && !liTo && olFirst);
+ const newPos = nodeOl ? numberRange(state.selection.from).findIndex(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) : state.selection.from;
+ const node = (state.selection as NodeSelection).node ?? (newPos >= 0 ? state.doc.nodeAt(newPos) : undefined);
if (node?.type === schema.nodes.ordered_list) {
let attrs = node.attrs;
if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family };
if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: mark.attrs.fontSize };
if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
- const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
- dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
- } else if (dontToggle) {
- const tr = state.tr.addMark(state.selection.from, state.selection.to, mark);
- dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise
- } else {
- toggleMark(mark.type, mark.attrs)(state, dispatch);
+ const tr = updateBullets(state.tr.setNodeMarkup(newPos, node.type, attrs), state.schema);
+ dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to))));
+ }
+ {
+ const state = this.view?.state;
+ const tr = this.view?.state.tr;
+ if (tr && state) {
+ if (dontToggle) {
+ tr.addMark(state.selection.from, state.selection.to, mark);
+ dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise
+ } else {
+ toggleMark(mark.type, mark.attrs)(state, dispatch);
+ }
+ }
}
}
};
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 8bafc2cef..3e2afd2ce 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -280,11 +280,8 @@ export class RichTextRules {
this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
}
const target = DocServer.FindDocByTitle(docTitle);
- if (target) {
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target[Id], hideKey: false });
- return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
- }
- return state.tr;
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false });
+ return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
}),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 7e17008bb..6f07588b3 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -348,7 +348,7 @@ export const marks: { [index: string]: MarkSpec } = {
excludes: 'user_mark',
group: 'inline',
toDOM(node: any) {
- const uid = node.attrs.userid.replace('.', '').replace('@', '');
+ const uid = node.attrs.userid.replace(/\./g, '').replace(/@/g, '');
const min = Math.round(node.attrs.modified / 60);
const hr = Math.round(min / 60);
const day = Math.round(hr / 60 / 24);
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss
index bf56b4d9e..0b51813a5 100644
--- a/src/client/views/nodes/trails/PresBox.scss
+++ b/src/client/views/nodes/trails/PresBox.scss
@@ -121,7 +121,7 @@
.dropdown.active {
transform: rotate(180deg);
color: $light-blue;
- opacity: 0.8;
+ opacity: 0.7;
}
.presBox-radioButtons {
@@ -187,9 +187,6 @@
font-size: 11;
font-weight: 200;
height: 20;
- background-color: $white;
- color: $black;
- border: solid 1px $black;
display: flex;
margin-left: 5px;
margin-top: 5px;
@@ -210,13 +207,11 @@
.ribbon-propertyUpDownItem {
cursor: pointer;
- color: white;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
- background: $black;
}
.ribbon-propertyUpDownItem:hover {
@@ -609,7 +604,6 @@
font-weight: 200;
height: 20;
background-color: $white;
- border: solid 1px rgba(0, 0, 0, 0.5);
display: flex;
color: $black;
margin-top: 5px;
@@ -691,16 +685,19 @@
padding-right: 5px;
padding-top: 3;
padding-bottom: 3;
+ opacity: 0.8;
}
.presBox-dropdownOption:hover {
position: relative;
- background-color: lightgrey;
+ opacity: 1;
+ font-weight: bold;
}
.presBox-dropdownOption.active {
position: relative;
- background-color: $light-blue;
+ opacity: 1;
+ font-weight: bold;
}
.presBox-dropdownOptions {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f750392f5..383b400c8 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -13,7 +13,7 @@ import { listSpec } from '../../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { AudioField } from '../../../../fields/URLField';
-import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
+import { emptyFunction, emptyPath, lightOrDark, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -28,6 +28,7 @@ import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from
import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
import { CollectionView } from '../../collections/CollectionView';
import { TabDocView } from '../../collections/TabDocView';
+import { TreeView } from '../../collections/TreeView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
import { LightboxView } from '../../LightboxView';
@@ -280,6 +281,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return listItems.filter(doc => !doc.layout_unrendered);
}
};
+
+ // go to documents chain
+ runSubroutines = (childrenToRun: Doc[], normallyNextSlide: Doc) => {
+ console.log(childrenToRun, normallyNextSlide, 'runSUBFUNC');
+ if (childrenToRun === undefined) {
+ console.log('children undefined');
+ return;
+ }
+ if (childrenToRun[0] === normallyNextSlide) {
+ return;
+ }
+
+ childrenToRun.forEach(child => {
+ DocumentManager.Instance.showDocument(child, {});
+ });
+ };
+
// Called when the user activates 'next' - to move to the next part of the pres. trail
@action
next = () => {
@@ -320,6 +338,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Case 1: No more frames in current doc and next slide is defined, therefore move to next slide
const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]);
const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex;
+
+ // before moving onto next slide, run the subroutines :)
+ const currentDoc = this.childDocs[this.itemIndex];
+ //could i do this.childDocs[this.itemIndex] for first arg?
+ this.runSubroutines(TreeView.GetRunningChildren.get(currentDoc)?.(), this.childDocs[this.itemIndex + 1]);
+
this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1);
progressiveReveal(true); // shows first progressive document, but without a transition effect
} else {
@@ -365,11 +389,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (from?.mediaStopTriggerList && this.layoutDoc.presentation_status !== PresStatus.Edit) {
DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
}
- if (from?.mediaStop === 'auto' && this.layoutDoc.presentation_status !== PresStatus.Edit) {
+ if (from?.presentation_mediaStop === 'auto' && this.layoutDoc.presentation_status !== PresStatus.Edit) {
this.stopTempMedia(from.presentation_targetDoc);
}
// If next slide is audio / video 'Play automatically' then the next slide should be played
- if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') {
+ if (this.layoutDoc.presentation_status !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.presentation_mediaStart === 'auto') {
this.startTempMedia(this.targetDoc, this.activeItem);
}
if (!group) this.clearSelectedArray();
@@ -441,7 +465,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const fkey = Doc.LayoutFieldKey(bestTarget);
const setData = bestTargetView?.ComponentView?.setData;
if (setData) setData(activeItem.config_data);
- else Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+ else {
+ const current = Doc.GetProto(bestTarget)[fkey];
+ Doc.GetProto(bestTarget)[fkey + '_' + Date.now()] = current instanceof ObjectField ? current[Copy]() : current;
+ Doc.GetProto(bestTarget)[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+ }
bestTarget[fkey + '_usePath'] = activeItem.config_usePath;
setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10);
}
@@ -711,6 +739,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
pinDoc.config_viewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]);
}
}
+
+ @action
+ static reversePin(pinDoc: Doc, targetDoc: Doc) {
+ // const fkey = Doc.LayoutFieldKey(targetDoc);
+ pinDoc.config_data = targetDoc.data;
+
+ console.log(pinDoc.presData);
+ }
+
/**
* This method makes sure that cursor navigates to the element that
* has the option open and last in the group.
@@ -763,8 +800,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
zoomTextSelections: BoolCast(activeItem.presentation_zoomText),
playAudio: BoolCast(activeItem.presPlayAudio),
+ playMedia: activeItem.presentation_mediaStart === 'auto',
};
- if (activeItem.presOpenInLightbox) {
+ if (activeItem.presentation_openInLightbox) {
const context = DocCast(targetDoc.annotationOn) ?? targetDoc;
if (!DocumentManager.Instance.getLightboxDocumentView(context)) {
LightboxView.SetLightboxDoc(context);
@@ -1040,8 +1078,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (doc.type === DocumentType.LABEL) {
const audio = Cast(doc.annotationOn, Doc, null);
if (audio) {
- audio.mediaStart = 'manual';
- audio.mediaStop = 'manual';
audio.config_clipStart = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
audio.config_clipEnd = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
audio.presentation_duration = audio.config_clipStart - audio.config_clipEnd;
@@ -1117,7 +1153,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (index >= 0 && index < this.childDocs.length) {
this.rootDoc._itemIndex = index;
}
- } else this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
+ } else {
+ this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
+ }
this.updateCurrentPresentation(DocCast(doc.embedContainer));
};
@@ -1340,7 +1378,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get paths() {
let pathPoints = '';
this.childDocs.forEach((doc, index) => {
- const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
+ const tagDoc = PresBox.targetRenderedDoc(doc);
if (tagDoc) {
const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
@@ -1435,8 +1473,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
@action
updateOpenDoc = (activeItem: Doc) => {
- activeItem.presOpenInLightbox = !activeItem.presOpenInLightbox;
- this.selectedArray.forEach(doc => (doc.presOpenInLightbox = activeItem.presOpenInLightbox));
+ activeItem.presentation_openInLightbox = !activeItem.presentation_openInLightbox;
+ this.selectedArray.forEach(doc => (doc.presentation_openInLightbox = activeItem.presentation_openInLightbox));
};
@undoBatch
@@ -1455,6 +1493,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect)));
static _sliderBatch: any;
+ static endBatch = () => {
+ PresBox._sliderBatch.end();
+ document.removeEventListener('pointerup', PresBox.endBatch, true);
+ };
public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => {
return (
<input
@@ -1464,13 +1506,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
max={max}
value={value}
readOnly={true}
- style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }}
+ style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)`, background: SettingsManager.userColor, color: SettingsManager.userVariantColor }}
className={`toolbar-slider ${active ? '' : 'none'}`}
onPointerDown={e => {
PresBox._sliderBatch = UndoManager.StartBatch('pres slider');
+ document.addEventListener('pointerup', PresBox.endBatch, true);
e.stopPropagation();
}}
- onPointerUp={() => PresBox._sliderBatch.end()}
onChange={e => {
e.stopPropagation();
change(e.target.value);
@@ -1495,7 +1537,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
});
};
- @computed get visibiltyDurationDropdown() {
+ @computed get visibilityDurationDropdown() {
const activeItem = this.activeItem;
if (activeItem && this.targetDoc) {
const targetType = this.targetDoc.type;
@@ -1504,30 +1546,49 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return (
<div className="presBox-ribbon">
<div className="ribbon-doubleButton">
- <Tooltip title={<div className="dash-tooltip">{'Hide before presented'}</div>}>
- <div className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+ <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}>
+ <div
+ className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`}
+ style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideBefore ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+ onClick={() => this.updateHideBefore(activeItem)}>
Hide before
</div>
</Tooltip>
<Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}>
- <div className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}>
+ <div
+ className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`}
+ style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hide ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+ onClick={() => this.updateHide(activeItem)}>
Hide
</div>
</Tooltip>
<Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}>
- <div className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+ <div
+ className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`}
+ style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideAfter ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+ onClick={() => this.updateHideAfter(activeItem)}>
Hide after
</div>
</Tooltip>
<Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presOpenInLightbox ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
+ <div
+ className="ribbon-toggle"
+ style={{
+ border: `solid 1px ${SettingsManager.userColor}`,
+ color: SettingsManager.userColor,
+ background: activeItem.presentation_openInLightbox ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
+ }}
+ onClick={() => this.updateOpenDoc(activeItem)}>
Lightbox
</div>
</Tooltip>
- <Tooltip title={<div className="dash-tooltip">{'Transition movement style'}</div>}>
- <div className="ribbon-toggle" onClick={() => this.updateEaseFunc(activeItem)}>
+ <Tooltip title={<div className="dash-tooltip">Transition movement style</div>}>
+ <div
+ className="ribbon-toggle"
+ style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presEaseFunc === 'ease' ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }}
+ onClick={() => this.updateEaseFunc(activeItem)}>
{`${StrCast(activeItem.presEaseFunc, 'ease')}`}
</div>
</Tooltip>
@@ -1536,10 +1597,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<>
<div className="ribbon-doubleButton">
<div className="presBox-subheading">Slide Duration</div>
- <div className="ribbon-property">
+ <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
<input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
</div>
- <div className="ribbon-propertyUpDown">
+ <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
<div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}>
<FontAwesomeIcon icon={'caret-up'} />
</div>
@@ -1578,7 +1639,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presBox-subheading">Progressivize Collection</div>
<input
className="presBox-checkbox"
- style={{ margin: 10 }}
+ style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
type="checkbox"
onChange={() => {
activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
@@ -1601,7 +1662,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presBox-subheading">Progressivize First Bullet</div>
<input
className="presBox-checkbox"
- style={{ margin: 10 }}
+ style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
type="checkbox"
onChange={() => (activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1)}
checked={!NumCast(activeItem.presentation_indexedStart)}
@@ -1609,7 +1670,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
<div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
<div className="presBox-subheading">Expand Current Bullet</div>
- <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presBulletExpand = !activeItem.presBulletExpand)} checked={BoolCast(activeItem.presBulletExpand)} />
+ <input
+ className="presBox-checkbox"
+ style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+ type="checkbox"
+ onChange={() => (activeItem.presBulletExpand = !activeItem.presBulletExpand)}
+ checked={BoolCast(activeItem.presBulletExpand)}
+ />
</div>
<div className="ribbon-box">
@@ -1620,10 +1687,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
e.stopPropagation();
this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
})}
- style={{ borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5, border: this._openBulletEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+ style={{
+ color: SettingsManager.userColor,
+ background: SettingsManager.userVariantColor,
+ borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
+ border: this._openBulletEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+ }}>
{effect?.toString()}
<FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openBulletEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
- <div className={'presBox-dropdownOptions'} style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+ <div
+ className={'presBox-dropdownOptions'}
+ style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none', color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
+ onPointerDown={e => e.stopPropagation()}>
{bulletEffect(PresEffect.None)}
{bulletEffect(PresEffect.Fade)}
{bulletEffect(PresEffect.Flip)}
@@ -1654,7 +1729,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
);
const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
- const color = activeItem.presentation_effectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presentation_effectDirection) ? Colors.LIGHT_BLUE : 'black';
+ const color = activeItem.presentation_effectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presentation_effectDirection) ? SettingsManager.userVariantColor : SettingsManager.userColor;
return (
<Tooltip title={<div className="dash-tooltip">{direction}</div>}>
<div
@@ -1688,10 +1763,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
e.stopPropagation();
this._openMovementDropdown = !this._openMovementDropdown;
})}
- style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+ style={{
+ color: SettingsManager.userColor,
+ background: SettingsManager.userVariantColor,
+ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5,
+ border: this._openMovementDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+ }}>
{this.movementName(activeItem)}
<FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}>
+ <div
+ className="presBox-dropdownOptions"
+ id={'presBoxMovementDropdown'}
+ onPointerDown={StopEvent}
+ style={{
+ color: SettingsManager.userColor,
+ background: SettingsManager.userBackgroundColor,
+ display: this._openMovementDropdown ? 'grid' : 'none',
+ }}>
{presMovement(PresMovement.None)}
{presMovement(PresMovement.Center)}
{presMovement(PresMovement.Zoom)}
@@ -1701,10 +1789,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
<div className="presBox-subheading">Zoom (% screen filled)</div>
- <div className="ribbon-property">
+ <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
<input className="presBox-input" type="number" readOnly={true} value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
</div>
- <div className="ribbon-propertyUpDown">
+ <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
<div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}>
<FontAwesomeIcon icon={'caret-up'} />
</div>
@@ -1716,10 +1804,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
{PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}
<div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
<div className="presBox-subheading">Transition Time</div>
- <div className="ribbon-property">
+ <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
<input className="presBox-input" type="number" readOnly={true} value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
</div>
- <div className="ribbon-propertyUpDown">
+ <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}>
<div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), 1000)}>
<FontAwesomeIcon icon={'caret-up'} />
</div>
@@ -1739,13 +1827,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
Effects
<div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
<div className="presBox-subheading">Play Audio Annotation</div>
- <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} />
+ <input
+ className="presBox-checkbox"
+ style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
+ type="checkbox"
+ onChange={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))}
+ checked={BoolCast(activeItem.presPlayAudio)}
+ />
</div>
<div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
<div className="presBox-subheading">Zoom Text Selections</div>
<input
className="presBox-checkbox"
- style={{ margin: 10 }}
+ style={{ margin: 10, border: `solid 1px ${SettingsManager.userColor}` }}
type="checkbox"
onChange={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))}
checked={BoolCast(activeItem.presentation_zoomText)}
@@ -1757,10 +1851,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
e.stopPropagation();
this._openEffectDropdown = !this._openEffectDropdown;
})}
- style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+ style={{
+ color: SettingsManager.userColor,
+ background: SettingsManager.userVariantColor,
+ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5,
+ border: this._openEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.userColor}`,
+ }}>
{effect?.toString()}
<FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+ <div
+ className="presBox-dropdownOptions"
+ id={'presBoxMovementDropdown'}
+ style={{
+ color: SettingsManager.userColor,
+ background: SettingsManager.userBackgroundColor,
+ display: this._openEffectDropdown ? 'grid' : 'none',
+ }}
+ onPointerDown={e => e.stopPropagation()}>
{preseEffect(PresEffect.None)}
{preseEffect(PresEffect.Fade)}
{preseEffect(PresEffect.Flip)}
@@ -1771,7 +1878,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
<div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}>
<div className="presBox-subheading">Effect direction</div>
- <div className="ribbon-property">{StrCast(this.activeItem.presentation_effectDirection)}</div>
+ <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}>
+ {StrCast(this.activeItem.presentation_effectDirection)}
+ </div>
</div>
<div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
{presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
@@ -1793,8 +1902,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get mediaOptionsDropdown() {
const activeItem = this.activeItem;
if (activeItem && this.targetDoc) {
- const clipStart = NumCast(activeItem.clipStart);
- const clipEnd = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '_duration']));
+ const renderTarget = PresBox.targetRenderedDoc(this.activeItem);
+ const clipStart = NumCast(renderTarget.clipStart);
+ const clipEnd = NumCast(renderTarget.clipEnd, clipStart + NumCast(renderTarget[Doc.LayoutFieldKey(renderTarget) + '_duration']));
+ const config_clipEnd = NumCast(activeItem.config_clipEnd) < NumCast(activeItem.config_clipStart) ? clipEnd - clipStart : NumCast(activeItem.config_clipEnd);
return (
<div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div>
@@ -1805,7 +1916,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="slider-text" style={{ fontWeight: 500 }}>
Start time (s)
</div>
- <div id={'startTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+ <div id="startTime" className="slider-number" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
<input
className="presBox-input"
style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
@@ -1813,9 +1924,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
readOnly={true}
value={NumCast(activeItem.config_clipStart).toFixed(2)}
onKeyDown={e => e.stopPropagation()}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- activeItem.config_clipStart = Number(e.target.value);
- })}
+ onChange={action(e => (activeItem.config_clipStart = Number(e.target.value)))}
/>
</div>
</div>
@@ -1823,25 +1932,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="slider-text" style={{ fontWeight: 500 }}>
Duration (s)
</div>
- <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}>
- {Math.round((NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart)) * 10) / 10}
+ <div className="slider-number" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
+ {Math.round((config_clipEnd - NumCast(activeItem.config_clipStart)) * 10) / 10}
</div>
</div>
<div className="slider-block">
<div className="slider-text" style={{ fontWeight: 500 }}>
End time (s)
</div>
- <div id={'endTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+ <div id="endTime" className="slider-number" style={{ color: SettingsManager.userColor, backgroundColor: SettingsManager.userBackgroundColor }}>
<input
className="presBox-input"
onKeyDown={e => e.stopPropagation()}
style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
type="number"
readOnly={true}
- value={NumCast(activeItem.config_clipEnd).toFixed(2)}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
- activeItem.config_clipEnd = Number(e.target.value);
- })}
+ value={config_clipEnd.toFixed(2)}
+ onChange={action(e => (activeItem.config_clipEnd = Number(e.target.value)))}
/>
</div>
</div>
@@ -1852,16 +1959,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
step="0.1"
min={clipStart}
max={clipEnd}
- value={NumCast(activeItem.config_clipEnd)}
- style={{ gridColumn: 1, gridRow: 1 }}
+ value={config_clipEnd}
+ style={{ gridColumn: 1, gridRow: 1, background: SettingsManager.userColor, color: SettingsManager.userVariantColor }}
className={`toolbar-slider ${'end'}`}
id="toolbar-slider"
onPointerDown={e => {
this._batch = UndoManager.StartBatch('config_clipEnd');
const endBlock = document.getElementById('endTime');
if (endBlock) {
- endBlock.style.color = Colors.LIGHT_GRAY;
- endBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
+ endBlock.style.backgroundColor = SettingsManager.userVariantColor;
}
e.stopPropagation();
}}
@@ -1869,8 +1975,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._batch?.end();
const endBlock = document.getElementById('endTime');
if (endBlock) {
- endBlock.style.color = Colors.BLACK;
- endBlock.style.backgroundColor = Colors.LIGHT_GRAY;
+ endBlock.style.backgroundColor = SettingsManager.userBackgroundColor;
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1891,8 +1996,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._batch = UndoManager.StartBatch('config_clipStart');
const startBlock = document.getElementById('startTime');
if (startBlock) {
- startBlock.style.color = Colors.LIGHT_GRAY;
- startBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
+ startBlock.style.backgroundColor = SettingsManager.userVariantColor;
}
e.stopPropagation();
}}
@@ -1900,8 +2004,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._batch?.end();
const startBlock = document.getElementById('startTime');
if (startBlock) {
- startBlock.style.color = Colors.BLACK;
- startBlock.style.backgroundColor = Colors.LIGHT_GRAY;
+ startBlock.style.backgroundColor = SettingsManager.userBackgroundColor;
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -1921,22 +2024,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presBox-subheading">Start playing:</div>
<div className="presBox-radioButtons">
<div className="checkbox-container">
- <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'manual')} checked={activeItem.mediaStart === 'manual'} />
+ <input
+ className="presBox-checkbox"
+ type="checkbox"
+ style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+ onChange={() => (activeItem.presentation_mediaStart = 'manual')}
+ checked={activeItem.presentation_mediaStart === 'manual'}
+ />
<div>On click</div>
</div>
<div className="checkbox-container">
- <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'auto')} checked={activeItem.mediaStart === 'auto'} />
+ <input
+ className="presBox-checkbox"
+ style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+ type="checkbox"
+ onChange={() => (activeItem.presentation_mediaStart = 'auto')}
+ checked={activeItem.presentation_mediaStart === 'auto'}
+ />
<div>Automatically</div>
</div>
</div>
<div className="presBox-subheading">Stop playing:</div>
<div className="presBox-radioButtons">
<div className="checkbox-container">
- <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'manual')} checked={activeItem.mediaStop === 'manual'} />
- <div>At audio end time</div>
+ <input
+ className="presBox-checkbox"
+ type="checkbox"
+ style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+ onChange={() => (activeItem.presentation_mediaStop = 'manual')}
+ checked={activeItem.presentation_mediaStop === 'manual'}
+ />
+ <div>At media end time</div>
</div>
<div className="checkbox-container">
- <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'auto')} checked={activeItem.mediaStop === 'auto'} />
+ <input
+ className="presBox-checkbox"
+ type="checkbox"
+ style={{ border: `solid 1px ${SettingsManager.userColor}` }}
+ onChange={() => (activeItem.presentation_mediaStop = 'auto')}
+ checked={activeItem.presentation_mediaStop === 'auto'}
+ />
<div>On slide change</div>
</div>
{/* <div className="checkbox-container">
@@ -2184,8 +2311,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
const mode = StrCast(this.rootDoc._type_collection) as CollectionViewType;
const isMini: boolean = this.toolbarWidth <= 100;
- const activeColor = Colors.LIGHT_BLUE;
- const inactiveColor = Colors.WHITE;
+ const activeColor = SettingsManager.userVariantColor;
+ const inactiveColor = lightOrDark(SettingsManager.userBackgroundColor) === Colors.WHITE ? Colors.WHITE : SettingsManager.userBackgroundColor;
return mode === CollectionViewType.Carousel3D || Doc.IsInMyOverlay(this.rootDoc) ? null : (
<div id="toolbarContainer" className={'presBox-toolbar'}>
{/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
@@ -2195,7 +2322,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<Tooltip title={<div className="dash-tooltip">View paths</div>}>
<div
style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
- className={'toolbar-button'}
+ className="toolbar-button"
onClick={this.childDocs.length > 1 ? () => this.togglePath() : undefined}>
<FontAwesomeIcon icon={'exchange-alt'} />
</div>
@@ -2527,7 +2654,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
removeDocument={returnFalse}
dontRegisterView={true}
focus={this.focusElement}
- scriptContext={this}
ScreenToLocalTransform={this.getTransform}
AddToMap={this.AddToMap}
RemFromMap={this.RemFromMap}
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 4f95f0c1f..9ac2b5a94 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -4,6 +4,10 @@ $light-background: #ececec;
$slide-background: #d5dce2;
$slide-active: #5b9fdd;
+.testingv2 {
+ background-color: red;
+}
+
.presItem-container {
cursor: grab;
display: flex;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 529a5024f..82ed9e8d5 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -6,7 +6,7 @@ import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType } from '../../../documents/DocumentTypes';
@@ -25,6 +25,9 @@ import { PresBox } from './PresBox';
import './PresElementBox.scss';
import { PresMovement } from './PresEnums';
import React = require('react');
+import { TreeView } from '../../collections/TreeView';
+import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
+import { MultiToggle, Type } from 'browndash-components';
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
@@ -301,8 +304,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
activeItem.config_rotation = NumCast(targetDoc.rotation);
activeItem.config_width = NumCast(targetDoc.width);
activeItem.config_height = NumCast(targetDoc.height);
- activeItem.config_pinLayout = true;
+ activeItem.config_pinLayout = !activeItem.config_pinLayout;
+ // activeItem.config_pinLayout = true;
};
+
+ //wait i dont think i have to do anything here since by default it'll revert to the previously saved if I don't save
+ //so basically, don't have an onClick for this, just let it do nada for now
+ @undoBatch
+ @action
+ revertToPreviouslySaved = (presTargetDoc: Doc, activeItem: Doc) => {
+ const target = DocCast(activeItem.annotationOn) ?? activeItem;
+ PresBox.reversePin(activeItem, target);
+ };
+
/**
* Method called for updating the view of the currently selected document
*
@@ -399,6 +413,18 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
+ @undoBatch
+ @action
+ lfg = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ // TODO: fix this bug
+ const { toggleChildrenRun } = this.rootDoc;
+ TreeView.ToggleChildrenRun.get(this.rootDoc)?.();
+
+ // call this.rootDoc.recurChildren() to get all the children
+ // if (iconClick) PresElementBox.showVideo = false;
+ };
+
@computed
get toolbarWidth(): number {
const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
@@ -409,13 +435,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
@computed get presButtons() {
- const presBox = this.presBox; //presBox
- const presBoxColor: string = StrCast(presBox?._backgroundColor);
- const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
- const targetDoc: Doc = this.targetDoc;
- const activeItem: Doc = this.rootDoc;
+ const presBox = this.presBox;
+ const presBoxColor = StrCast(presBox?._backgroundColor);
+ const presColorBool = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
+ const targetDoc = this.targetDoc;
+ const activeItem = this.rootDoc;
+ const hasChildren = BoolCast(this.rootDoc?.hasChildren);
const items: JSX.Element[] = [];
+
items.push(
<Tooltip key="slide" title={<div className="dash-tooltip">Update captured doc layout</div>}>
<div
@@ -486,6 +514,22 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
</Tooltip>
);
+ if (!Doc.noviceMode && hasChildren) {
+ // TODO: replace with if treeveiw, has childrenDocs
+ items.push(
+ <Tooltip key="children" title={<div className="dash-tooltip">Run child processes (tree only)</div>}>
+ <div
+ className="slideButton"
+ onClick={e => {
+ e.stopPropagation();
+ this.lfg(e);
+ }}
+ style={{ fontWeight: 700 }}>
+ <FontAwesomeIcon icon={'circle-play'} onPointerDown={e => e.stopPropagation()} />
+ </div>
+ </Tooltip>
+ );
+ }
items.push(
<Tooltip key="trash" title={<div className="dash-tooltip">Remove from presentation</div>}>
<div className={'slideButton'} onClick={this.removePresentationItem}>
@@ -532,7 +576,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
) : (
<div
ref={this._dragRef}
- className={`presItem-slide ${isCurrent ? 'active' : ''}`}
+ className={`presItem-slide ${isCurrent ? 'active' : ''}${this.rootDoc.runProcess ? ' testingv2' : ''}`}
style={{
display: 'infline-block',
backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),