Skip to content

Commit

Permalink
Merge pull request #2397 from ProjectMirador/2380-add-window
Browse files Browse the repository at this point in the history
Don't reshuffle when a window is added, and add them in a specific order
  • Loading branch information
cbeer authored Apr 8, 2019
2 parents c34cd71 + 59846d4 commit 38c943d
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 17 deletions.
1 change: 1 addition & 0 deletions __tests__/src/actions/window.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('window actions', () => {
collectionIndex: 0,
height: 400,
id: 'helloworld',
layoutOrder: 3,
manifestId: null,
maximized: false,
rangeId: null,
Expand Down
6 changes: 5 additions & 1 deletion __tests__/src/components/WorkspaceMosaic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ describe('WorkspaceMosaic', () => {
});
});
it('by default use workspace.layout', () => {
wrapper = createWrapper({ windows: {}, workspace: { layout: 'foo' } });
wrapper = createWrapper({ windows: { foo: 'bar' }, workspace: { layout: 'foo' } });
expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
});
it('generates a new layout if windows do not match current layout', () => {
wrapper = createWrapper({ windows: { foo: 'bar' }, workspace: { layout: { first: 'foo', second: 'bark' } } });
expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
});
it('when window ids match workspace layout', () => {
Expand Down
51 changes: 51 additions & 0 deletions __tests__/src/lib/MosaicLayout.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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');
});
});
});
36 changes: 20 additions & 16 deletions src/components/WorkspaceMosaic.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import PropTypes from 'prop-types';
import {
Mosaic, MosaicWindow, getLeaves, createBalancedTreeFromLeaves,
} from 'react-mosaic-component';
import { createRemoveUpdate, updateTree } from 'react-mosaic-component/lib/util/mosaicUpdates';
import 'react-mosaic-component/react-mosaic-component.css';
import difference from 'lodash/difference';
import toPairs from 'lodash/toPairs';
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
Expand Down Expand Up @@ -48,14 +49,10 @@ export class WorkspaceMosaic extends React.Component {
return;
}

// Generate a set of "removeUpdates" to update layout binary tree
const removedWindows = difference(prevWindows, currentWindows);
const removeUpdates = removedWindows
.map(windowId => (
createRemoveUpdate(workspace.layout, this.windowPaths[windowId])
));
const newTree = updateTree(workspace.layout, removeUpdates);
updateWorkspaceMosaicLayout(newTree);
const layout = new MosaicLayout(workspace.layout);
layout.removeWindows(removedWindows, this.windowPaths);
updateWorkspaceMosaicLayout(layout.layout);
}
// Handles when Windows are added (not via Add Resource UI)
// TODO: If a window is added, add it in a better way #2380
Expand All @@ -76,22 +73,29 @@ export class WorkspaceMosaic extends React.Component {

/**
* Used to determine whether or not a "new" layout should be autogenerated.
* TODO: If a window is added, add it in a better way #2380
*/
determineWorkspaceLayout() {
const { windows, workspace } = this.props;
const windowKeys = Object.keys(windows).sort();
const sortedWindows = toPairs(windows)
.sort((a, b) => a.layoutOrder - b.layoutOrder).map(val => val[0]);
const leaveKeys = getLeaves(workspace.layout);
// Windows were added
if (!windowKeys.every(e => leaveKeys.includes(e))) {
if (!sortedWindows.every(e => leaveKeys.includes(e))) {
// No current layout, so just generate a new one
if (leaveKeys.length === 0) {
return createBalancedTreeFromLeaves(windowKeys);
if (leaveKeys.length < 2) {
return createBalancedTreeFromLeaves(sortedWindows);
}
// TODO: Here is where we will determine where to add a new Window #2380
return createBalancedTreeFromLeaves(windowKeys);
// Add new windows to layout
const addedWindows = difference(sortedWindows, leaveKeys);
const layout = new MosaicLayout(workspace.layout);
layout.addWindows(addedWindows);
return layout.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 => sortedWindows.includes(e))) {
return createBalancedTreeFromLeaves(sortedWindows);
}

return workspace.layout;
}

Expand Down
79 changes: 79 additions & 0 deletions src/lib/MosaicLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
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);
}
}
1 change: 1 addition & 0 deletions src/state/actions/window.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function addWindow(options) {
displayAllAnnotations: config.displayAllAnnotations || false,
height: 400,
id: `window-${uuid()}`,
layoutOrder: numWindows + 1,
manifestId: null,
maximized: false,
rangeId: null,
Expand Down

0 comments on commit 38c943d

Please sign in to comment.