aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx15
-rw-r--r--src/client/views/nodes/ColorBox.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx44
-rw-r--r--src/client/views/nodes/DocumentView.scss7
-rw-r--r--src/client/views/nodes/DocumentView.tsx70
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/FilterBox.tsx47
-rw-r--r--src/client/views/nodes/ImageBox.tsx50
-rw-r--r--src/client/views/nodes/PDFBox.tsx4
-rw-r--r--src/client/views/nodes/ScreenshotBox.scss7
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx181
-rw-r--r--src/client/views/nodes/VideoBox.scss1
-rw-r--r--src/client/views/nodes/VideoBox.tsx47
-rw-r--r--src/client/views/nodes/WebBox.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx44
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx4
16 files changed, 347 insertions, 183 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 1fda4cc5e..092823603 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -37,10 +37,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"]; // fields that are configured to be animatable using animation frames
@observable _animPos: number[] | undefined = undefined;
@observable _contentView: DocumentView | undefined | null;
- random(min: number, max: number) { /* min should not be equal to max */ return min + ((Math.abs(this.X * this.Y) * 9301 + 49297) % 233280 / 233280) * (max - min); }
get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; }
- get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
+ get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.props.jitterRotation}deg)`; }
get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
@@ -156,7 +155,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
returnThis = () => this;
render() {
TraceMobx();
- const backgroundColor = () => this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor);
const divProps: DocumentViewProps = {
...this.props,
CollectionFreeFormDocumentView: this.returnThis,
@@ -175,17 +173,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
zIndex: this.ZInd,
mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
- display: this.ZInd === -99 ? "none" : undefined,
+ display: this.ZInd === -99 ? "none" : undefined
}} >
-
- {Doc.UserDoc().renderStyle !== "comic" ? (null) :
- <div style={{ width: "100%", height: "100%", position: "absolute" }}>
- <svg style={{ transform: `scale(1,${this.props.PanelHeight() / this.props.PanelWidth()})`, transformOrigin: "top left", overflow: "visible" }} viewBox="0 0 12 14">
- <path d="M 7 0 C 9 -1 13 1 12 4 C 11 10 13 12 10 12 C 6 12 7 13 2 12 Q -1 11 0 8 C 1 4 0 4 0 2 C 0 0 1 0 1 0 C 3 0 3 1 7 0"
- style={{ stroke: "black", fill: backgroundColor(), strokeWidth: 0.2 }} />
- </svg>
- </div>}
-
<DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
</div>;
}
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 46c599abe..8da5cd1b1 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -41,7 +41,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== "") {
Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + "-color"] = color.hex;
} else {
- Doc.Layout(view.props.Document)._backgroundColor = color.hex; // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment
+ Doc.Layout(view.props.Document)._backgroundColor = color.hex + (color.rgb.a ? Math.round(color.rgb.a * 255).toString(16) : ""); // '_backgroundColor' is template specific. 'backgroundColor' would apply to all templates, but has no UI at the moment
}
}
});
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b53827371..f0a54e4ac 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,46 +1,45 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt, Field, AclPrivate } from "../../../fields/Doc";
-import { Cast, StrCast, NumCast } from "../../../fields/Types";
-import { OmitKeys, Without, emptyPath } from "../../../Utils";
+import { AclPrivate, Doc, Opt } from "../../../fields/Doc";
+import { ScriptField } from "../../../fields/ScriptField";
+import { Cast, StrCast } from "../../../fields/Types";
+import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
+import { emptyPath, OmitKeys, Without } from "../../../Utils";
import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CollectionSchemaView } from "../collections/CollectionSchemaView";
import { CollectionView } from "../collections/CollectionView";
+import { InkingStroke } from "../InkingStroke";
+import { PresElementBox } from "../presentationview/PresElementBox";
+import { SearchBox } from "../search/SearchBox";
+import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
import { AudioBox } from "./AudioBox";
-import { LabelBox } from "./LabelBox";
-import { EquationBox } from "./EquationBox";
-import { FunctionPlotBox } from "./FunctionPlotBox";
-import { SliderBox } from "./SliderBox";
-import { LinkBox } from "./LinkBox";
-import { ScriptingBox } from "./ScriptingBox";
+import { ColorBox } from "./ColorBox";
+import { ComparisonBox } from "./ComparisonBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
-import { FontIconBox } from "./FontIconBox";
+import { EquationBox } from "./EquationBox";
import { FieldView, FieldViewProps } from "./FieldView";
+import { FilterBox } from "./FilterBox";
+import { FontIconBox } from "./FontIconBox";
import { FormattedTextBox, FormattedTextBoxProps } from "./formattedText/FormattedTextBox";
+import { FunctionPlotBox } from "./FunctionPlotBox";
import { ImageBox } from "./ImageBox";
import { KeyValueBox } from "./KeyValueBox";
+import { LabelBox } from "./LabelBox";
+import { LinkAnchorBox } from "./LinkAnchorBox";
+import { LinkBox } from "./LinkBox";
import { PDFBox } from "./PDFBox";
import { PresBox } from "./PresBox";
-import { SearchBox } from "../search/SearchBox";
-import { FilterBox } from "./FilterBox";
-import { ColorBox } from "./ColorBox";
-import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
-import { LinkAnchorBox } from "./LinkAnchorBox";
-import { PresElementBox } from "../presentationview/PresElementBox";
import { ScreenshotBox } from "./ScreenshotBox";
-import { ComparisonBox } from "./ComparisonBox";
+import { ScriptingBox } from "./ScriptingBox";
+import { SliderBox } from "./SliderBox";
import { VideoBox } from "./VideoBox";
import { WebBox } from "./WebBox";
-import { InkingStroke } from "../InkingStroke";
import React = require("react");
-import { TraceMobx, GetEffectiveAcl } from "../../../fields/util";
-import { ScriptField } from "../../../fields/ScriptField";
import XRegExp = require("xregexp");
-import { DocumentType } from "../../documents/DocumentTypes";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -226,7 +225,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox,
- ScreenshotBox, HTMLtag, ComparisonBox
+ ScreenshotBox,
+ HTMLtag, ComparisonBox
}}
bindings={bindings}
jsx={layoutFrame}
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 36572b861..bdbece621 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,6 +4,13 @@
border-radius: inherit;
}
+.documentView-customBorder {
+ width:100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+}
+
.documentView-node,
.documentView-node-topmost {
position: inherit;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8abcc3f6a..22bdfb1cf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -47,6 +47,7 @@ import { PresBox } from './PresBox';
import { RadialMenu } from './RadialMenu';
import React = require("react");
import { ScriptingBox } from "./ScriptingBox";
+import { FormattedTextBox } from "./formattedText/FormattedTextBox";
const { Howl } = require('howler');
interface Window {
@@ -120,6 +121,7 @@ export interface DocumentViewSharedProps {
dropAction?: dropActionType;
dontRegisterView?: boolean;
hideLinkButton?: boolean;
+ hideCaptions?: boolean;
ignoreAutoHeight?: boolean;
disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
pointerEvents?: string;
@@ -200,7 +202,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
@computed get nativeWidth() { return this.props.NativeWidth(); }
@computed get nativeHeight() { return this.props.NativeHeight(); }
- @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
+ @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onfClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
@computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
@@ -385,7 +387,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
SelectionManager.DeselectAll();
}
- startDragging(x: number, y: number, dropAction: dropActionType) {
+ startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
if (this._mainCont.current) {
const dragData = new DragManager.DocumentDragData([this.props.Document]);
const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
@@ -396,7 +398,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dragData.moveDocument = this.props.moveDocument;
const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
- DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart },
+ DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) },
() => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
}
}
@@ -453,10 +455,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}, console.log);
UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click");
} else if (!Doc.IsSystem(this.rootDoc)) {
- if (this.props.Document.type !== DocumentType.LABEL) {
- UndoManager.RunInBatch(() => this.props.addDocTab((this.rootDoc._fullScreenView as Doc) || this.rootDoc, "lightbox"), "double tap");
- SelectionManager.DeselectAll();
- }
+ UndoManager.RunInBatch(() =>
+ LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.())
+ //this.props.addDocTab((this.rootDoc._fullScreenView as Doc) || this.rootDoc, "lightbox")
+ , "double tap");
+ SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
} else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
@@ -508,6 +511,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
!(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
+ !this.Document.ignoreClick &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
!CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
@@ -668,6 +672,20 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const appearance = cm.findByDescription("UI Controls...");
const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
!Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+ appearanceItems.push({
+ description: "Add a Field", event: () => {
+ const alias = Doc.MakeAlias(this.rootDoc);
+ alias.layout = FormattedTextBox.LayoutString("newfield");
+ alias.title = "newfield";
+ alias._yMargin = 10;
+ alias._height = 35;
+ alias._width = 100;
+ alias.syncLayoutFieldWithTitle = true;
+ alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
+ alias.y = NumCast(this.rootDoc.y);
+ this.props.addDocument?.(alias);
+ }, icon: "eye"
+ });
DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
!appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
@@ -890,22 +908,19 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
TraceMobx();
const showTitle = this.ShowTitle?.split(":")[0];
const showTitleHover = this.ShowTitle?.includes(":hover");
- const showCaption = StrCast(this.layoutDoc._showCaption);
+ const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined;
const captionView = !showCaption ? (null) :
- <div className="documentView-captionWrapper"
- style={{
- backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]),
- color: StrCast(this.layoutDoc["caption-color"])
- }}>
- <DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
- yMargin={10}
- xMargin={10}
+ <div className="documentView-captionWrapper">
+ <FormattedTextBox {...OmitKeys(this.props, ['children']).omit}
+ yPadding={10}
+ xPadding={10}
+ fieldKey={showCaption}
+ fontSize={Math.min(32, 12 * this.props.ScreenToLocalTransform().Scale)}
hideOnLeave={true}
styleProvider={this.captionStyleProvider}
dontRegisterView={true}
- LayoutTemplateString={`<FormattedTextBox {...props} fieldKey={'${showCaption}'}/>`}
onClick={this.onClickFunc}
- layoutKey={this.finalLayoutKey} />
+ />
</div>;
const titleView = !showTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
@@ -960,6 +975,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]"; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way
+ const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
+ const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
@@ -975,8 +992,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
border: highlighting && this.borderRounding && highlightStyle === "dashed" ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
boxShadow,
+ clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined
}}>
- {PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc}
+ {!borderPath.path ? internal :
+ <>
+ {/* <div style={{ clipPath: `path('${borderPath.fill}')` }}>
+ {internal}
+ </div> */}
+ {internal}
+ <div key="border2" className="documentView-customBorder" style={{ pointerEvents: "none" }} >
+ <svg style={{ overflow: "visible" }} viewBox={`0 0 ${this.props.PanelWidth()} ${this.props.PanelHeight()}`}>
+ <path d={borderPath.path} style={{ stroke: "black", fill: "transparent", strokeWidth: borderPath.width }} />
+ </svg>
+ </div>
+ </>
+ }
</div>;
}
}
@@ -1076,6 +1106,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}), 400);
});
+ startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
+
docViewPathFunc = () => this.docViewPath;
isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
select = (extendSelection: boolean) => SelectionManager.SelectView(this, !SelectionManager.Views().some(v => v.props.Document === this.props.ContainingCollectionDoc) && extendSelection);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 4f4df32b3..86250c9d1 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -32,8 +32,8 @@ export interface FieldViewProps extends DocumentViewSharedProps {
width?: number;
background?: string;
color?: string;
- xMargin?: number;
- yMargin?: number;
+ xPadding?: number;
+ yPadding?: number;
}
@observer
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 090099dd8..c97de3402 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -1,8 +1,9 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { computed, observable, action, reaction, runInAction } from "mobx";
+import { action, computed, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, Field, Opt, DocListCastAsync, HeightSym } from "../../../fields/Doc";
+import Select from "react-select";
+import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { List } from "../../../fields/List";
import { RichTextField } from "../../../fields/RichTextField";
@@ -12,24 +13,22 @@ import { Cast, StrCast } from "../../../fields/Types";
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { UserOptions } from "../../util/GroupManager";
+import { Scripting } from "../../util/Scripting";
+import { SelectionManager } from "../../util/SelectionManager";
import { CollectionTreeView } from "../collections/CollectionTreeView";
+import { CollectionView } from "../collections/CollectionView";
import { ViewBoxBaseComponent } from "../DocComponent";
+import { EditableView } from "../EditableView";
import { SearchBox } from "../search/SearchBox";
+import { DashboardToggleButton, DefaultStyleProvider, StyleProp } from "../StyleProvider";
+import { DocumentViewProps } from "./DocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import './FilterBox.scss';
-import { Scripting } from "../../util/Scripting";
-import { SelectionManager } from "../../util/SelectionManager";
-import { CollectionView } from "../collections/CollectionView";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-import Select from "react-select";
-import { UserOptions } from "../../util/GroupManager";
-import { DocumentViewProps } from "./DocumentView";
-import { DefaultStyleProvider, StyleProp } from "../StyleProvider";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { EditableView } from "../EditableView";
-import { undoBatch } from "../../util/UndoManager";
type FilterBoxDocument = makeInterface<[typeof documentSchema]>;
const FilterBoxDocument = makeInterface(documentSchema);
@@ -135,7 +134,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
}
gatherFieldValues(dashboard: Doc, facetKey: string) {
- const childDocs = DocListCast((dashboard.data as any)[0].data);
+ const childDocs = DocListCast(dashboard.data);
const valueSet = new Set<string>();
let rtFields = 0;
childDocs.forEach((d) => {
@@ -206,7 +205,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
let nonNumbers = 0;
let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
facetValues.strings.map(val => {
- const num = Number(val);
+ const num = val ? Number(val) : Number.NaN;
if (Number.isNaN(num)) {
nonNumbers++;
} else {
@@ -217,8 +216,8 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
let newFacet: Opt<Doc>;
if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) {
newFacet = Docs.Create.TextDocument("", {
- _width: 100, _height: 25, system: true, _stayInCollection: true, target: targetDoc,
- treeViewExpandedView: "layout", title: facetHeader, _treeViewOpen: true, _forceActive: true, ignoreClick: true
+ title: facetHeader, system: true, target: targetDoc, _width: 100, _height: 25, _stayInCollection: true,
+ treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, ignoreClick: true
});
Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
newFacet._textBoxPadding = 4;
@@ -226,7 +225,8 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
} else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) {
newFacet = Docs.Create.SliderDocument({
- title: facetHeader, _overflow: "visible", _fitWidth: true, target: targetDoc, _height: 40, _stayInCollection: true, treeViewExpandedView: "layout", _treeViewOpen: true
+ title: facetHeader, system: true, target: targetDoc, _fitWidth: true, _height: 40, _stayInCollection: true,
+ treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, _overflow: "visible",
});
const newFacetField = Doc.LayoutFieldKey(newFacet);
const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
@@ -346,16 +346,9 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
* add lock and hide button decorations for the "Dashboards" flyout TreeView
*/
FilterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
- switch (property.split(":")[0]) {
- case StyleProp.Decorations:
- return !doc || doc.treeViewHideHeaderFields ? (null) :
- <>
- <div className={`styleProvider-treeView-hide${doc.hidden ? "-active" : ""}`} onClick={undoBatch(e =>
- this.removeFilter(StrCast(doc.title))
- )}>
- <FontAwesomeIcon icon={"trash"} size="sm" />
- </div>
- </>;
+ if (property.split(":")[0] === StyleProp.Decorations) {
+ return !doc || doc.treeViewHideHeaderFields ? (null) :
+ DashboardToggleButton(doc, "hidden", "trash", "trash", () => this.removeFilter(StrCast(doc.title)));
}
return this.props.styleProvider?.(doc, props, property);
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8a6946b78..e2e08a0e6 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,14 +1,13 @@
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap, untracked } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from "mobx-react";
-import { Dictionary } from 'typescript-collections';
import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc';
import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { createSchema, makeInterface } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, OmitKeys, returnOne, Utils } from '../../../Utils';
@@ -30,8 +29,6 @@ import React = require("react");
const path = require('path');
export const pageSchema = createSchema({
- _curPage: "number",
- fitWidth: "boolean",
googlePhotosUrl: "string",
googlePhotosTags: "string"
});
@@ -59,8 +56,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this._dropDisposer?.();
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
}
+ setViewSpec = (anchor: Doc, preview: boolean) => {
+
+ } // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
componentDidMount() {
+ this.props.setContentView?.(this); // bcz: do not remove this. without it, stepping into an image in the lightbox causes an infinite loop....
this._disposers.sizer = reaction(() => (
{
forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
@@ -102,8 +103,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const targetDoc = layoutDoc[DataSym];
if (targetDoc[targetField] instanceof ImageField) {
this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
- Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc));
- Doc.SetNativeWidth(this.dataDoc, Doc.NativeHeight(targetDoc));
+ Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
+ Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
e.stopPropagation();
}
}
@@ -315,6 +316,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _marqueeing: number[] | undefined;
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get annotationLayer() {
+ TraceMobx();
return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
}
@action
@@ -331,7 +333,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
TraceMobx();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
- return (<div className={`imageBox`} onContextMenu={this.specificContextMenu} ref={this._mainCont}
+ return (<div className="imageBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
style={{
width: this.props.PanelWidth() ? undefined : `100%`,
height: this.props.PanelWidth() ? undefined : `100%`,
@@ -340,32 +342,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}} >
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- CollectionView={undefined}
- PanelHeight={this.props.PanelHeight}
- scaling={returnOne}
- ScreenToLocalTransform={this.screenToLocalTransform}
- PanelWidth={this.props.PanelWidth}
fieldKey={this.annotationKey}
+ CollectionView={undefined}
isAnnotationOverlay={true}
- docFilters={this.props.docFilters}
- docRangeFilters={this.props.docRangeFilters}
- searchFilterDocs={this.props.searchFilterDocs}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
annotationLayerHostsContent={true}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
+ PanelWidth={this.props.PanelWidth}
+ PanelHeight={this.props.PanelHeight}
+ ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
isContentActive={this.isContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}>
+ scaling={returnOne}
+ whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocument}>
{this.contentFunc}
</CollectionFreeFormView>
{this.annotationLayer}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
- <MarqueeAnnotator rootDoc={this.rootDoc}
- scrollTop={0} down={this._marqueeing}
+ <MarqueeAnnotator
+ rootDoc={this.rootDoc}
+ scrollTop={0}
+ down={this._marqueeing}
scaling={this.props.scaling}
docView={this.props.docViewPath().lastElement()}
addDocument={this.addDocument}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d5056d934..feaeb9e21 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -243,6 +243,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
</div>;
}
+ anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
+
@computed get renderPdfView() {
TraceMobx();
return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}
@@ -258,7 +260,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
pdf={this._pdf!}
url={this.pdfUrl!.url.pathname}
isContentActive={this.isContentActive}
- anchorMenuClick={this._sidebarRef.current?.anchorMenuClick}
+ anchorMenuClick={this.anchorMenuClick}
loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
setPdfViewer={this.setPdfViewer}
addDocument={this.addDocument}
diff --git a/src/client/views/nodes/ScreenshotBox.scss b/src/client/views/nodes/ScreenshotBox.scss
index ab54cf526..6fb5ea7b3 100644
--- a/src/client/views/nodes/ScreenshotBox.scss
+++ b/src/client/views/nodes/ScreenshotBox.scss
@@ -10,6 +10,13 @@
// }
}
+#CANCAN {
+ canvas {
+ width:100% !important;
+ height: 100% !important;
+ }
+}
+
.screenshotBox-content, .screenshotBox-content-interactive, .screenshotBox-cont-fullScreen {
width: 100%;
z-index: -1; // 0; // logically this should be 0 (or unset) which would give us transparent brush strokes over videos. However, this makes Chrome crawl to a halt
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index c4f8d1143..252c029e4 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,7 +1,9 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
+// import { Canvas } from '@react-three/fiber';
+import { action, computed, observable, reaction } from "mobx";
import { observer } from "mobx-react";
+// import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
import { DateField } from "../../../fields/DateField";
import { Doc, WidthSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
@@ -12,7 +14,7 @@ import { ComputedField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
import { AudioField, VideoField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, numberRange, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils";
import { DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { Networking } from "../../Network";
@@ -33,18 +35,101 @@ declare class MediaRecorder {
type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
const ScreenshotDocument = makeInterface(documentSchema);
+// interface VideoTileProps {
+// raised: { coord: Vector2, off: Vector3 }[];
+// setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void;
+// x: number;
+// y: number;
+// rootDoc: Doc;
+// color: string;
+// }
+
+// @observer
+// export class VideoTile extends React.Component<VideoTileProps> {
+// @observable _videoRef: HTMLVideoElement | undefined;
+// _mesh: any = undefined;
+
+// render() {
+// const topLeft = [this.props.x, this.props.y];
+// const raised = this.props.raised;
+// const find = (raised: { coord: Vector2, off: Vector3 }[], what: Vector2) => raised.find(r => r.coord.x === what.x && r.coord.y === what.y);
+// const tl1 = find(raised, new Vector2(topLeft[0], topLeft[1] + 1));
+// const tl2 = find(raised, new Vector2(topLeft[0] + 1, topLeft[1] + 1));
+// const tl3 = find(raised, new Vector2(topLeft[0] + 1, topLeft[1]));
+// const tl4 = find(raised, new Vector2(topLeft[0], topLeft[1]));
+// const quad_indices = [0, 2, 1, 0, 3, 2];
+// const quad_uvs = [0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0];
+// const quad_normals = [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,];
+// const quad_vertices =
+// [
+// topLeft[0] - 0.0 + (tl1?.off.x || 0), topLeft[1] + 1.0 + (tl1?.off.y || 0), 0.0 + (tl1?.off.z || 0),
+// topLeft[0] + 1.0 + (tl2?.off.x || 0), topLeft[1] + 1.0 + (tl2?.off.y || 0), 0.0 + (tl2?.off.z || 0),
+// topLeft[0] + 1.0 + (tl3?.off.x || 0), topLeft[1] - 0.0 + (tl3?.off.y || 0), 0.0 + (tl3?.off.z || 0),
+// topLeft[0] - 0.0 + (tl4?.off.x || 0), topLeft[1] - 0.0 + (tl4?.off.y || 0), 0.0 + (tl4?.off.z || 0)
+// ];
+
+// const vertices = new Float32Array(quad_vertices);
+// const normals = new Float32Array(quad_normals);
+// const uvs = new Float32Array(quad_uvs); // Each vertex has one uv coordinate for texture mapping
+// const indices = new Uint32Array(quad_indices); // Use the four vertices to draw the two triangles that make up the square.
+// const popOut = () => NumCast(this.props.rootDoc.popOut);
+// const popOff = () => NumCast(this.props.rootDoc.popOff);
+// return (
+// <mesh key={`mesh${topLeft[0]}${topLeft[1]}`} onClick={action(async e => {
+// this.props.setRaised([
+// { coord: new Vector2(topLeft[0], topLeft[1]), off: new Vector3(-popOff(), -popOff(), popOut()) },
+// { coord: new Vector2(topLeft[0] + 1, topLeft[1]), off: new Vector3(popOff(), -popOff(), popOut()) },
+// { coord: new Vector2(topLeft[0], topLeft[1] + 1), off: new Vector3(-popOff(), popOff(), popOut()) },
+// { coord: new Vector2(topLeft[0] + 1, topLeft[1] + 1), off: new Vector3(popOff(), popOff(), popOut()) }
+// ]);
+// if (!this._videoRef) {
+// (navigator.mediaDevices as any).getDisplayMedia({ video: true }).then(action((stream: any) => {
+// //const videoSettings = stream.getVideoTracks()[0].getSettings();
+// this._videoRef = document.createElement("video");
+// Object.assign(this._videoRef, {
+// srcObject: stream,
+// //height: videoSettings.height,
+// //width: videoSettings.width,
+// autoplay: true
+// });
+// }));
+// }
+// })} ref={(r: any) => this._mesh = r}>
+// <bufferGeometry attach="geometry" ref={(r: any) => {
+// // itemSize = 3 because there are 3 values (components) per vertex
+// r?.setAttribute('position', new BufferAttribute(vertices, 3));
+// r?.setAttribute('normal', new BufferAttribute(normals, 3));
+// r?.setAttribute('uv', new BufferAttribute(uvs, 2));
+// r?.setIndex(new BufferAttribute(indices, 1));
+// }} />
+// {!this._videoRef ? <meshStandardMaterial color={this.props.color} /> :
+// <meshBasicMaterial >
+// <videoTexture attach="map" args={[this._videoRef]} />
+// </meshBasicMaterial>}
+// </mesh>
+// );
+// }
+// }
+
@observer
export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
- private _videoRef: HTMLVideoElement | null = null;
private _audioRec: any;
private _videoRec: any;
+ @observable private _videoRef: HTMLVideoElement | null = null;
@observable _screenCapture = false;
@computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
constructor(props: any) {
super(props);
- this.setupDictation();
+ if (this.rootDoc.videoWall) {
+ this.rootDoc.nativeWidth = undefined;
+ this.rootDoc.nativeHeight = undefined;
+ this.layoutDoc.popOff = 0;
+ this.layoutDoc.popOut = 1;
+ } else {
+ this.setupDictation();
+ }
}
getAnchor = () => {
const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
@@ -67,6 +152,16 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
componentDidMount() {
this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+ // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+ // ({ width, height }) => {
+ // if (this._camera) {
+ // const angle = -Math.abs(1 - width / height);
+ // const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
+ // this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
+ // this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+ // (this._camera as any).updateProjectionMatrix();
+ // }
+ // });
}
componentWillUnmount() {
const ind = DocUtils.ActiveRecordings.indexOf(this);
@@ -79,26 +174,50 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
}
@computed get content() {
+ if (this.rootDoc.videoWall) return (null);
const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
return <video className={"videoBox-content" + interactive} key="video"
ref={r => {
this._videoRef = r;
setTimeout(() => {
- if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) { // TODO glr: use mediaState
+ if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
this.toggleRecording();
}
- }, 1000);
+ }, 100);
}}
- autoPlay={true}
- style={{ width: "100%", height: "100%" }}
+ autoPlay={this._screenCapture}
+ style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
onCanPlay={this.videoLoad}
controls={true}
onClick={e => e.preventDefault()}>
<source type="video/mp4" />
Not supported.
- </video>;
+ </video>;
}
+ // _numScreens = 5;
+ // _camera: Camera | undefined;
+ // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
+ // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
+ @computed get threed() {
+ // if (this.rootDoc.videoWall) {
+ // const screens: any[] = [];
+ // const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
+ // let count = 0;
+ // numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
+ // <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+ // return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+ // this._camera = props.camera;
+ // props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
+ // props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+ // }}>
+ // {/* <ambientLight />*/}
+ // <pointLight position={[10, 10, 10]} intensity={1} />
+ // {screens}
+ // </ Canvas>;
+ // }
+ return (null);
+ }
toggleRecording = action(async () => {
this._screenCapture = !this._screenCapture;
if (this._screenCapture) {
@@ -155,7 +274,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
}
- contentFunc = () => [this.content];
+ contentFunc = () => [this.threed, this.content];
videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], 1) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], 1) * this.props.PanelWidth();
formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
render() {
@@ -183,28 +302,26 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
{this.contentFunc}
</CollectionFreeFormView></div>
<div style={{ position: "relative", height: this.formattedPanelHeight() }}>
- <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- Document={this.dataDoc[this.fieldKey + "-dictation"]}
- fieldKey={"text"}
- PanelHeight={this.formattedPanelHeight}
- PanelWidth={this.props.PanelWidth}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- isAnnotationOverlay={true}
- select={emptyFunction}
- isContentActive={returnFalse}
- scaling={returnOne}
- xMargin={25}
- yMargin={10}
- whenChildContentsActiveChanged={emptyFunction}
- removeDocument={returnFalse}
- moveDocument={returnFalse}
- addDocument={returnFalse}
- CollectionView={undefined}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
- </FormattedTextBox></div>
+ {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
+ <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ Document={this.dataDoc[this.fieldKey + "-dictation"]}
+ fieldKey={"text"}
+ PanelHeight={this.formattedPanelHeight}
+ isAnnotationOverlay={true}
+ select={emptyFunction}
+ isContentActive={returnFalse}
+ scaling={returnOne}
+ xPadding={25}
+ yPadding={10}
+ whenChildContentsActiveChanged={emptyFunction}
+ removeDocument={returnFalse}
+ moveDocument={returnFalse}
+ addDocument={returnFalse}
+ CollectionView={undefined}
+ renderDepth={this.props.renderDepth + 1}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+ </FormattedTextBox>}
+ </div>
</div>
{!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
<div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 30f0c4393..f593f74fb 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -66,6 +66,7 @@
background-color:rgba(50, 50, 50, 0.2);
transform-origin: left top;
pointer-events:all;
+ cursor: default;
}
.videoBox-timelineButton {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fb58ddefc..263fd5a19 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -26,6 +26,8 @@ import { StyleProp } from "../StyleProvider";
import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from "./LinkDocPreview";
import "./VideoBox.scss";
+import { DragManager } from "../../util/DragManager";
+import { DocumentManager } from "../../util/DocumentManager";
const path = require('path');
type VideoDocument = makeInterface<[typeof documentSchema]>;
@@ -137,7 +139,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
}
- @action public Snapshot() {
+ @action public Snapshot(downX?: number, downY?: number) {
const width = (this.layoutDoc._width || 0);
const height = (this.layoutDoc._height || 0);
const canvas = document.createElement('canvas');
@@ -176,11 +178,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, "");
const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
- returnedFilename && this.createRealSummaryLink(returnedFilename));
+ returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY));
}
}
- private createRealSummaryLink = (relative: string) => {
+ private createRealSummaryLink = (relative: string, downX?: number, downY?: number) => {
const url = this.choosePath(Utils.prepend(relative));
const width = this.layoutDoc._width || 1;
const height = this.layoutDoc._height || 0;
@@ -192,7 +194,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
this.props.addDocument?.(imageSummary);
- DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+ const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+ link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
+ setTimeout(() =>
+ (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
}
@action
@@ -383,7 +388,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<span>{"" + formatTime(curTime)}</span>
<span style={{ fontSize: 8 }}>{" " + Math.round((curTime - Math.trunc(curTime)) * 100)}</span>
</div>,
- <div className="videoBox-snapshot" key="snap" onClick={this.onSnapshot} >
+ <div className="videoBox-snapshot" key="snap" onPointerDown={this.onSnapshotDown} >
<FontAwesomeIcon icon="camera" size="lg" />
</div>,
<div className="videoBox-timelineButton" key="timeline" onPointerDown={this.onTimelineHdlDown} style={{ bottom: `${100 - this.heightPercent}%` }}>
@@ -409,10 +414,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
e.preventDefault();
}
- onSnapshot = (e: React.MouseEvent) => {
- this.Snapshot();
- e.stopPropagation();
- e.preventDefault();
+ onSnapshotDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, (e) => {
+ this.Snapshot(e.clientX, e.clientY);
+ return true;
+ }, emptyFunction, () => this.Snapshot());
}
onTimelineHdlDown = action((e: React.PointerEvent) => {
@@ -492,7 +498,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
playLink = (doc: Doc) => {
- const startTime = Math.max(0, (this._stackedTimeline.current?.anchorStart(doc) || 0) - .25);
+ const startTime = Math.max(0, (this._stackedTimeline.current?.anchorStart(doc) || 0));
const endTime = this._stackedTimeline.current?.anchorEnd(doc);
if (startTime !== undefined) {
if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
@@ -567,22 +573,22 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
<div style={{ position: "absolute", transition: this.transition, width: this.panelWidth(), height: this.panelHeight(), top: 0, left: `${(100 - this.heightPercent) / 2}%` }}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+ renderDepth={this.props.renderDepth + 1}
fieldKey={this.annotationKey}
+ CollectionView={undefined}
isAnnotationOverlay={true}
annotationLayerHostsContent={true}
- select={emptyFunction}
- isContentActive={this.isContentActive}
- scaling={returnOne}
- docFilters={this.timelineDocFilter}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
ScreenToLocalTransform={this.screenToLocalTransform}
+ docFilters={this.timelineDocFilter}
+ select={emptyFunction}
+ isContentActive={this.isContentActive}
+ scaling={returnOne}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
removeDocument={this.removeDocument}
moveDocument={this.moveDocument}
- addDocument={this.addDocWithTimecode}
- CollectionView={undefined}
- renderDepth={this.props.renderDepth + 1}>
+ addDocument={this.addDocWithTimecode}>
{this.contentFunc}
</CollectionFreeFormView>
</div>
@@ -591,16 +597,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{this.renderTimeline}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
<MarqueeAnnotator
- scrollTop={0}
rootDoc={this.rootDoc}
+ scrollTop={0}
down={this._marqueeing}
- docView={this.props.docViewPath().lastElement()}
scaling={this.marqueeFitScaling}
+ docView={this.props.docViewPath().lastElement()}
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
savedAnnotations={this._savedAnnotations}
- annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current}
+ annotationLayer={this._annotationLayer.current}
+ mainCont={this._mainCont.current}
/>}
</div>
</div >);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index fc6f9ceab..cb7e58559 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -478,6 +478,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+ anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
render() {
const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false;
const scale = this.props.scaling?.() || 1;
@@ -531,7 +532,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
<MarqueeAnnotator rootDoc={this.rootDoc}
iframe={this.isFirefox() ? this.iframeClick : undefined}
iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
- anchorMenuClick={this._sidebarRef.current?.anchorMenuClick}
+ anchorMenuClick={this.anchorMenuClick}
scrollTop={0}
down={this._marqueeing} scaling={returnOne}
addDocument={this.addDocument}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c2860af76..911ec1560 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isEqual } from "lodash";
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, observable } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap, selectAll } from "prosemirror-commands";
import { history } from "prosemirror-history";
@@ -68,8 +68,8 @@ const translateGoogleApi = require("translate-google-api");
export interface FormattedTextBoxProps {
makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
hideOnLeave?: boolean; // used by DocumentView for setting caption's hide on leave (bcz: would prefer to have caption-hideOnLeave field set or something similar)
- xMargin?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
- yMargin?: number;
+ xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView
+ yPadding?: number;
noSidebar?: boolean;
dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded
}
@@ -1064,8 +1064,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
});
}
setupEditor(config: any, fieldKey: string) {
- const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null);
- const rtfField = Cast((!curText?.Text && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
+ const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.props.fieldKey]);
+ const rtfField = Cast((!curText && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
if (this.ProseRef) {
const self = this;
this._editorView?.destroy();
@@ -1075,8 +1075,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const docPos = editorView.coordsAtPos(editorView.state.selection.to);
const viewRect = self._ref.current!.getBoundingClientRect();
const scrollRef = self._scrollRef.current;
- if ((docPos.bottom < viewRect.top || docPos.bottom > viewRect.bottom) && scrollRef) {
- const scrollPos = scrollRef.scrollTop + (docPos.bottom - viewRect.top) * self.props.ScreenToLocalTransform().Scale;
+ const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
+ const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
+ if ((topOff || botOff) && scrollRef) {
+ const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
+ const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale;
if (this._focusSpeed !== undefined) {
scrollPos && smoothScroll(this._focusSpeed, scrollRef, scrollPos);
} else {
@@ -1118,7 +1121,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
FormattedTextBox.SelectOnLoadChar = "";
- } else if (curText?.Text && !FormattedTextBox.DontSelectInitialText) {
+ } else if (curText && !FormattedTextBox.DontSelectInitialText) {
selectAll(this._editorView!.state, this._editorView?.dispatch);
this.startUndoTypingBatch();
}
@@ -1132,6 +1135,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
componentWillUnmount() {
+ FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
this.unhighlightSearchTerms();
@@ -1236,8 +1240,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@action
onFocused = (e: React.FocusEvent): void => {
//applyDevTools.applyDevTools(this._editorView);
+ FormattedTextBox.Focused = this;
this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
}
+ @observable public static Focused: FormattedTextBox | undefined;
onClick = (e: React.MouseEvent): void => {
if (Math.abs(e.clientX - this._downX) > 4 || Math.abs(e.clientY - this._downY) > 4) {
@@ -1339,7 +1345,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._undoTyping = undefined;
return wasUndoing;
}
+ @action
onBlur = (e: any) => {
+ FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined);
if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
}
@@ -1472,8 +1480,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
NativeHeight={returnZero}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.sidebarWidth}
- xMargin={0}
- yMargin={0}
+ xPadding={0}
+ yPadding={0}
scaleField={this.SidebarKey + "-scale"}
isAnnotationOverlay={false}
select={emptyFunction}
@@ -1505,10 +1513,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
const minimal = this.props.ignoreAutoHeight;
- const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0);
+ const margins = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
const selPad = Math.min(margins, 10);
const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
+ const col = this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ const back = this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
return (
<div className="formattedTextBox-cont"
onWheel={e => this.isContentActive() && e.stopPropagation()}
@@ -1524,12 +1534,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
style={{
overflow: this.autoHeight ? "hidden" : undefined,
height: this.props.height || (this.autoHeight && this.props.renderDepth ? "max-content" : undefined),
- background: this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)),
- color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color)),
- pointerEvents: interactive ? undefined : "none",
- fontSize: this.props.fontSize || Cast(this.layoutDoc._fontSize, "string", null),
+ background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+ color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+ fontSize: this.props.fontSize ? this.props.fontSize : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
fontWeight: Cast(this.layoutDoc._fontWeight, "number", null),
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
+ pointerEvents: interactive ? undefined : "none",
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyDown}
@@ -1551,7 +1561,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
onScroll={this.onScroll} onDrop={this.ondrop} >
<div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
style={{
- padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
+ padding: StrCast(this.layoutDoc._textBoxPadding, `${padding}px`),
pointerEvents: !active && !SnappingManager.GetIsDragging() ? (this.layoutDoc.isLinkButton ? "none" : undefined) : undefined
}}
/>
@@ -1560,7 +1570,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
{(this.props.noSidebar || this.Document._noSidebar) || this.Document._singleLine ? (null) : this.sidebarHandle}
{!this.layoutDoc._showAudio ? (null) : this.audioHandle}
</div>
- </div>
+ </div >
);
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 6821935a0..1fde6e5f0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -85,7 +85,7 @@ export class FormattedTextBoxComment {
static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = "") {
FormattedTextBoxComment.textBox = textBox;
if ((hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection))) {
- FormattedTextBoxComment.setupPreview(view, textBox, hrefs?.trim().split(" "));
+ FormattedTextBoxComment.setupPreview(view, textBox, hrefs?.trim().split(" ").filter(h => h));
}
}
@@ -109,7 +109,7 @@ export class FormattedTextBoxComment {
}
// this checks if the selection is a hyperlink. If so, it displays the target doc's text for internal links, and the url of the target for external links.
- if (state.selection.$from && hrefs) {
+ if (state.selection.$from && hrefs?.length) {
const nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
nbef && naft && LinkDocPreview.SetLinkInfo({