Select Git revision
WorkspaceMosaic.js
WorkspaceMosaic.js 5.14 KiB
import React from 'react';
import PropTypes from 'prop-types';
import {
MosaicWithoutDragDropContext, MosaicWindow, getLeaves, createBalancedTreeFromLeaves,
} from 'react-mosaic-component';
import 'react-mosaic-component/react-mosaic-component.css';
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 React.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 = <div />;
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 (
<div className="mosaic-preview" aria-hidden>
<MosaicRenderPreview windowId={mosaicProps.windowId} />
</div>
);
}
/**
* 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 (
<MosaicWindow
toolbarControls={this.toolbarControls}
additionalControls={this.additionalControls}
path={path}
windowId={id}
renderPreview={WorkspaceMosaic.renderPreview}
>
<Window
key={`${id}-${workspaceId}`}
windowId={id}
/>
</MosaicWindow>
);
}
/**
* Update the redux store when the Mosaic is changed.
*/
mosaicChange(newLayout) {
const { updateWorkspaceMosaicLayout } = this.props;
updateWorkspaceMosaicLayout(newLayout);
}
/**
*/
render() {
const { layout, classes } = this.props;
return (
<MosaicWithoutDragDropContext
renderTile={this.tileRenderer}
initialValue={layout || this.determineWorkspaceLayout()}
onChange={this.mosaicChange}
className={classNames('mirador-mosaic', classes.root)}
zeroStateView={this.zeroStateView}
/>
);
}
}
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: [],
};