From 71a219096f4dd310073e928e26d08bbb97ce43ab Mon Sep 17 00:00:00 2001 From: "Andrew Winget (Standard)" <scipioaffricanus@gmail.com> Date: Mon, 28 Jan 2019 16:03:10 -0800 Subject: [PATCH] adds overlapping, panning windowing system using react-Rnd --- __tests__/src/actions/window.test.js | 49 ++++++- __tests__/src/actions/workspace.test.js | 25 ++++ __tests__/src/components/App.test.js | 2 +- __tests__/src/components/Window.test.js | 8 +- __tests__/src/components/Workspace.test.js | 16 +- .../src/components/WorkspaceElastic.test.js | 137 ++++++++++++++++++ __tests__/src/reducers/windows.test.js | 62 ++++++++ __tests__/src/reducers/workspace.test.js | 23 +++ package.json | 2 + src/components/App.js | 2 +- src/components/Workspace.js | 5 +- src/components/WorkspaceElastic.js | 84 +++++++++++ src/config/settings.js | 2 +- src/containers/Window.js | 1 + src/containers/WorkspaceElastic.js | 50 +++++++ src/state/actions/action-types.js | 4 + src/state/actions/window.js | 39 ++++- src/state/actions/workspace.js | 31 +++- src/state/reducers/windows.js | 26 +++- src/state/reducers/workspace.js | 15 +- src/styles/index.scss | 25 +++- 21 files changed, 588 insertions(+), 20 deletions(-) create mode 100644 __tests__/src/components/WorkspaceElastic.test.js create mode 100644 src/components/WorkspaceElastic.js create mode 100644 src/containers/WorkspaceElastic.js diff --git a/__tests__/src/actions/window.test.js b/__tests__/src/actions/window.test.js index 2dde5c7c1..7776bb956 100644 --- a/__tests__/src/actions/window.test.js +++ b/__tests__/src/actions/window.test.js @@ -19,7 +19,10 @@ describe('window actions', () => { manifestId: null, rangeId: null, thumbnailNavigationPosition: 'bottom', - xywh: [0, 0, 400, 400], + x: 2700, + y: 2700, + width: 400, + height: 400, rotation: null, view: 'single', }, @@ -141,4 +144,48 @@ describe('window actions', () => { }); }); }); + + describe('setWindowSize', () => { + it('returns the appropriate action type', () => { + const id = 'abc123'; + const expectedAction = { + type: ActionTypes.SET_WINDOW_SIZE, + payload: { + windowId: id, + size: { + x: 20, + y: 20, + width: 200, + height: 200, + }, + }, + }; + expect(actions.setWindowSize(id, { + x: 20, + y: 20, + width: 200, + height: 200, + })).toEqual(expectedAction); + }); + }); + + describe('updateWindowPosition', () => { + it('returns the appropriate action type', () => { + const id = 'abc123'; + const expectedAction = { + type: ActionTypes.UPDATE_WINDOW_POSITION, + payload: { + windowId: id, + position: { + x: 20, + y: 20, + }, + }, + }; + expect(actions.updateWindowPosition(id, { + x: 20, + y: 20, + })).toEqual(expectedAction); + }); + }); }); diff --git a/__tests__/src/actions/workspace.test.js b/__tests__/src/actions/workspace.test.js index 587f6709b..5f430096a 100644 --- a/__tests__/src/actions/workspace.test.js +++ b/__tests__/src/actions/workspace.test.js @@ -49,4 +49,29 @@ describe('workspace actions', () => { expect(actions.setWorkspaceAddVisibility(true)).toEqual(expectedAction); }); }); + describe('setWorkspaceViewportPosition', () => { + it('should set the workspace add visibility', () => { + const expectedAction = { + type: ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION, + payload: { + position: { + x: 20, + y: 20, + }, + }, + }; + expect(actions.setWorkspaceViewportPosition({ + x: 20, + y: 20, + })).toEqual(expectedAction); + }); + }); + describe('toggleWorkspaceExposeMode', () => { + it('should set the exposeMode to true', () => { + const expectedAction = { + type: ActionTypes.TOGGLE_WORKSPACE_EXPOSE_MODE, + }; + expect(actions.toggleWorkspaceExposeMode()).toEqual(expectedAction); + }); + }); }); diff --git a/__tests__/src/components/App.test.js b/__tests__/src/components/App.test.js index 4a9b93966..3c34047dc 100644 --- a/__tests__/src/components/App.test.js +++ b/__tests__/src/components/App.test.js @@ -28,7 +28,7 @@ function createWrapper(props) { describe('App', () => { it('should render outer element correctly', () => { const wrapper = createWrapper(); - expect(wrapper.find('div.mirador-app').length).toBe(1); + expect(wrapper.find('div.mirador-viewer').length).toBe(1); }); it('should render all needed elements ', () => { diff --git a/__tests__/src/components/Window.test.js b/__tests__/src/components/Window.test.js index 301b75b78..da8c84ca4 100644 --- a/__tests__/src/components/Window.test.js +++ b/__tests__/src/components/Window.test.js @@ -17,7 +17,13 @@ function createWrapper(props, context) { describe('Window', () => { let wrapper; - const window = { id: 123, xywh: [0, 0, 400, 500] }; + const window = { + id: 123, + x: 2700, + y: 2700, + width: 400, + height: 400, + }; it('should render nothing, if provided with no window data', () => { wrapper = shallow(<Window />); expect(wrapper.find('.mirador-window')).toHaveLength(0); diff --git a/__tests__/src/components/Workspace.test.js b/__tests__/src/components/Workspace.test.js index 83ff8808f..92c40025b 100644 --- a/__tests__/src/components/Workspace.test.js +++ b/__tests__/src/components/Workspace.test.js @@ -1,6 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import WorkspaceMosaic from '../../../src/containers/WorkspaceMosaic'; +import WorkspaceElastic from '../../../src/containers/WorkspaceElastic'; import Window from '../../../src/containers/Window'; import { Workspace } from '../../../src/components/Workspace'; @@ -22,12 +23,23 @@ function createWrapper(props) { } describe('Workspace', () => { + describe('if workspace type is elastic', () => { + it('should render <WorkspaceElastic/> properly', () => { + const wrapper = createWrapper({ workspaceType: 'elastic' }); + + expect(wrapper.matchesElement( + <div className="mirador-workspace-viewport mirador-workspace-with-control-panel"> + <WorkspaceElastic /> + </div>, + )).toBe(true); + }); + }); describe('if workspace type is mosaic', () => { it('should render <WorkspaceMosaic/> properly', () => { const wrapper = createWrapper(); expect(wrapper.matchesElement( - <div className="mirador-workspace mirador-workspace-with-control-panel"> + <div className="mirador-workspace-viewport mirador-workspace-with-control-panel"> <WorkspaceMosaic windows={windows} /> </div>, )).toBe(true); @@ -38,7 +50,7 @@ describe('Workspace', () => { const wrapper = createWrapper({ workspaceType: 'bubu' }); expect(wrapper.matchesElement( - <div className="mirador-workspace mirador-workspace-with-control-panel"> + <div className="mirador-workspace-viewport mirador-workspace-with-control-panel"> <Window window={{ id: 1 }} /> <Window window={{ id: 2 }} /> </div>, diff --git a/__tests__/src/components/WorkspaceElastic.test.js b/__tests__/src/components/WorkspaceElastic.test.js new file mode 100644 index 000000000..9efa1145e --- /dev/null +++ b/__tests__/src/components/WorkspaceElastic.test.js @@ -0,0 +1,137 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { Rnd } from 'react-rnd'; +import WorkspaceElastic from '../../../src/components/WorkspaceElastic'; + +/** create wrapper */ +function createWrapper(props) { + return shallow( + <WorkspaceElastic + windows={{}} + workspace={{ + viewportPosition: { + x: 20, + y: 20, + }, + }} + setWorkspaceViewportPosition={() => {}} + setWindowSize={() => {}} + updateWindowPosition={() => {}} + {...props} + />, + ); +} + +describe('WorkspaceElastic', () => { + const windows = { + 1: { + id: 1, + x: 20, + y: 20, + width: 200, + height: 200, + }, + 2: { + id: 2, + x: 25, + y: 25, + width: 300, + height: 400, + }, + }; + let wrapper; + beforeEach(() => { + wrapper = createWrapper({ windows }); + }); + it('should render properly with an initialValue', () => { + expect(wrapper.find(Rnd).length).toBe(3); + expect(wrapper + .find(Rnd) + .at(1) + .props().size) + .toEqual({ + width: 200, + height: 200, + }); + expect(wrapper + .find(Rnd) + .at(2) + .props().position) + .toEqual({ + x: 25, + y: 25, + }); + expect(wrapper + .find(Rnd) + .at(2) + .props().size) + .toEqual({ + width: 300, + height: 400, + }); + }); + describe('window behaviour', () => { + it('when windows are dragged', () => { + const mockDragStop = jest.fn(); + wrapper = createWrapper({ + windows, + updateWindowPosition: mockDragStop, + }); + wrapper + .find(Rnd) + .at(1) + .props() + .onDragStop('myevent', { + x: 200, + y: 200, + }); + expect(mockDragStop).toHaveBeenCalledWith(1, { + x: 200, + y: 200, + }); + }); + it('when windows are resized', () => { + const mockOnResize = jest.fn(); + wrapper = createWrapper({ + windows, + setWindowSize: mockOnResize, + }); + wrapper + .find(Rnd) + .at(1) + .props() + .onResize('myevent', 'direction', { + style: { + width: 400, + height: 200, + }, + }); + expect(mockOnResize).toHaveBeenCalledWith(1, { + width: 400, + height: 200, + }); + }); + }); + + describe('workspace behaviour', () => { + it('when workspace itself is dragged', () => { + const mockDragStop = jest.fn(); + wrapper = createWrapper({ + windows, + setWorkspaceViewportPosition: mockDragStop, + }); + wrapper + .find(Rnd) + .at(0) + .props() + .onDragStop('myevent', { + x: 200, + y: 200, + }); + expect(mockDragStop).toHaveBeenCalledWith({ + x: 200, + y: 200, + }); + }); + }); +}); diff --git a/__tests__/src/reducers/windows.test.js b/__tests__/src/reducers/windows.test.js index b70ba0c02..410784716 100644 --- a/__tests__/src/reducers/windows.test.js +++ b/__tests__/src/reducers/windows.test.js @@ -170,4 +170,66 @@ describe('windows reducer', () => { expect(windowsReducer(beforeState, action)).toEqual(expectedState); }); }); + + it('should handle SET_WINDOW_SIZE', () => { + expect(windowsReducer({ + abc123: { + id: 'abc123', + }, + def456: { + id: 'def456', + }, + }, { + type: ActionTypes.SET_WINDOW_SIZE, + payload: { + windowId: 'abc123', + size: { + x: 20, + y: 20, + width: 200, + height: 200, + }, + }, + })).toEqual({ + abc123: { + id: 'abc123', + x: 20, + y: 20, + width: 200, + height: 200, + }, + def456: { + id: 'def456', + }, + }); + }); + + it('should handle UPDATE_WINDOW_POSITION', () => { + expect(windowsReducer({ + abc123: { + id: 'abc123', + }, + def456: { + id: 'def456', + }, + }, { + type: ActionTypes.UPDATE_WINDOW_POSITION, + payload: { + windowId: 'abc123', + position: { + x: 20, + y: 20, + }, + }, + })).toEqual({ + abc123: { + id: 'abc123', + x: 20, + y: 20, + }, + def456: { + id: 'def456', + }, + }); + }); }); diff --git a/__tests__/src/reducers/workspace.test.js b/__tests__/src/reducers/workspace.test.js index 26e02a0d8..9873e434d 100644 --- a/__tests__/src/reducers/workspace.test.js +++ b/__tests__/src/reducers/workspace.test.js @@ -42,4 +42,27 @@ describe('workspace reducer', () => { isWorkspaceAddVisible: true, }); }); + it('should handle SET_WORKSPACE_VIEWPORT_POSITION', () => { + expect(workspaceReducer([], { + type: ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION, + payload: { + position: { + x: 50, + y: 50, + }, + }, + })).toEqual({ + viewportPosition: { + x: 50, + y: 50, + }, + }); + }); + it('should handle TOGGLE_WORKSPACE_EXPOSE_MODE', () => { + expect(workspaceReducer([], { + type: ActionTypes.TOGGLE_WORKSPACE_EXPOSE_MODE, + })).toEqual({ + exposeModeOn: true, + }); + }); }); diff --git a/package.json b/package.json index 2e30890ca..7b64a3c37 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "react-mosaic-component": "^2.1.0", "react-placeholder": "^3.0.1", "react-redux": "^6.0.0", + "react-rnd": "^9.1.1", "react-virtualized": "^9.21.0", "redux": "4.0.1", "redux-devtools-extension": "^2.13.2", @@ -85,6 +86,7 @@ "jest": "^24.1.0", "jest-fetch-mock": "^2.1.1", "jest-puppeteer": "^4.0.0", + "jsdom": "13.2.0", "json-server": "^0.14.2", "puppeteer": "^1.12.0", "react-dev-utils": "^7.0.3", diff --git a/src/components/App.js b/src/components/App.js index 41712e1e7..44c84b030 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -57,7 +57,7 @@ export class App extends Component { }); return ( - <div className={classNames(classes.background, ns('app'))}> + <div className={classNames(classes.background, ns('viewer'))}> <I18nextProvider i18n={this.i18n}> <MuiThemeProvider theme={createMuiTheme(theme)}> <Fullscreen diff --git a/src/components/Workspace.js b/src/components/Workspace.js index 8de5b4976..bc8deccf6 100644 --- a/src/components/Workspace.js +++ b/src/components/Workspace.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import Window from '../containers/Window'; import WorkspaceMosaic from '../containers/WorkspaceMosaic'; +import WorkspaceElastic from '../containers/WorkspaceElastic'; import ns from '../config/css-ns'; /** @@ -17,6 +18,8 @@ export class Workspace extends React.Component { workspaceByType() { const { workspaceType, windows } = this.props; switch (workspaceType) { + case 'elastic': + return <WorkspaceElastic />; case 'mosaic': return <WorkspaceMosaic windows={windows} />; default: @@ -39,7 +42,7 @@ export class Workspace extends React.Component { <div className={ classNames( - ns('workspace'), + ns('workspace-viewport'), (isWorkspaceControlPanelVisible && ns('workspace-with-control-panel')), ) } diff --git a/src/components/WorkspaceElastic.js b/src/components/WorkspaceElastic.js new file mode 100644 index 000000000..65de11c19 --- /dev/null +++ b/src/components/WorkspaceElastic.js @@ -0,0 +1,84 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Rnd } from 'react-rnd'; +import Window from '../containers/Window'; +import ns from '../config/css-ns'; + +/** + * Represents a work area that contains any number of windows + * @memberof Workspace + * @private + */ +class WorkspaceElastic extends React.Component { + /** + */ + render() { + const { + workspace, + windows, + setWorkspaceViewportPosition, + updateWindowPosition, + setWindowSize, + } = this.props; + return ( + <Rnd + default={{ + width: 5000, + height: 5000, + }} + position={{ x: workspace.viewportPosition.x, y: workspace.viewportPosition.y }} + enableResizing={{ + top: false, + right: false, + bottom: false, + left: false, + topRight: false, + bottomRight: false, + bottomLeft: false, + topLeft: false, + }} + onDragStop={(e, d) => { + setWorkspaceViewportPosition({ x: d.x, y: d.y }); + }} + cancel={`.${ns('window')}`} + className={ns('workspace')} + > + { + Object.values(windows).map(window => ( + <Rnd + key={window.id} + size={{ width: window.width, height: window.height }} + position={{ x: window.x, y: window.y }} + bounds="parent" + onDragStop={(e, d) => { + updateWindowPosition(window.id, { x: d.x, y: d.y }); + }} + onResize={(e, direction, ref, delta, position) => { + setWindowSize(window.id, { + width: ref.style.width, + height: ref.style.height, + ...position, + }); + }} + dragHandleClassName={ns('window-top-bar')} + > + <Window + window={window} + /> + </Rnd> + )) + } + </Rnd> + ); + } +} + +WorkspaceElastic.propTypes = { + setWorkspaceViewportPosition: PropTypes.func.isRequired, + windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + workspace: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + updateWindowPosition: PropTypes.func.isRequired, + setWindowSize: PropTypes.func.isRequired, +}; + +export default WorkspaceElastic; diff --git a/src/config/settings.js b/src/config/settings.js index 5cb87c448..bbb9561d6 100644 --- a/src/config/settings.js +++ b/src/config/settings.js @@ -44,5 +44,5 @@ export default { }, workspaceControlPanel: { enabled: true, - } + }, }; diff --git a/src/containers/Window.js b/src/containers/Window.js index a3e940cf0..a6de2f963 100644 --- a/src/containers/Window.js +++ b/src/containers/Window.js @@ -2,6 +2,7 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { Window } from '../components/Window'; + /** * mapStateToProps - used to hook up connect to action creators * @memberof Window diff --git a/src/containers/WorkspaceElastic.js b/src/containers/WorkspaceElastic.js new file mode 100644 index 000000000..8c0b8d714 --- /dev/null +++ b/src/containers/WorkspaceElastic.js @@ -0,0 +1,50 @@ +import { compose } from 'redux'; +import { connect } from 'react-redux'; +import * as actions from '../state/actions'; +import WorkspaceElastic from '../components/WorkspaceElastic'; + +/** + * mapStateToProps - to hook up connect + * @memberof Workspace + * @private + */ +const mapStateToProps = state => ( + { + workspace: state.workspace, + windows: state.windows, + } +); + + +/** + * mapDispatchToProps - used to hook up connect to action creators + * @memberof Workspace + * @private + */ +const mapDispatchToProps = (dispatch, props) => ({ + setWorkspaceViewportPosition: (position) => { + dispatch( + actions.setWorkspaceViewportPosition(position), + ); + }, + toggleWorkspaceExposeMode: size => dispatch( + actions.toggleWorkspaceExposeMode(), + ), + updateWindowPosition: (windowId, position) => { + dispatch( + actions.updateWindowPosition(windowId, position), + ); + }, + setWindowSize: (windowId, size) => { + dispatch( + actions.setWindowSize(windowId, size), + ); + }, +}); + +const enhance = compose( + connect(mapStateToProps, mapDispatchToProps), + // further HOC go here +); + +export default enhance(WorkspaceElastic); diff --git a/src/state/actions/action-types.js b/src/state/actions/action-types.js index 6b72b8683..e6ebd50c6 100644 --- a/src/state/actions/action-types.js +++ b/src/state/actions/action-types.js @@ -6,9 +6,13 @@ const ActionTypes = { FOCUS_WINDOW: 'FOCUS_WINDOW', SET_WORKSPACE_FULLSCREEN: 'SET_WORKSPACE_FULLSCREEN', + SET_WORKSPACE_VIEWPORT_POSITION: 'SET_WORKSPACE_VIEWPORT_POSITION', + TOGGLE_WORKSPACE_EXPOSE_MODE: 'TOGGLE_WORKSPACE_EXPOSE_MODE', ADD_MANIFEST: 'ADD_MANIFEST', ADD_WINDOW: 'ADD_WINDOW', SET_CANVAS: 'SET_CANVAS', + UPDATE_WINDOW_POSITION: 'UPDATE_WINDOW_POSITION', + SET_WINDOW_SIZE: 'SET_WINDOW_SIZE', REMOVE_WINDOW: 'REMOVE_WINDOW', PICK_WINDOWING_SYSTEM: 'PICK_WINDOWING_SYSTEM', REQUEST_MANIFEST: 'REQUEST_MANIFEST', diff --git a/src/state/actions/window.js b/src/state/actions/window.js index afb5c766f..6df558240 100644 --- a/src/state/actions/window.js +++ b/src/state/actions/window.js @@ -26,7 +26,10 @@ export function addWindow(options) { manifestId: null, rangeId: null, thumbnailNavigationPosition: 'bottom', // bottom by default in settings.js - xywh: [0, 0, 400, 400], + width: 400, + height: 400, + x: 2700, + y: 2700, companionWindowIds: [], rotation: null, view: 'single', @@ -138,3 +141,37 @@ export function setWindowThumbnailPosition(windowId, position) { export function setWindowViewType(windowId, viewType) { return { type: ActionTypes.SET_WINDOW_VIEW_TYPE, windowId, viewType }; } + +/** + * updateWindowPosition - action creator + * + * @param {String} windowId + * @param {Array} position + * @memberof ActionCreators + */ +export function updateWindowPosition(windowId, position) { + return { + type: ActionTypes.UPDATE_WINDOW_POSITION, + payload: { + windowId, + position, + }, + }; +} + +/** + * setWindowSize - action creator + * + * @param {String} windowId + * @param {Object} size + * @memberof ActionCreators + */ +export function setWindowSize(windowId, size) { + return { + type: ActionTypes.SET_WINDOW_SIZE, + payload: { + windowId, + size, + }, + }; +} diff --git a/src/state/actions/workspace.js b/src/state/actions/workspace.js index b8b03d96e..6da0e4898 100644 --- a/src/state/actions/workspace.js +++ b/src/state/actions/workspace.js @@ -1,6 +1,5 @@ import ActionTypes from './action-types'; -/* eslint-disable import/prefer-default-export */ /** * setWorkspaceFullscreen - action creator * @@ -39,3 +38,33 @@ export function updateWorkspaceMosaicLayout(layout) { export function setWorkspaceAddVisibility(isWorkspaceAddVisible) { return { type: ActionTypes.SET_WORKSPACE_ADD_VISIBILITY, isWorkspaceAddVisible }; } + +/** + * setWorkspaceViewportPosition - action creator + * + * @param {Object} position + * @memberof ActionCreators + */ +export function setWorkspaceViewportPosition(position) { + return { + type: ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION, + payload: { + position: { + x: position.x, + y: position.y, + }, + }, + }; +} + +/** + * toggleWorkspaceExposeMode - action creator + * + * @param {Object} position + * @memberof ActionCreators + */ +export function toggleWorkspaceExposeMode() { + return { + type: ActionTypes.TOGGLE_WORKSPACE_EXPOSE_MODE, + }; +} diff --git a/src/state/reducers/windows.js b/src/state/reducers/windows.js index b5e0a2ba4..96a95cd81 100644 --- a/src/state/reducers/windows.js +++ b/src/state/reducers/windows.js @@ -55,6 +55,30 @@ export const windowsReducer = (state = {}, action) => { ), }, }; + case ActionTypes.UPDATE_WINDOW_POSITION: + return { + ...state, + [action.payload.windowId]: { + ...state[action.payload.windowId], + x: action.payload.position.x, + y: action.payload.position.y, + }, + }; + case ActionTypes.SET_WINDOW_SIZE: + return { + ...state, + [action.payload.windowId]: { + ...state[action.payload.windowId], + width: action.payload.size.width, + height: action.payload.size.height, + x: action.payload.size.x, + y: action.payload.size.y, + }, + }; + case ActionTypes.NEXT_CANVAS: + return setCanvasIndex(state, action.windowId, currentIndex => currentIndex + 1); + case ActionTypes.PREVIOUS_CANVAS: + return setCanvasIndex(state, action.windowId, currentIndex => currentIndex - 1); case ActionTypes.SET_CANVAS: return setCanvasIndex(state, action.windowId, currentIndex => action.canvasIndex); default: @@ -66,7 +90,7 @@ export const windowsReducer = (state = {}, action) => { * @param {Object} state * @param {String} windowId * @param {Function} getIndex - gets curent canvas index passed and should return new index -*/ + */ function setCanvasIndex(state, windowId, getIndex) { return Object.values(state).reduce((object, window) => { if (window.id === windowId) { diff --git a/src/state/reducers/workspace.js b/src/state/reducers/workspace.js index 52a339a47..6abaeb93a 100644 --- a/src/state/reducers/workspace.js +++ b/src/state/reducers/workspace.js @@ -3,7 +3,16 @@ import ActionTypes from '../actions/action-types'; /** * workspaceReducer */ -export const workspaceReducer = (state = {}, action) => { +export const workspaceReducer = ( + state = { // we'll need to abstract this more, methinks. + viewportPosition: { + x: -2500, + y: -2500, + }, + exposeModeOn: false, + }, + action, +) => { switch (action.type) { case ActionTypes.FOCUS_WINDOW: return { ...state, focusedWindowId: action.windowId }; @@ -15,6 +24,10 @@ export const workspaceReducer = (state = {}, action) => { return { ...state, layout: action.layout }; case ActionTypes.SET_WORKSPACE_ADD_VISIBILITY: return { ...state, isWorkspaceAddVisible: action.isWorkspaceAddVisible }; + case ActionTypes.SET_WORKSPACE_VIEWPORT_POSITION: + return { ...state, viewportPosition: action.payload.position }; + case ActionTypes.TOGGLE_WORKSPACE_EXPOSE_MODE: + return { ...state, exposeModeOn: !state.exposeModeOn }; default: return state; } diff --git a/src/styles/index.scss b/src/styles/index.scss index 60c03c799..97ac3b299 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,10 +1,13 @@ @import 'variables'; .mirador { - &-app { - height: 100%; - position: relative; - width: 100%; + &-viewer { + bottom: 0; + left: 0; + overflow: hidden; + position: absolute; + right: 0; + top: 0; .mosaic-window-toolbar { display: none; @@ -17,17 +20,22 @@ } } - &-workspace { + &-workspace-viewport { bottom: 0; - box-sizing: border-box; left: 0; - margin: 0; - overflow: scroll; + overflow: hidden; position: absolute; right: 0; top: 0; } + &-workspace { + box-sizing: border-box; + margin: 0; + overflow: scroll; + position: absolute; + } + &-workspace-with-control-panel { padding-left: 100px; // The width of the control panel } @@ -48,6 +56,7 @@ display: flex; flex-direction: column; height: 100%; + width: 100%; } &-osd-container { -- GitLab