| /** |
| * 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; |