blob: 6889c91ab3de222a305a661cf1ec9433c6462726 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { componentShape } from '../util/propShapes';
import DashboardComponent from '../containers/DashboardComponent';
import DragDroppable from './dnd/DragDroppable';
import { GRID_GUTTER_SIZE, GRID_COLUMN_COUNT } from '../util/constants';
const propTypes = {
depth: PropTypes.number.isRequired,
editMode: PropTypes.bool.isRequired,
gridComponent: componentShape.isRequired,
handleComponentDrop: PropTypes.func.isRequired,
isComponentVisible: PropTypes.bool.isRequired,
resizeComponent: PropTypes.func.isRequired,
setDirectPathToChild: PropTypes.func.isRequired,
width: PropTypes.number.isRequired,
};
const defaultProps = {};
class DashboardGrid extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isResizing: false,
rowGuideTop: null,
};
this.handleResizeStart = this.handleResizeStart.bind(this);
this.handleResize = this.handleResize.bind(this);
this.handleResizeStop = this.handleResizeStop.bind(this);
this.handleTopDropTargetDrop = this.handleTopDropTargetDrop.bind(this);
this.getRowGuidePosition = this.getRowGuidePosition.bind(this);
this.setGridRef = this.setGridRef.bind(this);
this.handleChangeTab = this.handleChangeTab.bind(this);
}
getRowGuidePosition(resizeRef) {
if (resizeRef && this.grid) {
return (
resizeRef.getBoundingClientRect().bottom -
this.grid.getBoundingClientRect().top -
2
);
}
return null;
}
setGridRef(ref) {
this.grid = ref;
}
handleResizeStart({ ref, direction }) {
let rowGuideTop = null;
if (direction === 'bottom' || direction === 'bottomRight') {
rowGuideTop = this.getRowGuidePosition(ref);
}
this.setState(() => ({
isResizing: true,
rowGuideTop,
}));
}
handleResize({ ref, direction }) {
if (direction === 'bottom' || direction === 'bottomRight') {
this.setState(() => ({ rowGuideTop: this.getRowGuidePosition(ref) }));
}
}
handleResizeStop({ id, widthMultiple: width, heightMultiple: height }) {
this.props.resizeComponent({ id, width, height });
this.setState(() => ({
isResizing: false,
rowGuideTop: null,
}));
}
handleTopDropTargetDrop(dropResult) {
if (dropResult) {
this.props.handleComponentDrop({
...dropResult,
destination: {
...dropResult.destination,
// force appending as the first child if top drop target
index: 0,
},
});
}
}
handleChangeTab({ pathToTabIndex }) {
this.props.setDirectPathToChild(pathToTabIndex);
}
render() {
const {
gridComponent,
handleComponentDrop,
depth,
editMode,
width,
isComponentVisible,
} = this.props;
const columnPlusGutterWidth =
(width + GRID_GUTTER_SIZE) / GRID_COLUMN_COUNT;
const columnWidth = columnPlusGutterWidth - GRID_GUTTER_SIZE;
const { isResizing, rowGuideTop } = this.state;
return width < 100 ? null : (
<div className="dashboard-grid" ref={this.setGridRef}>
<div className="grid-content" data-test="grid-content">
{/* make the area above components droppable */}
{editMode && (
<DragDroppable
component={gridComponent}
depth={depth}
parentComponent={null}
index={0}
orientation="column"
onDrop={this.handleTopDropTargetDrop}
className="empty-droptarget"
editMode
>
{({ dropIndicatorProps }) =>
dropIndicatorProps && (
<div className="drop-indicator drop-indicator--bottom" />
)
}
</DragDroppable>
)}
{gridComponent.children.map((id, index) => (
<DashboardComponent
key={id}
id={id}
parentId={gridComponent.id}
depth={depth + 1}
index={index}
availableColumnCount={GRID_COLUMN_COUNT}
columnWidth={columnWidth}
isComponentVisible={isComponentVisible}
onResizeStart={this.handleResizeStart}
onResize={this.handleResize}
onResizeStop={this.handleResizeStop}
onChangeTab={this.handleChangeTab}
/>
))}
{/* make the area below components droppable */}
{editMode && gridComponent.children.length > 0 && (
<DragDroppable
component={gridComponent}
depth={depth}
parentComponent={null}
index={gridComponent.children.length}
orientation="column"
onDrop={handleComponentDrop}
className="empty-droptarget"
editMode
>
{({ dropIndicatorProps }) =>
dropIndicatorProps && (
<div className="drop-indicator drop-indicator--top" />
)
}
</DragDroppable>
)}
{isResizing &&
Array(GRID_COLUMN_COUNT)
.fill(null)
.map((_, i) => (
<div
key={`grid-column-${i}`}
className="grid-column-guide"
style={{
left: i * GRID_GUTTER_SIZE + i * columnWidth,
width: columnWidth,
}}
/>
))}
{isResizing && rowGuideTop && (
<div
className="grid-row-guide"
style={{
top: rowGuideTop,
width,
}}
/>
)}
</div>
</div>
);
}
}
DashboardGrid.propTypes = propTypes;
DashboardGrid.defaultProps = defaultProps;
export default DashboardGrid;