react-grid-layout
一个好用的拖拽、自适应布局 react 插件
基本使用:
// 显示全部 chart 内容区域 import React,{PureComponent} from 'react'; import {Responsive, WidthProvider } from "react-grid-layout"; const ReactGridLayout = WidthProvider(Responsive); export default class ChartDashboard extends PureComponent { // 设置默认的 props static defaultProps = { className: "layout", rowHeight: 100, isDraggable: true, isResizable: true, onLayoutChange: function() {}, cols: 12, //布局中的列数 mounted: false }; constructor(props) { super(props); this.state = { newCounter: 0, content: [], } } componentDidMount () { this.setState({ mounted: true }) } componentWillReceiveProps(props) { // ... } // 使用从此返回的 cols 来计算添加的新项目的位置 onBreakpointChange = (breakpoint, cols) => { // ... } // 删除项目 onRemoveItem = (i) => { // ... } // 改变某一个项目 onLayoutChange = (layout) => { let layoutObj = {}; layout.forEach(({i, x, y, w, h})=>{ layoutObj[i] = {i, x, y, w, h} }) // ... } // 改变 div 大小 onResizeStop(a,b,) { // ... } render() { return <ReactGridLayout measureBeforeMount={this.state.mounted} useCSSTransforms={this.state.mounted} draggableCancel='禁止拖拽的元素的类名' margin={[8,8]} draggableHandle='.drag-element' ref='ReactGridLayoutRef' onResizeStop={this.onResizeStop} onLayoutChange={this.onLayoutChange} onBreakpointChange={this.onBreakpointChange}> {this.state.content} </ReactGridLayout> } }
如何设置 change 时的背影:
.react-grid-layout { position: relative; transition: height 200ms ease; } .react-grid-item { transition: all 200ms ease; transition-property: left, top; } .react-grid-item.cssTransforms { transition-property: transform; } .react-grid-item.resizing { z-index: 1; } .react-grid-item.react-draggable-dragging { transition: none; z-index: 3; } .react-grid-item.react-grid-placeholder { background: #000; opacity: 0.1; transition-duration: 100ms; z-index: 2; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; } .react-grid-item:hover > .react-resizable-handle { position: absolute; width: 20px; height: 20px; bottom: 0; right: 0; background: url(图片); background-position: bottom right; padding: 0 3px 3px 0; background-repeat: no-repeat; background-origin: content-box; box-sizing: border-box; cursor: se-resize; }
我的某个项目源码:
// 显示全部 chart 内容区域 import React,{PureComponent} from 'react'; import echarts from 'echarts/lib/echarts'; //必须 import { connect } from 'react-redux'; import {Responsive, WidthProvider } from "react-grid-layout"; import ChartContent from './chartContent'; import configureStore from '@/store/configureStore'; import {getDashboardChartInfo} from '@/actions'; import Request from '../request'; import Editor from './quill'; import deletePng from '@/common/images/delete.png'; import {Tooltip} from 'antd'; const ReactGridLayout = WidthProvider(Responsive); class ChartDashboard extends PureComponent { // 设置默认的 props static defaultProps = { className: "layout", rowHeight: 100, isDraggable: true, isResizable: true, onLayoutChange: function() {}, cols: 12, //布局中的列数 mounted: false }; constructor(props) { super(props); this.state = { newCounter: 0, content: [], } this.cacheContent = []; this.onLayoutChange = this.onLayoutChange.bind(this); this.onResizeStop = this.onResizeStop.bind(this); this.getContent = this.getContent.bind(this); this.onBreakpointChange = this.onBreakpointChange.bind(this); } componentDidMount () { this.setState({ mounted: true }) } componentWillReceiveProps(props) { let {dashboardChartInfo} = props; let content = dashboardChartInfo && dashboardChartInfo.length > 0 ? this.getContent(dashboardChartInfo) : [] this.setState ({ dashboardChartInfo:dashboardChartInfo, content: content }) } // 使用从此返回的 cols 来计算添加的新项目的位置 onBreakpointChange(breakpoint, cols) { console.log(breakpoint,cols) } // 删除项目 onRemoveItem(i) { let curContent = [...this.state.content]; let stateChartInfo = [...this.state.dashboardChartInfo]; curContent.deleteObjectInArray('key',i); stateChartInfo.deleteObjectInArray('position','i',i) this.updateDashboardInfo(stateChartInfo); return false; } // 改变某一个项目 onLayoutChange(layout) { let layoutObj = {}; layout.forEach(({i, x, y, w, h})=>{ layoutObj[i] = {i, x, y, w, h} }) // state 中保存的 dashboardChartInfo 信息 let stateChartInfo = this.state.dashboardChartInfo; let dashboardChartInfo = stateChartInfo.map(val=>{ let position = layoutObj[val.position.i]; return {id:val.id,position,type:val.type||'chart'} }); if (this.isContIdentical(stateChartInfo, dashboardChartInfo)) { this.updateDashboardInfo(dashboardChartInfo); } } // 改变 div 大小,重新渲染 echarts 图表 onResizeStop(a,b,) { let idname = `.react-grid-layout div[data-id="${b.i}"]`; let parentDom = document.querySelector(idname); // let parentDom = event.target.parentElement; let echartDom = parentDom.querySelector('div[_echarts_instance_]'); if(echartDom) { let echartObj = echarts.getInstanceByDom(echartDom); echartObj.resize(); } } getContent(dashboardChartInfo) { return dashboardChartInfo.map((val,index)=>{ if(val.position.y === null) { val.position.y = Infinity; } if(val.type=='text') { return (<div className="drag-element" key={val.position.i} data-id={val.position.i} data-grid={{...val.position}}> <Editor placeholder='请输入内容...' id={val.id} dashboardChartInfo={dashboardChartInfo} user_chart_set_id={this.props.dashboardInfo && this.props.dashboardInfo.userChartSetId} curContent={val.content}/> <Tooltip placement="top" title='删除'><img src={deletePng} className="remove" key={val.position.i} onClick={this.onRemoveItem.bind(this,val.position.i)}/></Tooltip> </div>) }else { return (<div className="drag-element" key={val.position.i} data-id={val.position.i} data-grid={{...val.position}}> <ChartContent id={val.id} history={this.props.history}/> <Tooltip placement="top" title='删除'><img src={deletePng} className="remove" key={val.position.i} onClick={this.onRemoveItem.bind(this,val.position.i)}/></Tooltip> </div>) } }) } isContIdentical(cacheContent, content) { if (cacheContent.length === content.length) { for (let i = cacheContent.length - 1; i >= 0; i --) { let cacheData = cacheContent[i].position; let contData = content[i].position; let flag = cacheData.i === contData.i && cacheData.x === contData.x && cacheData.y === contData.y && cacheData.w === contData.w && cacheData.h === contData.h; if (!flag) return true; } return false; } return true; } /** * 更新 dashboard 信息 * @param {} user_chart_set_info */ updateDashboardInfo(user_chart_set_info) { Request.selectDashboardInfo({ operationType: 'edit', user_chart_set_id: this.props.dashboardInfo.userChartSetId, user_chart_set_info: JSON.stringify(user_chart_set_info) },(data)=>{ if(data.code == 1) { this.setState ({ dashboardChartInfo:user_chart_set_info, }) configureStore.dispatch(getDashboardChartInfo(user_chart_set_info)); } }) } render() { return <div className='right-dashboard' style={{ paddingRight: '30px', overflow: 'auto', marginBottom: '20px' }}> <ReactGridLayout style={{paddingLeft: '8px'}} measureBeforeMount={this.state.mounted} useCSSTransforms={this.state.mounted} draggableCancel='.map,.quill,.remove,.edit-chart' margin={[8,8]} draggableHandle='.drag-element' ref='ReactGridLayoutRef' onResizeStop={this.onResizeStop} onLayoutChange={this.onLayoutChange} onBreakpointChange={this.onBreakpointChange}> {this.state.content} </ReactGridLayout> </div> } } const mapStateToProps = (state) => { return { dashboardChartInfo: state.DashBoard.dashboardChartInfo, dashboardInfo: state.DashBoard.dashboardInfo } } export default connect(mapStateToProps)(ChartDashboard); /** * 删除数组中的某个对象 * @param {*} key1 * @param {*} key2 * @param {*} key3 */ Array.prototype.deleteObjectInArray = function (key1,key2,key3) { if(arguments.length == 3) { this.forEach((val,index)=>{ if(val[key1][key2] == key3) { this.splice(index,1) } }); }else { this.forEach((val,index)=>{ if(val[key1] == key2) { this.splice(index,1) } }); } return this; }
学习、交流、记录、编辑一部自己的知识库