aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2020-02-29 15:26:58 -0500
committerSam Wilkins <samwilkins333@gmail.com>2020-02-29 15:26:58 -0500
commit99a23aea54f1430594e70724b252da8f8693a24e (patch)
tree7ff95f302766d1fbb8b1dee3797a7dcbb95148b0 /src/client/views/collections
parent8c39fb5678bfdd414249f10b0b80e823370f7228 (diff)
parentbb2f6955bef4f079c0fa7213e80fde7a76847799 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx71
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx3
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx1
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx13
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx14
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx43
-rw-r--r--src/client/views/collections/CollectionStackingView.scss19
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx35
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx89
-rw-r--r--src/client/views/collections/CollectionSubView.tsx5
-rw-r--r--src/client/views/collections/CollectionTimeView.scss4
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss2
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx28
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss7
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx186
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx111
19 files changed, 467 insertions, 178 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 83dbb4263..b85cc9b56 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -238,6 +238,75 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
return true;
}
+
+ //
+ // Creates a split on the any side of the docking view, based on the passed input pullSide and then adds the Document to the requested side
+ //
+ @undoBatch
+ @action
+ public static AddSplit(document: Doc, pullSide: string, dataDoc: Doc | undefined, libraryPath?: Doc[]) {
+ if (!CollectionDockingView.Instance) return false;
+ const instance = CollectionDockingView.Instance;
+ const newItemStackConfig = {
+ type: 'stack',
+ content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+ };
+
+ const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+
+ if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
+ instance._goldenLayout.root.addChild(newContentItem);
+ } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
+ if (pullSide === "left") {
+ instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
+ } else if (pullSide === "right") {
+ instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
+ } else if (pullSide === "top" || pullSide === "bottom") {
+ // if not going in a row layout, must add already existing content into column
+ const rowlayout = instance._goldenLayout.root.contentItems[0];
+ const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
+ rowlayout.parent.replaceChild(rowlayout, newColumn);
+ if (pullSide === "top") {
+ newColumn.addChild(rowlayout, undefined, true);
+ newColumn.addChild(newContentItem, 0, true);
+ } else if (pullSide === "bottom") {
+ newColumn.addChild(newContentItem, undefined, true);
+ newColumn.addChild(rowlayout, 0, true);
+ }
+
+ rowlayout.config.height = 50;
+ newContentItem.config.height = 50;
+ }
+ } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
+ if (pullSide === "top") {
+ instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
+ } else if (pullSide === "bottom") {
+ instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
+ } else if (pullSide === "left" || pullSide === "right") {
+ // if not going in a row layout, must add already existing content into column
+ const collayout = instance._goldenLayout.root.contentItems[0];
+ const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+ collayout.parent.replaceChild(collayout, newRow);
+
+ if (pullSide === "left") {
+ newRow.addChild(collayout, undefined, true);
+ newRow.addChild(newContentItem, 0, true);
+ } else if (pullSide === "right") {
+ newRow.addChild(newContentItem, undefined, true);
+ newRow.addChild(collayout, 0, true);
+ }
+
+ collayout.config.width = 50;
+ newContentItem.config.width = 50;
+ }
+ }
+
+ newContentItem.callDownwards('_$init');
+ instance.layoutChanged();
+ return true;
+ }
+
+
//
// Creates a vertical split on the right side of the docking view, and then adds the Document to that split
//
@@ -715,7 +784,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
TraceMobx();
if (!this._document) return (null);
const document = this._document;
- const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) && this._document[DataSym];// document.layout instanceof Doc ? document : this._dataDoc;
+ const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined;// document.layout instanceof Doc ? document : this._dataDoc;
return <DocumentView key={document[Id]}
LibraryPath={this._libraryPath}
Document={document}
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 4a14a30cd..9384eb381 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -82,13 +82,14 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
render() {
const guid = Utils.GenerateGuid();
+ const flexDir: any = StrCast(this.Document.flexDirection);
return <div className="collectionLinearView-outer">
<div className="collectionLinearView" ref={this.createDashEventsTarget} >
<input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
onChange={action((e: any) => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
<label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
- <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document._width, 25) }}>
+ <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document._width, 25), flexDirection: flexDir }}>
{this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => {
const nested = pair.layout._viewType === CollectionViewType.Linear;
const dref = React.createRef<HTMLDivElement>();
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index c9b7ca221..3c2cbb5b0 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -75,6 +75,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@undoBatch
rowDrop = action((e: Event, de: DragManager.DropEvent) => {
+ console.log("masronry row drop");
this._createAliasSelected = false;
if (de.complete.docDragData) {
(this.props.parent.Document.dropConverter instanceof ScriptField) &&
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index facde3648..df7abad61 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -23,6 +23,7 @@ import { faExpand } from '@fortawesome/free-solid-svg-icons';
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { KeyCodes } from "../../northstar/utils/KeyCodes";
import { undoBatch } from "../../util/UndoManager";
+import { List } from "lodash";
library.add(faExpand);
@@ -83,10 +84,20 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
@action
- onPointerDown = (e: React.PointerEvent): void => {
+ onPointerDown = async (e: React.PointerEvent): Promise<void> => {
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
this.props.setPreviewDoc(this.props.rowProps.original);
+ let url: string;
+ if (url = StrCast(this.props.rowProps.row.href)) {
+ try {
+ new URL(url);
+ const temp = window.open(url)!;
+ temp.blur();
+ window.focus();
+ } catch { }
+ }
+
// this._isEditing = true;
// this.props.setIsEditing(true);
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index c585506b3..507ee89e4 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -291,13 +291,11 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
onKeyDown = (e: React.KeyboardEvent): void => {
if (e.key === "Enter") {
const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
- this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
-
- if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
+ if (keyOptions.length) {
+ this.onSelect(keyOptions[0]);
+ } else if (this._searchTerm !== "" && this.props.canAddNew) {
+ this.setSearchTerm(this._searchTerm || this._key);
this.onSelect(this._searchTerm);
- } else {
- this.setSearchTerm(this._key);
}
}
}
@@ -338,7 +336,7 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
const options = keyOptions.map(key => {
- return <div key={key} className="key-option" onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>;
+ return <div key={key} className="key-option" onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>;
});
// if search term does not already exist as a group type, give option to create new group type
@@ -356,7 +354,7 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
<div className="keys-dropdown">
<input className="keys-search" ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
onChange={e => this.onChange(e.target.value)} onFocus={this.onFocus} onBlur={this.onBlur}></input>
- <div className="keys-options-wrapper" onPointerEnter={this.onPointerEnter} onPointerOut={this.onPointerOut}>
+ <div className="keys-options-wrapper" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}>
{this.renderOptions()}
</div>
</div >
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 9486d195a..6eeceb552 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -28,6 +28,7 @@ import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionView } from "./CollectionView";
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
+import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
library.add(faCog, faPlus, faSortUp, faSortDown);
library.add(faTable);
@@ -43,8 +44,8 @@ export enum ColumnType {
// this map should be used for keys that should have a const type of value
const columnTypes: Map<string, ColumnType> = new Map([
["title", ColumnType.String],
- ["x", ColumnType.Number], ["y", ColumnType.Number], ["width", ColumnType.Number], ["height", ColumnType.Number],
- ["nativeWidth", ColumnType.Number], ["nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
+ ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
+ ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
]);
@@ -54,9 +55,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
private _startPreviewWidth = 0;
private DIVIDER_WIDTH = 4;
- @observable previewScript: string = "";
@observable previewDoc: Doc | undefined = undefined;
- @observable private _node: HTMLDivElement | null = null;
@observable private _focusedTable: Doc = this.props.Document;
@computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
@@ -75,9 +74,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action setPreviewDoc = (doc: Doc) => this.previewDoc = doc;
- @undoBatch
- @action setPreviewScript = (script: string) => this.previewScript = script
-
//toggles preview side-panel of schema
@action
toggleExpander = () => {
@@ -86,27 +82,17 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
onDividerDown = (e: React.PointerEvent) => {
this._startPreviewWidth = this.previewWidth();
- e.stopPropagation();
- e.preventDefault();
- document.addEventListener("pointermove", this.onDividerMove);
- document.addEventListener('pointerup', this.onDividerUp);
+ setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, action(() => this.toggleExpander()));
}
@action
- onDividerMove = (e: PointerEvent): void => {
+ onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
const nativeWidth = this._mainCont!.getBoundingClientRect();
const minWidth = 40;
const maxWidth = 1000;
const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
this.props.Document.schemaPreviewWidth = width;
- }
- @action
- onDividerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onDividerMove);
- document.removeEventListener('pointerup', this.onDividerUp);
- if (this._startPreviewWidth === this.previewWidth()) {
- this.toggleExpander();
- }
+ return false;
}
onPointerDown = (e: React.PointerEvent): void => {
@@ -119,9 +105,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
@computed
- get previewDocument(): Doc | undefined {
- return this.previewDoc ? (this.previewScript && this.previewScript !== "this" ? FieldValue(Cast(this.previewDoc[this.previewScript], Doc)) : this.previewDoc) : undefined;
- }
+ get previewDocument(): Doc | undefined { return this.previewDoc; }
getPreviewTransform = (): Transform => {
return this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth);
@@ -477,8 +461,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@undoBatch
createRow = () => {
- const newDoc = Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 });
- this.props.addDocument(newDoc);
+ this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
}
@undoBatch
@@ -559,16 +542,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
columns[index] = columnField;
this.columns = columns;
}
-
- // const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc));
- // if (!typesDoc) {
- // let newTypesDoc = new Doc();
- // newTypesDoc[key] = type;
- // this.props.Document.schemaColumnTypes = newTypesDoc;
- // return;
- // } else {
- // typesDoc[key] = type;
- // }
}
@undoBatch
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 293dc5414..bfa5ea278 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -160,9 +160,7 @@
}
.collectionStackingView-sectionHeader {
text-align: center;
- margin-left: 2px;
- margin-right: 2px;
- margin-top: 10px;
+ margin: auto;
background: $main-accent;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
@@ -214,6 +212,7 @@
left: 0;
top: 0;
height: 100%;
+ display: none;
[class*="css"] {
max-width: 102px;
@@ -251,6 +250,7 @@
right: 0;
top: 0;
height: 100%;
+ display: none;
[class*="css"] {
max-width: 102px;
@@ -285,6 +285,18 @@
right: 25px;
top: 0;
height: 100%;
+ display: none;
+ }
+ }
+ .collectionStackingView-sectionHeader:hover {
+ .collectionStackingView-sectionColor {
+ display:unset;
+ }
+ .collectionStackingView-sectionOptions {
+ display:unset;
+ }
+ .collectionStackingView-sectionDelete {
+ display:unset;
}
}
@@ -294,7 +306,6 @@
overflow: hidden;
margin: auto;
width: 90%;
- color: lightgrey;
overflow: ellipses;
.editableView-container-editing-oneLine,
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a1cc21319..d1f45af90 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../new_fields/Schema";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types";
import { TraceMobx } from "../../../new_fields/util";
-import { Utils } from "../../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction } from "../../../Utils";
import { DragManager } from "../../util/DragManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -24,6 +24,7 @@ import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from "./CollectionView";
+import { Docs } from "../../documents/Documents";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -60,7 +61,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
const dxf = () => this.getDocTransform(d, dref.current!);
this._docXfs.push({ dxf: dxf, width: width, height: height });
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- const style = this.isStackingView ? { width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ const style = this.isStackingView ? { width: width(), marginTop: this.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.getDisplayDoc(d, this.props.DataDoc, dxf, width)}
</div>;
@@ -78,8 +79,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0);
return new Map<SchemaHeaderField, Doc[]>();
}
- const sectionHeaders = this.sectionHeaders;
+ const sectionHeaders: SchemaHeaderField[] = Array.from(this.sectionHeaders);
const fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+ let changed = false;
this.filteredChildren.map(d => {
const sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object;
// the next five lines ensures that floating point rounding errors don't create more than one section -syip
@@ -95,8 +97,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`);
fields.set(newSchemaHeader, [d]);
sectionHeaders.push(newSchemaHeader);
+ changed = true;
}
});
+ changed && setTimeout(action(() => { if (this.sectionHeaders) { this.sectionHeaders.length = 0; this.sectionHeaders.push(...sectionHeaders); } }), 0);
return fields;
}
@@ -203,26 +207,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
columnDividerDown = (e: React.PointerEvent) => {
- e.stopPropagation();
- e.preventDefault();
runInAction(() => this._cursor = "grabbing");
- document.addEventListener("pointermove", this.onDividerMove);
- document.addEventListener('pointerup', this.onDividerUp);
- this._columnStart = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+ setupMoveUpEvents(this, e, this.onDividerMove, action(() => this._cursor = "grab"), emptyFunction);
}
@action
- onDividerMove = (e: PointerEvent): void => {
- const dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
- const delta = dragPos - this._columnStart;
- this._columnStart = dragPos;
- this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta);
- }
-
- @action
- onDividerUp = (e: PointerEvent): void => {
- runInAction(() => this._cursor = "grab");
- document.removeEventListener("pointermove", this.onDividerMove);
- document.removeEventListener('pointerup', this.onDividerUp);
+ onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ this.layoutDoc.columnWidth = Math.max(10, this.columnWidth + delta[0]);
+ return false;
}
@computed get columnDragger() {
@@ -348,7 +339,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@action
addGroup = (value: string) => {
if (value && this.sectionHeaders) {
- this.sectionHeaders.push(new SchemaHeaderField(value));
+ const schemaHdrField = new SchemaHeaderField(value);
+ this.sectionHeaders.push(schemaHdrField);
+ Doc.addEnumerationToTextField(undefined, this.sectionFilter, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]);
return true;
}
return false;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 8c23ecd49..516e583d4 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -20,7 +20,10 @@ import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
import { CollectionStackingView } from "./CollectionStackingView";
+import { setupMoveUpEvents, emptyFunction } from "../../../Utils";
import "./CollectionStackingView.scss";
+import { listSpec } from "../../../new_fields/Schema";
+import { Schema } from "prosemirror-model";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -42,20 +45,15 @@ interface CSVFieldColumnProps {
@observer
export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
@observable private _background = "inherit";
- @observable private _createAliasSelected: boolean = false;
- private _dropRef: HTMLDivElement | null = null;
private dropDisposer?: DragManager.DragDropDisposer;
private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
- private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
- private _sensitivity: number = 16;
@observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
@observable _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
createColumnDropRef = (ele: HTMLDivElement | null) => {
- this._dropRef = ele;
- this.dropDisposer && this.dropDisposer();
+ this.dropDisposer?.();
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
}
@@ -63,16 +61,10 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@undoBatch
columnDrop = action((e: Event, de: DragManager.DropEvent) => {
- this._createAliasSelected = false;
if (de.complete.docDragData) {
const key = StrCast(this.props.parent.props.Document.sectionFilter);
const castedValue = this.getValue(this._heading);
- if (castedValue) {
- de.complete.docDragData.droppedDocuments.forEach(d => d[key] = castedValue);
- }
- else {
- de.complete.docDragData.droppedDocuments.forEach(d => d[key] = undefined);
- }
+ de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, false));
this.props.parent.onInternalDrop(e, de);
e.stopPropagation();
}
@@ -93,7 +85,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
headingChanged = (value: string, shiftDown?: boolean) => {
- this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document.sectionFilter);
const castedValue = this.getValue(value);
if (castedValue) {
@@ -114,7 +105,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
changeColumnColor = (color: string) => {
- this._createAliasSelected = false;
if (this.props.headingObject) {
this.props.headingObject.setColor(color);
this._color = color;
@@ -124,22 +114,18 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
pointerEntered = () => {
if (SelectionManager.GetIsDragging()) {
- this._createAliasSelected = false;
this._background = "#b4b4b4";
}
}
@action
pointerLeave = () => {
- this._createAliasSelected = false;
this._background = "inherit";
- document.removeEventListener("pointermove", this.startDrag);
}
@action
addDocument = (value: string, shiftDown?: boolean) => {
if (!value) return false;
- this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document.sectionFilter);
const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true });
newDoc[key] = this.getValue(this.props.heading);
@@ -151,7 +137,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
deleteColumn = () => {
- this._createAliasSelected = false;
const key = StrCast(this.props.parent.props.Document.sectionFilter);
this.props.docList.forEach(d => d[key] = undefined);
if (this.props.parent.sectionHeaders && this.props.headingObject) {
@@ -162,7 +147,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
collapseSection = () => {
- this._createAliasSelected = false;
if (this.props.headingObject) {
this._headingsHack++;
this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed);
@@ -170,46 +154,23 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
}
- startDrag = (e: PointerEvent) => {
- const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
- if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
- const alias = Doc.MakeAlias(this.props.parent.props.Document);
- const key = StrCast(this.props.parent.props.Document.sectionFilter);
- let value = this.getValue(this._heading);
- value = typeof value === "string" ? `"${value}"` : value;
- alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name });
- if (alias.viewSpecScript) {
- DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
- }
-
- e.stopPropagation();
- document.removeEventListener("pointermove", this.startDrag);
- document.removeEventListener("pointerup", this.pointerUp);
- }
- }
-
- pointerUp = (e: PointerEvent) => {
- e.stopPropagation();
- e.preventDefault();
-
- document.removeEventListener("pointermove", this.startDrag);
- document.removeEventListener("pointerup", this.pointerUp);
- }
-
headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
- e.stopPropagation();
- e.preventDefault();
-
- const [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY);
- this._startDragPosition = { x: dx, y: dy };
+ setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
+ }
- if (this._createAliasSelected) {
- document.removeEventListener("pointermove", this.startDrag);
- document.addEventListener("pointermove", this.startDrag);
- document.removeEventListener("pointerup", this.pointerUp);
- document.addEventListener("pointerup", this.pointerUp);
+ startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
+ const alias = Doc.MakeAlias(this.props.parent.props.Document);
+ alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.props.Document.sectionHeaders, listSpec(SchemaHeaderField))?.length || 1);
+ alias.sectionFilter = undefined;
+ const key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let value = this.getValue(this._heading);
+ value = typeof value === "string" ? `"${value}"` : value;
+ alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name });
+ if (alias.viewSpecScript) {
+ DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+ return true;
}
- runInAction(() => this._createAliasSelected = false);
+ return false;
}
renderColorPicker = () => {
@@ -242,17 +203,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
);
}
- @action
- toggleAlias = () => {
- this._createAliasSelected = true;
- }
-
renderMenu = () => {
- const selected = this._createAliasSelected;
return (
<div className="collectionStackingView-optionPicker">
<div className="optionOptions">
- <div className={"optionPicker" + (selected === true ? " active" : "")} onClick={this.toggleAlias}>Create Alias</div>
+ <div className={"optionPicker" + (true ? " active" : "")} onClick={action(() => { })}>Add options here</div>
</div>
</div >
);
@@ -328,6 +283,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const heading = this._heading;
const style = this.props.parent;
const singleColumn = style.isStackingView;
+ const columnYMargin = this.props.headingObject ? 0 : NumCast(this.props.parent.props.Document._yMargin);
const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
const evContents = heading ? heading : this.props.type && this.props.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
const headerEditableViewProps = {
@@ -352,6 +308,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{
+ marginTop: NumCast(this.props.parent.props.Document._yMargin),
width: (style.columnWidth) /
((uniqueHeadings.length +
((this.props.parent.props.Document._chromeStatus !== 'view-mode' && this.props.parent.props.Document._chromeStatus !== 'disabled') ? 1 : 0)) || 1)
@@ -404,7 +361,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
<div>
<div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
style={{
- padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`,
+ padding: singleColumn ? `${columnYMargin}px ${0}px ${style.yMargin}px ${0}px` : `${columnYMargin}px ${0}px`,
margin: "auto",
width: "max-content", //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
height: 'max-content',
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 6ad187e5c..aa31d604e 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { listSpec } from "../../../new_fields/Schema";
import { ScriptField } from "../../../new_fields/ScriptField";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
@@ -54,11 +54,13 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
private gestureDisposer?: GestureUtils.GestureEventDisposer;
protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
private _childLayoutDisposer?: IReactionDisposer;
+ protected _mainCont?: HTMLDivElement;
protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
this.dropDisposer?.();
this.gestureDisposer?.();
this.multiTouchDisposer?.();
if (ele) {
+ this._mainCont = ele;
this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this));
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
@@ -150,7 +152,6 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
@undoBatch
protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
-
}
@undoBatch
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index 6ea5e6908..865fc3cd2 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -10,7 +10,6 @@
.collectionTimeView-backBtn {
background: green;
display: inline;
- margin-right: 20px;
}
.collectionFreeform-customText {
@@ -68,6 +67,9 @@
padding: 5px;
border: 1px solid black;
display:none;
+ span {
+ margin-left : 10px;
+ }
}
.collectionTimeView-treeView {
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 157229ec8..2d56f00d5 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -132,7 +132,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
- ContextMenu.Instance.clearItems();
+ ContextMenu.Instance && ContextMenu.Instance.clearItems();
if (index !== -1) {
value.splice(index, 1);
targetDataDoc[this.props.fieldKey] = new List<Doc>(value);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 0c96a662c..8602b2369 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -278,7 +278,7 @@
display:flex;
flex-direction: row;
width: 150px;
- margin: auto 0 auto auto;
+ margin: auto auto auto auto;
}
.react-autosuggest__container {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 49a9e8200..4f504ab1c 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -166,7 +166,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
this._viewSpecsOpen = true;
//@ts-ignore
- if (!e.target?.classList[0]?.startsWith("qs")) {
+ if (!e.target ?.classList[0] ?.startsWith("qs")) {
this.closeDatePicker();
}
@@ -291,7 +291,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
if (de.complete.docDragData && de.complete.docDragData.draggedDocuments.length) {
- this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData?.draggedDocuments || []));
+ this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData ?.draggedDocuments || []));
e.stopPropagation();
}
return true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 7e37f646d..637c81fe2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -277,16 +277,16 @@ export function computeTimelineLayout(
const stack = findStack(x, stacking);
prevKey = key;
if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
- groupNames.push({ type: "text", text: key.toString(), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: "text", text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
}
layoutDocsAtTime(keyDocs, key);
});
if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
x = (curTime - minTime) * scaling;
- groupNames.push({ type: "text", text: curTime.toString(), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: "text", text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined });
}
if (Math.ceil(maxTime - minTime) * scaling > x + 25) {
- groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
@@ -314,7 +314,7 @@ export function computeTimelineLayout(
function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<Doc, ViewDefBounds>,
poolData: Map<string, PoolData>, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[],
- extraDocs: Doc[]):ViewDefResult[] {
+ extraDocs: Doc[]): ViewDefResult[] {
const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as ViewDefBounds);
@@ -342,16 +342,16 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
extraDocs.map(ed => poolData.set(ed[Id], { x: 0, y: 0, zIndex: -99 }));
return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
- type: gname.type,
- text: gname.text,
- x: gname.x * scale,
- y: gname.y * scale,
- color: gname.color,
- width: gname.width === undefined ? undefined : gname.width * scale,
- height: gname.height === -1 ? 1 : Math.max(fontHeight, (gname.height || 0) * scale),
- fontSize: gname.fontSize,
- payload: gname.payload
- })));
+ type: gname.type,
+ text: gname.text,
+ x: gname.x * scale,
+ y: gname.y * scale,
+ color: gname.color,
+ width: gname.width === undefined ? undefined : gname.width * scale,
+ height: gname.height === -1 ? 1 : Math.max(fontHeight, (gname.height || 0) * scale),
+ fontSize: gname.fontSize,
+ payload: gname.payload
+ })));
}
export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 0b5e44ccb..730392ab5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -36,6 +36,7 @@
height: 100%;
display: flex;
align-items: center;
+
.collectionfreeformview-placeholderSpan {
font-size: 32;
display: flex;
@@ -99,4 +100,10 @@
#prevCursor {
animation: blink 1s infinite;
+}
+
+.pullpane-indicator {
+ z-index: 99999;
+ background-color: rgba($color: #000000, $alpha: .4);
+ position: absolute;
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4458c7dcf..a73e601fd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,16 +1,16 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
-import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload, faTextHeight } from "@fortawesome/free-solid-svg-icons";
+import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../../new_fields/Doc";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
import { Id } from "../../../../new_fields/FieldSymbols";
-import { InkTool } from "../../../../new_fields/InkField";
+import { InkTool, InkField, InkData } from "../../../../new_fields/InkField";
import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { Cast, NumCast, ScriptCast, BoolCast, StrCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, ScriptCast, BoolCast, StrCast, FieldValue } from "../../../../new_fields/Types";
import { TraceMobx } from "../../../../new_fields/util";
import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
@@ -29,7 +29,7 @@ import { ContextMenu } from "../../ContextMenu";
import { ContextMenuProps } from "../../ContextMenuItem";
import { InkingControl } from "../../InkingControl";
import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocumentViewProps } from "../../nodes/DocumentView";
+import { DocumentContentsView } from "../../nodes/DocumentContentsView";
import { FormattedTextBox } from "../../nodes/FormattedTextBox";
import { pageSchema } from "../../nodes/ImageBox";
import PDFMenu from "../../pdf/PDFMenu";
@@ -40,6 +40,13 @@ import "./CollectionFreeFormView.scss";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
+import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
+import { RichTextField } from "../../../../new_fields/RichTextField";
+import { List } from "../../../../new_fields/List";
+import { DocumentViewProps } from "../../nodes/DocumentView";
+import { CollectionDockingView } from "../CollectionDockingView";
+import { MainView } from "../../MainView";
+import { TouchScrollableMenuItem } from "../../TouchScrollableMenu";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -51,8 +58,8 @@ export const panZoomSchema = createSchema({
arrangeInit: ScriptField,
useClusters: "boolean",
fitToBox: "boolean",
- xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
- yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+ _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+ _yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
panTransformType: "string",
scrollHeight: "number",
fitX: "number",
@@ -68,11 +75,16 @@ const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSch
export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _lastX: number = 0;
private _lastY: number = 0;
+ private _inkToTextStartX: number | undefined;
+ private _inkToTextStartY: number | undefined;
+ private _wordPalette: Map<string, string> = new Map<string, string>();
private _clusterDistance: number = 75;
private _hitCluster = false;
private _layoutComputeReaction: IReactionDisposer | undefined;
- private _layoutPoolData = observable.map<string, any>();
+ private _layoutPoolData = new ObservableMap<string, any>();
private _cachedPool: Map<string, any> = new Map();
+ @observable private _pullCoords: number[] = [0, 0];
+ @observable private _pullDirection: string = "";
public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@@ -411,8 +423,91 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
});
this.addDocument(Docs.Create.FreeformDocument(sel, { title: "nested collection", x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
sel.forEach(d => this.props.removeDocument(d));
+ e.stopPropagation();
break;
-
+ case GestureUtils.Gestures.StartBracket:
+ const start = this.getTransform().transformPoint(Math.min(...ge.points.map(p => p.X)), Math.min(...ge.points.map(p => p.Y)));
+ this._inkToTextStartX = start[0];
+ this._inkToTextStartY = start[1];
+ console.log("start");
+ break;
+ case GestureUtils.Gestures.EndBracket:
+ console.log("end");
+ if (this._inkToTextStartX && this._inkToTextStartY) {
+ const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
+ const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === "text" && s.color);
+ const sets = setDocs.map((sd) => {
+ return Cast(sd.data, RichTextField)?.Text as string;
+ });
+ if (sets.length && sets[0]) {
+ this._wordPalette.clear();
+ const colors = setDocs.map(sd => FieldValue(sd.color) as string);
+ sets.forEach((st: string, i: number) => {
+ const words = st.split(",");
+ words.forEach(word => {
+ this._wordPalette.set(word, colors[i]);
+ });
+ });
+ }
+ const inks = this.getActiveDocuments().filter(doc => {
+ if (doc.type === "ink") {
+ const l = NumCast(doc.x);
+ const r = l + doc[WidthSym]();
+ const t = NumCast(doc.y);
+ const b = t + doc[HeightSym]();
+ const pass = !(this._inkToTextStartX! > r || end[0] < l || this._inkToTextStartY! > b || end[1] < t);
+ return pass;
+ }
+ return false;
+ });
+ // const inkFields = inks.map(i => Cast(i.data, InkField));
+ const strokes: InkData[] = [];
+ inks.forEach(i => {
+ const d = Cast(i.data, InkField);
+ const x = NumCast(i.x);
+ const y = NumCast(i.y);
+ const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
+ const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+ if (d) {
+ strokes.push(d.inkData.map(pd => ({ X: pd.X + x - left, Y: pd.Y + y - top })));
+ }
+ });
+
+ CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
+ console.log(results);
+ const wordResults = results.filter((r: any) => r.category === "inkWord");
+ for (const word of wordResults) {
+ const indices: number[] = word.strokeIds;
+ indices.forEach(i => {
+ const otherInks: Doc[] = [];
+ indices.forEach(i2 => i2 !== i && otherInks.push(inks[i2]));
+ inks[i].relatedInks = new List<Doc>(otherInks);
+ const uniqueColors: string[] = [];
+ Array.from(this._wordPalette.values()).forEach(c => uniqueColors.indexOf(c) === -1 && uniqueColors.push(c));
+ inks[i].alternativeColors = new List<string>(uniqueColors);
+ if (this._wordPalette.has(word.recognizedText.toLowerCase())) {
+ inks[i].color = this._wordPalette.get(word.recognizedText.toLowerCase());
+ }
+ else if (word.alternates) {
+ for (const alt of word.alternates) {
+ if (this._wordPalette.has(alt.recognizedString.toLowerCase())) {
+ inks[i].color = this._wordPalette.get(alt.recognizedString.toLowerCase());
+ break;
+ }
+ }
+ }
+ });
+ }
+ });
+ this._inkToTextStartX = end[0];
+ }
+ break;
+ case GestureUtils.Gestures.Text:
+ if (ge.text) {
+ const B = this.getTransform().transformPoint(ge.points[0].X, ge.points[0].Y);
+ this.addDocument(Docs.Create.TextDocument(ge.text, { title: ge.text, x: B[0], y: B[1] }));
+ e.stopPropagation();
+ }
}
}
@@ -429,7 +524,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
// I think it makes sense for the marquee menu to go away when panned. -syip2
- MarqueeOptionsMenu.Instance.fadeOut(true);
+ MarqueeOptionsMenu.Instance && MarqueeOptionsMenu.Instance.fadeOut(true);
let x = this.Document._panX || 0;
let y = this.Document._panY || 0;
@@ -547,7 +642,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// use the centerx and centery as the "new mouse position"
const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2;
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
- this.pan({ clientX: centerX, clientY: centerY });
+ // const transformed = this.getTransform().inverse().transformPoint(centerX, centerY);
+
+ if (!this._pullDirection) { // if we are not bezel movement
+ this.pan({ clientX: centerX, clientY: centerY });
+ } else {
+ this._pullCoords = [centerX, centerY];
+ }
+
this._lastX = centerX;
this._lastY = centerY;
}
@@ -572,6 +674,27 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
this._lastX = centerX;
this._lastY = centerY;
+ const screenBox = this._mainCont?.getBoundingClientRect();
+
+
+ // determine if we are using a bezel movement
+ if (screenBox) {
+ if ((screenBox.right - centerX) < 100) {
+ this._pullCoords = [centerX, centerY];
+ this._pullDirection = "right";
+ } else if (centerX - screenBox.left < 100) {
+ this._pullCoords = [centerX, centerY];
+ this._pullDirection = "left";
+ } else if (screenBox.bottom - centerY < 100) {
+ this._pullCoords = [centerX, centerY];
+ this._pullDirection = "bottom";
+ } else if (centerY - screenBox.top < 100) {
+ this._pullCoords = [centerX, centerY];
+ this._pullDirection = "top";
+ }
+ }
+
+
this.removeMoveListeners();
this.addMoveListeners();
this.removeEndListeners();
@@ -582,12 +705,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
cleanUpInteractions = () => {
+
+ switch (this._pullDirection) {
+
+ case "left":
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), "left", undefined);
+ break;
+ case "right":
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), "right", undefined);
+ break;
+ case "top":
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), "top", undefined);
+ break;
+ case "bottom":
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), "bottom", undefined);
+ break;
+ default:
+ break;
+ }
+ console.log("");
+
+ this._pullDirection = "";
+ this._pullCoords = [0, 0];
+
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
this.removeMoveListeners();
this.removeEndListeners();
}
+
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
let deltaScale = deltaY > 0 ? (1 / 1.1) : 1.1;
@@ -1035,6 +1182,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
</CollectionFreeFormViewPannableContents>
</MarqueeView>;
}
+
@computed get contentScaling() {
if (this.props.annotationsKey) return 0;
const hscale = this.nativeHeight ? this.props.PanelHeight() / this.nativeHeight : 1;
@@ -1043,6 +1191,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
}
render() {
TraceMobx();
+ const clientRect = this._mainCont?.getBoundingClientRect();
// update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
// this.Document.fitX = this.contentBounds && this.contentBounds.x;
// this.Document.fitY = this.contentBounds && this.contentBounds.y;
@@ -1064,7 +1213,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
{!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
- </div>;
+
+ <div className={"pullpane-indicator"}
+ style={{
+ display: this._pullDirection ? "block" : "none",
+ top: clientRect ? this._pullDirection === "bottom" ? this._pullCoords[1] - clientRect.y : 0 : "auto",
+ // left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x - MainView.Instance.flyoutWidth : 0 : "auto",
+ left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x : 0 : "auto",
+ width: clientRect ? this._pullDirection === "left" ? this._pullCoords[0] - clientRect.left : this._pullDirection === "right" ? clientRect.right - this._pullCoords[0] : clientRect.width : 0,
+ height: clientRect ? this._pullDirection === "top" ? this._pullCoords[1] - clientRect.top : this._pullDirection === "bottom" ? clientRect.bottom - this._pullCoords[1] : clientRect.height : 0,
+
+ }}>
+ </div>
+
+ </div >;
}
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 71f265484..db4b674b5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -11,6 +11,7 @@ export default class MarqueeOptionsMenu extends AntimodeMenu {
public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public inkToText: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public showMarquee: () => void = unimplementedFunction;
public hideMarquee: () => void = unimplementedFunction;
@@ -43,6 +44,13 @@ export default class MarqueeOptionsMenu extends AntimodeMenu {
onPointerDown={this.delete}>
<FontAwesomeIcon icon="trash-alt" size="lg" />
</button>,
+ <button
+ className="antimodeMenu-button"
+ title="Change to Text"
+ key="inkToText"
+ onPointerDown={this.inkToText}>
+ <FontAwesomeIcon icon="font" size="lg" />
+ </button>,
];
return this.getElement(buttons);
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3cfefc25e..d4f1a5444 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,10 +1,10 @@
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../../new_fields/Doc";
-import { InkField } from "../../../../new_fields/InkField";
+import { Doc, DocListCast, DataSym, WidthSym, HeightSym } from "../../../../new_fields/Doc";
+import { InkField, InkData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField";
-import { Cast, NumCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, FieldValue } from "../../../../new_fields/Types";
import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils";
import { Utils } from "../../../../Utils";
import { Docs, DocUtils } from "../../../documents/Documents";
@@ -17,7 +17,10 @@ import { SubCollectionViewProps } from "../CollectionSubView";
import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
import "./MarqueeView.scss";
import React = require("react");
+import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
+import { RichTextField } from "../../../../new_fields/RichTextField";
import { CollectionView } from "../CollectionView";
+import { FormattedTextBox } from "../../nodes/FormattedTextBox";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -103,8 +106,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
});
} else if (!e.ctrlKey) {
+ FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout ? e.key : "";
this.props.addLiveTextDocument(
- Docs.Create.TextDocument("", { _width: 200, _height: 100, x: x, y: y, _autoHeight: true, title: "-typed text-" }));
+ Docs.Create.TextDocument("", { _width: NumCast((FormattedTextBox.DefaultLayout as Doc)?._width) || 200, _height: 100, layout: FormattedTextBox.DefaultLayout, x: x, y: y, _autoHeight: true, title: "-typed text-" }));
} else if (e.keyCode > 48 && e.keyCode <= 57) {
const notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data);
const text = Docs.Create.TextDocument("", { _width: 200, _height: 100, x: x, y: y, _autoHeight: true, title: "-typed text-" });
@@ -207,6 +211,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.createCollection = this.collection;
MarqueeOptionsMenu.Instance.delete = this.delete;
MarqueeOptionsMenu.Instance.summarize = this.summary;
+ MarqueeOptionsMenu.Instance.inkToText = this.syntaxHighlight;
MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
@@ -302,7 +307,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.hideMarquee();
}
- getCollection = (selected: Doc[], asTemplate: boolean) => {
+ getCollection = (selected: Doc[], asTemplate: boolean, isBackground: boolean = false) => {
const bounds = this.Bounds;
// const inkData = this.ink ? this.ink.inkData : undefined;
const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument;
@@ -311,7 +316,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
y: bounds.top,
_panX: 0,
_panY: 0,
- backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : undefined,
+ isBackground,
+ backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined,
_width: bounds.width,
_height: bounds.height,
_LODdisable: true,
@@ -345,6 +351,85 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
@action
+ syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ const selected = this.marqueeSelect(false);
+ if (e instanceof KeyboardEvent ? e.key === "i" : true) {
+ const inks = selected.filter(s => s.proto?.type === "ink");
+ const setDocs = selected.filter(s => s.proto?.type === "text" && s.color);
+ const sets = setDocs.map((sd) => {
+ return Cast(sd.data, RichTextField)?.Text as string;
+ });
+ const colors = setDocs.map(sd => FieldValue(sd.color) as string);
+ const wordToColor = new Map<string, string>();
+ sets.forEach((st: string, i: number) => {
+ const words = st.split(",");
+ words.forEach(word => {
+ wordToColor.set(word, colors[i]);
+ });
+ });
+ const strokes: InkData[] = [];
+ inks.forEach(i => {
+ const d = Cast(i.data, InkField);
+ const x = NumCast(i.x);
+ const y = NumCast(i.y);
+ const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
+ const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+ if (d) {
+ strokes.push(d.inkData.map(pd => ({ X: pd.X + x - left, Y: pd.Y + y - top })));
+ }
+ });
+ CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
+ // const wordResults = results.filter((r: any) => r.category === "inkWord");
+ // console.log(wordResults);
+ // console.log(results);
+ // for (const word of wordResults) {
+ // const indices: number[] = word.strokeIds;
+ // indices.forEach(i => {
+ // if (wordToColor.has(word.recognizedText.toLowerCase())) {
+ // inks[i].color = wordToColor.get(word.recognizedText.toLowerCase());
+ // }
+ // else {
+ // for (const alt of word.alternates) {
+ // if (wordToColor.has(alt.recognizedString.toLowerCase())) {
+ // inks[i].color = wordToColor.get(alt.recognizedString.toLowerCase());
+ // break;
+ // }
+ // }
+ // }
+ // })
+ // }
+ // const wordResults = results.filter((r: any) => r.category === "inkWord");
+ // for (const word of wordResults) {
+ // const indices: number[] = word.strokeIds;
+ // indices.forEach(i => {
+ // const otherInks: Doc[] = [];
+ // indices.forEach(i2 => i2 !== i && otherInks.push(inks[i2]));
+ // inks[i].relatedInks = new List<Doc>(otherInks);
+ // const uniqueColors: string[] = [];
+ // Array.from(wordToColor.values()).forEach(c => uniqueColors.indexOf(c) === -1 && uniqueColors.push(c));
+ // inks[i].alternativeColors = new List<string>(uniqueColors);
+ // if (wordToColor.has(word.recognizedText.toLowerCase())) {
+ // inks[i].color = wordToColor.get(word.recognizedText.toLowerCase());
+ // }
+ // else if (word.alternates) {
+ // for (const alt of word.alternates) {
+ // if (wordToColor.has(alt.recognizedString.toLowerCase())) {
+ // inks[i].color = wordToColor.get(alt.recognizedString.toLowerCase());
+ // break;
+ // }
+ // }
+ // }
+ // });
+ // }
+ const lines = results.filter((r: any) => r.category === "line");
+ console.log(lines);
+ const text = lines.map((l: any) => l.recognizedText).join("\r\n");
+ this.props.addDocument(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
+ });
+ }
+ }
+
+ @action
summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
const bounds = this.Bounds;
const selected = this.marqueeSelect(false);
@@ -366,6 +451,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
}
+ @action
+ background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ const newCollection = this.getCollection([], false, true);
+ this.props.addDocument(newCollection);
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ this.hideMarquee();
+ setTimeout(() => this.props.selectDocuments([newCollection], []), 0);
+ }
@undoBatch
@action
@@ -380,7 +473,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.delete();
e.stopPropagation();
}
- if (e.key === "c" || e.key === "t" || e.key === "s" || e.key === "S") {
+ if (e.key === "c" || e.key === "b" || e.key === "t" || e.key === "s" || e.key === "S") {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
@@ -388,10 +481,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (e.key === "c" || e.key === "t") {
this.collection(e);
}
-
if (e.key === "s" || e.key === "S") {
this.summary(e);
}
+ if (e.key === "b") {
+ this.background(e);
+ }
this.cleanupInteractions(false);
}
}