aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/DocumentButtonBar.tsx1
-rw-r--r--src/client/views/EditableView.tsx16
-rw-r--r--src/client/views/GestureOverlay.tsx4
-rw-r--r--src/client/views/MainView.scss11
-rw-r--r--src/client/views/OverlayView.tsx1
-rw-r--r--src/client/views/RecommendationsBox.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.scss6
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx7
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx9
-rw-r--r--src/client/views/collections/CollectionView.tsx25
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx57
-rw-r--r--src/client/views/nodes/ImageBox.tsx5
-rw-r--r--src/client/views/nodes/ScriptingBox.scss213
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx683
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx22
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx6
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts4
19 files changed, 942 insertions, 134 deletions
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index a35a8869c..62a95116f 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -281,7 +281,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
const dragDocView = this.view0!;
const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
- dragData.embedDoc = true;
dragData.dropAction = "alias";
DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, {
offsetX: dragData.offset[0],
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index e21d431b1..ee3ce1cf3 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -5,6 +5,7 @@ import * as Autosuggest from 'react-autosuggest';
import { ObjectField } from '../../fields/ObjectField';
import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
import "./EditableView.scss";
+import { DragManager } from '../util/DragManager';
export interface EditableProps {
/**
@@ -48,6 +49,8 @@ export interface EditableProps {
HeadingObject?: SchemaHeaderField | undefined;
toggle?: () => void;
color?: string | undefined;
+ onDrop?: any;
+ placeholder?: string;
}
/**
@@ -78,6 +81,13 @@ export class EditableView extends React.Component<EditableProps> {
// }
// }
+ @action
+ componentDidMount() {
+ if (this._ref.current && this.props.onDrop) {
+ DragManager.MakeDropTarget(this._ref.current, this.props.onDrop.bind(this));
+ }
+ }
+
_didShow = false;
@action
@@ -169,6 +179,7 @@ export class EditableView extends React.Component<EditableProps> {
onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true)}
onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
style={{ display: this.props.display, fontSize: this.props.fontSize }}
+ placeholder={this.props.placeholder}
/>;
} else {
this.props.autosuggestProps?.resetValue();
@@ -176,8 +187,9 @@ export class EditableView extends React.Component<EditableProps> {
<div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
ref={this._ref}
style={{ display: this.props.display, minHeight: "20px", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
- onClick={this.onClick}>
- <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{this.props.contents}</span>
+ onClick={this.onClick} placeholder={this.props.placeholder}>
+
+ <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>
</div>
);
}
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index fd4495edb..79d407c7a 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -915,7 +915,7 @@ Scripting.addGlobal(function resetPen() {
SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
SetActiveInkWidth(GestureOverlay.Instance.SavedWidth ?? "2");
});
-});
+}, "resets the pen tool");
Scripting.addGlobal(function createText(text: any, x: any, y: any) {
GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text);
-}); \ No newline at end of file
+}, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)"); \ No newline at end of file
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index dca2a1e3e..a7048eb23 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -32,9 +32,6 @@
}
.mainView-container, .mainView-container-dark {
- input {
- color: unset !important;
- }
width: 100%;
height: 100%;
position: absolute;
@@ -50,6 +47,10 @@
.mainView-container {
color:dimgray;
+ .lm_title {
+ background: #cacaca;
+ color:black;
+ }
}
.mainView-container-dark {
@@ -57,6 +58,10 @@
.lm_goldenlayout {
background: dimgray;
}
+ .lm_title {
+ background: black;
+ color:unset;
+ }
.marquee {
border-color: white;
}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index cfa869fb2..f6e5e1705 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -12,7 +12,6 @@ import './OverlayView.scss';
import { Scripting } from "../util/Scripting";
import { ScriptingRepl } from './ScriptingRepl';
import { DragManager } from "../util/DragManager";
-import { listSpec } from "../../fields/Schema";
import { List } from "../../fields/List";
export type OverlayDisposer = () => void;
diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx
index 8ca81c070..c98363214 100644
--- a/src/client/views/RecommendationsBox.tsx
+++ b/src/client/views/RecommendationsBox.tsx
@@ -56,7 +56,7 @@ export class RecommendationsBox extends React.Component<FieldViewProps> {
}
const returnXDimension = () => 150;
const returnYDimension = () => 150;
- const scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension());
+ const scale = () => returnXDimension() / NumCast(renderDoc._nativeWidth, returnXDimension());
//let scale = () => 1;
const newRenderDoc = Doc.MakeAlias(renderDoc); /// newRenderDoc -> renderDoc -> render"data"Doc -> TextProt
newRenderDoc.height = NumCast(this.props.Document.documentIconHeight);
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 2fafcecb2..18d642510 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -2,13 +2,17 @@
.lm_title {
margin-top: 3px;
- background: black;
border-radius: 5px;
border: solid 1px dimgray;
border-width: 2px 2px 0px;
height: 20px;
transform: translate(0px, -3px);
+ cursor: grab;
}
+.lm_title.focus-visible {
+ cursor: text;
+}
+
.lm_title_wrap {
overflow: hidden;
height: 19px;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6f5a3dfe4..1c0b60b2b 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -44,7 +44,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
props: {
documentId: document[Id],
libraryPath: libraryPath?.map(d => d[Id])
- //collectionDockingView: CollectionDockingView.Instance
}
};
}
@@ -465,7 +464,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
if (docids) {
const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc);
- Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
+ docs.map(doc => Doc.AddDocToList(Doc.GetProto(this.props.Document), this.props.fieldKey, doc));
+ // Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs);
}
}
@@ -859,5 +859,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
</div >);
}
}
-Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); });
+Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); },
+ "opens up the inputted document on the right side of the screen", "(doc: any)");
Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); });
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0a1b03522..9289c4cf6 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -476,7 +476,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
transformOrigin: "top left",
}}
onScroll={action(e => {
- if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
+ if (!this.props.isSelected() && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
})}
onDrop={this.onExternalDrop.bind(this)}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 180bcdd02..be0339345 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -83,8 +83,9 @@ class TreeView extends React.Component<TreeViewProps> {
private _tref = React.createRef<HTMLDivElement>();
private _docRef = React.createRef<DocumentView>();
+ get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
- get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); }
+ get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, this.noviceMode ? "layout" : "fields"); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; }
@computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.props.document.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; }
@@ -249,7 +250,7 @@ class TreeView extends React.Component<TreeViewProps> {
const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]());
if (aspect) return this.docWidth() * aspect;
if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
- return layoutDoc._fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection._height) :
+ return layoutDoc._fitWidth ? (!this.props.document._nativeHeight ? NumCast(this.props.containingCollection._height) :
Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth,
NumCast(this.props.containingCollection._height)))) :
NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50;
@@ -420,10 +421,10 @@ class TreeView extends React.Component<TreeViewProps> {
<span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView}
onPointerDown={action(() => {
if (this.treeViewOpen) {
- this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" :
+ this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" :
this.treeViewExpandedView === "layout" && this.props.document.links ? "links" :
- this.childDocs ? this.fieldKey : "fields";
+ this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
}
this.treeViewOpen = true;
})}>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 215b5bce8..be79dbce1 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -45,6 +45,7 @@ import { CollectionTreeView } from "./CollectionTreeView";
import { CollectionGridView } from './collectionGrid/CollectionGridView';
import './CollectionView.scss';
import { CollectionViewBaseChrome } from './CollectionViewChromes';
+import { UndoManager } from '../../util/UndoManager';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -166,7 +167,17 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return true;
}
const first = doc instanceof Doc ? doc : doc[0];
- return !first?.stayInCollection && addDocument !== returnFalse && this.removeDocument(doc) ? addDocument(doc) : false;
+ if (!first?.stayInCollection && addDocument !== returnFalse) {
+ if (UndoManager.RunInTempBatch(() => this.removeDocument(doc))) {
+ const added = addDocument(doc);
+ if (!added) UndoManager.UndoTempBatch();
+ else UndoManager.ClearTempBatch();
+
+ return added;
+ }
+ UndoManager.ClearTempBatch();
+ }
+ return false;
}
showIsTagged = () => {
@@ -282,10 +293,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
}));
!existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
- const more = ContextMenu.Instance.findByDescription("More...");
- const moreItems = more && "subitems" in more ? more.subitems : [];
- moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
- !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+ if (!Doc.UserDoc().noviceMode) {
+ const more = ContextMenu.Instance.findByDescription("More...");
+ const moreItems = more && "subitems" in more ? more.subitems : [];
+ moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) });
+ !more && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+ }
}
}
@@ -429,7 +442,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
@computed get filterView() {
const facetCollection = this.props.Document;
const flyout = (
- <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}>
+ <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => fmovede.stopPropagation()}>
{this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}>
<input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey + "-filter"]).some(d => d.title === facet)} />
<span className="checkmark" />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 736c5fd06..72cb2d218 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -258,8 +258,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp);
} else if (de.complete.docDragData?.droppedDocuments.length && this.internalDocDrop(e, de, de.complete.docDragData, xp, yp)) {
return true;
- } else {
- UndoManager.Undo();
}
return false;
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index da5327f48..ffadc6edf 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -17,10 +17,8 @@ import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { ClientRecommender } from '../../ClientRecommender';
-import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
-import { ClientUtils } from '../../util/ClientUtils';
import { DocumentManager } from "../../util/DocumentManager";
import { SnappingManager } from '../../util/SnappingManager';
import { DragManager, dropActionType } from "../../util/DragManager";
@@ -137,7 +135,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.removeEndListeners();
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
- console.log(SelectionManager.SelectedDocuments());
if (RadialMenu.Instance._display === false) {
this.addHoldMoveListeners();
this.addHoldEndListeners();
@@ -732,7 +729,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let options = cm.findByDescription("Options...");
const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
- optionItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
templateDoc && optionItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" });
if (!options) {
options = { description: "Options...", subitems: optionItems, icon: "compass" };
@@ -759,30 +755,32 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
const more = cm.findByDescription("More...");
- const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
- moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
- moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+ const moreItems = more && "subitems" in more ? more.subitems : [];
+ if (!Doc.UserDoc().noviceMode) {
+ moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
+ moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
+
+ if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
+ moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
+ moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
+ moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+ }
+ moreItems.push({
+ description: "Download document", icon: "download", event: async () => {
+ const response = await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
+ qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
+ });
+ console.log(response ? JSON.parse(response) : undefined);
+ }
+ // const a = document.createElement("a");
+ // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+ // a.href = url;
+ // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+ // a.click();
+ });
+ }
moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
-
- if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
- moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
- moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
- moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
- }
- moreItems.push({
- description: "Download document", icon: "download", event: async () => {
- const response = await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), {
- qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' }
- });
- console.log(response ? JSON.parse(response) : undefined);
- }
- // const a = document.createElement("a");
- // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
- // a.href = url;
- // a.download = `DocExport-${this.props.Document[Id]}.zip`;
- // a.click();
- });
moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" });
!more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
@@ -790,11 +788,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const help = cm.findByDescription("Help...");
const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
- helpItems.push({
- description: "Keyboard Shortcuts Ctrl+/",
- event: () => this.props.addDocTab(Docs.Create.PdfDocument("http://localhost:1050/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "onRight"),
- icon: "keyboard"
- });
+ helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("http://localhost:1050/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" });
+ helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
cm.addItem({ description: "Help...", subitems: helpItems, icon: "question" });
const existingAcls = cm.findByDescription("Privacy...");
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index cabf30c13..69cffb6c2 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -182,11 +182,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
!existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-
-
- const existingMore = ContextMenu.Instance.findByDescription("More...");
- const mores: ContextMenuProps[] = existingMore && "subitems" in existingMore ? existingMore.subitems : [];
- !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" });
}
}
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
index 43695f00d..a937364a8 100644
--- a/src/client/views/nodes/ScriptingBox.scss
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -5,31 +5,220 @@
flex-direction: column;
background-color: rgb(241, 239, 235);
padding: 10px;
+
+ .boxed {
+ border: 1px solid black;
+ background-color: rgb(212, 198, 179);
+ width: auto;
+ height: auto;
+ font-size: 12px;
+ position: absolute;
+ z-index: 100;
+ padding: 5px;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
.scriptingBox-inputDiv {
display: flex;
flex-direction: column;
- height: calc(100% - 30px);
+ height: 100%;
+ max-height: 100%;
+ overflow: hidden;
+ table-layout: fixed;
+
+ white-space: nowrap;
+
+ .scriptingBox-wrapper {
+ width: 100%;
+ height: 100%;
+ max-height: calc(100%-30px);
+ display: flex;
+ flex-direction: row;
+ overflow: auto;
+ justify-content: center;
+
+ .descriptor {
+ overflow: hidden;
+ }
+
+ .scriptingBox-textArea, .scriptingBox-textArea-inputs {
+ flex: 70;
+ height: 100%;
+ max-width: 95%;
+ min-width: none;
+ box-sizing: border-box;
+ resize: none;
+ padding: 7px;
+ overflow-y: auto;
+ overflow-x: hidden;
+
+ body {
+ font-family: Arial, Helvetica, sans-serif;
+ border: 1px solid red;
+ }
+
+ .rta {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ margin-bottom: 60px !important;
+ overflow-y: auto;
+ overflow-x: hidden;
+ overflow: hidden;
+ }
+
+ .rta__textarea {
+ width: 100%;
+ height: 100%;
+ font-size: 10px;
+ }
+
+ .rta__autocomplete {
+ position: absolute;
+ display: block;
+ margin-top: 1em;
+ }
+
+ .rta__autocomplete--top {
+ margin-top: 0;
+ margin-bottom: 1em;
+ max-height: 100px;
+ }
+
+ .rta__list {
+ margin: 0;
+ padding: 0;
+ background: #fff;
+ border: 1px solid #dfe2e5;
+ border-radius: 3px;
+ box-shadow: 0 0 5px rgba(27, 31, 35, 0.1);
+ list-style: none;
+ overflow-y: auto;
+ overflow-x: hidden;
+ }
+
+ .rta__entity {
+ background: white;
+ width: 100%;
+ text-align: left;
+ outline: none;
+ overflow-y: auto;
+ }
+
+ .rta__entity:hover {
+ cursor: pointer;
+ }
+
+ .rta__entity>* {
+ padding-left: 4px;
+ padding-right: 4px;
+ }
+
+ .rta__entity--selected {
+ color: #fff;
+ text-decoration: none;
+ background: #0366d6;
+ }
+ }
+
+ .scriptingBox-textArea-inputs {
+ max-width: 100%;
+ height: 40%;
+ width: 100%;
+ resize: none;
+ }
+ .scriptingBox-textArea-script {
+ resize: none;
+ height: 100%;
+ }
+
+ .scriptingBox-plist {
+ flex: 30;
+ width: 30%;
+ height: 100%;
+ box-sizing: border-box;
+ resize: none;
+ padding: 2px;
+ overflow-y: auto;
+
+ .scriptingBox-pborder {
+ background-color: rgb(241, 239, 235);
+ }
+
+ .scriptingBox-viewBase {
+ display: flex;
+
+ .scriptingBox-viewPicker {
+ font-size: 75%;
+ //text-transform: uppercase;
+ letter-spacing: 2px;
+ background: rgb(238, 238, 238);
+ color: grey;
+ outline-color: black;
+ border: none;
+ padding: 12px 10px 11px 10px;
+ }
+
+ .scriptingBox-viewPicker:active {
+ outline-color: black;
+ }
+
+ .commandEntry-outerDiv {
+ pointer-events: all;
+ background-color: gray;
+ display: flex;
+ flex-direction: row;
+ }
+ }
+ }
+
+ .scriptingBox-paramNames {
+ flex: 60;
+ width: 60%;
+ box-sizing: border-box;
+ resize: none;
+ padding: 7px;
+ overflow-y: clip;
+ }
+
+ .scriptingBox-paramInputs {
+ flex: 40;
+ width: 40%;
+ box-sizing: border-box;
+ resize: none;
+ padding: 2px;
+ overflow-y: hidden;
+ }
+ }
+
.scriptingBox-errorMessage {
overflow: auto;
+ background: "red";
+ background-color: "red";
+ height: 45px;
}
+
.scripting-params {
- background: "beige";
- }
- .scriptingBox-textArea {
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- resize: none;
- padding: 7px;
+ background: rgb(241, 239, 235);
+ outline-style: solid;
+ outline-color: black;
}
}
.scriptingBox-toolbar {
width: 100%;
height: 30px;
+ overflow: hidden;
+
.scriptingBox-button {
- width: 50%
+ font-size: xx-small;
+ width: 50%;
+ resize: auto;
}
- }
-}
+ .scriptingBox-button-third {
+ width: 33%;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 0944edf60..8912b113c 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,19 +1,28 @@
-import { action, observable, computed } from "mobx";
+import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
+import "@webscopeio/react-textarea-autocomplete/style.css";
+import { action, computed, observable, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
+import { Doc } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
-import { createSchema, makeInterface, listSpec } from "../../../fields/Schema";
+import { List } from "../../../fields/List";
+import { createSchema, listSpec, makeInterface } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
-import { StrCast, ScriptCast, Cast } from "../../../fields/Types";
+import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../fields/Types";
+import { returnEmptyString } from "../../../Utils";
+import { DragManager } from "../../util/DragManager";
import { InteractionUtils } from "../../util/InteractionUtils";
-import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting";
+import { CompileScript, Scripting, ScriptParam } from "../../util/Scripting";
+import { ScriptManager } from "../../util/ScriptManager";
+import { ContextMenu } from "../ContextMenu";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { EditableView } from "../EditableView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import "./ScriptingBox.scss";
import { OverlayView } from "../OverlayView";
import { DocumentIconContainer } from "./DocumentIcon";
-import { List } from "../../../fields/List";
+import "./ScriptingBox.scss";
+import { TraceMobx } from "../../../fields/util";
+const _global = (window /* browser */ || global /* node */) as any;
const ScriptingSchema = createSchema({});
type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
@@ -21,78 +30,664 @@ const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema);
@observer
export class ScriptingBox extends ViewBoxAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) {
+
+ private dropDisposer?: DragManager.DragDropDisposer;
protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); }
-
- _overlayDisposer?: () => void;
+ private _overlayDisposer?: () => void;
+ private _caretPos = 0;
@observable private _errorMessage: string = "";
+ @observable private _applied: boolean = false;
+ @observable private _function: boolean = false;
+ @observable private _spaced: boolean = false;
+
+ @observable private _scriptKeys: any = Scripting.getGlobals();
+ @observable private _scriptingDescriptions: any = Scripting.getDescriptions();
+ @observable private _scriptingParams: any = Scripting.getParameters();
+
+ @observable private _currWord: string = "";
+ @observable private _suggestions: string[] = [];
+
+ @observable private _suggestionBoxX: number = 0;
+ @observable private _suggestionBoxY: number = 0;
+ @observable private _lastChar: string = "";
+
+ @observable private _suggestionRef: any = React.createRef();
+ @observable private _scriptTextRef: any = React.createRef();
+
+ @observable private _selection: any = 0;
+
+ @observable private _paramSuggestion: boolean = false;
+ @observable private _scriptSuggestedParams: any = "";
+ @observable private _scriptParamsText: any = "";
+
+ // vars included in fields that store parameters types and names and the script itself
+ @computed({ keepAlive: true }) get paramsNames() { return this.compileParams.map(p => p.split(":")[0].trim()); }
+ @computed({ keepAlive: true }) get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); }
+ @computed({ keepAlive: true }) get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], ""); }
+ @computed({ keepAlive: true }) get functionName() { return StrCast(this.dataDoc[this.props.fieldKey + "-functionName"], ""); }
+ @computed({ keepAlive: true }) get functionDescription() { return StrCast(this.dataDoc[this.props.fieldKey + "-functionDescription"], ""); }
+ @computed({ keepAlive: true }) get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), []); }
- @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], StrCast(this.layoutDoc[this.props.fieldKey + "-rawScript"])); }
- @computed get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), Cast(this.layoutDoc[this.props.fieldKey + "-params"], listSpec("string"), [])); }
set rawScript(value) { this.dataDoc[this.props.fieldKey + "-rawScript"] = value; }
- set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = value; }
+ set functionName(value) { this.dataDoc[this.props.fieldKey + "-functionName"] = value; }
+ set functionDescription(value) { this.dataDoc[this.props.fieldKey + "-functionDescription"] = value; }
+
+ set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = new List<string>(value); }
+
+ getValue(result: any, descrip: boolean) {
+ if (typeof result === "object") {
+ const text = descrip ? result[1] : result[2];
+ return text !== undefined ? text : "";
+ } else {
+ return "";
+ }
+ }
@action
componentDidMount() {
- this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript;
+ this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript ?? this.rawScript;
+
+ const observer = new _global.ResizeObserver(action((entries: any) => {
+ const area = document.querySelector('textarea');
+ if (area) {
+ for (const { } of entries) {
+ const getCaretCoordinates = require('textarea-caret');
+ const caret = getCaretCoordinates(area, this._selection);
+ this.resetSuggestionPos(caret);
+ }
+ }
+ }));
+ observer.observe(document.getElementsByClassName("scriptingBox")[0]);
}
- componentWillUnmount() { this._overlayDisposer?.(); }
+ @action
+ resetSuggestionPos(caret: any) {
+ if (!this._suggestionRef.current || !this._scriptTextRef.current) return;
+ console.log('(top, left, height) = (%s, %s, %s)', caret.top, caret.left, caret.height);
+ const suggestionWidth = this._suggestionRef.current.offsetWidth;
+ const scriptWidth = this._scriptTextRef.current.offsetWidth;
+ const top = caret.top;
+ const x = this.dataDoc.x;
+ let left = caret.left;
+ if ((left + suggestionWidth) > (x + scriptWidth)) {
+ const diff = (left + suggestionWidth) - (x + scriptWidth);
+ left = left - diff;
+ }
+
+ this._suggestionBoxX = left;
+ this._suggestionBoxY = top;
+ }
+ componentWillUnmount() {
+ this._overlayDisposer?.();
+ }
+
+ protected createDashEventsTarget = (ele: HTMLDivElement, dropFunc: (e: Event, de: DragManager.DropEvent) => void) => { //used for stacking and masonry view
+ if (ele) {
+ this.dropDisposer?.();
+ this.dropDisposer = DragManager.MakeDropTarget(ele, dropFunc, this.layoutDoc);
+ }
+ }
+
+ // only included in buttons, transforms scripting UI to a button
+ @action
+ onFinish = () => {
+ this.rootDoc.layoutKey = "layout";
+ this.rootDoc._height = 50;
+ this.rootDoc._width = 100;
+ this.dataDoc.documentText = this.rawScript;
+ }
+
+ // displays error message
+ @action
+ onError = (error: any) => {
+ this._errorMessage = error?.message ? error.message : error?.map((entry: any) => entry.messageText).join(" ") || "";
+ }
+
+ // checks if the script compiles using CompileScript method and inputting params
@action
onCompile = () => {
- const params = this.compileParams.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam);
+ const params: ScriptParam = {};
+ this.compileParams.forEach(p => params[p.split(":")[0].trim()] = p.split(":")[1].trim());
+
const result = CompileScript(this.rawScript, {
editable: true,
transformer: DocumentIconContainer.getTransformer(),
params,
typecheck: false
});
- this._errorMessage = isCompileError(result) ? result.errors.map(e => e.messageText).join("\n") : "";
- return this.dataDoc[this.props.fieldKey] = result.compiled ? new ScriptField(result) : undefined;
+ this.dataDoc.documentText = this.rawScript;
+ this.dataDoc.data = result.compiled ? new ScriptField(result) : undefined;
+ this.onError(result.compiled ? undefined : result.errors);
+ return result.compiled;
}
+ // checks if the script compiles and then runs the script
@action
onRun = () => {
- this.onCompile()?.script.run({}, err => this._errorMessage = err.map((e: any) => e.messageText).join("\n"));
+ if (this.onCompile()) {
+ const bindings: { [name: string]: any } = {};
+ this.paramsNames.forEach(key => bindings[key] = this.dataDoc[key]);
+ // binds vars so user doesnt have to refer to everything as self.<var>
+ ScriptCast(this.dataDoc.data, null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError);
+ }
+ }
+
+ // checks if the script compiles and switches to applied UI
+ @action
+ onApply = () => {
+ if (this.onCompile()) {
+ this._applied = true;
+ }
}
+ @action
+ onEdit = () => {
+ this._errorMessage = "";
+ this._applied = false;
+ this._function = false;
+ }
+
+ @action
+ onSave = () => {
+ if (this.onCompile()) {
+ this._function = true;
+ } else {
+ this._errorMessage = "Can not save script, does not compile";
+ }
+ }
+
+ @action
+ onCreate = () => {
+ this._errorMessage = "";
+
+ if (this.functionName.length === 0) {
+ this._errorMessage = "Must enter a function name";
+ return false;
+ }
+
+ if (this.functionName.indexOf(" ") > 0) {
+ this._errorMessage = "Name can not include spaces";
+ return false;
+ }
+
+ if (this.functionName.indexOf(".") > 0) {
+ this._errorMessage = "Name can not include '.'";
+ return false;
+ }
+
+ this.dataDoc.name = this.functionName;
+ this.dataDoc.description = this.functionDescription;
+ //this.dataDoc.parameters = this.compileParams;
+ this.dataDoc.script = this.rawScript;
+
+ ScriptManager.Instance.addScript(this.dataDoc);
+
+ this._scriptKeys = Scripting.getGlobals();
+ this._scriptingDescriptions = Scripting.getDescriptions();
+ this._scriptingParams = Scripting.getParameters();
+ }
+
+ // overlays document numbers (ex. d32) over all documents when clicked on
onFocus = () => {
this._overlayDisposer?.();
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
}
+ // sets field of the corresponding field key (param name) to be dropped document
+ @action
+ onDrop = (e: Event, de: DragManager.DropEvent, fieldKey: string) => {
+ this.dataDoc[fieldKey] = de.complete.docDragData?.droppedDocuments[0];
+ e.stopPropagation();
+ }
+
+ // deletes a param from all areas in which it is stored
+ @action
+ onDelete = (num: number) => {
+ this.dataDoc[this.paramsNames[num]] = undefined;
+ this.compileParams.splice(num, 1);
+ return true;
+ }
+
+ // sets field of the param name to the selected value in drop down box
+ @action
+ viewChanged = (e: React.ChangeEvent, name: string) => {
+ //@ts-ignore
+ const val = e.target.selectedOptions[0].value;
+ this.dataDoc[name] = val[0] === "S" ? val.substring(1) : val[0] === "N" ? parseInt(val.substring(1)) : val.substring(1) === "true";
+ }
+
+ // creates a copy of the script document
+ onCopy = () => {
+ const copy = Doc.MakeCopy(this.rootDoc, true);
+ copy.x = NumCast(this.dataDoc.x) + NumCast(this.dataDoc._width);
+ this.props.addDocument?.(copy);
+ }
+
+ // adds option to create a copy to the context menu
+ specificContextMenu = (): void => {
+ const existingOptions = ContextMenu.Instance.findByDescription("Options...");
+ const options = existingOptions && "subitems" in existingOptions ? existingOptions.subitems : [];
+ options.push({ description: "Create a Copy", event: this.onCopy, icon: "copy" });
+ !existingOptions && ContextMenu.Instance.addItem({ description: "Options...", subitems: options, icon: "hand-point-right" });
+ }
+
+ renderFunctionInputs() {
+ const descriptionInput =
+ <textarea
+ className="scriptingBox-textarea-inputs"
+ onChange={e => this.functionDescription = e.target.value}
+ placeholder="enter description here"
+ value={this.functionDescription}
+ />;
+ const nameInput =
+ <textarea
+ className="scriptingBox-textarea-inputs"
+ onChange={e => this.functionName = e.target.value}
+ placeholder="enter name here"
+ value={this.functionName}
+ />;
+
+ return <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected() && e.stopPropagation()} >
+ <div className="scriptingBox-wrapper" style={{ maxWidth: "100%" }}>
+ <div className="container" style={{ maxWidth: "100%" }}>
+ <div className="descriptor" style={{ textAlign: "center", display: "inline-block", maxWidth: "100%" }}> Enter a function name: </div>
+ <div style={{ maxWidth: "100%" }}> {nameInput}</div>
+ <div className="descriptor" style={{ textAlign: "center", display: "inline-block", maxWidth: "100%" }}> Enter a function description: </div>
+ <div style={{ maxWidth: "100%" }}>{descriptionInput}</div>
+ </div>
+ </div>
+ {this.renderErrorMessage()}
+ </div>;
+ }
+
+ renderErrorMessage() {
+ return !this._errorMessage ? (null) : <div className="scriptingBox-errorMessage"> {this._errorMessage} </div>;
+ }
+
+ // rendering when a doc's value can be set in applied UI
+ renderDoc(parameter: string) {
+ return <div className="scriptingBox-paramInputs" onFocus={this.onFocus} onBlur={() => this._overlayDisposer?.()}
+ ref={ele => ele && this.createDashEventsTarget(ele, (e, de) => this.onDrop(e, de, parameter))} >
+ <EditableView display={"block"} maxHeight={72} height={35} fontSize={14}
+ contents={this.dataDoc[parameter]?.title ?? "undefined"}
+ GetValue={() => this.dataDoc[parameter]?.title ?? "undefined"}
+ SetValue={action((value: string) => {
+ const script = CompileScript(value, {
+ addReturn: true,
+ typecheck: false,
+ transformer: DocumentIconContainer.getTransformer()
+ });
+ const results = script.compiled && script.run();
+ if (results && results.success) {
+ this._errorMessage = "";
+ this.dataDoc[parameter] = results.result;
+ return true;
+ }
+ this._errorMessage = "invalid document";
+ return false;
+ })}
+ />
+ </div>;
+ }
+
+ // rendering when a string's value can be set in applied UI
+ renderBasicType(parameter: string, isNum: boolean) {
+ const strVal = (isNum ? NumCast(this.dataDoc[parameter]).toString() : StrCast(this.dataDoc[parameter]));
+ return <div className="scriptingBox-paramInputs" style={{ overflowY: "hidden" }}>
+ <EditableView display={"block"} maxHeight={72} height={35} fontSize={14}
+ contents={strVal ?? "undefined"}
+ GetValue={() => strVal ?? "undefined"}
+ SetValue={action((value: string) => {
+ const setValue = isNum ? parseInt(value) : value;
+ if (setValue !== undefined && setValue !== " ") {
+ this._errorMessage = "";
+ this.dataDoc[parameter] = setValue;
+ return true;
+ }
+ this._errorMessage = "invalid input";
+ return false;
+ })}
+ />
+ </div>;
+ }
+
+ // rendering when an enum's value can be set in applied UI (drop down box)
+ renderEnum(parameter: string, types: (string | boolean | number)[]) {
+ return <div className="scriptingBox-paramInputs">
+ <div className="scriptingBox-viewBase">
+ <div className="commandEntry-outerDiv">
+ <select className="scriptingBox-viewPicker"
+ onPointerDown={e => e.stopPropagation()}
+ onChange={e => this.viewChanged(e, parameter)}
+ value={typeof (this.dataDoc[parameter]) === "string" ? "S" + StrCast(this.dataDoc[parameter]) :
+ typeof (this.dataDoc[parameter]) === "number" ? "N" + NumCast(this.dataDoc[parameter]) :
+ "B" + BoolCast(this.dataDoc[parameter])}>
+ {types.map(type =>
+ <option className="scriptingBox-viewOption" value={(typeof (type) === "string" ? "S" : typeof (type) === "number" ? "N" : "B") + type}> {type.toString()} </option>
+ )}
+ </select>
+ </div>
+ </div>
+ </div>;
+ }
+
+ // setting a parameter (checking type and name before it is added)
+ compileParam(value: string, whichParam?: number) {
+ if (value.includes(":")) {
+ const ptype = value.split(":")[1].trim();
+ const pname = value.split(":")[0].trim();
+ if (ptype === "Doc" || ptype === "string" || ptype === "number" || ptype === "boolean" || ptype.split("|")[1]) {
+ if ((whichParam !== undefined && pname === this.paramsNames[whichParam]) || !this.paramsNames.includes(pname)) {
+ this._errorMessage = "";
+ if (whichParam !== undefined) {
+ this.compileParams[whichParam] = value;
+ } else {
+ this.compileParams = [...value.split(";").filter(s => s), ...this.compileParams];
+ }
+ return true;
+ }
+ this._errorMessage = "this name has already been used";
+ } else {
+ this._errorMessage = "this type is not supported";
+ }
+ } else {
+ this._errorMessage = "must set type of parameter";
+ }
+ return false;
+ }
+
+ @action
+ handleToken(str: string) {
+ this._currWord = str;
+ this._suggestions = [];
+ this._scriptKeys.forEach((element: string) => {
+ if (element.toLowerCase().indexOf(this._currWord.toLowerCase()) >= 0) {
+ this._suggestions.push(StrCast(element));
+ }
+ });
+ return (this._suggestions);
+ }
+
+ @action
+ handleFunc(pos: number) {
+ const scriptString = this.rawScript.slice(0, pos - 2);
+ this._currWord = scriptString.split(" ")[scriptString.split(" ").length - 1];
+ this._suggestions = [StrCast(this._scriptingParams[this._currWord])];
+ return (this._suggestions);
+ }
+
+
+ getDescription(value: string) {
+ const descrip = this._scriptingDescriptions[value];
+ return descrip?.length > 0 ? descrip : "";
+ }
+
+ getParams(value: string) {
+ const params = this._scriptingParams[value];
+ return params?.length > 0 ? params : "";
+ }
+
+ returnParam(item: string) {
+ const params = item.split(",");
+ let value = "";
+ let first = true;
+ params.forEach((element) => {
+ if (first) {
+ value = element.split(":")[0].trim();
+ first = false;
+ } else {
+ value = value + ", " + element.split(":")[0].trim();
+ }
+ });
+ return value;
+ }
+
+ getSuggestedParams(pos: number) {
+ const firstScript = this.rawScript.slice(0, pos);
+ const indexP = firstScript.lastIndexOf(".");
+ const indexS = firstScript.lastIndexOf(" ");
+ const func = firstScript.slice((indexP > indexS ? indexP : indexS) + 1, firstScript.length + 1);
+ return this._scriptingParams[func];
+ }
+
+ @action
+ suggestionPos = () => {
+ const getCaretCoordinates = require('textarea-caret');
+ const This = this;
+ document.querySelector('textarea')?.addEventListener("input", function () {
+ const caret = getCaretCoordinates(this, this.selectionEnd);
+ This._selection = this;
+ This.resetSuggestionPos(caret);
+ });
+ }
+
+ @action
+ keyHandler(e: any, pos: number) {
+ if (this._lastChar === "Enter") {
+ this.rawScript = this.rawScript + " ";
+ }
+ if (e.key === "(") {
+ this.suggestionPos();
+
+ this._scriptParamsText = this.getSuggestedParams(pos);
+ this._scriptSuggestedParams = this.getSuggestedParams(pos);
+
+ if (this._scriptParamsText !== undefined && this._scriptParamsText.length > 0) {
+ if (this.rawScript[pos - 2] !== "(") {
+ this._paramSuggestion = true;
+ }
+ }
+ } else if (e.key === ")") {
+ this._paramSuggestion = false;
+ } else {
+ if (e.key === "Backspace") {
+ if (this._lastChar === "(") {
+ this._paramSuggestion = false;
+ } else if (this._lastChar === ")") {
+ if (this.rawScript.slice(0, this.rawScript.length - 1).split("(").length - 1 > this.rawScript.slice(0, this.rawScript.length - 1).split(")").length - 1) {
+ if (this._scriptParamsText.length > 0) {
+ this._paramSuggestion = true;
+ }
+ }
+ }
+ } else if (this.rawScript.split("(").length - 1 <= this.rawScript.split(")").length - 1) {
+ this._paramSuggestion = false;
+ }
+ }
+ this._lastChar = e.key === "Backspace" ? this.rawScript[this.rawScript.length - 2] : e.key;
+
+ if (this._paramSuggestion) {
+ const parameters = this._scriptParamsText.split(",");
+ const index = this.rawScript.lastIndexOf("(");
+ const enteredParams = this.rawScript.slice(index, this.rawScript.length);
+ const splitEntered = enteredParams.split(",");
+ const numEntered = splitEntered.length;
+
+ parameters.forEach((element: string, i: number) => {
+ if (i !== parameters.length - 1) {
+ parameters[i] = element + ",";
+ }
+ });
+
+ let first = "";
+ let last = "";
+
+ parameters.forEach((element: string, i: number) => {
+ if (i < numEntered - 1) {
+ first = first + element;
+ } else if (i > numEntered - 1) {
+ last = last + element;
+ }
+ });
+
+ this._scriptSuggestedParams = <div> {first} <b>{parameters[numEntered - 1]}</b> {last} </div>;
+ }
+ }
+
+ @action
+ handlePosChange(number: any) {
+ this._caretPos = number;
+ if (this._caretPos === 0) {
+ this.rawScript = " " + this.rawScript;
+ } else if (this._spaced) {
+ this._spaced = false;
+ if (this.rawScript[this._caretPos - 1] === " ") {
+ this.rawScript = this.rawScript.slice(0, this._caretPos - 1) +
+ this.rawScript.slice(this._caretPos, this.rawScript.length);
+ }
+ }
+ }
+
+ @computed({ keepAlive: true }) get renderScriptingBox() {
+ TraceMobx();
+ return <div style={{ width: this.compileParams.length > 0 ? "70%" : "100%" }} ref={this._scriptTextRef}>
+ <ReactTextareaAutocomplete className="ScriptingBox-textarea-script"
+ minChar={1}
+ placeholder="write your script here"
+ onFocus={this.onFocus}
+ onBlur={() => this._overlayDisposer?.()}
+ onChange={e => this.rawScript = e.target.value}
+ value={this.rawScript}
+ movePopupAsYouType={true}
+ loadingComponent={() => <span>Loading</span>}
+
+ trigger={{
+ " ": {
+ dataProvider: (token: any) => this.handleToken(token),
+ component: ({ entity: value }) => this.renderFuncListElement(value),
+ output: (item: any, trigger) => {
+ this._spaced = true;
+ return trigger + item.trim();
+ },
+ },
+ ".": {
+ dataProvider: (token: any) => this.handleToken(token),
+ component: ({ entity: value }) => this.renderFuncListElement(value),
+ output: (item: any, trigger) => {
+ this._spaced = true;
+ return trigger + item.trim();
+ },
+ }
+ }}
+ onKeyDown={(e) => this.keyHandler(e, this._caretPos)}
+ onCaretPositionChange={(number: any) => this.handlePosChange(number)}
+ />
+ </div>;
+ }
+
+ renderFuncListElement(value: string) {
+ return <div>
+ <div style={{ fontSize: "14px" }}>
+ {value}
+ </div>
+ <div key="desc" style={{ fontSize: "10px" }}>{this.getDescription(value)}</div>
+ <div key="params" style={{ fontSize: "10px" }}>{this.getParams(value)}</div>
+ </div>;
+ }
+
+ // inputs for scripting div (script box, params box, and params column)
+ @computed({ keepAlive: true }) get renderScriptingInputs() {
+
+ // should there be a border? style={{ borderStyle: "groove", borderBlockWidth: "1px" }}
+ // params box on bottom
+ const parameterInput = <div className="scriptingBox-params">
+ <EditableView display={"block"} maxHeight={72} height={35} fontSize={22}
+ contents={""}
+ GetValue={returnEmptyString}
+ SetValue={value => value && value !== " " ? this.compileParam(value) : false}
+ placeholder={"enter parameters here"}
+ />
+ </div>;
+
+ // params column on right side (list)
+ const definedParameters = !this.compileParams.length ? (null) :
+ <div className="scriptingBox-plist" style={{ width: "30%" }}>
+ {this.compileParams.map((parameter, i) =>
+ <div className="scriptingBox-pborder" onKeyPress={e => e.key === "Enter" && this._overlayDisposer?.()} >
+ <EditableView display={"block"} maxHeight={72} height={35} fontSize={12} background-color={"beige"}
+ contents={parameter}
+ GetValue={() => parameter}
+ SetValue={value => value && value !== " " ? this.compileParam(value, i) : this.onDelete(i)}
+ />
+ </div>
+ )}
+ </div>;
+
+ return <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected() && e.stopPropagation()} >
+ <div className="scriptingBox-wrapper">
+ {this.renderScriptingBox}
+ {definedParameters}
+ </div>
+ {parameterInput}
+ {this.renderErrorMessage()}
+ </div>;
+ }
+
+ // toolbar (with compile and apply buttons) for scripting UI
+ renderScriptingTools() {
+ const buttonStyle = "scriptingBox-button" + (this.rootDoc.layoutKey === "layout_onClick" ? "third" : "");
+ return <div className="scriptingBox-toolbar">
+ <button className={buttonStyle} style={{ width: "33%" }} onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button>
+ <button className={buttonStyle} style={{ width: "33%" }} onPointerDown={e => { this.onApply(); e.stopPropagation(); }}>Apply</button>
+ <button className={buttonStyle} style={{ width: "33%" }} onPointerDown={e => { this.onSave(); e.stopPropagation(); }}>Save</button>
+
+ {this.rootDoc.layoutKey !== "layout_onClick" ? (null) :
+ <button className={buttonStyle} onPointerDown={e => { this.onFinish(); e.stopPropagation(); }}>Finish</button>}
+ </div>;
+ }
+
+ // inputs UI for params which allows you to set values for each displayed in a list
+ renderParamsInputs() {
+ return <div className="scriptingBox-inputDiv" onPointerDown={e => this.props.isSelected(true) && e.stopPropagation()} >
+ {!this.compileParams.length || !this.paramsNames ? (null) :
+ <div className="scriptingBox-plist">
+ {this.paramsNames.map((parameter: string, i: number) =>
+ <div className="scriptingBox-pborder" onKeyPress={e => e.key === "Enter" && this._overlayDisposer?.()} >
+ <div className="scriptingBox-wrapper" style={{ maxHeight: "40px" }}>
+ <div className="scriptingBox-paramNames" > {`${parameter}:${this.paramsTypes[i]} = `} </div>
+ {this.paramsTypes[i] === "boolean" ? this.renderEnum(parameter, [true, false]) : (null)}
+ {this.paramsTypes[i] === "string" ? this.renderBasicType(parameter, false) : (null)}
+ {this.paramsTypes[i] === "number" ? this.renderBasicType(parameter, true) : (null)}
+ {this.paramsTypes[i] === "Doc" ? this.renderDoc(parameter) : (null)}
+ {this.paramsTypes[i]?.split("|")[1] ? this.renderEnum(parameter, this.paramsTypes[i].split("|").map(s => !isNaN(parseInt(s.trim())) ? parseInt(s.trim()) : s.trim())) : (null)}
+ </div>
+ </div>)}
+ </div>}
+ {this.renderErrorMessage()}
+ </div>;
+ }
+
+ // toolbar (with edit and run buttons and error message) for params UI
+ renderTools(toolSet: string, func: () => void) {
+ const buttonStyle = "scriptingBox-button" + (this.rootDoc.layoutKey === "layout_onClick" ? "third" : "");
+ return <div className="scriptingBox-toolbar">
+ <button className={buttonStyle} onPointerDown={e => { this.onEdit(); e.stopPropagation(); }}>Edit</button>
+ <button className={buttonStyle} onPointerDown={e => { func(); e.stopPropagation(); }}>{toolSet}</button>
+ {this.rootDoc.layoutKey !== "layout_onClick" ? (null) :
+ <button className={buttonStyle} onPointerDown={e => { this.onFinish(); e.stopPropagation(); }}>Finish</button>}
+ </div>;
+ }
+
+ // renders script UI if _applied = false and params UI if _applied = true
render() {
- const params = <EditableView
- contents={this.compileParams.join(" ")}
- display={"block"}
- maxHeight={72}
- height={35}
- fontSize={28}
- GetValue={() => ""}
- SetValue={value => { this.compileParams = new List<string>(value.split(" ").filter(s => s !== " ")); return true; }}
- />;
return (
- <div className="scriptingBox-outerDiv"
- onWheel={e => this.props.isSelected(true) && e.stopPropagation()}>
- <div className="scriptingBox-inputDiv"
- onPointerDown={e => this.props.isSelected(true) && e.stopPropagation()} >
- <textarea className="scriptingBox-textarea"
- placeholder="write your script here"
- onChange={e => this.rawScript = e.target.value}
- value={this.rawScript}
- onFocus={this.onFocus}
- onBlur={e => this._overlayDisposer?.()} />
- <div className="scriptingBox-errorMessage" style={{ background: this._errorMessage ? "red" : "" }}>{this._errorMessage}</div>
- <div className="scriptingBox-params" >{params}</div>
- </div>
- {this.rootDoc.layout === "layout" ? <div></div> : (null)}
- <div className="scriptingBox-toolbar">
- <button className="scriptingBox-button" onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button>
- <button className="scriptingBox-button" onPointerDown={e => { this.onRun(); e.stopPropagation(); }}>Run</button>
+ <div className={`scriptingBox`} onContextMenu={this.specificContextMenu}
+ onPointerUp={!this._function ? this.suggestionPos : undefined}>
+ <div className="scriptingBox-outerDiv"
+ onWheel={e => this.props.isSelected(true) && e.stopPropagation()}>
+ {this._paramSuggestion ? <div className="boxed" ref={this._suggestionRef} style={{ left: this._suggestionBoxX + 20, top: this._suggestionBoxY - 15, display: "inline" }}> {this._scriptSuggestedParams} </div> : null}
+ {!this._applied && !this._function ? this.renderScriptingInputs : null}
+ {this._applied && !this._function ? this.renderParamsInputs() : null}
+ {!this._applied && this._function ? this.renderFunctionInputs() : null}
+
+ {!this._applied && !this._function ? this.renderScriptingTools() : null}
+ {this._applied && !this._function ? this.renderTools("Run", () => this.onRun()) : null}
+ {!this._applied && this._function ? this.renderTools("Create Function", () => this.onCreate()) : null}
</div>
</div>
);
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index ad204f3df..5c75a589a 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -43,24 +43,24 @@ interface IDashDocCommentViewInternal {
export class DashDocCommentViewInternal extends React.Component<IDashDocCommentViewInternal>{
constructor(props: IDashDocCommentViewInternal) {
- super(props)
- this.onPointerLeaveCollapsed = this.onPointerLeaveCollapsed.bind(this)
- this.onPointerEnterCollapsed = this.onPointerEnterCollapsed.bind(this)
- this.onPointerUpCollapsed = this.onPointerUpCollapsed.bind(this)
- this.onPointerDownCollapsed = this.onPointerDownCollapsed.bind(this)
+ super(props);
+ this.onPointerLeaveCollapsed = this.onPointerLeaveCollapsed.bind(this);
+ this.onPointerEnterCollapsed = this.onPointerEnterCollapsed.bind(this);
+ this.onPointerUpCollapsed = this.onPointerUpCollapsed.bind(this);
+ this.onPointerDownCollapsed = this.onPointerDownCollapsed.bind(this);
}
onPointerLeaveCollapsed(e: any) {
DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
e.preventDefault();
e.stopPropagation();
- };
+ }
onPointerEnterCollapsed(e: any) {
DocServer.GetRefField(this.props.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
e.preventDefault();
e.stopPropagation();
- };
+ }
onPointerUpCollapsed(e: any) {
const target = this.targetNode();
@@ -75,11 +75,11 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
}, 0);
}
e.stopPropagation();
- };
+ }
onPointerDownCollapsed(e: any) {
e.stopPropagation();
- };
+ }
targetNode = () => { // search forward in the prosemirror doc for the attached dashDocNode that is the target of the comment anchor
const state = this.props.view.state;
@@ -94,7 +94,7 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
this.props.view.dispatch(state.tr.insert(this.props.getPos() + 1, dashDoc));
setTimeout(() => { try { this.props.view.dispatch(state.tr.setSelection(TextSelection.create(state.tr.doc, this.props.getPos() + 2))); } catch (e) { } }, 0);
return undefined;
- };
+ }
render() {
return (
@@ -107,6 +107,6 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
onPointerDown={this.onPointerDownCollapsed}
>
</span>
- )
+ );
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ba36a1618..1f1e51e66 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -291,8 +291,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
e.stopPropagation();
}
- // embed document when dragging with a userDropAction or an embedDoc flag set
- } else if (dragData.userDropAction || dragData.embedDoc) {
+ // embed document when dragg marked as embed
+ } else if (de.embedKey) {
const target = dragData.droppedDocuments[0];
// const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title);
// if (link) {
@@ -481,7 +481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}), icon: "eye"
});
});
- changeItems.push({ description: "FreeForm", event: undoBatch(() => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" });
+ changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
!change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 114288a9d..0a4c52ef9 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { SelectionManager } from "../../../util/SelectionManager";
import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc } from "../../../../fields/Doc";
+import { Doc, DataSym } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
import { Id } from "../../../../fields/FieldSymbols";
import { Docs } from "../../../documents/Documents";
@@ -142,6 +142,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
if (force || props.Document._singleLine) {
const layoutKey = StrCast(originalDoc.layoutKey);
const newDoc = Doc.MakeCopy(originalDoc, true);
+ newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = originalDoc[layoutKey];
@@ -166,6 +167,7 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
if (originalDoc instanceof Doc) {
const layoutKey = StrCast(originalDoc.layoutKey);
const newDoc = Doc.MakeCopy(originalDoc, true);
+ newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = originalDoc[layoutKey];