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/ContentFittingDocumentView.tsx6
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx9
-rw-r--r--src/client/views/nodes/DocumentView.scss1
-rw-r--r--src/client/views/nodes/DocumentView.tsx85
-rw-r--r--src/client/views/nodes/FieldView.tsx9
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx22
-rw-r--r--src/client/views/nodes/IconBox.scss23
-rw-r--r--src/client/views/nodes/IconBox.tsx93
-rw-r--r--src/client/views/nodes/ImageBox.tsx9
-rw-r--r--src/client/views/nodes/KeyValueBox.scss4
-rw-r--r--src/client/views/nodes/PresBox.scss8
-rw-r--r--src/client/views/nodes/PresBox.tsx140
-rw-r--r--src/client/views/nodes/SliderBox-components.tsx256
-rw-r--r--src/client/views/nodes/SliderBox-tooltip.css33
-rw-r--r--src/client/views/nodes/SliderBox.scss8
-rw-r--r--src/client/views/nodes/SliderBox.tsx130
-rw-r--r--src/client/views/nodes/VideoBox.tsx3
-rw-r--r--src/client/views/nodes/WebBox.tsx3
18 files changed, 555 insertions, 287 deletions
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index bd1b6166f..671f5b96e 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { action, computed } from "mobx";
import { observer } from "mobx-react";
import "react-table/react-table.css";
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
import { ComputedField, ScriptField } from "../../../new_fields/ScriptField";
import { NumCast, StrCast } from "../../../new_fields/Types";
import { emptyFunction, returnEmptyString, returnOne } from "../../../Utils";
@@ -18,6 +18,7 @@ import { TraceMobx } from "../../../new_fields/util";
interface ContentFittingDocumentViewProps {
Document?: Doc;
DataDocument?: Doc;
+ LayoutDoc?: () => Opt<Doc>;
LibraryPath: Doc[];
childDocs?: Doc[];
renderDepth: number;
@@ -42,7 +43,7 @@ interface ContentFittingDocumentViewProps {
@observer
export class ContentFittingDocumentView extends React.Component<ContentFittingDocumentViewProps>{
public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
- private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); }
+ private get layoutDoc() { return this.props.Document && (this.props.LayoutDoc?.() || Doc.Layout(this.props.Document)); }
private get nativeWidth() { return NumCast(this.layoutDoc?._nativeWidth, this.props.PanelWidth()); }
private get nativeHeight() { return NumCast(this.layoutDoc?._nativeHeight, this.props.PanelHeight()); }
@computed get scaling() {
@@ -97,6 +98,7 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
<DocumentView {...this.props}
Document={this.props.Document}
DataDoc={this.props.DataDocument}
+ LayoutDoc={this.props.LayoutDoc}
LibraryPath={this.props.LibraryPath}
fitToBox={this.props.fitToBox}
onClick={this.props.onClick}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index ac80e2ec1..11a33b70a 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,7 +1,6 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
import { Doc } from "../../../new_fields/Doc";
-import { ScriptField } from "../../../new_fields/ScriptField";
import { Cast, StrCast } from "../../../new_fields/Types";
import { OmitKeys, Without } from "../../../Utils";
import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
@@ -14,13 +13,13 @@ import { LinkFollowBox } from "../linking/LinkFollowBox";
import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
import { AudioBox } from "./AudioBox";
import { ButtonBox } from "./ButtonBox";
+import { SliderBox } from "./SliderBox";
import { DocumentBox } from "./DocumentBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
import { FontIconBox } from "./FontIconBox";
import { FieldView, FieldViewProps } from "./FieldView";
import { FormattedTextBox } from "./FormattedTextBox";
-import { IconBox } from "./IconBox";
import { ImageBox } from "./ImageBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
@@ -83,10 +82,10 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
return this.props.DataDoc instanceof Promise ? undefined : this.props.DataDoc;
}
get layoutDoc() {
- if (this.props.DataDoc === undefined && typeof Doc.LayoutField(this.props.Document) !== "string") {
+ if (this.props.DataDoc === undefined && (this.props.LayoutDoc || typeof Doc.LayoutField(this.props.Document) !== "string")) {
// if there is no dataDoc (ie, we're not rendering a template layout), but this document has a layout document (not a layout string),
// then we render the layout document as a template and use this document as the data context for the template layout.
- return Doc.expandTemplateLayout(Doc.Layout(this.props.Document), this.props.Document);
+ return Doc.expandTemplateLayout(this.props.LayoutDoc?.() || Doc.Layout(this.props.Document), this.props.Document);
}
return Doc.Layout(this.props.Document);
}
@@ -106,7 +105,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
<ObserverJsxParser
blacklistedAttrs={[]}
components={{
- FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FontIconBox: FontIconBox, ButtonBox, FieldView,
+ FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, ButtonBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox,
ColorBox, DashWebRTCVideo, DocuLinkBox, InkingStroke, DocumentBox
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index b9045b11e..b121c6c18 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -71,7 +71,6 @@
width: 100%;
height: 25;
background: rgba(0, 0, 0, .4);
- padding: 4px;
text-align: center;
text-overflow: ellipsis;
white-space: pre;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 787f6b79b..95858af4c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,36 +1,42 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import * as fa from '@fortawesome/free-solid-svg-icons';
-import { action, computed, runInAction, trace } from "mobx";
+import { action, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
import { Document, PositionDocument } from '../../../new_fields/documentSchemas';
import { Id } from '../../../new_fields/FieldSymbols';
+import { InkTool } from '../../../new_fields/InkField';
+import { List } from '../../../new_fields/List';
+import { RichTextField } from '../../../new_fields/RichTextField';
import { listSpec } from "../../../new_fields/Schema";
import { ScriptField } from '../../../new_fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
-import { ImageField, PdfField, VideoField, AudioField } from '../../../new_fields/URLField';
+import { AudioField, ImageField, PdfField, VideoField } from '../../../new_fields/URLField';
+import { TraceMobx } from '../../../new_fields/util';
+import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { emptyFunction, returnTransparent, returnTrue, Utils, returnOne } from "../../../Utils";
+import { emptyFunction, returnOne, returnTransparent, returnTrue, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { DocServer } from "../../DocServer";
-import { Docs, DocUtils, DocumentOptions } from "../../documents/Documents";
+import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
import { ClientUtils } from '../../util/ClientUtils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
+import { InteractionUtils } from '../../util/InteractionUtils';
import { Scripting } from '../../util/Scripting';
import { SelectionManager } from "../../util/SelectionManager";
import SharingManager from '../../util/SharingManager';
import { Transform } from "../../util/Transform";
import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionViewType } from '../collections/CollectionView';
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionView } from "../collections/CollectionView";
+import { CollectionView, CollectionViewType } from '../collections/CollectionView';
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
+import { InkingControl } from '../InkingControl';
import { OverlayView } from '../OverlayView';
import { ScriptBox } from '../ScriptBox';
import { ScriptingRepl } from '../ScriptingRepl';
@@ -38,19 +44,7 @@ import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import { FormattedTextBox } from './FormattedTextBox';
import React = require("react");
-import { InteractionUtils } from '../../util/InteractionUtils';
-import { InkingControl } from '../InkingControl';
-import { InkTool } from '../../../new_fields/InkField';
-import { TraceMobx } from '../../../new_fields/util';
-import { List } from '../../../new_fields/List';
-import { FormattedTextBoxComment } from './FormattedTextBoxComment';
-import { GestureUtils } from '../../../pen-gestures/GestureUtils';
-import { RadialMenu } from './RadialMenu';
-import { RadialMenuProps } from './RadialMenuItem';
-import { CollectionStackingView } from '../collections/CollectionStackingView';
-import { RichTextField } from '../../../new_fields/RichTextField';
-import { HistoryUtil } from '../../util/History';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -61,6 +55,7 @@ export interface DocumentViewProps {
ContainingCollectionDoc: Opt<Doc>;
Document: Doc;
DataDoc?: Doc;
+ LayoutDoc?: () => Opt<Doc>;
LibraryPath: Doc[];
fitToBox?: boolean;
onClick?: ScriptField;
@@ -269,12 +264,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
} else if (this.onClickHandler && this.onClickHandler.script) {
+ SelectionManager.DeselectAll();
this.onClickHandler.script.run({ this: this.Document.isTemplateForField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, containingCollection: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log);
} else if (this.Document.type === DocumentType.BUTTON) {
ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY);
- } else if (this.props.Document.isButton === "Selector") { // this should be moved to an OnClick script
- FormattedTextBoxComment.Hide();
- this.Document.links?.[0] instanceof Doc && (Doc.UserDoc().SelectedDocs = new List([Doc.LinkOtherAnchor(this.Document.links[0], this.props.Document)]));
} else if (this.Document.isButton) {
SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered.
this.buttonClick(e.altKey, e.ctrlKey);
@@ -284,29 +277,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
preventDefault && e.preventDefault();
}
- })
+ });
buttonClick = async (altKey: boolean, ctrlKey: boolean) => {
- const maximizedDocs = await DocListCastAsync(this.Document.maximizedDocs);
- const summarizedDocs = await DocListCastAsync(this.Document.summarizedDocs);
const linkDocs = DocListCast(this.props.Document.links);
- let expandedDocs: Doc[] = [];
- expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs;
- expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs;
- // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),];
- if (expandedDocs.length) {
- SelectionManager.DeselectAll();
- let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace");
- maxLocation = this.Document.maximizeLocation = (!ctrlKey ? !altKey ? maxLocation : (maxLocation !== "inPlace" ? "inPlace" : "onRight") : (maxLocation !== "inPlace" ? "inPlace" : "inTab"));
- if (maxLocation === "inPlace") {
- expandedDocs.forEach(maxDoc => this.props.addDocument && this.props.addDocument(maxDoc));
- const scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.layoutDoc.width) / 2, NumCast(this.layoutDoc.height) / 2);
- DocumentManager.Instance.animateBetweenPoint(scrpt, expandedDocs);
- } else {
- expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation)));
- }
- }
- else if (linkDocs.length) {
+ if (linkDocs.length) {
DocumentManager.Instance.FollowLink(undefined, this.props.Document,
// open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards
(doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)),
@@ -563,7 +538,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const docTemplate = docLayoutTemplate || creator(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate));
- Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, customName, undefined);
+ Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
} else {
doc.layoutKey = customName;
}
@@ -617,13 +592,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@undoBatch
- @action
- freezeNativeDimensions = (): void => {
- this.layoutDoc._autoHeight = false;
- this.layoutDoc.ignoreAspect = !this.layoutDoc.ignoreAspect;
- if (!this.layoutDoc.ignoreAspect && !this.layoutDoc._nativeWidth) {
- this.layoutDoc._nativeWidth = this.props.PanelWidth();
- this.layoutDoc._nativeHeight = this.props.PanelHeight();
+ public static unfreezeNativeDimensions = action((layoutDoc: Doc): void => {
+ layoutDoc._nativeWidth = undefined;
+ layoutDoc._nativeHeight = undefined;
+ });
+
+ toggleNativeDimensions = () => {
+ if (this.Document._nativeWidth || this.Document._nativeHeight) {
+ DocumentView.unfreezeNativeDimensions(this.layoutDoc);
+ }
+ else {
+ Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight());
}
}
@@ -657,7 +636,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (StrCast(tempDoc.title) === layout) {
foundLayout = tempDoc;
}
- })
+ });
DocumentView.
makeCustomViewClicked(this.props.Document, this.props.DataDoc, Docs.Create.StackingDocument, layout, foundLayout);
} else {
@@ -737,7 +716,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
layoutItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
- layoutItems.push({ description: this.Document.ignoreAspect || !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" });
+ layoutItems.push({ description: !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" });
layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" });
layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
@@ -817,7 +796,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
});
const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc().LibraryBtn as Doc).sourcePanel as Doc) ? "" : d.title), "");
- cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" });
cm.addItem({
description: `path: ${path}`, event: () => {
this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true);
@@ -856,6 +834,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
Document={this.props.Document}
DataDoc={this.props.DataDoc}
+ LayoutDoc={this.props.LayoutDoc}
fitToBox={this.props.fitToBox}
LibraryPath={this.props.LibraryPath}
addDocument={this.props.addDocument}
@@ -938,7 +917,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.contents
:
<div className="documentView-styleWrapper" >
- <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? "calc(100% - 29px)" : "100%", top: showTextTitle ? "29px" : undefined }}>
+ <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? "calc(100% - 25px)" : "100%", top: showTextTitle ? "25px" : undefined }}>
{this.contents}
</div>
{titleView}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index dbbb76f83..8250f41f3 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -5,15 +5,10 @@ import { DateField } from "../../../new_fields/DateField";
import { Doc, FieldResult, Opt } from "../../../new_fields/Doc";
import { IconField } from "../../../new_fields/IconField";
import { List } from "../../../new_fields/List";
-import { RichTextField } from "../../../new_fields/RichTextField";
import { AudioField, ImageField, VideoField } from "../../../new_fields/URLField";
import { Transform } from "../../util/Transform";
import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
-import { FormattedTextBox } from "./FormattedTextBox";
-import { IconBox } from "./IconBox";
-import { ImageBox } from "./ImageBox";
-import { PDFBox } from "./PDFBox";
import { VideoBox } from "./VideoBox";
import { ScriptField } from "../../../new_fields/ScriptField";
@@ -48,6 +43,7 @@ export interface FieldViewProps {
setVideoBox?: (player: VideoBox) => void;
ContentScaling: () => number;
ChromeHeight?: () => number;
+ childLayoutTemplate?: () => Opt<Doc>;
}
@observer
@@ -78,9 +74,6 @@ export class FieldView extends React.Component<FieldViewProps> {
// else if (field instaceof PresBox) {
// return <PresBox {...this.props} />;
// }
- else if (field instanceof IconField) {
- return <IconBox {...this.props} />;
- }
else if (field instanceof VideoField) {
return <VideoBox {...this.props} />;
}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9370d3745..0cc276458 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -26,7 +26,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { DictationManager } from '../../util/DictationManager';
import { DragManager } from "../../util/DragManager";
import buildKeymap from "../../util/ProsemirrorExampleTransfer";
-import { inpRules } from "../../util/RichTextRules";
+import { RichTextRules } from "../../util/RichTextRules";
import { DashDocCommentView, FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummaryView, DashFieldView } from "../../util/RichTextSchema";
import { SelectionManager } from "../../util/SelectionManager";
import { undoBatch, UndoManager } from "../../util/UndoManager";
@@ -197,7 +197,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
updateTitle = () => {
- if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
+ if ((this.props.Document.isTemplateForField === "data" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
+ StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
const str = this._editorView.state.doc.textContent;
const titlestr = str.substr(0, Math.min(40, str.length));
this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
@@ -464,13 +465,14 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
_keymap: any = undefined;
+ _rules: RichTextRules | undefined;
@computed get config() {
this._keymap = buildKeymap(schema);
- (schema as any).Document = this.props.Document;
+ this._rules = new RichTextRules(this.props.Document, this);
return {
schema,
plugins: [
- inputRules(inpRules),
+ inputRules(this._rules.inpRules),
this.richTextMenuPlugin(),
history(),
keymap(this._keymap),
@@ -574,7 +576,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
if (ret.frag.size > 2 && ret.start >= 0) {
let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
if (ret.frag.firstChild) {
- selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
+ selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
}
editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
@@ -769,7 +771,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
});
- this._editorView.state.schema.Document = this.props.Document;
const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field);
if (startupText) {
this._editorView.dispatch(this._editorView.state.tr.insertText(startupText));
@@ -1010,14 +1011,14 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
}
const state = this._editorView!.state;
if (!state.selection.empty && e.key === "%") {
- state.schema.EnteringStyle = true;
+ this._rules!.EnteringStyle = true;
e.preventDefault();
e.stopPropagation();
return;
}
- if (state.selection.empty || !state.schema.EnteringStyle) {
- state.schema.EnteringStyle = false;
+ if (state.selection.empty || !this._rules!.EnteringStyle) {
+ this._rules!.EnteringStyle = false;
}
if (e.key === "Escape") {
this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
@@ -1122,8 +1123,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
CollectionView={undefined}
ScreenToLocalTransform={this.sidebarScreenToLocal}
renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- chromeCollapsed={true}>
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
</CollectionFreeFormView>
<div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown} onClick={e => this.toggleSidebar()} />
</div>}
diff --git a/src/client/views/nodes/IconBox.scss b/src/client/views/nodes/IconBox.scss
deleted file mode 100644
index 488681027..000000000
--- a/src/client/views/nodes/IconBox.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-
-@import "../globalCssVariables";
-.iconBox-container {
- position: inherit;
- left:0;
- top:0;
- height: auto;
- width: max-content;
- // overflow: hidden;
- pointer-events: all;
- svg {
- width: $MINIMIZED_ICON_SIZE !important;
- height: $MINIMIZED_ICON_SIZE !important;
- height: auto;
- background: white;
- }
- .iconBox-label {
- position: absolute;
- width:max-content;
- font-size: 14px;
- margin-top: 3px;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx
deleted file mode 100644
index 172338eb6..000000000
--- a/src/client/views/nodes/IconBox.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import React = require("react");
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTag, faTextHeight } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { FieldView, FieldViewProps } from './FieldView';
-import "./IconBox.scss";
-import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { IconField } from "../../../new_fields/IconField";
-import { ContextMenu } from "../ContextMenu";
-import Measure from "react-measure";
-import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss";
-import { Scripting } from "../../util/Scripting";
-import { ComputedField } from "../../../new_fields/ScriptField";
-
-
-library.add(faCaretUp);
-library.add(faObjectGroup);
-library.add(faStickyNote);
-library.add(faFilePdf);
-library.add(faFilm, faTag, faTextHeight);
-
-@observer
-export class IconBox extends React.Component<FieldViewProps> {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(IconBox, fieldKey); }
-
- @observable _panelWidth: number = 0;
- @observable _panelHeight: number = 0;
- @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; }
- @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); }
-
- public static summaryTitleScript(inputDoc: Doc) {
- const sumDoc = Cast(inputDoc.summaryDoc, Doc) as Doc;
- if (sumDoc && StrCast(sumDoc.title).startsWith("-")) {
- return sumDoc.title + ".expanded";
- }
- return "???";
- }
- public static titleScript(inputDoc: Doc) {
- const maxDoc = DocListCast(inputDoc.maximizedDocs);
- if (maxDoc.length === 1) {
- return maxDoc[0].title + ".icon";
- }
- return maxDoc.length > 1 ? "-multiple-.icon" : "???";
- }
-
- public static AutomaticTitle(doc: Doc) {
- Doc.GetProto(doc).title = ComputedField.MakeFunction('iconTitle(this);');
- }
-
- public static DocumentIcon(layout: string) {
- const button = layout.indexOf("PDFBox") !== -1 ? faFilePdf :
- layout.indexOf("ImageBox") !== -1 ? faImage :
- layout.indexOf("Formatted") !== -1 ? faStickyNote :
- layout.indexOf("Video") !== -1 ? faFilm :
- layout.indexOf("Collection") !== -1 ? faObjectGroup :
- faCaretUp;
- return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />;
- }
-
- setLabelField = (): void => {
- this.props.Document.hideLabel = !this.props.Document.hideLabel;
- }
-
- specificContextMenu = (): void => {
- const cm = ContextMenu.Instance;
- cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" });
- if (!this.props.Document.hideLabel) {
- cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" });
- }
- }
- render() {
- const label = this.props.Document.hideLabel ? "" : this.props.Document.title;
- return (
- <div className="iconBox-container" onContextMenu={this.specificContextMenu}>
- {this.minimizedIcon}
- <Measure offset onResize={(r) => runInAction(() => {
- if (r.offset!.width || this.props.Document.hideLabel) {
- this.props.Document.iconWidth = (r.offset!.width + Number(MINIMIZED_ICON_SIZE));
- if (this.props.Document._height === Number(MINIMIZED_ICON_SIZE)) this.props.Document._width = this.props.Document.iconWidth;
- }
- })}>
- {({ measureRef }) =>
- <span ref={measureRef} className="iconBox-label">{label}</span>
- }
- </Measure>
- </div>);
- }
-}
-Scripting.addGlobal(function iconTitle(doc: any) { return IconBox.titleScript(doc); });
-Scripting.addGlobal(function summaryTitle(doc: any) { return IconBox.summaryTitleScript(doc); }); \ No newline at end of file
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 896ac1685..c0e102195 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -207,7 +207,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
return url.href;
} else if (url.href.indexOf(window.location.origin) === -1) {
return Utils.CorsProxy(url.href);
- } else if (!(lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) {
+ } else if (!/\.(png|jpg|jpeg|gif)$/.test(lower)) {
return url.href;//Why is this here
}
const ext = path.extname(url.href);
@@ -321,12 +321,12 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
const { dataDoc } = this;
const { success, failure, idle, loading } = uploadIcons;
runInAction(() => this.uploadIcon = loading);
- const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] });
+ const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] });
dataDoc.originalUrl = primary;
let succeeded = true;
let data: ImageField | undefined;
try {
- data = new ImageField(Utils.prepend(clientAccessPath));
+ data = new ImageField(Utils.prepend(accessPaths.agnostic.client));
} catch {
succeeded = false;
}
@@ -436,8 +436,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
CollectionView={undefined}
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- chromeCollapsed={true}>
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
</div >);
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index 6e8a36c6a..a26880c9e 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -74,7 +74,7 @@ $header-height: 30px;
.keyValueBox-evenRow {
position: relative;
- display: inline-block;
+ display: flex;
width:100%;
height:$header-height;
background: $light-color;
@@ -114,7 +114,7 @@ $header-height: 30px;
.keyValueBox-oddRow {
position: relative;
- display: inline-block;
+ display: flex;
width:100%;
height:30px;
background: $light-color-secondary;
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index 7618aa7e3..6a20751cc 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -2,13 +2,11 @@
position: absolute;
z-index: 2;
box-shadow: #AAAAAA .2vw .2vw .4vw;
- right: 0;
- top: 0;
bottom: 0;
width: 100%;
min-width: 120px;
height: 100%;
- min-height: 50px;
+ min-height: 41px;
letter-spacing: 2px;
overflow: hidden;
transition: 0.7s opacity ease;
@@ -17,6 +15,10 @@
.presBox-buttons {
padding: 10px;
width: 100%;
+ background: gray;
+ padding-right: 10px;
+ padding-top: 5px;
+ padding-bottom: 5px;
.presBox-button {
margin-right: 2.5%;
margin-left: 2.5%;
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 6c4cbba12..d4a47c159 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -2,27 +2,25 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faTimes } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
-import { listSpec, makeInterface } from "../../../new_fields/Schema";
-import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { InkTool } from "../../../new_fields/InkField";
+import { listSpec } from "../../../new_fields/Schema";
+import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { returnFalse } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { undoBatch } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionView, CollectionViewType } from "../collections/CollectionView";
import { ContextMenu } from "../ContextMenu";
-import { FieldView, FieldViewProps } from './FieldView';
-import "./PresBox.scss";
-import { CollectionCarouselView } from "../collections/CollectionCarouselView";
-import { returnFalse } from "../../../Utils";
import { ContextMenuProps } from "../ContextMenuItem";
-import { CollectionTimeView } from "../collections/CollectionTimeView";
-import { documentSchema } from "../../../new_fields/documentSchemas";
import { InkingControl } from "../InkingControl";
-import { InkTool } from "../../../new_fields/InkField";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./PresBox.scss";
+import { PrefetchProxy } from "../../../new_fields/Proxy";
library.add(faArrowLeft);
library.add(faArrowRight);
@@ -37,33 +35,14 @@ library.add(faEdit);
export class PresBox extends React.Component<FieldViewProps> {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
_childReaction: IReactionDisposer | undefined;
- _slideshowReaction: IReactionDisposer | undefined;
@observable _isChildActive = false;
-
componentDidMount() {
- const userDoc = CurrentUserUtils.UserDocument;
- this._slideshowReaction = reaction(() => this.props.Document._slideshow,
- (slideshow) => {
- if (!slideshow) {
- let presTemp = Cast(userDoc.presentationTemplate, Doc);
- if (presTemp instanceof Promise) {
- presTemp.then(presTemp => this.props.Document.childLayout = presTemp);
- }
- else if (presTemp === undefined) {
- presTemp = userDoc.presentationTemplate = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent", _xMargin: 5, isTemplateDoc: true, isTemplateForField: "data" });
- }
- else {
- this.props.Document.childLayout = presTemp;
- }
- } else {
- this.props.Document.childLayout = undefined;
- }
- }, { fireImmediately: true });
+ this.props.Document._forceRenderEngine = "timeline";
+ this.props.Document._replacedChrome = "replaced";
this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true });
}
componentWillUnmount() {
this._childReaction?.();
- this._slideshowReaction?.();
}
@computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); }
@@ -343,24 +322,27 @@ export class PresBox extends React.Component<FieldViewProps> {
});
}
- toggleMinimize = undoBatch(action((e: React.PointerEvent) => {
- if (this.props.Document.inOverlay) {
- Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document);
- CollectionDockingView.AddRightSplit(this.props.Document, this.props.DataDoc);
- this.props.Document.inOverlay = false;
- } else {
- this.props.Document.x = e.clientX + 25;
- this.props.Document.y = e.clientY - 25;
- this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close");
- Doc.AddDocToList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document);
+ updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: number) => {
+ const toggle = BoolCast(this.props.Document.inOverlay) !== (mode === CollectionViewType.Invalid);
+ if (toggle) {
+ if (this.props.Document.inOverlay) {
+ Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document);
+ CollectionDockingView.AddRightSplit(this.props.Document, this.props.DataDoc);
+ this.props.Document.inOverlay = false;
+ } else {
+ this.props.Document.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25;
+ this.props.Document.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25;
+ this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close");
+ Doc.AddDocToList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document);
+ }
}
}));
specificContextMenu = (e: React.MouseEvent): void => {
const funcs: ContextMenuProps[] = [];
- funcs.push({ description: "Show as Slideshow", event: action(() => this.props.Document._slideshow = "slideshow"), icon: "asterisk" });
- funcs.push({ description: "Show as Timeline", event: action(() => this.props.Document._slideshow = "timeline"), icon: "asterisk" });
- funcs.push({ description: "Show as List", event: action(() => this.props.Document._slideshow = undefined), icon: "asterisk" });
+ funcs.push({ description: "Show as Slideshow", event: action(() => this.props.Document._viewType = CollectionViewType.Carousel), icon: "asterisk" });
+ funcs.push({ description: "Show as Timeline", event: action(() => this.props.Document._viewType = CollectionViewType.Time), icon: "asterisk" });
+ funcs.push({ description: "Show as List", event: action(() => this.props.Document._viewType = CollectionViewType.Invalid), icon: "asterisk" });
ContextMenu.Instance.addItem({ description: "Presentation Funcs...", subitems: funcs, icon: "asterisk" });
}
@@ -369,13 +351,11 @@ export class PresBox extends React.Component<FieldViewProps> {
* that they will be displayed in a canvas with scale 1.
*/
initializeScaleViews = (docList: Doc[], viewtype: number) => {
- this.props.Document._chromeStatus = "disabled";
const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46;
docList.forEach((doc: Doc) => {
doc.presBox = this.props.Document;
doc.presBoxKey = this.props.fieldKey;
doc.collapsedHeight = hgt;
- doc._nativeWidth = doc._nativeHeight = undefined;
const curScale = NumCast(doc.viewScale, null);
if (curScale === undefined) {
doc.viewScale = 1;
@@ -389,42 +369,48 @@ export class PresBox extends React.Component<FieldViewProps> {
}
getTransform = () => {
- return this.props.ScreenToLocalTransform().translate(0, -50);// listBox padding-left and pres-box-cont minHeight
+ return this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
}
panelHeight = () => {
return this.props.PanelHeight() - 20;
}
+
+ @undoBatch
+ viewChanged = action((e: React.ChangeEvent) => {
+ //@ts-ignore
+ this.props.Document._viewType = Number(e.target.selectedOptions[0].value);
+ this.updateMinimize(e, Number(this.props.Document._viewType));
+ });
+
+ childLayoutTemplate = () => this.props.Document._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined;
render() {
- this.initializeScaleViews(this.childDocs, NumCast(this.props.Document._viewType));
- return (this.props.Document._slideshow ?
- <div className="presBox-cont" onContextMenu={this.specificContextMenu} style={{ pointerEvents: this.active() ? "all" : "none" }} >
- {this.props.Document.inOverlay ? (null) :
- <div className="presBox-listCont" >
- {this.props.Document._slideshow === "slideshow" ?
- <CollectionCarouselView {...this.props} PanelHeight={this.panelHeight} chromeCollapsed={true} annotationsKey={""} CollectionView={undefined}
- moveDocument={returnFalse}
- addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
- :
- <CollectionTimeView {...this.props} PanelHeight={this.panelHeight} chromeCollapsed={true} annotationsKey={""} CollectionView={undefined}
- moveDocument={returnFalse}
- addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
- }
- </div>}
- <button className="presBox-backward" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
- <button className="presBox-forward" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ const mode = NumCast(this.props.Document._viewType, CollectionViewType.Invalid);
+ this.initializeScaleViews(this.childDocs, mode);
+ return <div className="presBox-cont" onContextMenu={this.specificContextMenu} style={{ minWidth: this.props.Document.inOverlay ? 240 : undefined, pointerEvents: this.active() || this.props.Document.inOverlay ? "all" : "none" }} >
+ <div className="presBox-buttons" style={{ display: this.props.Document._chromeStatus === "disabled" ? "none" : undefined }}>
+ <select style={{ minWidth: 50, width: "5%", height: "25", position: "relative", display: "inline-block" }}
+ className="collectionViewBaseChrome-viewPicker"
+ onPointerDown={e => e.stopPropagation()}
+ onChange={this.viewChanged}
+ value={mode}>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Invalid}>Min</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Time}>Time</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel}>Slides</option>
+ </select>
+ <button className="presBox-button" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
+ <button className="presBox-button" title={"Reset Presentation" + this.props.Document.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}>
+ <FontAwesomeIcon icon={this.props.Document.presStatus ? "stop" : "play"} />
+ </button>
+ <button className="presBox-button" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
+ </div>
+ <div className="presBox-listCont" >
+ {mode !== CollectionViewType.Invalid ?
+ <CollectionView {...this.props} PanelHeight={this.panelHeight} moveDocument={returnFalse} childLayoutTemplate={this.childLayoutTemplate}
+ addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
+ : (null)
+ }
</div>
- : <div className="presBox-cont" onContextMenu={this.specificContextMenu}>
- <div className="presBox-buttons">
- <button className="presBox-button" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button>
- <button className="presBox-button" title={"Reset Presentation" + this.props.Document.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}>
- <FontAwesomeIcon icon={this.props.Document.presStatus ? "stop" : "play"} />
- </button>
- <button className="presBox-button" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button>
- <button className="presBox-button" title={this.props.Document.inOverlay ? "Expand" : "Minimize"} onClick={this.toggleMinimize}><FontAwesomeIcon icon={"eye"} /></button>
- {this.props.Document.inOverlay ? (null) :
- <div className="presBox-listCont">
- <CollectionView {...this.props} whenActiveChanged={this.whenActiveChanged} PanelHeight={this.panelHeight} addDocument={this.addDocument} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} />
- </div>}
- </div></div>);
+ </div>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/SliderBox-components.tsx b/src/client/views/nodes/SliderBox-components.tsx
new file mode 100644
index 000000000..874a1108f
--- /dev/null
+++ b/src/client/views/nodes/SliderBox-components.tsx
@@ -0,0 +1,256 @@
+import * as React from "react";
+import { SliderItem } from "react-compound-slider";
+import "./SliderBox-tooltip.css";
+
+const { Component, Fragment } = React;
+
+// *******************************************************
+// TOOLTIP RAIL
+// *******************************************************
+const railStyle: React.CSSProperties = {
+ position: "absolute",
+ width: "100%",
+ height: 40,
+ top: -13,
+ borderRadius: 7,
+ cursor: "pointer",
+ opacity: 0.3,
+ zIndex: 300,
+ border: "1px solid grey"
+};
+
+const railCenterStyle: React.CSSProperties = {
+ position: "absolute",
+ width: "100%",
+ height: 14,
+ borderRadius: 7,
+ cursor: "pointer",
+ pointerEvents: "none",
+ backgroundColor: "rgb(155,155,155)"
+};
+
+interface TooltipRailProps {
+ activeHandleID: string;
+ getRailProps: (props: object) => object;
+ getEventData: (e: Event) => object;
+}
+
+export class TooltipRail extends Component<TooltipRailProps> {
+ state = {
+ value: null,
+ percent: null
+ };
+
+ static defaultProps = {
+ disabled: false
+ };
+
+ onMouseEnter = () => {
+ document.addEventListener("mousemove", this.onMouseMove);
+ }
+
+ onMouseLeave = () => {
+ this.setState({ value: null, percent: null });
+ document.removeEventListener("mousemove", this.onMouseMove);
+ }
+
+ onMouseMove = (e: Event) => {
+ const { activeHandleID, getEventData } = this.props;
+
+ if (activeHandleID) {
+ this.setState({ value: null, percent: null });
+ } else {
+ this.setState(getEventData(e));
+ }
+ }
+
+ render() {
+ const { value, percent } = this.state;
+ const { activeHandleID, getRailProps } = this.props;
+
+ return (
+ <Fragment>
+ {!activeHandleID && value ? (
+ <div
+ style={{
+ left: `${percent}%`,
+ position: "absolute",
+ marginLeft: "-11px",
+ marginTop: "-35px"
+ }}
+ >
+ <div className="tooltip">
+ <span className="tooltiptext">Value: {value}</span>
+ </div>
+ </div>
+ ) : null}
+ <div
+ style={railStyle}
+ {...getRailProps({
+ onMouseEnter: this.onMouseEnter,
+ onMouseLeave: this.onMouseLeave
+ })}
+ />
+ <div style={railCenterStyle} />
+ </Fragment>
+ );
+ }
+}
+
+// *******************************************************
+// HANDLE COMPONENT
+// *******************************************************
+interface HandleProps {
+ key: string;
+ handle: SliderItem;
+ isActive: Boolean;
+ disabled?: Boolean;
+ domain: number[];
+ getHandleProps: (id: string, config: object) => object;
+}
+
+export class Handle extends Component<HandleProps> {
+ static defaultProps = {
+ disabled: false
+ };
+
+ state = {
+ mouseOver: false
+ };
+
+ onMouseEnter = () => {
+ this.setState({ mouseOver: true });
+ }
+
+ onMouseLeave = () => {
+ this.setState({ mouseOver: false });
+ }
+
+ render() {
+ const {
+ domain: [min, max],
+ handle: { id, value, percent },
+ isActive,
+ disabled,
+ getHandleProps
+ } = this.props;
+ const { mouseOver } = this.state;
+
+ return (
+ <Fragment>
+ {(mouseOver || isActive) && !disabled ? (
+ <div
+ style={{
+ left: `${percent}%`,
+ position: "absolute",
+ marginLeft: "-11px",
+ marginTop: "-35px"
+ }}
+ >
+ <div className="tooltip">
+ <span className="tooltiptext">Value: {value}</span>
+ </div>
+ </div>
+ ) : null}
+ <div
+ role="slider"
+ aria-valuemin={min}
+ aria-valuemax={max}
+ aria-valuenow={value}
+ style={{
+ left: `${percent}%`,
+ position: "absolute",
+ marginLeft: "-11px",
+ marginTop: "-6px",
+ zIndex: 400,
+ width: 24,
+ height: 24,
+ cursor: "pointer",
+ border: 0,
+ borderRadius: "50%",
+ boxShadow: "1px 1px 1px 1px rgba(0, 0, 0, 0.4)",
+ backgroundColor: disabled ? "#666" : "#3e1db3"
+ }}
+ {...getHandleProps(id, {
+ onMouseEnter: this.onMouseEnter,
+ onMouseLeave: this.onMouseLeave
+ })}
+ />
+ </Fragment>
+ );
+ }
+}
+
+// *******************************************************
+// TRACK COMPONENT
+// *******************************************************
+interface TrackProps {
+ source: SliderItem;
+ target: SliderItem;
+ disabled: Boolean;
+ getTrackProps: () => object;
+}
+
+export function Track({
+ source,
+ target,
+ getTrackProps,
+ disabled = false
+}: TrackProps) {
+ return (
+ <div
+ style={{
+ position: "absolute",
+ height: 14,
+ zIndex: 1,
+ backgroundColor: disabled ? "#999" : "#3e1db3",
+ borderRadius: 7,
+ cursor: "pointer",
+ left: `${source.percent}%`,
+ width: `${target.percent - source.percent}%`
+ }}
+ {...getTrackProps()}
+ />
+ );
+}
+
+// *******************************************************
+// TICK COMPONENT
+// *******************************************************
+interface TickProps {
+ tick: SliderItem;
+ count: number;
+ format: (val: number) => string;
+}
+
+const defaultFormat = (d: number) => `d`;
+
+export function Tick({ tick, count, format = defaultFormat }: TickProps) {
+ return (
+ <div>
+ <div
+ style={{
+ position: "absolute",
+ marginTop: 17,
+ width: 1,
+ height: 5,
+ backgroundColor: "rgb(200,200,200)",
+ left: `${tick.percent}%`
+ }}
+ />
+ <div
+ style={{
+ position: "absolute",
+ marginTop: 25,
+ fontSize: 10,
+ textAlign: "center",
+ marginLeft: `${-(100 / count) / 2}%`,
+ width: `${100 / count}%`,
+ left: `${tick.percent}%`
+ }}
+ >
+ {format(tick.value)}
+ </div>
+ </div>
+ );
+}
diff --git a/src/client/views/nodes/SliderBox-tooltip.css b/src/client/views/nodes/SliderBox-tooltip.css
new file mode 100644
index 000000000..8afde8eb5
--- /dev/null
+++ b/src/client/views/nodes/SliderBox-tooltip.css
@@ -0,0 +1,33 @@
+.tooltip {
+ position: relative;
+ display: inline-block;
+ border-bottom: 1px dotted #222;
+ margin-left: 22px;
+ }
+
+ .tooltip .tooltiptext {
+ width: 100px;
+ background-color: #222;
+ color: #fff;
+ opacity: 0.8;
+ text-align: center;
+ border-radius: 6px;
+ padding: 5px 0;
+ position: absolute;
+ z-index: 1;
+ bottom: 150%;
+ left: 50%;
+ margin-left: -60px;
+ }
+
+ .tooltip .tooltiptext::after {
+ content: "";
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: #222 transparent transparent transparent;
+ }
+ \ No newline at end of file
diff --git a/src/client/views/nodes/SliderBox.scss b/src/client/views/nodes/SliderBox.scss
new file mode 100644
index 000000000..4ef277d8c
--- /dev/null
+++ b/src/client/views/nodes/SliderBox.scss
@@ -0,0 +1,8 @@
+.sliderBox-outerDiv {
+ width: 100%;
+ height: 100%;
+ pointer-events: all;
+ border-radius: inherit;
+ display: flex;
+ flex-direction: column;
+} \ No newline at end of file
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
new file mode 100644
index 000000000..844d95d11
--- /dev/null
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -0,0 +1,130 @@
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faEdit } from '@fortawesome/free-regular-svg-icons';
+import { computed, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Handles, Rail, Slider, Tracks, Ticks } from 'react-compound-slider';
+import { Doc } from '../../../new_fields/Doc';
+import { documentSchema } from '../../../new_fields/documentSchemas';
+import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
+import { ScriptField } from '../../../new_fields/ScriptField';
+import { BoolCast, FieldValue, StrCast, NumCast, Cast } from '../../../new_fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { DocComponent } from '../DocComponent';
+import './SliderBox.scss';
+import { Handle, TooltipRail, Track, Tick } from './SliderBox-components';
+import { FieldView, FieldViewProps } from './FieldView';
+import { ScriptBox } from '../ScriptBox';
+
+
+library.add(faEdit as any);
+
+const SliderSchema = createSchema({
+ _sliderMin: "number",
+ _sliderMax: "number",
+ _sliderMinThumb: "number",
+ _sliderMaxThumb: "number",
+});
+
+type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema]>;
+const SliderDocument = makeInterface(SliderSchema, documentSchema);
+
+@observer
+export class SliderBox extends DocComponent<FieldViewProps, SliderDocument>(SliderDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); }
+ private dropDisposer?: DragManager.DragDropDisposer;
+
+ @computed get dataDoc() {
+ return this.props.DataDoc &&
+ (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) ||
+ this.props.DataDoc.layout === this.Document) ? this.props.DataDoc : Doc.GetProto(this.Document);
+ }
+
+ specificContextMenu = (e: React.MouseEvent): void => {
+ const funcs: ContextMenuProps[] = [];
+ funcs.push({ description: "Edit Thumb Change Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Thumb Change ...", this.props.Document, "onThumbChange", obj.x, obj.y) });
+ ContextMenu.Instance.addItem({ description: "Slider Funcs...", subitems: funcs, icon: "asterisk" });
+ }
+ onChange = (values: readonly number[]) => runInAction(() => {
+ this.Document._sliderMinThumb = values[0];
+ this.Document._sliderMaxThumb = values[1];
+ Cast(this.Document.onThumbChanged, ScriptField, null)?.script.run({ range: values, this: this.props.Document });
+ })
+
+ render() {
+ const domain = [NumCast(this.props.Document._sliderMin), NumCast(this.props.Document._sliderMax)];
+ const defaultValues = [NumCast(this.props.Document._sliderMinThumb), NumCast(this.props.Document._sliderMaxThumb)];
+ return (
+ <div className="sliderBox-outerDiv" onContextMenu={this.specificContextMenu} onPointerDown={e => e.stopPropagation()}
+ style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
+ <div className="sliderBox-mainButton" onContextMenu={this.specificContextMenu} style={{
+ background: this.Document.backgroundColor, color: this.Document.color || "black",
+ fontSize: this.Document.fontSize, letterSpacing: this.Document.letterSpacing || ""
+ }} >
+ <Slider
+ mode={2}
+ step={1}
+ 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 ? this.Document._sliderMinThumb : this.Document._sliderMaxThumb;
+ 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-tracks">
+ {ticks.map((tick) => (
+ <Tick
+ key={tick.id}
+ tick={tick}
+ count={ticks.length}
+ format={(val: number) => val.toString()}
+ />
+ ))}
+ </div>
+ )}
+ </Ticks>
+ </Slider>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index d12a8d151..6695e04c3 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -354,8 +354,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
CollectionView={undefined}
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- chromeCollapsed={true}>
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{this.contentFunc}
</CollectionFreeFormView>
</div>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a48dc286e..7e49d957d 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -211,8 +211,7 @@ export class WebBox extends DocAnnotatableComponent<FieldViewProps, WebDocument>
CollectionView={undefined}
ScreenToLocalTransform={this.props.ScreenToLocalTransform}
renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- chromeCollapsed={true}>
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
{() => [this.content]}
</CollectionFreeFormView>
</div >);