aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/CollectionView.tsx37
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss104
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx158
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.scss156
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx423
-rw-r--r--src/client/views/collections/collectionGrid/Grid.tsx56
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx42
-rw-r--r--src/client/views/nodes/DocumentView.tsx6
8 files changed, 951 insertions, 31 deletions
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a25a864af..524997d20 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -42,6 +42,7 @@ import { CollectionStaffView } from './CollectionStaffView';
import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
+import { CollectionGridView } from './collectionGrid/CollectionGridView';
import './CollectionView.scss';
import { CollectionViewBaseChrome } from './CollectionViewChromes';
const higflyout = require("@hig/flyout");
@@ -67,6 +68,7 @@ export enum CollectionViewType {
Linear = "linear",
Staff = "staff",
Map = "map",
+ Grid = "grid",
Pile = "pileup"
}
export interface CollectionViewCustomProps {
@@ -91,7 +93,7 @@ export interface CollectionRenderProps {
export class CollectionView extends Touchable<FieldViewProps & CollectionViewCustomProps> {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
- private _isChildActive = false; //TODO should this be observable?
+ _isChildActive = false; //TODO should this be observable?
get _isLightboxOpen() { return BoolCast(this.props.Document.isLightboxOpen); }
set _isLightboxOpen(value) { this.props.Document.isLightboxOpen = value; }
@observable private _curLightboxImg = 0;
@@ -194,6 +196,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (<CollectionStackingView key="collview" {...props} />); }
case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); }
case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />);
+ case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />);
case CollectionViewType.Freeform:
default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} />); }
}
@@ -231,6 +234,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+ subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) {
subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
}
@@ -240,6 +244,37 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
onContextMenu = (e: React.MouseEvent): void => {
if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ const existingVm = ContextMenu.Instance.findByDescription("View Modes...");
+ const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
+ subItems.push({ description: "Freeform", event: () => { this.props.Document._viewType = CollectionViewType.Freeform; }, icon: "signature" });
+ if (CollectionView._safeMode) {
+ ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document._viewType = CollectionViewType.Invalid, icon: "project-diagram" });
+ }
+ subItems.push({ description: "Schema", event: () => this.props.Document._viewType = CollectionViewType.Schema, icon: "th-list" });
+ subItems.push({ description: "Treeview", event: () => this.props.Document._viewType = CollectionViewType.Tree, icon: "tree" });
+ subItems.push({ description: "Stacking", event: () => this.props.Document._viewType = CollectionViewType.Stacking, icon: "ellipsis-v" });
+ subItems.push({
+ description: "Stacking (AutoHeight)", event: () => {
+ this.props.Document._viewType = CollectionViewType.Stacking;
+ this.props.Document._autoHeight = true;
+ }, icon: "ellipsis-v"
+ });
+ subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" });
+ subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" });
+ subItems.push({ description: "Multirow", event: () => this.props.Document._viewType = CollectionViewType.Multirow, icon: "columns" });
+ subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" });
+ subItems.push({ description: "Carousel", event: () => this.props.Document._viewType = CollectionViewType.Carousel, icon: "columns" });
+ subItems.push({ description: "Pivot/Time", event: () => this.props.Document._viewType = CollectionViewType.Time, icon: "columns" });
+ subItems.push({ description: "Map", event: () => this.props.Document._viewType = CollectionViewType.Map, icon: "globe-americas" });
+ subItems.push({ description: "Grid", event: () => this.props.Document._viewType = CollectionViewType.Grid, icon: "th-list" });
+ switch (this.props.Document._viewType) {
+ case CollectionViewType.Freeform: {
+ subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) });
+ break;
+ }
+ }
+ subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" });
+ !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" });
this.setupViewTypes("Add a Perspective...", vtype => {
const newRendition = Doc.MakeAlias(this.props.Document);
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 03bd9a01a..bfa20f42a 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -3,7 +3,7 @@
.collectionViewChrome-cont {
position: absolute;
- width:100%;
+ width: 100%;
opacity: 0.9;
z-index: 9001;
transition: top .5s;
@@ -13,7 +13,7 @@
.collectionViewChrome {
display: flex;
padding-bottom: 1px;
- height:32px;
+ height: 32px;
border-bottom: .5px solid rgb(180, 180, 180);
overflow: hidden;
@@ -35,7 +35,7 @@
outline-color: black;
}
- .collectionViewBaseChrome-button{
+ .collectionViewBaseChrome-button {
font-size: 75%;
text-transform: uppercase;
letter-spacing: 2px;
@@ -46,6 +46,7 @@
padding: 12px 10px 11px 10px;
margin-left: 10px;
}
+
.collectionViewBaseChrome-cmdPicker {
margin-left: 3px;
margin-right: 0px;
@@ -54,15 +55,17 @@
border: none;
color: grey;
}
+
.commandEntry-outerDiv {
pointer-events: all;
background-color: gray;
display: flex;
flex-direction: row;
- height:30px;
+ height: 30px;
+
.commandEntry-drop {
- color:white;
- width:25px;
+ color: white;
+ width: 25px;
margin-top: auto;
margin-bottom: auto;
}
@@ -76,15 +79,17 @@
pointer-events: all;
// margin-top: 10px;
}
+
.collectionViewBaseChrome-template,
.collectionViewBaseChrome-viewModes {
display: grid;
background: rgb(238, 238, 238);
- color:grey;
- margin-top:auto;
- margin-bottom:auto;
+ color: grey;
+ margin-top: auto;
+ margin-bottom: auto;
margin-left: 5px;
}
+
.collectionViewBaseChrome-viewModes {
margin-left: 25px;
}
@@ -92,7 +97,7 @@
.collectionViewBaseChrome-viewSpecs {
margin-left: 5px;
display: grid;
-
+
.collectionViewBaseChrome-filterIcon {
position: relative;
display: flex;
@@ -163,13 +168,54 @@
}
}
-
.collectionStackingViewChrome-cont,
.collectionTreeViewChrome-cont {
display: flex;
justify-content: space-between;
}
+ .collectionGridViewChrome-cont {
+ display: flex;
+ margin-left: 10;
+
+ .collectionGridViewChrome-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;
+ }
+
+ .collectionGridViewChrome-viewPicker:active {
+ outline-color: black;
+ }
+
+ .grid-control {
+ align-self: center;
+ width: 30%;
+ display: flex;
+ flex-direction: row;
+ margin-right: 5px;
+
+ .grid-icon {
+ margin-right: 5px;
+ align-self: center;
+ }
+
+ .flexLabel {
+ margin-bottom: 0;
+ }
+ }
+
+ .collectionGridViewChrome-entryBox {
+ width: 50%;
+ }
+ }
+
+
.collectionStackingViewChrome-sort,
.collectionTreeViewChrome-sort {
display: flex;
@@ -199,13 +245,13 @@
.collectionTreeViewChrome-pivotField-label {
vertical-align: center;
padding-left: 10px;
- margin:auto;
+ margin: auto;
}
.collectionStackingViewChrome-pivotField,
.collectionTreeViewChrome-pivotField {
color: white;
- width:100%;
+ width: 100%;
min-width: 100px;
display: flex;
align-items: center;
@@ -215,7 +261,7 @@
input,
.editableView-container-editing-oneLine,
.editableView-container-editing {
- margin:auto;
+ margin: auto;
border: 0px;
color: grey;
text-align: center;
@@ -236,6 +282,7 @@
.collectionTreeViewChrome-pivotField:hover {
cursor: text;
}
+
}
}
@@ -244,7 +291,10 @@
display: flex;
position: relative;
align-items: center;
- .fwdKeyframe, .numKeyframe, .backKeyframe {
+
+ .fwdKeyframe,
+ .numKeyframe,
+ .backKeyframe {
cursor: pointer;
position: absolute;
width: 20;
@@ -253,26 +303,31 @@
background: gray;
display: flex;
align-items: center;
- color:white;
+ color: white;
}
+
.backKeyframe {
- left:0;
+ left: 0;
+
svg {
- display:block;
- margin:auto;
+ display: block;
+ margin: auto;
}
}
+
.numKeyframe {
- left:20;
+ left: 20;
display: flex;
flex-direction: column;
padding: 5px;
}
+
.fwdKeyframe {
- left:40;
+ left: 40;
+
svg {
- display:block;
- margin:auto;
+ display: block;
+ margin: auto;
}
}
}
@@ -334,8 +389,9 @@
flex-direction: column;
height: 40px;
}
+
.commandEntry-inputArea {
- display:flex;
+ display: flex;
flex-direction: row;
width: 150px;
margin: auto auto auto auto;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 3dc740c25..7654c9d9e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
-import { Doc, DocListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
@@ -201,6 +201,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
+ case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />);
default: return null;
}
}
@@ -562,3 +563,158 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewChro
}
}
+/**
+ * Chrome for grid view.
+ */
+@observer
+export class CollectionGridViewChrome extends React.Component<CollectionViewChromeProps> {
+
+ private clicked: boolean = false;
+ private entered: boolean = false;
+ private decrementLimitReached: boolean = false;
+
+ /**
+ * Sets the value of `numCols` on the grid's Document to the value entered.
+ */
+ @undoBatch
+ onNumColsEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ if (e.key === "Enter" || e.key === "Tab") {
+ if (e.currentTarget.valueAsNumber > 0 && this.props.CollectionView.props.Document.numCols as number !== e.currentTarget.valueAsNumber) {
+ this.props.CollectionView.props.Document.numCols = e.currentTarget.valueAsNumber;
+ }
+
+ }
+ }
+
+ /**
+ * Sets the value of `rowHeight` on the grid's Document to the value entered.
+ */
+ // @undoBatch
+ // onRowHeightEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ // if (e.key === "Enter" || e.key === "Tab") {
+ // if (e.currentTarget.valueAsNumber > 0 && this.props.CollectionView.props.Document.rowHeight as number !== e.currentTarget.valueAsNumber) {
+ // this.props.CollectionView.props.Document.rowHeight = e.currentTarget.valueAsNumber;
+ // }
+ // }
+ // }
+
+ /**
+ * Sets whether the grid is flexible or not on the grid's Document.
+ */
+ toggleFlex = () => {
+ this.props.CollectionView.props.Document.flexGrid = !this.props.CollectionView.props.Document.flexGrid;
+ }
+
+ /**
+ * Increments the value of numCols on button click
+ */
+ onIncrementButtonClick = () => {
+ this.clicked = true;
+ this.entered && (this.props.CollectionView.props.Document.numCols as number)--;
+ undoBatch(() => (this.props.CollectionView.props.Document.numCols as number)++)();
+ this.entered = false;
+ }
+
+ /**
+ * Decrements the value of numCols on button click
+ */
+ onDecrementButtonClick = () => {
+ this.clicked = true;
+ if (!this.decrementLimitReached) {
+ this.entered && (this.props.CollectionView.props.Document.numCols as number)++;
+ undoBatch(() => (this.props.CollectionView.props.Document.numCols as number)--)();
+ }
+ this.entered = false;
+ }
+
+ /**
+ * Increments the value of numCols on button hover
+ */
+ incrementValue = () => {
+ this.entered = true;
+ if (!this.clicked && !this.decrementLimitReached) {
+ (this.props.CollectionView.props.Document.numCols as number)++;
+ }
+ this.decrementLimitReached = false;
+ this.clicked = false;
+ }
+
+ /**
+ * Decrements the value of numCols on button hover
+ */
+ decrementValue = () => {
+ this.entered = true;
+ if (!this.clicked) {
+ if (this.props.CollectionView.props.Document.numCols as number !== 1) {
+ (this.props.CollectionView.props.Document.numCols as number)--;
+ }
+ else {
+ this.decrementLimitReached = true;
+ }
+ }
+
+ this.clicked = false;
+ }
+
+ /**
+ * Toggles the value of preventCollision
+ */
+ toggleCollisions = () => {
+ this.props.CollectionView.props.Document.preventCollision = !this.props.CollectionView.props.Document.preventCollision;
+ }
+
+ /**
+ * Changes the value of the compactType
+ */
+ changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => {
+ this.props.CollectionView.props.Document.compactType = e.target.selectedOptions[0].value;
+ }
+
+ render() {
+ return (
+ <div className="collectionGridViewChrome-cont" >
+ <span className="grid-control">
+ <span className="grid-icon">
+ <FontAwesomeIcon icon="columns" size="1x" />
+ </span>
+ <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.numCols as string} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+ <input className="columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
+ <input className="columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
+ </span>
+ {/* <span className="grid-control">
+ <span className="grid-icon">
+ <FontAwesomeIcon icon="text-height" size="1x" />
+ </span>
+ <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+ </span> */}
+ <span className="grid-control" style={{ width: "20%" }}>
+ <input type="checkbox" style={{ marginRight: 5 }} onClick={this.toggleCollisions} defaultChecked={!this.props.CollectionView.props.Document.preventCollision} />
+ <label className="flexLabel">Collisions</label>
+ </span>
+
+ <select className="collectionGridViewChrome-viewPicker"
+ style={{ marginRight: 5 }}
+ onPointerDown={stopPropagation}
+ onChange={this.changeCompactType}
+ value={StrCast(this.props.CollectionView.props.Document.compactType)}>
+ >
+ {["vertical", "horizontal", "null"].map(type =>
+ <option className="collectionGridViewChrome-viewOption"
+ onPointerDown={stopPropagation}
+ value={type}>
+ {"Compact: " + type}
+ </option>
+ )}
+ </select>
+
+ <span className="grid-control">
+ <input style={{ marginRight: 5 }} type="checkbox" onClick={this.toggleFlex} defaultChecked={this.props.CollectionView.props.Document.flexGrid as boolean} />
+ <label className="flexLabel">Flexible</label>
+ </span>
+
+ <button onClick={() => this.props.CollectionView.props.Document.resetLayout = true}>Reset</button>
+
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
new file mode 100644
index 000000000..de7d62475
--- /dev/null
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -0,0 +1,156 @@
+.collectionGridView-contents {
+ display: flex;
+ overflow: hidden;
+ width: 100%;
+ height: 100%;
+ flex-direction: column;
+
+ .collectionGridView-gridContainer {
+ height: 100%;
+ overflow-y: auto;
+ background-color: white;
+ overflow-x: hidden;
+
+ display: flex;
+ flex-direction: row;
+
+ .imageBox-cont img {
+ height: auto;
+ width: auto;
+ max-height: 100%;
+ max-width: 100%;
+ }
+
+ .react-grid-item>.react-resizable-handle {
+ z-index: 4; // doesn't work on prezi otherwise
+ }
+
+ .react-grid-item>.react-resizable-handle::after {
+ // grey so it can be seen on the audiobox
+ border-right: 2px solid slategrey;
+ border-bottom: 2px solid slategrey;
+ }
+
+ .rowHeightSlider {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 100%;
+ height: 15px;
+ background: #d3d3d3;
+
+ position: absolute;
+ height: 3;
+ left: 5;
+ top: 40;
+ transform-origin: left;
+ transform: rotate(90deg);
+ outline: none;
+ opacity: 0.7;
+ }
+
+ .rowHeightSlider:hover {
+ opacity: 1;
+ }
+
+ .rowHeightSlider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: darkgrey;
+ opacity: 1;
+ }
+
+ .rowHeightSlider::-moz-range-thumb {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background: darkgrey;
+ opacity: 1;
+ }
+ }
+
+ .collectionGridView-addDocumentButton {
+ display: flex;
+ overflow: hidden;
+ margin: auto;
+ width: 90%;
+ cursor: text;
+ min-height: 30px;
+ max-height: 30px;
+ font-size: 75%;
+ letter-spacing: 2px;
+
+ .editableView-input {
+ outline-color: black;
+ letter-spacing: 2px;
+ color: grey;
+ border: 0px;
+ padding: 12px 10px 11px 10px;
+ }
+
+ .editableView-container-editing,
+ .editableView-container-editing-oneLine {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ height: 20px;
+
+ width: 100%;
+ color: grey;
+ padding: 10px;
+
+ span::before,
+ span::after {
+ content: "";
+ width: 50%;
+ position: relative;
+ display: inline-block;
+ }
+
+ span::before {
+ margin-right: 10px;
+ }
+
+ span::after {
+ margin-left: 10px;
+ }
+
+ span {
+ position: relative;
+ text-align: center;
+ white-space: nowrap;
+ overflow: visible;
+ display: flex;
+ color: gray;
+ align-items: center;
+ }
+ }
+ }
+
+}
+
+// .documentDecorations-container .documentDecorations-resizer {
+// pointer-events: none;
+// }
+
+// #documentDecorations-bottomRightResizer,
+// #documentDecorations-bottomLeftResizer,
+// #documentDecorations-topRightResizer,
+// #documentDecorations-topLeftResizer {
+// visibility: collapse;
+// }
+
+
+/* Chrome, Safari, Edge, Opera */
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+/* Firefox */
+input[type=number] {
+ -moz-appearance: textfield;
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
new file mode 100644
index 000000000..a820b7e0e
--- /dev/null
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -0,0 +1,423 @@
+import { computed, observable, Lambda, action, reaction } from 'mobx';
+import * as React from "react";
+import { Doc, Opt } from '../../../../fields/Doc';
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, StrCast, ScriptCast } from '../../../../fields/Types';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
+import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView';
+import { CollectionSubView } from '../CollectionSubView';
+import { SubCollectionViewProps } from '../CollectionSubView';
+import { returnZero } from '../../../../Utils';
+import Grid, { Layout } from "./Grid";
+import { Id } from '../../../../fields/FieldSymbols';
+import { observer } from 'mobx-react';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Docs } from '../../../documents/Documents';
+import { EditableView, EditableProps } from '../../EditableView';
+import "./CollectionGridView.scss";
+import { ContextMenu } from '../../ContextMenu';
+import { List } from '../../../../fields/List';
+import { ContextMenuProps } from '../../ContextMenuItem';
+
+
+type GridSchema = makeInterface<[typeof documentSchema]>;
+const GridSchema = makeInterface(documentSchema);
+
+@observer
+export class CollectionGridView extends CollectionSubView(GridSchema) {
+ private containerRef: React.RefObject<HTMLDivElement>;
+ @observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
+ private changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
+ private rowHeight: number = 0; // temporary store of row height to make change undoable
+ private mounted: boolean = false; // hack to fix the issue of not rerendering when mounting
+ private resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
+
+ constructor(props: Readonly<SubCollectionViewProps>) {
+ super(props);
+
+ this.props.Document.numCols = NumCast(this.props.Document.numCols, 10);
+ this.props.Document.rowHeight = NumCast(this.props.Document.rowHeight, 100);
+
+ // determines whether the grid is static/flexible i.e. whether can nodes be moved around and resized or not
+ this.props.Document.flexGrid = BoolCast(this.props.Document.flexGrid, true);
+
+ // determines whether nodes should remain in position, be bound to the top, or to the left
+ this.props.Document.compactType = StrCast(this.props.Document.compactType, "vertical");
+
+ // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
+ this.props.Document.preventCollision = BoolCast(this.props.Document.preventCollision, false);
+
+ // sets the default width and height of the grid nodes
+ this.props.Document.defaultW = NumCast(this.props.Document.defaultW, 2);
+ this.props.Document.defaultH = NumCast(this.props.Document.defaultH, 2);
+
+ // sets the margin between grid nodes
+ this.props.Document.margin = NumCast(this.props.Document.margin, 10);
+
+ // sets the css display type of the ContentFittingDocumentView component
+ this.props.Document.display = StrCast(this.props.Document.display, "contents");
+
+ this.setLayout = this.setLayout.bind(this);
+ this.onSliderChange = this.onSliderChange.bind(this);
+ this.onContextMenu = this.onContextMenu.bind(this);
+
+ this.containerRef = React.createRef();
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+
+ this.changeListenerDisposer = computed(() => this.childLayoutPairs).observe(({ oldValue, newValue }) => {
+
+ const layouts: Layout[] = this.parsedLayoutList;
+
+ // if grid view has been opened and then exited and a document has been deleted
+ // this deletes the layout of that document from the layouts list
+
+ if (!oldValue && newValue.length) {
+ layouts.forEach(({ i }, index) => {
+ const targetId = i;
+ if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) {
+ layouts.splice(index, 1);
+ }
+ });
+ }
+
+ if (!oldValue || newValue.length > oldValue.length) {
+ // for each document that was added, add a corresponding grid layout object
+ newValue.forEach(({ layout }, i) => {
+ const targetId = layout[Id];
+ if (!layouts.find((gridLayout: Layout) => gridLayout.i === targetId)) {
+ layouts.push({
+ i: targetId,
+ w: this.defaultW,
+ h: this.defaultH,
+ x: this.defaultW * (i % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW)),
+ y: this.defaultH * Math.floor(i / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH)),
+ static: !this.props.Document.flexGrid
+ });
+ }
+ });
+ } else {
+ // for each document that was removed, remove its corresponding grid layout object
+ oldValue.forEach(({ layout }) => {
+ const targetId = layout[Id];
+ if (!newValue.find(({ layout: preserved }) => preserved[Id] === targetId)) {
+ const index = layouts.findIndex((gridLayout: Layout) => gridLayout.i === targetId);
+ index !== -1 && layouts.splice(index, 1);
+ }
+ });
+ }
+ this.unStringifiedLayoutList = layouts;
+ }, true);
+
+ // updates the layouts if the reset button has been clicked
+ this.resetListenerDisposer = reaction(() => this.props.Document.resetLayout, () => {
+ if (this.props.Document.flexGrid) {
+ const layouts: Layout[] = this.parsedLayoutList;
+ this.setLayout(
+ layouts.map(({ i }, index) => ({
+ i: i,
+ x: this.defaultW * (index % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW)),
+ y: this.defaultH * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH)),
+ w: this.defaultW,
+ h: this.defaultH,
+ })));
+ }
+ this.props.Document.resetLayout = false;
+ });
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ this.changeListenerDisposer && this.changeListenerDisposer();
+ this.resetListenerDisposer?.();
+ }
+
+ /**
+ * @returns the transform that will correctly place the document decorations box.
+ */
+ private lookupIndividualTransform = (layout: Layout) => {
+ const index = this.childLayoutPairs.findIndex(({ layout: layoutDoc }) => layoutDoc[Id] === layout.i);
+
+ // translations depend on whether the grid is flexible or static
+ const xTranslation = (this.props.Document.flexGrid ? NumCast(layout.x) : this.defaultW * (index % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW))) * this.colWidthPlusGap + this.margin;
+ const yTranslation = (this.props.Document.flexGrid ? NumCast(layout.y) : this.defaultH * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH))) * this.rowHeightPlusGap + this.margin - this._scroll + 30; // 30 is the height of the add text doc bar
+
+ return this.props.ScreenToLocalTransform().translate(-xTranslation, -yTranslation);
+ }
+
+ @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); }
+
+ addDocTab = (doc: Doc, where: string) => {
+ if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+ this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
+ return true;
+ }
+ return this.props.addDocTab(doc, where);
+ }
+
+ @computed get colWidthPlusGap() { return (this.props.PanelWidth() - this.margin) / NumCast(this.props.Document.numCols); }
+ @computed get rowHeightPlusGap() { return NumCast(this.props.Document.rowHeight) + this.margin; }
+
+ @computed get margin() { return NumCast(this.props.Document.margin); }
+ @computed get defaultW() { return NumCast(this.props.Document.defaultW); }
+ @computed get defaultH() { return NumCast(this.props.Document.defaultH); }
+
+ /**
+ * @returns the layout list converted from JSON
+ */
+ get parsedLayoutList() {
+ return this.props.Document.gridLayoutString ? JSON.parse(StrCast(this.props.Document.gridLayoutString)) : [];
+ }
+
+ /**
+ * Stores the layout list on the Document as JSON
+ */
+ set unStringifiedLayoutList(layouts: Layout[]) {
+
+ // sometimes there are issues with rendering when you switch from a different view
+ // where the nodes are all squeezed together on the left hand side of the screen
+ // until you click on the screen or close the chrome or interact with it in some way
+ // the component doesn't rerender when the component mounts
+ // this seems to fix that though it isn't very elegant
+
+ this.mounted && (this.props.Document.gridLayoutString = "");
+ this.props.Document.gridLayoutString = JSON.stringify(layouts);
+ this.mounted = false;
+ }
+
+
+ /**
+ * Sets the width of the decorating box.
+ * @param layout
+ */
+ @observable private width = (layout: Layout) => (this.props.Document.flexGrid ? layout.w : this.defaultW) * this.colWidthPlusGap - this.margin;
+
+ /**
+ * Sets the height of the decorating box.
+ * @param layout
+ */
+ @observable private height = (layout: Layout) => (this.props.Document.flexGrid ? layout.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
+
+ /**
+ *
+ * @param layout
+ * @param dxf the x- and y-translations of the decorations box as a transform i.e. this.lookupIndividualTransform
+ * @param width
+ * @param height
+ * @returns the `ContentFittingDocumentView` of the node
+ */
+ getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
+ return <ContentFittingDocumentView
+ {...this.props}
+ Document={layout}
+ DataDoc={layout.resolvedDataDoc as Doc}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ addDocTab={this.addDocTab}
+ backgroundColor={this.props.backgroundColor}
+ ContainingCollectionDoc={this.props.Document}
+ PanelWidth={width}
+ PanelHeight={height}
+ ScreenToLocalTransform={dxf}
+ onClick={this.onChildClickHandler}
+ renderDepth={this.props.renderDepth + 1}
+ parentActive={this.props.active}
+ display={StrCast(this.props.Document.display)}
+ />;
+ }
+
+ /**
+ * Saves the layouts received from the Grid to the Document.
+ * @param layouts `Layout[]`
+ */
+ @undoBatch
+ @action
+ setLayout(layoutArray: Layout[]) {
+ // for every child in the collection, check to see if there's a corresponding grid layout object and
+ // updated layout object. If both exist, which they should, update the grid layout object from the updated object
+
+ if (this.props.Document.flexGrid) {
+ const layouts: Layout[] = this.parsedLayoutList;
+ this.childLayoutPairs.forEach(({ layout: doc }) => {
+ let update: Opt<Layout>;
+ const targetId = doc[Id];
+ const gridLayout = layouts.find(gridLayout => gridLayout.i === targetId);
+ if (gridLayout && (update = layoutArray.find(layout => layout.i === targetId))) {
+ gridLayout.x = update.x;
+ gridLayout.y = update.y;
+ gridLayout.w = update.w;
+ gridLayout.h = update.h;
+ }
+ });
+
+ this.unStringifiedLayoutList = layouts;
+ }
+ }
+
+ /**
+ * @returns a list of `ContentFittingDocumentView`s inside wrapper divs.
+ * The key of the wrapper div must be the same as the `i` value of the corresponding layout.
+ */
+ @computed
+ private get contents(): JSX.Element[] {
+
+ const { childLayoutPairs } = this;
+ const collector: JSX.Element[] = [];
+ const layouts: Layout[] = this.parsedLayoutList;
+ if (!layouts.length || layouts.length !== childLayoutPairs.length) {
+ return [];
+ }
+
+ for (let i = 0; i < childLayoutPairs.length; i++) {
+ const { layout } = childLayoutPairs[i];
+ const gridLayout = layouts[i];
+ const dxf = () => this.lookupIndividualTransform(gridLayout);
+ const width = () => this.width(gridLayout);
+ const height = () => this.height(gridLayout);
+ collector.push(
+ <div className={this.props.Document.flexGrid && (this.props.isSelected() ? true : false) ? "document-wrapper" : "document-wrapper static"}
+ key={gridLayout.i}
+ >
+ {this.getDisplayDoc(layout, dxf, width, height)}
+ </div >
+ );
+ }
+ return collector;
+ }
+
+ /**
+ * @returns a list of `Layout` objects with attributes depending on whether the grid is flexible or static
+ */
+ get layoutList(): Layout[] {
+
+ const layouts: Layout[] = this.parsedLayoutList;
+
+ return this.props.Document.flexGrid ?
+ layouts.map(({ i, x, y, w, h }) => ({
+ i: i,
+ x: x + w > NumCast(this.props.Document.numCols) ? 0 : x, // handles wrapping around of nodes when numCols decreases
+ y: y,
+ w: w > NumCast(this.props.Document.numCols) ? NumCast(this.props.Document.numCols) : w, // reduces width if greater than numCols
+ h: h,
+ static: BoolCast(this.childLayoutPairs.find(({ layout }) => layout[Id] === i)?.layout.lockedPosition, false) // checks if the lock position item has been selected in the context menu
+ }))
+ : layouts.map(({ i }, index) => ({
+ i: i,
+ x: this.defaultW * (index % Math.floor(NumCast(this.props.Document.numCols) / this.defaultW)),
+ y: this.defaultH * Math.floor(index / Math.floor(NumCast(this.props.Document.numCols) / this.defaultH)),
+ w: this.defaultW,
+ h: this.defaultH,
+ static: true
+ }));
+ }
+
+ /**
+ * Handles the change in the value of the rowHeight slider.
+ */
+ onSliderChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ this.props.Document.rowHeight = event.currentTarget.valueAsNumber;
+ }
+
+ /**
+ * Saves the rowHeight in a temporary variable to make it undoable later.
+ */
+ onSliderDown = () => {
+ this.rowHeight = NumCast(this.props.Document.rowHeight);
+ }
+
+ /**
+ * Uses the stored rowHeight to make the rowHeight change undoable.
+ */
+ onSliderUp = () => {
+ const tempVal = this.props.Document.rowHeight;
+ this.props.Document.rowHeight = this.rowHeight;
+ undoBatch(() => this.props.Document.rowHeight = tempVal)();
+ }
+
+ /**
+ * Creates a text document and adds it to the grid.
+ */
+ @undoBatch @action addTextDocument = (value: string) => this.props.addDocument(Docs.Create.TextDocument(value, { title: value }));
+
+ /**
+ * Adds the display option to change the css display attribute of the `ContentFittingDocumentView`s
+ */
+ onContextMenu = () => {
+ const displayOptionsMenu: ContextMenuProps[] = [];
+ displayOptionsMenu.push({ description: "Contents", event: () => this.props.Document.display = "contents", icon: "copy" });
+ displayOptionsMenu.push({ description: "Undefined", event: () => this.props.Document.display = undefined, icon: "exclamation" });
+
+ ContextMenu.Instance.addItem({ description: "Display", subitems: displayOptionsMenu, icon: "tv" });
+ }
+
+ render() {
+
+ // for the add text document EditableView
+ const newEditableViewProps: EditableProps = {
+ GetValue: () => "",
+ SetValue: this.addTextDocument,
+ contents: "+ ADD TEXT DOCUMENT",
+ };
+
+ const childDocumentViews: JSX.Element[] = this.contents;
+ const chromeStatus = this.props.Document._chromeStatus;
+ const showChrome = (chromeStatus !== 'view-mode' && chromeStatus !== 'disabled');
+
+ return (
+ <div className="collectionGridView-contents"
+ style={{
+ pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined
+ }}
+ onContextMenu={this.onContextMenu}
+ ref={this.createDashEventsTarget}
+ onPointerDown={e => {
+ if (this.props.active(true)) {
+ if (this.props.isSelected(true)) {
+ e.stopPropagation();
+ }
+ }
+ // is the following section needed? it prevents the slider from being easily used and I'm not sure what it's preventing
+
+ // if (this.props.isSelected(true)) {
+ // !((e.target as any)?.className.includes("react-resizable-handle")) && e.preventDefault();
+ // }
+
+ }} // the grid doesn't stopPropagation when its widgets are hit, so we need to otherwise the outer documents will respond
+ >
+ {showChrome ?
+ <div className="collectionGridView-addDocumentButton">
+ <EditableView {...newEditableViewProps} />
+ </div> : null
+ }
+ <div className="collectionGridView-gridContainer"
+ ref={this.containerRef}
+ onScroll={action(e => {
+ if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
+ else this._scroll = e.currentTarget.scrollTop;
+ })}
+ onWheel={e => e.stopPropagation()}
+ >
+ <input className="rowHeightSlider" type="range" value={NumCast(this.props.Document.rowHeight)} onPointerDown={this.onSliderDown} onPointerUp={this.onSliderUp} onChange={this.onSliderChange} style={{ width: this.props.PanelHeight() - 40 }} min={1} max={this.props.PanelHeight() - 40} />
+ <Grid
+ width={this.props.PanelWidth()}
+ nodeList={childDocumentViews.length ? childDocumentViews : null}
+ layout={childDocumentViews.length ? this.layoutList : undefined}
+ childrenDraggable={this.props.isSelected() ? true : false}
+ numCols={NumCast(this.props.Document.numCols)}
+ rowHeight={NumCast(this.props.Document.rowHeight)}
+ setLayout={this.setLayout}
+ transformScale={this.props.ScreenToLocalTransform().Scale}
+ compactType={StrCast(this.props.Document.compactType)}
+ preventCollision={BoolCast(this.props.Document.preventCollision)}
+ margin={this.margin}
+ />
+
+ </div>
+ </div >
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
new file mode 100644
index 000000000..6232fca7c
--- /dev/null
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -0,0 +1,56 @@
+import * as React from 'react';
+import { observer } from "mobx-react";
+
+
+import "../../../../../node_modules/react-grid-layout/css/styles.css";
+import "../../../../../node_modules/react-resizable/css/styles.css";
+
+import * as GridLayout from 'react-grid-layout';
+import { Layout } from 'react-grid-layout';
+export { Layout } from 'react-grid-layout';
+
+
+interface GridProps {
+ width: number;
+ nodeList: JSX.Element[] | null;
+ layout: Layout[] | undefined;
+ numCols: number;
+ rowHeight: number;
+ setLayout: (layout: Layout[]) => void;
+ transformScale: number;
+ childrenDraggable: boolean;
+ preventCollision: boolean;
+ compactType: string;
+ margin: number;
+}
+
+/**
+ * Wrapper around the actual GridLayout of `react-grid-layout`.
+ */
+@observer
+export default class Grid extends React.Component<GridProps> {
+
+ render() {
+ const compactType = this.props.compactType === "vertical" || this.props.compactType === "horizontal" ? this.props.compactType : null;
+ return (
+ <GridLayout className="layout"
+ layout={this.props.layout}
+ cols={this.props.numCols}
+ rowHeight={this.props.rowHeight}
+ width={this.props.width}
+ compactType={compactType}
+ isDroppable={true}
+ isDraggable={this.props.childrenDraggable}
+ isResizable={this.props.childrenDraggable}
+ useCSSTransforms={true}
+ onLayoutChange={this.props.setLayout}
+ preventCollision={this.props.preventCollision}
+ transformScale={1 / this.props.transformScale} // still doesn't work :(
+ style={{ zIndex: 5 }}
+ margin={[this.props.margin, this.props.margin]}
+ >
+ {this.props.nodeList}
+ </GridLayout>
+ );
+ }
+}
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index a90b4668e..d9e7d072f 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -9,7 +9,42 @@ import { emptyFunction, returnOne } from "../../../Utils";
import '../DocumentDecorations.scss';
import { DocumentView, DocumentViewProps } from "../nodes/DocumentView";
import "./ContentFittingDocumentView.scss";
+import { dropActionType } from "../../util/DragManager";
+import { CollectionView } from "../collections/CollectionView";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { Transform } from "nodemailer/lib/xoauth2";
+interface ContentFittingDocumentViewProps {
+ Document: Doc;
+ DataDocument?: Doc;
+ LayoutDoc?: () => Opt<Doc>;
+ NativeWidth?: () => number;
+ NativeHeight?: () => number;
+ FreezeDimensions?: boolean;
+ LibraryPath: Doc[];
+ renderDepth: number;
+ fitToBox?: boolean;
+ layoutKey?: string;
+ dropAction?: dropActionType;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ focus?: (doc: Doc) => void;
+ CollectionView?: CollectionView;
+ CollectionDoc?: Doc;
+ onClick?: ScriptField;
+ backgroundColor?: (doc: Doc) => string | undefined;
+ getTransform: () => Transform;
+ addDocument?: (document: Doc) => boolean;
+ moveDocument?: (document: Doc, target: Doc | undefined, addDoc: ((doc: Doc) => boolean)) => boolean;
+ removeDocument?: (document: Doc) => boolean;
+ active: (outsideReaction: boolean) => boolean;
+ whenActiveChanged: (isActive: boolean) => void;
+ addDocTab: (document: Doc, where: string) => boolean;
+ pinToPres: (document: Doc) => void;
+ dontRegisterView?: boolean;
+ rootSelected: (outsideReaction?: boolean) => boolean;
+ Display?: string;
+}
@observer
export class ContentFittingDocumentView extends React.Component<DocumentViewProps>{
@@ -38,8 +73,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
@computed get panelHeight() { return this.nativeHeight && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
private getTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
- private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
- private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
+ private get centeringOffset() { return this.nativeWidth() && !this.props.Document._fitWidth && this.props.display !== "contents" ? (this.props.PanelWidth() - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
+ private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 && this.props.display !== "contents" ? (this.props.PanelHeight() - this.nativeHeight() * this.contentScaling()) / 2 : 0; }
@computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); }
@@ -47,7 +82,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
TraceMobx();
return (<div className="contentFittingDocumentView" style={{
width: Math.abs(this.centeringYOffset) > 0.001 ? "auto" : this.props.PanelWidth(),
- height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight()
+ height: Math.abs(this.centeringOffset) > 0.0001 ? "auto" : this.props.PanelHeight(),
+ display: this.props.display /* just added for grid */
}}>
{!this.props.Document || !this.props.PanelWidth ? (null) : (
<div className="contentFittingDocumentView-previewDoc"
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1d98a2628..e4e4dfdd6 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -96,6 +96,7 @@ export interface DocumentViewProps {
dontRegisterView?: boolean;
layoutKey?: string;
radialMenu?: String[];
+ display?: string;
}
@observer
@@ -516,6 +517,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) {
if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
e.stopPropagation();
+ if (SelectionManager.IsSelected(this, true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
// TODO: check here for panning/inking
}
return;
@@ -529,8 +531,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
!this.Document.inOverlay) {
- e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
-
+ e.stopPropagation();
+ if (SelectionManager.IsSelected(this, true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
}
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);