DashboardGrid.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import React from 'react';
  2. import { hot } from 'react-hot-loader';
  3. import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
  4. import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants';
  5. import { DashboardPanel } from './DashboardPanel';
  6. import { DashboardModel, PanelModel } from '../state';
  7. import classNames from 'classnames';
  8. import sizeMe from 'react-sizeme';
  9. let lastGridWidth = 1200;
  10. let ignoreNextWidthChange = false;
  11. interface GridWrapperProps {
  12. size: { width: number; };
  13. layout: ReactGridLayout.Layout[];
  14. onLayoutChange: (layout: ReactGridLayout.Layout[]) => void;
  15. children: JSX.Element | JSX.Element[];
  16. onDragStop: ItemCallback;
  17. onResize: ItemCallback;
  18. onResizeStop: ItemCallback;
  19. onWidthChange: () => void;
  20. className: string;
  21. isResizable?: boolean;
  22. isDraggable?: boolean;
  23. isFullscreen?: boolean;
  24. }
  25. function GridWrapper({
  26. size,
  27. layout,
  28. onLayoutChange,
  29. children,
  30. onDragStop,
  31. onResize,
  32. onResizeStop,
  33. onWidthChange,
  34. className,
  35. isResizable,
  36. isDraggable,
  37. isFullscreen,
  38. }: GridWrapperProps) {
  39. const width = size.width > 0 ? size.width : lastGridWidth;
  40. // logic to ignore width changes (optimization)
  41. if (width !== lastGridWidth) {
  42. if (ignoreNextWidthChange) {
  43. ignoreNextWidthChange = false;
  44. } else if (!isFullscreen && Math.abs(width - lastGridWidth) > 8) {
  45. onWidthChange();
  46. lastGridWidth = width;
  47. }
  48. }
  49. return (
  50. <ReactGridLayout
  51. width={lastGridWidth}
  52. className={className}
  53. isDraggable={isDraggable}
  54. isResizable={isResizable}
  55. containerPadding={[0, 0]}
  56. useCSSTransforms={false}
  57. margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
  58. cols={GRID_COLUMN_COUNT}
  59. rowHeight={GRID_CELL_HEIGHT}
  60. draggableHandle=".grid-drag-handle"
  61. layout={layout}
  62. onResize={onResize}
  63. onResizeStop={onResizeStop}
  64. onDragStop={onDragStop}
  65. onLayoutChange={onLayoutChange}
  66. >
  67. {children}
  68. </ReactGridLayout>
  69. );
  70. }
  71. const SizedReactLayoutGrid = sizeMe({ monitorWidth: true })(GridWrapper);
  72. export interface DashboardGridProps {
  73. dashboard: DashboardModel;
  74. }
  75. export class DashboardGrid extends React.Component<DashboardGridProps> {
  76. gridToPanelMap: any;
  77. panelMap: { [id: string]: PanelModel };
  78. constructor(props: DashboardGridProps) {
  79. super(props);
  80. // subscribe to dashboard events
  81. const dashboard = this.props.dashboard;
  82. dashboard.on('panel-added', this.triggerForceUpdate);
  83. dashboard.on('panel-removed', this.triggerForceUpdate);
  84. dashboard.on('repeats-processed', this.triggerForceUpdate);
  85. dashboard.on('view-mode-changed', this.onViewModeChanged);
  86. dashboard.on('row-collapsed', this.triggerForceUpdate);
  87. dashboard.on('row-expanded', this.triggerForceUpdate);
  88. }
  89. buildLayout() {
  90. const layout = [];
  91. this.panelMap = {};
  92. for (const panel of this.props.dashboard.panels) {
  93. const stringId = panel.id.toString();
  94. this.panelMap[stringId] = panel;
  95. if (!panel.gridPos) {
  96. console.log('panel without gridpos');
  97. continue;
  98. }
  99. const panelPos: any = {
  100. i: stringId,
  101. x: panel.gridPos.x,
  102. y: panel.gridPos.y,
  103. w: panel.gridPos.w,
  104. h: panel.gridPos.h,
  105. };
  106. if (panel.type === 'row') {
  107. panelPos.w = GRID_COLUMN_COUNT;
  108. panelPos.h = 1;
  109. panelPos.isResizable = false;
  110. panelPos.isDraggable = panel.collapsed;
  111. }
  112. layout.push(panelPos);
  113. }
  114. return layout;
  115. }
  116. onLayoutChange = (newLayout: ReactGridLayout.Layout[]) => {
  117. for (const newPos of newLayout) {
  118. this.panelMap[newPos.i].updateGridPos(newPos);
  119. }
  120. this.props.dashboard.sortPanelsByGridPos();
  121. }
  122. triggerForceUpdate = () => {
  123. this.forceUpdate();
  124. }
  125. onWidthChange = () => {
  126. for (const panel of this.props.dashboard.panels) {
  127. panel.resizeDone();
  128. }
  129. }
  130. onViewModeChanged = () => {
  131. ignoreNextWidthChange = true;
  132. this.forceUpdate();
  133. }
  134. updateGridPos = (item: ReactGridLayout.Layout, layout: ReactGridLayout.Layout[]) => {
  135. this.panelMap[item.i].updateGridPos(item);
  136. // react-grid-layout has a bug (#670), and onLayoutChange() is only called when the component is mounted.
  137. // So it's required to call it explicitly when panel resized or moved to save layout changes.
  138. this.onLayoutChange(layout);
  139. }
  140. onResize: ItemCallback = (layout, oldItem, newItem) => {
  141. console.log();
  142. this.panelMap[newItem.i].updateGridPos(newItem);
  143. }
  144. onResizeStop: ItemCallback = (layout, oldItem, newItem) => {
  145. this.updateGridPos(newItem, layout);
  146. this.panelMap[newItem.i].resizeDone();
  147. }
  148. onDragStop: ItemCallback = (layout, oldItem, newItem) => {
  149. this.updateGridPos(newItem, layout);
  150. }
  151. renderPanels() {
  152. const panelElements = [];
  153. for (const panel of this.props.dashboard.panels) {
  154. const panelClasses = classNames({ 'react-grid-item--fullscreen': panel.fullscreen });
  155. panelElements.push(
  156. <div key={panel.id.toString()} className={panelClasses} id={`panel-${panel.id}`}>
  157. <DashboardPanel
  158. panel={panel}
  159. dashboard={this.props.dashboard}
  160. isEditing={panel.isEditing}
  161. isFullscreen={panel.fullscreen}
  162. />
  163. </div>
  164. );
  165. }
  166. return panelElements;
  167. }
  168. render() {
  169. return (
  170. <SizedReactLayoutGrid
  171. className={classNames({ layout: true })}
  172. layout={this.buildLayout()}
  173. isResizable={this.props.dashboard.meta.canEdit}
  174. isDraggable={this.props.dashboard.meta.canEdit}
  175. onLayoutChange={this.onLayoutChange}
  176. onWidthChange={this.onWidthChange}
  177. onDragStop={this.onDragStop}
  178. onResize={this.onResize}
  179. onResizeStop={this.onResizeStop}
  180. isFullscreen={this.props.dashboard.meta.fullscreen}
  181. >
  182. {this.renderPanels()}
  183. </SizedReactLayoutGrid>
  184. );
  185. }
  186. }
  187. export default hot(module)(DashboardGrid);