diff options
-rw-r--r-- | .vscode/settings.json | 6 | ||||
-rw-r--r-- | package-lock.json | 82 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 34 | ||||
-rw-r--r-- | src/client/views/collections/collectionGrid/CollectionGridView.scss | 14 | ||||
-rw-r--r-- | src/client/views/collections/collectionGrid/CollectionGridView.tsx | 147 | ||||
-rw-r--r-- | src/client/views/collections/collectionGrid/Grid.tsx | 46 |
7 files changed, 319 insertions, 13 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index 0b45c3f46..e636c9d73 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,9 @@ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true, "search.usePCRE2": true, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "python.testing.promptToConfigure": false, + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": false, + "python.testing.nosetestsEnabled": false }
\ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 176d1f27e..a59e419c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -973,6 +973,14 @@ "@types/react": "*" } }, + "@types/react-grid-layout": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-0.17.1.tgz", + "integrity": "sha512-1ssQjX3X2A89jx94jECJ0Ze2EHFRYlBHjRh2pnlwjJj1WaEijXUNvwKnUzKwgNFnyZ91Pzqu9Z3V7Atzi9ge7A==", + "requires": { + "@types/react": "*" + } + }, "@types/react-measure": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.6.tgz", @@ -5551,7 +5559,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5569,7 +5578,8 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "bindings": { "version": "1.5.0", @@ -5583,6 +5593,7 @@ "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5595,15 +5606,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5706,7 +5720,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5716,6 +5731,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5728,17 +5744,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5755,6 +5774,7 @@ "mkdirp": { "version": "0.5.3", "bundled": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -5810,7 +5830,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -5835,7 +5856,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5845,6 +5867,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5913,7 +5936,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5943,6 +5967,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5960,6 +5985,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5998,11 +6024,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -13946,6 +13974,15 @@ "scheduler": "^0.19.1" } }, + "react-draggable": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.3.1.tgz", + "integrity": "sha512-m8QeV+eIi7LhD5mXoLqDzLbokc6Ncwa0T34fF6uJzWSs4vc4fdZI/XGqHYoEn91T8S6qO+BSXslONh7Jz9VPQQ==", + "requires": { + "classnames": "^2.2.5", + "prop-types": "^15.6.0" + } + }, "react-golden-layout": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/react-golden-layout/-/react-golden-layout-1.0.6.tgz", @@ -13956,6 +13993,18 @@ "react-dom": "^16.3.0" } }, + "react-grid-layout": { + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-0.18.3.tgz", + "integrity": "sha512-lHkrk941Tk5nTwZPa9uj6ttHBT0VehSHwEhWbINBJKvM1GRaFNOefvjcuxSyuCI5JWjVUP+Qm3ARt2470AlxMA==", + "requires": { + "classnames": "2.x", + "lodash.isequal": "^4.0.0", + "prop-types": "^15.0.0", + "react-draggable": "^4.0.0", + "react-resizable": "^1.9.0" + } + }, "react-image-lightbox-with-rotate": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/react-image-lightbox-with-rotate/-/react-image-lightbox-with-rotate-5.1.1.tgz", @@ -14066,6 +14115,15 @@ } } }, + "react-resizable": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.10.1.tgz", + "integrity": "sha512-Jd/bKOKx6+19NwC4/aMLRu/J9/krfxlDnElP41Oc+oLiUWs/zwV1S9yBfBZRnqAwQb6vQ/HRSk3bsSWGSgVbpw==", + "requires": { + "prop-types": "15.x", + "react-draggable": "^4.0.3" + } + }, "react-simple-dropdown": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/react-simple-dropdown/-/react-simple-dropdown-3.2.3.tgz", diff --git a/package.json b/package.json index f1b4be520..9c2d4602c 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "@types/react": "^16.9.19", "@types/react-autosuggest": "^9.3.13", "@types/react-color": "^2.17.3", + "@types/react-grid-layout": "^0.17.1", "@types/react-measure": "^2.0.6", "@types/react-table": "^6.8.6", "@types/request": "^2.48.4", @@ -230,10 +231,12 @@ "react-dimensions": "^1.3.1", "react-dom": "^16.12.0", "react-golden-layout": "^1.0.6", + "react-grid-layout": "^0.18.3", "react-image-lightbox-with-rotate": "^5.1.1", "react-jsx-parser": "^1.21.0", "react-measure": "^2.2.4", "react-mosaic": "0.0.20", + "react-resizable": "^1.10.1", "react-simple-dropdown": "^3.2.3", "react-split-pane": "^0.1.89", "react-table": "^6.11.5", diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 801704673..e8edf7279 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -46,6 +46,7 @@ import { InteractionUtils } from '../../util/InteractionUtils'; import { ObjectField } from '../../../new_fields/ObjectField'; import CollectionMapView from './CollectionMapView'; import { Transform } from 'prosemirror-transform'; +import { CollectionGridView } from './collectionGrid/CollectionGridView'; import { CollectionPileView } from './CollectionPileView'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -69,6 +70,7 @@ export enum CollectionViewType { Linear = "linear", Staff = "staff", Map = "map", + Grid = "grid", Pile = "pileup" } @@ -177,6 +179,7 @@ export class CollectionView extends Touchable<FieldViewProps> { 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} />); } } @@ -223,6 +226,37 @@ export class CollectionView extends Touchable<FieldViewProps> { 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: "rainbow" }); + 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("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); this.setupViewTypes("Add a Perspective...", vtype => { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss new file mode 100644 index 000000000..8f12c1a24 --- /dev/null +++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss @@ -0,0 +1,14 @@ +.collectionGridView_contents { + display: flex; + overflow: hidden; + width: 100%; + height: 100%; + flex-direction: column; + + .document-wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + } +}
\ 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..b94bd02a1 --- /dev/null +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -0,0 +1,147 @@ +import { action, computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from "react"; +import { Doc } from '../../../../new_fields/Doc'; +import { documentSchema } from '../../../../new_fields/documentSchemas'; +import { makeInterface } from '../../../../new_fields/Schema'; +import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../new_fields/Types'; +import { DragManager } from '../../../util/DragManager'; +import { Transform } from '../../../util/Transform'; +import { undoBatch } from '../../../util/UndoManager'; +import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; +import { CollectionSubView } from '../CollectionSubView'; +import { List } from '../../../../new_fields/List'; +import { returnZero } from '../../../../Utils'; +import Grid from "./Grid"; +import { Layout } from "./Grid"; + + +type GridSchema = makeInterface<[typeof documentSchema]>; +const GridSchema = makeInterface(documentSchema); + +export class CollectionGridView extends CollectionSubView(GridSchema) { + + @observable private layouts: Layout[] | undefined; + + /** + * @returns the transform that will correctly place + * the document decorations box, shifted to the right by + * the sum of all the resolved column widths of the + * documents before the target. + */ + private lookupIndividualTransform = (layout: Doc) => { + // const columnUnitLength = this.columnUnitLength; + // if (columnUnitLength === undefined) { + // return Transform.Identity(); // we're still waiting on promises to resolve + // } + let offset = 0; + for (const { layout: candidate } of this.childLayoutPairs) { + if (candidate === layout) { + return this.props.ScreenToLocalTransform().translate(-offset, 0); + } + offset += 194 + 10; + } + return Transform.Identity(); // type coersion, this case should never be hit + } + + + @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); + } + + getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { + return <ContentFittingDocumentView + {...this.props} + Document={layout} + DataDocument={layout.resolvedDataDoc as Doc} + NativeHeight={returnZero} + NativeWidth={returnZero} + addDocTab={this.addDocTab} + fitToBox={BoolCast(this.props.Document._freezeChildDimensions)} + FreezeDimensions={BoolCast(this.props.Document._freezeChildDimensions)} + backgroundColor={this.props.backgroundColor} + CollectionDoc={this.props.Document} + PanelWidth={width} + PanelHeight={height} + getTransform={dxf} + onClick={this.onChildClickHandler} + renderDepth={this.props.renderDepth + 1} + />; + } + + //@action + set layout(layouts: Layout[]) { + this.layouts = layouts; + console.log(this.layouts[0]); + } + + @computed + get layout() { + if (this.layouts === undefined) { + this.layouts = []; + console.log("empty"); + for (let i = 0; i < this.childLayoutPairs.length; i++) { + this.layouts.push( + { i: 'wrapper' + i, x: 2 * (i % 5), y: 2 * Math.floor(i / 5), w: 2, h: 2 } + ); + } + } + return this.layouts; + } + + @computed + private get contents(): [JSX.Element[], Layout[]] { + const { childLayoutPairs } = this; + const { Document } = this.props; + const collector: JSX.Element[] = []; + const layoutArray: Layout[] = []; + for (let i = 0; i < childLayoutPairs.length; i++) { + const { layout } = childLayoutPairs[i]; + const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)); + const width = () => 300; //this.lookupPixels(layout); + const height = () => 300;//PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); + collector.push( + <div className={"document-wrapper"} + key={"wrapper" + i} + //style={{ width: width() }} + > + {this.getDisplayDoc(layout, dxf, width, height)} + </div> + ); + + layoutArray.push( + { i: 'wrapper' + i, x: 2 * (i % 5), y: 2 * Math.floor(i / 5), w: 2, h: 2 } + // add values to document + ); + } + return [collector, layoutArray]; + } + + render(): JSX.Element { + + const contents: JSX.Element[] = this.contents?.[0]; + const layout: Layout[] = this.contents?.[1]; + + return ( + <div className="collectionGridView_contents" + style={{ + marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin), + marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin) + }} ref={this.createDashEventsTarget}> + + <Grid + width={this.props.PanelWidth()} + nodeList={contents} + layout={layout} + gridView={this} + /> + </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..a9e906488 --- /dev/null +++ b/src/client/views/collections/collectionGrid/Grid.tsx @@ -0,0 +1,46 @@ +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'; +import { CollectionGridView } from './CollectionGridView'; +export { Layout } from 'react-grid-layout'; + + +interface GridProps { + width: number; + nodeList: JSX.Element[] | null; + layout: Layout[]; + gridView: CollectionGridView; +} + +@observer +export default class Grid extends React.Component<GridProps, GridLayout.ResponsiveProps> { + + onLayoutChange(layout: Layout[]) { + // for (let i = 0; i < this.props.nodeList!.length; i++) { + // this.props.layout[i] = layout[i]; + // } + console.log("REACHED"); + this.props.gridView.layout = layout; + } + render() { + return ( + <GridLayout className="layout" + layout={this.props.layout} + cols={10} + rowHeight={100} + width={this.props.width} + compactType={null} + isDroppable={true} + onLayoutChange={layout => this.onLayoutChange(layout)} + > + {this.props.nodeList} + </GridLayout > + ); + } +} |