aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2022-07-26 12:29:41 -0400
committerbobzel <zzzman@gmail.com>2022-07-26 12:29:41 -0400
commitcd409364ec3ec40e2c060ede7b8d7610777483d6 (patch)
treee33e23c5d74a2fe5f864209b34b1ae00242ab1f6 /src
parent6ab111c7c4c2d2c0259f88d71781b618ddb2356e (diff)
fixed dragging webBoxes within a gridView to not marquee select.
Diffstat (limited to 'src')
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx278
1 files changed, 170 insertions, 108 deletions
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 4e4c33446..9468c5f06 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,6 +1,6 @@
import { action, computed, Lambda, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
import { Doc, Opt } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -15,8 +15,8 @@ import { ContextMenuProps } from '../../ContextMenuItem';
import { DocumentView } from '../../nodes/DocumentView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { CollectionSubView } from '../CollectionSubView';
-import "./CollectionGridView.scss";
-import Grid, { Layout } from "./Grid";
+import './CollectionGridView.scss';
+import Grid, { Layout } from './Grid';
@observer
export class CollectionGridView extends CollectionSubView() {
@@ -29,50 +29,76 @@ export class CollectionGridView extends CollectionSubView() {
onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
- @computed get numCols() { return NumCast(this.props.Document.gridNumCols, 10); }
- @computed get rowHeight() { return this._rowHeight === undefined ? NumCast(this.props.Document.gridRowHeight, 100) : this._rowHeight; }
- // sets the default width and height of the grid nodes
- @computed get defaultW() { return NumCast(this.props.Document.gridDefaultW, 2); }
- @computed get defaultH() { return NumCast(this.props.Document.gridDefaultH, 2); }
+ @computed get numCols() {
+ return NumCast(this.props.Document.gridNumCols, 10);
+ }
+ @computed get rowHeight() {
+ return this._rowHeight === undefined ? NumCast(this.props.Document.gridRowHeight, 100) : this._rowHeight;
+ }
+ // sets the default width and height of the grid nodes
+ @computed get defaultW() {
+ return NumCast(this.props.Document.gridDefaultW, 2);
+ }
+ @computed get defaultH() {
+ return NumCast(this.props.Document.gridDefaultH, 2);
+ }
- @computed get colWidthPlusGap() { return (this.props.PanelWidth() - this.margin) / this.numCols; }
- @computed get rowHeightPlusGap() { return this.rowHeight + this.margin; }
+ @computed get colWidthPlusGap() {
+ return (this.props.PanelWidth() - this.margin) / this.numCols;
+ }
+ @computed get rowHeightPlusGap() {
+ return this.rowHeight + this.margin;
+ }
- @computed get margin() { return NumCast(this.props.Document.margin, 10); } // sets the margin between grid nodes
+ @computed get margin() {
+ return NumCast(this.props.Document.margin, 10);
+ } // sets the margin between grid nodes
- @computed get flexGrid() { return BoolCast(this.props.Document.gridFlex, true); } // is grid static/flexible i.e. whether nodes be moved around and resized
- @computed get compaction() { return StrCast(this.props.Document.gridStartCompaction, StrCast(this.props.Document.gridCompaction, "vertical")); } // is grid static/flexible i.e. whether nodes be moved around and resized
+ @computed get flexGrid() {
+ return BoolCast(this.props.Document.gridFlex, true);
+ } // is grid static/flexible i.e. whether nodes be moved around and resized
+ @computed get compaction() {
+ return StrCast(this.props.Document.gridStartCompaction, StrCast(this.props.Document.gridCompaction, 'vertical'));
+ } // is grid static/flexible i.e. whether nodes be moved around and resized
/**
* Sets up the listeners for the list of documents and the reset button.
*/
componentDidMount() {
- this._changeListenerDisposer = reaction(() => this.childLayoutPairs, (pairs) => {
- const newLayouts: Layout[] = [];
- const oldLayouts = this.savedLayoutList;
- pairs.forEach((pair, i) => {
- const existing = oldLayouts.find(l => l.i === pair.layout[Id]);
- if (existing) newLayouts.push(existing);
- else {
- if (Object.keys(this.dropLocation).length) { // external drop
- this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number, y: number }, !this.flexGrid));
- this.dropLocation = {};
- }
- else { // internal drop
- this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
+ this._changeListenerDisposer = reaction(
+ () => this.childLayoutPairs,
+ pairs => {
+ const newLayouts: Layout[] = [];
+ const oldLayouts = this.savedLayoutList;
+ pairs.forEach((pair, i) => {
+ const existing = oldLayouts.find(l => l.i === pair.layout[Id]);
+ if (existing) newLayouts.push(existing);
+ else {
+ if (Object.keys(this.dropLocation).length) {
+ // external drop
+ this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid));
+ this.dropLocation = {};
+ } else {
+ // internal drop
+ this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
+ }
}
- }
- });
- pairs?.length && this.setLayoutList(newLayouts);
- }, { fireImmediately: true });
+ });
+ pairs?.length && this.setLayoutList(newLayouts);
+ },
+ { fireImmediately: true }
+ );
// updates the layouts if the reset button has been clicked
- this._resetListenerDisposer = reaction(() => this.props.Document.gridResetLayout, (reset) => {
- if (reset && this.flexGrid) {
- this.setLayout(this.childLayoutPairs.map((pair, index) => this.makeLayoutItem(pair.layout, this.unflexedPosition(index))));
+ this._resetListenerDisposer = reaction(
+ () => this.props.Document.gridResetLayout,
+ reset => {
+ if (reset && this.flexGrid) {
+ this.setLayout(this.childLayoutPairs.map((pair, index) => this.makeLayoutItem(pair.layout, this.unflexedPosition(index))));
+ }
+ this.props.Document.gridResetLayout = false;
}
- this.props.Document.gridResetLayout = false;
- });
+ );
}
/**
@@ -85,15 +111,15 @@ export class CollectionGridView extends CollectionSubView() {
/**
* @returns the default location of the grid node (i.e. when the grid is static)
- * @param index
+ * @param index
*/
- unflexedPosition(index: number): Omit<Layout, "i"> {
+ unflexedPosition(index: number): Omit<Layout, 'i'> {
return {
x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
w: this.defaultW,
h: this.defaultH,
- static: true
+ static: true,
};
}
@@ -110,9 +136,9 @@ export class CollectionGridView extends CollectionSubView() {
/**
* Creates a layout object for a grid item
*/
- makeLayoutItem = (doc: Doc, pos: { x: number, y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => {
- return ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static });
- }
+ makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => {
+ return { i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static };
+ };
/**
* Adds a layout to the list of layouts.
@@ -122,16 +148,16 @@ export class CollectionGridView extends CollectionSubView() {
f !== -1 && layouts.splice(f, 1);
layouts.push(layout);
return layouts;
- }
+ };
/**
- * @returns the transform that will correctly place the document decorations box.
+ * @returns the transform that will correctly place the document decorations box.
*/
private lookupIndividualTransform = (layout: Layout) => {
const xypos = this.flexGrid ? layout : this.unflexedPosition(this.renderedLayoutList.findIndex(l => l.i === layout.i));
const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
return this.props.ScreenToLocalTransform().translate(-pos.x, -pos.y);
- }
+ };
/**
* @returns the layout list converted from JSON
@@ -147,26 +173,32 @@ export class CollectionGridView extends CollectionSubView() {
this.props.Document.gridLayoutString = JSON.stringify(layouts);
}
+ isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+ isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
/**
- *
- * @param layout
+ *
+ * @param layout
* @param dxf the x- and y-translations of the decorations box as a transform i.e. this.lookupIndividualTransform
- * @param width
- * @param height
+ * @param width
+ * @param height
* @returns the `ContentFittingDocumentView` of the node
*/
getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
- return <DocumentView
- {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- Document={layout}
- DataDoc={layout.resolvedDataDoc as Doc}
- PanelWidth={width}
- PanelHeight={height}
- ScreenToLocalTransform={dxf}
- onClick={this.onChildClickHandler}
- renderDepth={this.props.renderDepth + 1}
- dontCenter={this.props.Document.centerY ? "" : "y"}
- />;
+ return (
+ <DocumentView
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+ Document={layout}
+ DataDoc={layout.resolvedDataDoc as Doc}
+ isContentActive={this.isChildContentActive}
+ PanelWidth={width}
+ PanelHeight={height}
+ ScreenToLocalTransform={dxf}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ onClick={this.onChildClickHandler}
+ renderDepth={this.props.renderDepth + 1}
+ dontCenter={this.props.Document.centerY ? '' : 'y'}
+ />
+ );
}
/**
@@ -176,7 +208,7 @@ export class CollectionGridView extends CollectionSubView() {
@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
+ // updated layout object. If both exist, which they should, update the grid layout object from the updated object
if (this.flexGrid) {
const savedLayouts = this.savedLayoutList;
this.childLayoutPairs.forEach(({ layout: doc }) => {
@@ -194,7 +226,7 @@ export class CollectionGridView extends CollectionSubView() {
undoBatch(() => this.setLayoutList(savedLayouts))();
}
}
- }
+ };
/**
* @returns a list of `ContentFittingDocumentView`s inside wrapper divs.
@@ -209,11 +241,12 @@ export class CollectionGridView extends CollectionSubView() {
const dxf = () => this.lookupIndividualTransform(l);
const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.margin;
const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
- child && collector.push(
- <div key={child.layout[Id]} className={"document-wrapper" + (this.flexGrid && this.props.isSelected() ? "" : " static")} >
- {this.getDisplayDoc(child.layout, dxf, width, height)}
- </div >
- );
+ child &&
+ collector.push(
+ <div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this.props.isSelected() ? '' : ' static')}>
+ {this.getDisplayDoc(child.layout, dxf, width, height)}
+ </div>
+ );
});
}
return collector;
@@ -223,14 +256,19 @@ export class CollectionGridView extends CollectionSubView() {
* @returns a list of `Layout` objects with attributes depending on whether the grid is flexible or static
*/
@computed get renderedLayoutList(): Layout[] {
- return this.flexGrid ?
- this.savedLayoutList.map(({ i, x, y, w, h }) => ({
- i, y, h,
- x: x + w > this.numCols ? 0 : x, // handles wrapping around of nodes when numCols decreases
- w: Math.min(w, this.numCols), // reduces width if greater than numCols
- 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
- })) :
- this.savedLayoutList.map((layout, index) => { Object.assign(layout, this.unflexedPosition(index)); return layout; });
+ return this.flexGrid
+ ? this.savedLayoutList.map(({ i, x, y, w, h }) => ({
+ i,
+ y,
+ h,
+ x: x + w > this.numCols ? 0 : x, // handles wrapping around of nodes when numCols decreases
+ w: Math.min(w, this.numCols), // reduces width if greater than numCols
+ 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
+ }))
+ : this.savedLayoutList.map((layout, index) => {
+ Object.assign(layout, this.unflexedPosition(index));
+ return layout;
+ });
}
/**
@@ -246,7 +284,7 @@ export class CollectionGridView extends CollectionSubView() {
return true;
}
return false;
- }
+ };
/**
* Handles external drop of images/PDFs etc from outside Dash.
@@ -255,7 +293,7 @@ export class CollectionGridView extends CollectionSubView() {
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
this.dropLocation = this.screenToCell(e.clientX, e.clientY);
super.onExternalDrop(e, {});
- }
+ };
/**
* Handles the change in the value of the rowHeight slider.
@@ -263,65 +301,83 @@ export class CollectionGridView extends CollectionSubView() {
@action
onSliderChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this._rowHeight = event.currentTarget.valueAsNumber;
- }
+ };
/**
* Handles the user clicking on the slider.
*/
@action
onSliderDown = (e: React.PointerEvent) => {
this._rowHeight = this.rowHeight; // uses _rowHeight during dragging and sets doc's rowHeight when finished so that operation is undoable
- setupMoveUpEvents(this, e, returnFalse, action(() => {
- undoBatch(() => this.props.Document.gridRowHeight = this._rowHeight)();
- this._rowHeight = undefined;
- }), emptyFunction, false, false);
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ action(() => {
+ undoBatch(() => (this.props.Document.gridRowHeight = this._rowHeight))();
+ this._rowHeight = undefined;
+ }),
+ emptyFunction,
+ false,
+ false
+ );
e.stopPropagation();
- }
+ };
/**
* Adds the display option to change the css display attribute of the `ContentFittingDocumentView`s
*/
onContextMenu = () => {
const displayOptionsMenu: ContextMenuProps[] = [];
- displayOptionsMenu.push({ description: "Toggle Content Display Style", event: () => this.props.Document.display = this.props.Document.display ? undefined : "contents", icon: "copy" });
- displayOptionsMenu.push({ description: "Toggle Vertical Centering", event: () => this.props.Document.centerY = !this.props.Document.centerY, icon: "copy" });
- ContextMenu.Instance.addItem({ description: "Display", subitems: displayOptionsMenu, icon: "tv" });
- }
+ displayOptionsMenu.push({ description: 'Toggle Content Display Style', event: () => (this.props.Document.display = this.props.Document.display ? undefined : 'contents'), icon: 'copy' });
+ displayOptionsMenu.push({ description: 'Toggle Vertical Centering', event: () => (this.props.Document.centerY = !this.props.Document.centerY), icon: 'copy' });
+ ContextMenu.Instance.addItem({ description: 'Display', subitems: displayOptionsMenu, icon: 'tv' });
+ };
/**
* Handles text document creation on double click.
*/
onPointerDown = (e: React.PointerEvent) => {
if (this.props.isContentActive(true)) {
- setupMoveUpEvents(this, e, returnFalse, returnFalse,
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ returnFalse,
(e: PointerEvent, doubleTap?: boolean) => {
if (doubleTap && !e.button) {
- undoBatch(action(() => {
- const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 });
- FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
- this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
- }))();
+ undoBatch(
+ action(() => {
+ const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 });
+ FormattedTextBox.SelectOnLoad = text[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ Doc.AddDocToList(this.props.Document, this.props.fieldKey, text);
+ this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
+ })
+ )();
}
},
- false);
+ false
+ );
if (this.props.isSelected(true)) e.stopPropagation();
}
- }
+ };
render() {
return (
- <div className="collectionGridView-contents" ref={this.createDashEventsTarget}
- style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined }}
+ <div
+ className="collectionGridView-contents"
+ ref={this.createDashEventsTarget}
+ style={{ pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined }}
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}
- onDrop={this.onExternalDrop}
- >
- <div className="collectionGridView-gridContainer" ref={this._containerRef}
- style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, "white") }}
+ onDrop={this.onExternalDrop}>
+ <div
+ className="collectionGridView-gridContainer"
+ ref={this._containerRef}
+ style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white') }}
onWheel={e => e.stopPropagation()}
onScroll={action(e => {
if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
- })} >
+ })}>
<Grid
width={this.props.PanelWidth()}
nodeList={this.contents.length ? this.contents : null}
@@ -332,15 +388,21 @@ export class CollectionGridView extends CollectionSubView() {
setLayout={this.setLayout}
transformScale={this.props.ScreenToLocalTransform().Scale}
compactType={this.compaction} // determines whether nodes should remain in position, be bound to the top, or to the left
- preventCollision={BoolCast(this.props.Document.gridPreventCollision)}// determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
+ preventCollision={BoolCast(this.props.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
margin={this.margin}
/>
- <input className="rowHeightSlider" type="range"
+ <input
+ className="rowHeightSlider"
+ type="range"
style={{ width: this.props.PanelHeight() - 30 }}
- min={1} value={this.rowHeight} max={this.props.PanelHeight() - 30}
- onPointerDown={this.onSliderDown} onChange={this.onSliderChange} />
+ min={1}
+ value={this.rowHeight}
+ max={this.props.PanelHeight() - 30}
+ onPointerDown={this.onSliderDown}
+ onChange={this.onSliderChange}
+ />
</div>
- </div >
+ </div>
);
}
-} \ No newline at end of file
+}