diff --git a/__tests__/src/actions/window.test.js b/__tests__/src/actions/window.test.js index 2dde5c7c11ba78e4f83b9e9cc8e93b369f3dbf30..7776bb9567d57ad31721232f9ef9f5bfd8b6661a 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 587f6709bda3cea36d6bcde701c6a0ec42cc362e..5f430096a1d0c3daf2baad8fa41da608b1a886ce 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 4a9b939663e61883d499bdd2edd2fefe41e1bdeb..3c34047dc72e8fa014b904e6bc8eae46d4669540 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 301b75b7852431700caa2758cd6a6463c80cf1a1..da8c84ca4ee286e648a1c10a5caabe6d050a8d17 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 83ff8808fdcaaf11e049ba712369c2a867d7c740..92c40025b979cf528eb3d06b5fea170556005890 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 0000000000000000000000000000000000000000..9efa1145efdb0192ca3d6d06d5b2979f08f147d7 --- /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 b70ba0c023a0724110b23ecdf9e22afa4fe9a30a..4107847164b411a87a5a14bb3f69f81307a7e0e1 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 26e02a0d877cb6704844bbe21a9319c369fd4e01..9873e434d0160319f421233d64e25bcfc374382f 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 2e30890ca30358fee9349ff9d5229dff6f098b93..7b64a3c3730e104bdf203d3a4d1451f69dfb0953 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 41712e1e7e3316f44d072a5cdfaaa67e11218842..44c84b0301c4155a8d7c4f0205099307f2085ac7 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 8de5b49767c52ee79eb5a3997dd393bac49b0b44..bc8deccf68bf624f5bbb2fe8eff41dbd5cd6bf12 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 0000000000000000000000000000000000000000..65de11c19693ac3567cfcba6aaed850c3d346357 --- /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 5cb87c448822c6eaa14ce75caa9c31eb786ae7a1..bbb9561d623d4830efbb965786ffb42c1b60cfb7 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 a3e940cf00ed0ac34f67a9c8f9017c1be59d0a59..a6de2f96331d7cfec0f22fadae6f72b81921189a 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 0000000000000000000000000000000000000000..8c0b8d714bb1d69c700178fd79aefac3612b1288 --- /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 6b72b8683796702452ba033cdd9fccf33d84e801..e6ebd50c6d6f7f35894f7e334631411df9afac2c 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 afb5c766f61eaea98e0ec3b6c4edca3861826e80..6df55824032c41bcea503d2082db2bc8dcda54bd 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 b8b03d96e4f23a21d85adc75c38f1fda046cb511..6da0e4898d127da4000cf334164bc1f670a71f1d 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 b5e0a2ba4bfb530900205452034e4a1c2f54496d..96a95cd8102517997298af7c850699ebcdcb0bfc 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 52a339a47828859267cab9085cf36b26890e7c95..6abaeb93abc2395bb2cf99343aa8cdaf5b67a3c2 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 60c03c799e62218a620bcd7e758537546a9f7101..97ac3b29902deed7ee26d88118f0b11b18492fef 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 {