{
+ const { dragDropManager } = useContext(DndContext);
+ const dispatch = useDispatch();
+
+ const wrappedDispatch = useCallback((action) => {
+ dispatch({ ...action, type: `mirador/grid/${action.type}` });
+ }, [dispatch]);
+
+ return (
+
+
+
+
+ {
+ windowIds.map(windowId => (
+
+ ))
+ }
+
+
+
+
+ );
+};
+
+WorkspaceGrid.propTypes = {
+ gridTemplate: PropTypes.shape({
+ areas: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
+ columns: PropTypes.arrayOf(PropTypes.number),
+ rows: PropTypes.arrayOf(PropTypes.number),
+ }).isRequired,
+ windowIds: PropTypes.arrayOf(PropTypes.string).isRequired,
+};
+
+export default WorkspaceGrid;
diff --git a/src/components/WorkspaceMosaic.js b/src/components/WorkspaceMosaic.js
deleted file mode 100644
index 620a8f6015..0000000000
--- a/src/components/WorkspaceMosaic.js
+++ /dev/null
@@ -1,171 +0,0 @@
-import { Component } from 'react';
-import PropTypes from 'prop-types';
-import {
- MosaicWithoutDragDropContext, MosaicWindow, getLeaves, createBalancedTreeFromLeaves,
-} from 'react-mosaic-component';
-import difference from 'lodash/difference';
-import isEqual from 'lodash/isEqual';
-import classNames from 'classnames';
-import MosaicRenderPreview from '../containers/MosaicRenderPreview';
-import Window from '../containers/Window';
-import MosaicLayout from '../lib/MosaicLayout';
-
-/**
- * Represents a work area that contains any number of windows
- * @memberof Workspace
- * @private
- */
-export class WorkspaceMosaic extends Component {
- /**
- */
- constructor(props) {
- super(props);
-
- this.tileRenderer = this.tileRenderer.bind(this);
- this.mosaicChange = this.mosaicChange.bind(this);
- this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this);
- this.zeroStateView = ;
- this.windowPaths = {};
- this.toolbarControls = [];
- this.additionalControls = [];
- }
-
- /** */
- componentDidMount() {
- const { updateWorkspaceMosaicLayout } = this.props;
-
- const newLayout = this.determineWorkspaceLayout();
- if (newLayout) updateWorkspaceMosaicLayout(newLayout);
- }
-
- /** */
- componentDidUpdate(prevProps) {
- const { windowIds, layout, updateWorkspaceMosaicLayout } = this.props;
- const prevWindows = prevProps.windowIds;
- // Handles when Windows are added (not via Add Resource UI) Could be a workspace import
- if (!windowIds.every(e => prevWindows.includes(e))) {
- const newLayout = this.determineWorkspaceLayout();
- if (!isEqual(newLayout, layout)) updateWorkspaceMosaicLayout(newLayout);
- return;
- }
-
- // Handles when Windows are removed from the state
- if (!prevWindows.every(e => windowIds.includes(e))) {
- // There are no more remaining Windows, just return an empty layout
- if (windowIds.length === 0) {
- updateWorkspaceMosaicLayout(null);
- return;
- }
-
- const removedWindows = difference(prevWindows, windowIds);
- const newLayout = new MosaicLayout(layout);
- newLayout.removeWindows(removedWindows, this.windowPaths);
- updateWorkspaceMosaicLayout(newLayout.layout);
- }
- }
-
- /**
- * bookkeepPath - used to book keep Window's path's
- * @param {String} windowId [description]
- * @param {Array} path [description]
- */
- bookkeepPath(windowId, path) {
- this.windowPaths[windowId] = path;
- }
-
- /**
- * Used to determine whether or not a "new" layout should be autogenerated.
- */
- determineWorkspaceLayout() {
- const { windowIds, layout } = this.props;
- const leaveKeys = getLeaves(layout);
- // Windows were added
- if (!windowIds.every(e => leaveKeys.includes(e))) {
- // No current layout, so just generate a new one
- if (leaveKeys.length < 2) {
- return createBalancedTreeFromLeaves(windowIds);
- }
- // Add new windows to layout
- const addedWindows = difference(windowIds, leaveKeys);
- const newLayout = new MosaicLayout(layout);
- newLayout.addWindows(addedWindows);
- return newLayout.layout;
- }
- // Windows were removed (perhaps in a different Workspace). We don't have a
- // way to reconfigure.. so we have to random generate
- if (!leaveKeys.every(e => windowIds.includes(e))) {
- return createBalancedTreeFromLeaves(windowIds);
- }
- return layout;
- }
-
- /** */
- static renderPreview(mosaicProps) {
- return (
-
-
-
- );
- }
-
- /**
- * Render a tile (Window) in the Mosaic.
- */
- tileRenderer(id, path) {
- const { windowIds, workspaceId } = this.props;
- if (!windowIds.includes(id)) return null;
- this.bookkeepPath(id, path);
- return (
-
-
-
- );
- }
-
- /**
- * Update the redux store when the Mosaic is changed.
- */
- mosaicChange(newLayout) {
- const { updateWorkspaceMosaicLayout } = this.props;
- updateWorkspaceMosaicLayout(newLayout);
- }
-
- /**
- */
- render() {
- const { layout, classes } = this.props;
- return (
-
- );
- }
-}
-
-WorkspaceMosaic.propTypes = {
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- layout: PropTypes.oneOfType(
- [PropTypes.object, PropTypes.string],
- ), // eslint-disable-line react/forbid-prop-types
- updateWorkspaceMosaicLayout: PropTypes.func.isRequired,
- windowIds: PropTypes.arrayOf(PropTypes.string),
- workspaceId: PropTypes.string.isRequired,
-};
-
-WorkspaceMosaic.defaultProps = {
- layout: undefined,
- windowIds: [],
-};
diff --git a/src/components/WorkspaceSelectionDialog.js b/src/components/WorkspaceSelectionDialog.js
index 839c227efe..9e2351a5fc 100644
--- a/src/components/WorkspaceSelectionDialog.js
+++ b/src/components/WorkspaceSelectionDialog.js
@@ -119,6 +119,28 @@ export class WorkspaceSelectionDialog extends Component {
+
diff --git a/src/config/settings.js b/src/config/settings.js
index 839e673708..1e6d198d24 100644
--- a/src/config/settings.js
+++ b/src/config/settings.js
@@ -323,7 +323,7 @@ export default {
exposeModeOn: false, // unused?
height: 5000, // height of the elastic mode's virtual canvas
showZoomControls: false, // Configure if zoom controls should be displayed by default
- type: 'mosaic', // Which workspace type to load by default. Other possible values are "elastic". If "mosaic" or "elastic" are not selected no worksapce type will be used.
+ type: 'grid', // Which workspace type to load by default. Other possible values are "elastic". If "mosaic" or "elastic" are not selected no worksapce type will be used.
viewportPosition: { // center coordinates for the elastic mode workspace
x: 0,
y: 0,
diff --git a/src/containers/WorkspaceGrid.js b/src/containers/WorkspaceGrid.js
new file mode 100644
index 0000000000..310a654d9f
--- /dev/null
+++ b/src/containers/WorkspaceGrid.js
@@ -0,0 +1,36 @@
+import { compose } from 'redux';
+import { connect } from 'react-redux';
+import { withStyles } from '@material-ui/core/styles';
+import { withPlugins } from '../extend/withPlugins';
+import { getWorkspace } from '../state/selectors';
+import WorkspaceGrid from '../components/WorkspaceGrid';
+
+/**
+ * mapStateToProps - to hook up connect
+ * @memberof Workspace
+ * @private
+ */
+const mapStateToProps = state => (
+ {
+ gridTemplate: state.gridLayout,
+ windowIds: getWorkspace(state).windowIds,
+ }
+);
+
+/**
+ * mapDispatchToProps - used to hook up connect to action creators
+ * @memberof Workspace
+ * @private
+ */
+const mapDispatchToProps = {};
+
+const styles = {};
+
+const enhance = compose(
+ withStyles(styles),
+ connect(mapStateToProps, mapDispatchToProps),
+ withPlugins('WorkspaceGrid'),
+ // further HOC go here
+);
+
+export default enhance(WorkspaceGrid);
diff --git a/src/containers/WorkspaceMosaic.js b/src/containers/WorkspaceMosaic.js
deleted file mode 100644
index b66e2f4aa6..0000000000
--- a/src/containers/WorkspaceMosaic.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import { compose } from 'redux';
-import { connect } from 'react-redux';
-import { withStyles } from '@material-ui/core/styles';
-import { withPlugins } from '../extend/withPlugins';
-import { getWorkspace } from '../state/selectors';
-import * as actions from '../state/actions';
-import { WorkspaceMosaic } from '../components/WorkspaceMosaic';
-import globalReactMosaicStyles from '../styles/react-mosaic-component';
-
-/**
- * mapStateToProps - to hook up connect
- * @memberof Workspace
- * @private
- */
-const mapStateToProps = state => (
- {
- layout: getWorkspace(state).layout,
- windowIds: getWorkspace(state).windowIds,
- workspaceId: getWorkspace(state).id,
- }
-);
-
-/**
- * mapDispatchToProps - used to hook up connect to action creators
- * @memberof Workspace
- * @private
- */
-const mapDispatchToProps = { updateWorkspaceMosaicLayout: actions.updateWorkspaceMosaicLayout };
-
-const styles = {
- root: {
- '& .mosaic-preview': {
- boxShadow: 'none',
- },
- '& .mosaic-tile': {
- boxShadow: '0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .2), 0 2px 1px -1px rgba(0, 0, 0, .2)',
- },
- '& .mosaic-window': {
- boxShadow: 'none',
- },
- '& .mosaic-window-toolbar': {
- display: 'none !important',
- },
- },
- ...globalReactMosaicStyles,
-};
-
-const enhance = compose(
- withStyles(styles),
- connect(mapStateToProps, mapDispatchToProps),
- withPlugins('WorkspaceMosaic'),
- // further HOC go here
-);
-
-export default enhance(WorkspaceMosaic);
diff --git a/src/lib/MosaicLayout.js b/src/lib/MosaicLayout.js
deleted file mode 100644
index c64710a7c3..0000000000
--- a/src/lib/MosaicLayout.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import { createRemoveUpdate, updateTree } from 'react-mosaic-component/lib/util/mosaicUpdates';
-import {
- getNodeAtPath, getOtherDirection, getPathToCorner, Corner,
-} from 'react-mosaic-component/lib/util/mosaicUtilities';
-import dropRight from 'lodash/dropRight';
-
-/** */
-export default class MosaicLayout {
- /** */
- constructor(layout) {
- this.layout = layout;
- }
-
- /** */
- pathToCorner(corner = Corner.TOP_RIGHT) {
- return getPathToCorner(this.layout, corner);
- }
-
- /** */
- pathToParent(path) {
- return getNodeAtPath(this.layout, dropRight(path));
- }
-
- /** */
- nodeAtPath(path) {
- return getNodeAtPath(this.layout, path);
- }
-
- /**
- * addWindows - updates the layout with new windows using an algorithm ported
- * from the react-mosaic-components examples. Will always add to the Top Right
- * https://github.com/nomcopter/react-mosaic/blob/5081df8d1528d4c3b83a72763a46a30b3048fe95/demo/ExampleApp.tsx#L119-L154
- * @param {Array} addedWindowIds [description]
- */
- addWindows(addedWindowIds) {
- addedWindowIds.forEach((windowId, i) => {
- const path = this.pathToCorner();
- const parent = this.pathToParent(path);
- const destination = this.nodeAtPath(path);
- const direction = parent ? getOtherDirection(parent.direction) : 'row';
- let first;
- let second;
- if (direction === 'row') {
- first = destination;
- second = addedWindowIds[i];
- } else {
- first = addedWindowIds[i];
- second = destination;
- }
- const update = {
- path,
- spec: {
- $set: {
- direction,
- first,
- second,
- },
- },
- };
- // We cannot batch the updates together because we need to recalculate
- // the new location for each new window
- this.layout = updateTree(this.layout, [update]);
- });
- }
-
- /**
- * removeWindows - Generate a set of "removeUpdates" to update layout binary
- * tree. Then update the layout.
- * @param {Array} removedWindowIds
- * @param {Object} windowPaths - a lookup table for window paths
- */
- removeWindows(removedWindowIds, windowPaths) {
- const removeUpdates = removedWindowIds
- .map(windowId => (
- createRemoveUpdate(this.layout, windowPaths[windowId])
- ));
- this.layout = updateTree(this.layout, removeUpdates);
- }
-}
diff --git a/src/state/reducers/gridLayout.js b/src/state/reducers/gridLayout.js
new file mode 100644
index 0000000000..6ac5470e46
--- /dev/null
+++ b/src/state/reducers/gridLayout.js
@@ -0,0 +1,17 @@
+import { reducer } from 'mirador-mosaic';
+import ActionTypes from '../actions/action-types';
+
+/**
+ * gridLayoutReducer
+ */
+export const gridLayoutReducer = (state = { areas: [], columns: [], rows: [] }, action) => {
+ switch (action.type) {
+ case ActionTypes.ADD_WINDOW:
+ return reducer(state, { id: action.window.id, type: 'add' });
+ case ActionTypes.IMPORT_MIRADOR_STATE:
+ return action.state.gridLayout || {};
+ default:
+ if (!action.type.startsWith('mirador/grid')) return state;
+ return reducer(state, { ...action, type: action.type.replace('mirador/grid/', '') });
+ }
+};
diff --git a/src/state/reducers/index.js b/src/state/reducers/index.js
index 7e361c2203..6568da3c4e 100644
--- a/src/state/reducers/index.js
+++ b/src/state/reducers/index.js
@@ -13,3 +13,4 @@ export * from './elasticLayout';
export * from './search';
export * from './layers';
export * from './catalog';
+export * from './gridLayout';
diff --git a/src/state/reducers/rootReducer.js b/src/state/reducers/rootReducer.js
index c1f808ac8e..6a2f6c3725 100644
--- a/src/state/reducers/rootReducer.js
+++ b/src/state/reducers/rootReducer.js
@@ -15,6 +15,7 @@ import {
searchesReducer,
layersReducer,
catalogReducer,
+ gridLayoutReducer,
} from '.';
/**
@@ -32,6 +33,7 @@ export default function createRootReducer(pluginReducers) {
config: configReducer,
elasticLayout: elasticLayoutReducer,
errors: errorsReducer,
+ gridLayout: gridLayoutReducer,
infoResponses: infoResponsesReducer,
layers: layersReducer,
manifests: manifestsReducer,
diff --git a/webpack.config.js b/webpack.config.js
index d33c348329..f31af73184 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -46,8 +46,6 @@ const baseConfig = mode => ({
],
resolve: {
alias: {
- // needs shared global state for context to work
- 'react-dnd': path.resolve(path.join(__dirname, 'node_modules', 'react-dnd')),
'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
'react/jsx-runtime': 'react/jsx-runtime.js',
},