From 66a8d1d4b9c8d27aa2b09d12a59460dc5e22f777 Mon Sep 17 00:00:00 2001 From: Chris Beer Date: Tue, 21 Feb 2023 16:27:27 -0800 Subject: [PATCH] Replace react-mosaic with mirador-mosaic --- __tests__/src/components/Workspace.test.js | 17 +- .../src/components/WorkspaceMosaic.test.js | 133 -------------- __tests__/src/lib/MosaicLayout.test.js | 51 ------ package.json | 19 +- src/components/AppProviders.js | 4 +- src/components/Window.js | 21 +-- src/components/Workspace.js | 6 +- src/components/WorkspaceGrid.js | 51 ++++++ src/components/WorkspaceMosaic.js | 171 ------------------ src/components/WorkspaceSelectionDialog.js | 22 +++ src/config/settings.js | 2 +- src/containers/WorkspaceGrid.js | 36 ++++ src/containers/WorkspaceMosaic.js | 55 ------ src/lib/MosaicLayout.js | 79 -------- src/state/reducers/gridLayout.js | 17 ++ src/state/reducers/index.js | 1 + src/state/reducers/rootReducer.js | 2 + webpack.config.js | 2 - 18 files changed, 154 insertions(+), 535 deletions(-) delete mode 100644 __tests__/src/components/WorkspaceMosaic.test.js delete mode 100644 __tests__/src/lib/MosaicLayout.test.js create mode 100644 src/components/WorkspaceGrid.js delete mode 100644 src/components/WorkspaceMosaic.js create mode 100644 src/containers/WorkspaceGrid.js delete mode 100644 src/containers/WorkspaceMosaic.js delete mode 100644 src/lib/MosaicLayout.js create mode 100644 src/state/reducers/gridLayout.js diff --git a/__tests__/src/components/Workspace.test.js b/__tests__/src/components/Workspace.test.js index fc71c76c83..2a9e50cfc0 100644 --- a/__tests__/src/components/Workspace.test.js +++ b/__tests__/src/components/Workspace.test.js @@ -1,6 +1,5 @@ import { shallow } from 'enzyme'; import Typography from '@material-ui/core/Typography'; -import WorkspaceMosaic from '../../../src/containers/WorkspaceMosaic'; import WorkspaceElastic from '../../../src/containers/WorkspaceElastic'; import Window from '../../../src/containers/Window'; import { Workspace } from '../../../src/components/Workspace'; @@ -17,7 +16,7 @@ function createWrapper(props) { isWorkspaceControlPanelVisible windowIds={['1', '2']} workspaceId="foo" - workspaceType="mosaic" + workspaceType="elastic" t={k => k} {...props} />, @@ -39,20 +38,6 @@ describe('Workspace', () => { )).toBe(true); }); }); - describe('if workspace type is mosaic', () => { - it('should render properly', () => { - const wrapper = createWrapper(); - - expect(wrapper.matchesElement( - -
- miradorViewer - -
-
, - )).toBe(true); - }); - }); describe('if workspace type is unknown', () => { it('should render components as list', () => { const wrapper = createWrapper({ workspaceType: 'bubu' }); diff --git a/__tests__/src/components/WorkspaceMosaic.test.js b/__tests__/src/components/WorkspaceMosaic.test.js deleted file mode 100644 index 9fde83e675..0000000000 --- a/__tests__/src/components/WorkspaceMosaic.test.js +++ /dev/null @@ -1,133 +0,0 @@ -import { shallow } from 'enzyme'; -import { MosaicWithoutDragDropContext } from 'react-mosaic-component'; -import MosaicRenderPreview from '../../../src/containers/MosaicRenderPreview'; -import { WorkspaceMosaic } from '../../../src/components/WorkspaceMosaic'; - -/** create wrapper */ -function createWrapper(props) { - return shallow( - {}} - {...props} - />, - ); -} - -describe('WorkspaceMosaic', () => { - const windowIds = ['1', '2']; - let wrapper; - beforeEach(() => { - wrapper = createWrapper({ windowIds }); - }); - it('should render properly with an initialValue', () => { - expect(wrapper.find(MosaicWithoutDragDropContext).length).toEqual(1); - expect(wrapper.find(MosaicWithoutDragDropContext).prop('initialValue')).toEqual({ - direction: 'row', first: '1', second: '2', - }); - }); - describe('componentDidUpdate', () => { - it('updates the workspace layout when windows change', () => { - const updateWorkspaceMosaicLayout = jest.fn(); - wrapper = createWrapper({ - updateWorkspaceMosaicLayout, - windowIds, - }); - - wrapper.setProps({ windowIds: [...windowIds, '3'] }); - - expect(updateWorkspaceMosaicLayout).toHaveBeenCalled(); - }); - it('updates the workspace layout when windows are removed', () => { - const updateWorkspaceMosaicLayout = jest.fn(); - wrapper = createWrapper({ - layout: { first: 1, second: 2 }, - updateWorkspaceMosaicLayout, - windowIds, - }); - wrapper.instance().windowPaths = { 2: ['second'] }; - wrapper.setProps({ windowIds: [1] }); - expect(updateWorkspaceMosaicLayout).toHaveBeenLastCalledWith(1); - }); - it('when no windows remain', () => { - const updateWorkspaceMosaicLayout = jest.fn(); - wrapper = createWrapper({ - updateWorkspaceMosaicLayout, - windowIds, - }); - wrapper.setProps({ windowIds: [] }); - expect(updateWorkspaceMosaicLayout).toHaveBeenLastCalledWith(null); - }); - it('when the new and old layouts are the same', () => { - const updateWorkspaceMosaicLayout = jest.fn(); - wrapper = createWrapper({ - layout: { first: 1, second: 2 }, - updateWorkspaceMosaicLayout, - windowIds, - }); - wrapper.setProps({ layout: { first: 1, second: 2 }, windowIds }); - expect(updateWorkspaceMosaicLayout).toHaveBeenCalledTimes(1); - }); - }); - describe('bookkeepPath', () => { - it('as windows are rendered keeps a reference to their path in binary tree', () => { - wrapper.instance().tileRenderer('1', 'foo'); - expect(wrapper.instance().windowPaths).toEqual({ 1: 'foo' }); - }); - }); - describe('determineWorkspaceLayout', () => { - it('when window ids do not match workspace layout', () => { - wrapper = createWrapper({ layout: {}, windowIds }); - expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({ - direction: 'row', first: '1', second: '2', - }); - }); - it('by default use workspace.layout', () => { - wrapper = createWrapper({ layout: {}, windowIds: ['foo'] }); - expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo'); - }); - it('generates a new layout if windows do not match current layout', () => { - wrapper = createWrapper({ layout: { first: 'foo', second: 'bark' }, windowIds: ['foo'] }); - expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo'); - }); - it('when window ids match workspace layout', () => { - wrapper = createWrapper({ layout: {}, windowIds: ['foo'] }); - expect(wrapper.instance().determineWorkspaceLayout()).toBe('foo'); - }); - }); - describe('tileRenderer', () => { - it('when window is available', () => { - const renderedTile = wrapper.instance().tileRenderer('1', 'foo'); - expect(renderedTile).not.toBeNull(); - expect(shallow(renderedTile).find('DropTarget(DragSource(InternalMosaicWindow))').length).toEqual(1); - expect(shallow(renderedTile).props()).toEqual(expect.objectContaining({ - additionalControls: [], - path: 'foo', - toolbarControls: [], - })); - - expect(shallow(shallow(renderedTile).props().renderPreview({ windowId: 1 })).matchesElement( -
- -
, - )).toBe(true); - }); - it('when window is not available', () => { - expect(wrapper.instance().tileRenderer('bar')).toBeNull(); - }); - }); - describe('mosaicChange', () => { - it('calls the provided prop to update layout', () => { - const updateWorkspaceMosaicLayout = jest.fn(); - wrapper = createWrapper({ - updateWorkspaceMosaicLayout, - windowIds, - }); - - wrapper.instance().mosaicChange(); - expect(updateWorkspaceMosaicLayout).toBeCalled(); - }); - }); -}); diff --git a/__tests__/src/lib/MosaicLayout.test.js b/__tests__/src/lib/MosaicLayout.test.js deleted file mode 100644 index a364e77393..0000000000 --- a/__tests__/src/lib/MosaicLayout.test.js +++ /dev/null @@ -1,51 +0,0 @@ -import MosaicLayout from '../../../src/lib/MosaicLayout'; - -describe('MosaicLayout', () => { - describe('constructor', () => { - it('sets layout', () => { - expect(new MosaicLayout('foo').layout).toEqual('foo'); - }); - }); - describe('addWindows', () => { - let instance; - beforeEach(() => { - instance = new MosaicLayout('foo'); - }); - it('case 1 window: adds to the top right', () => { - expect(instance.layout).toEqual('foo'); - instance.addWindows(['bar']); - expect(instance.layout).toEqual({ - direction: 'row', - first: 'foo', - second: 'bar', - }); - }); - it('case 3 windows: adds to the top right', () => { - expect(instance.layout).toEqual('foo'); - instance.addWindows(['bar', 'bat', 'bark']); - expect(instance.layout).toEqual({ - direction: 'row', - first: 'foo', - second: { - direction: 'column', - first: { - direction: 'row', - first: 'bat', - second: 'bark', - }, - second: 'bar', - }, - }); - }); - }); - describe('removeWindows', () => { - let instance; - beforeEach(() => { - instance = new MosaicLayout({ first: 'foo', second: 'bar' }); - }); - it('case 1 window: returns a single window', () => { - instance.removeWindows(['bar'], { bar: ['second'] }); - expect(instance.layout).toEqual('foo'); - }); - }); -}); diff --git a/package.json b/package.json index e170dd8093..76791d850c 100644 --- a/package.json +++ b/package.json @@ -47,20 +47,21 @@ "jss-rtl": "^0.3.0", "lodash": "^4.17.11", "manifesto.js": "^4.2.0", + "mirador-mosaic": "^0.0.1", "normalize-url": "^4.5.0", "openseadragon": "^2.4.2 || ^3.0.0 || ^4.0.0", "prop-types": "^15.6.2", + "rdndmb-html5-to-touch": "^8.0.0", "re-reselect": "^4.0.0", "react-copy-to-clipboard": "^5.0.1", - "react-dnd": "^10.0.2", - "react-dnd-html5-backend": "^10.0.2", - "react-dnd-multi-backend": "^5.0.0", - "react-dnd-touch-backend": "^10.0.2", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dnd-multi-backend": "^8.0.0", + "react-dnd-touch-backend": "^16.0.1", "react-full-screen": "^1.1.1", "react-i18next": "^11.7.0 || ^12.0.0", "react-image": "^4.0.1", "react-intersection-observer": "^9.0.0", - "react-mosaic-component": "^4.0.1", "react-redux": "^7.1.0 || ^8.0.0", "react-resize-observer": "^1.1.1", "react-rnd": "^10.1", @@ -114,8 +115,8 @@ "jsdom": "^21.0.0", "puppeteer": "^19.0.0", "raf": "^3.4.1", - "react": "^16.14.0", - "react-dom": "^16.14.0", + "react": "^17.0.0", + "react-dom": "^17.0.0", "react-refresh": "^0.14.0", "redux-mock-store": "^1.5.1", "redux-saga-test-plan": "^4.0.0-rc.3", @@ -125,7 +126,7 @@ "webpack-dev-server": "^4.7.4" }, "peerDependencies": { - "react": "^16.14.0", - "react-dom": "^16.14.0" + "react": "^17.0.0", + "react-dom": "^17.0.0" } } diff --git a/src/components/AppProviders.js b/src/components/AppProviders.js index 163af0f412..1ef4cf2b29 100644 --- a/src/components/AppProviders.js +++ b/src/components/AppProviders.js @@ -6,8 +6,8 @@ import { ThemeProvider, StylesProvider, createTheme, jssPreset, createGenerateClassName, } from '@material-ui/core/styles'; import { DndContext, DndProvider } from 'react-dnd'; -import MultiBackend from 'react-dnd-multi-backend'; -import HTML5toTouch from 'react-dnd-multi-backend/dist/cjs/HTML5toTouch'; +import { MultiBackend } from 'react-dnd-multi-backend'; +import { HTML5toTouch } from 'rdndmb-html5-to-touch'; import { create } from 'jss'; import rtl from 'jss-rtl'; import createI18nInstance from '../i18n'; diff --git a/src/components/Window.js b/src/components/Window.js index 6033c76919..8eb96d45d8 100644 --- a/src/components/Window.js +++ b/src/components/Window.js @@ -2,7 +2,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; import cn from 'classnames'; import Paper from '@material-ui/core/Paper'; -import { MosaicWindowContext } from 'react-mosaic-component/lib/contextTypes'; import ns from '../config/css-ns'; import WindowTopBar from '../containers/WindowTopBar'; import PrimaryWindow from '../containers/PrimaryWindow'; @@ -35,24 +34,19 @@ export class Window extends Component { */ wrappedTopBar() { const { - windowId, workspaceType, windowDraggable, + dragHandle, windowId, windowDraggable, } = this.props; const topBar = ( -
+
); - if (workspaceType === 'mosaic' && windowDraggable) { - const { mosaicWindowActions } = this.context; - return mosaicWindowActions.connectDragSource( - topBar, - ); - } return topBar; } @@ -119,10 +113,12 @@ export class Window extends Component { } } -Window.contextType = MosaicWindowContext; - Window.propTypes = { classes: PropTypes.objectOf(PropTypes.string), + dragHandle: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), focusWindow: PropTypes.func, isFetching: PropTypes.bool, label: PropTypes.string, @@ -133,11 +129,11 @@ Window.propTypes = { view: PropTypes.string, windowDraggable: PropTypes.bool, windowId: PropTypes.string.isRequired, - workspaceType: PropTypes.string, }; Window.defaultProps = { classes: {}, + dragHandle: null, focusWindow: () => {}, isFetching: false, label: null, @@ -146,5 +142,4 @@ Window.defaultProps = { sideBarOpen: false, view: undefined, windowDraggable: null, - workspaceType: null, }; diff --git a/src/components/Workspace.js b/src/components/Workspace.js index f99f61d4e4..f72dad24b2 100644 --- a/src/components/Workspace.js +++ b/src/components/Workspace.js @@ -4,8 +4,8 @@ import classNames from 'classnames'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; import Window from '../containers/Window'; -import WorkspaceMosaic from '../containers/WorkspaceMosaic'; import WorkspaceElastic from '../containers/WorkspaceElastic'; +import WorkspaceGrid from '../containers/WorkspaceGrid'; import ns from '../config/css-ns'; import { IIIFDropTarget } from './IIIFDropTarget'; @@ -45,8 +45,8 @@ export class Workspace extends Component { switch (workspaceType) { case 'elastic': return ; - case 'mosaic': - return ; + case 'grid': + return ; default: return windowIds.map(windowId => ( { + 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 {
+ this.handleWorkspaceTypeChange('grid')} + selected={workspaceType === 'grid'} + value="grid" + > + + +
+ + {t('grid')} + {t('gridDescription')} + +
+
+
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', },