This is an automated email from the ASF dual-hosted git repository. ccwilliams pushed a commit to branch chris--grid-root-and-spacer in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
commit 1a4bc3172d0b992c91b6ef9b05af83fcbd1f7e8d Author: Chris Williams <chris.willi...@airbnb.com> AuthorDate: Tue Apr 3 15:56:07 2018 -0700 [edit mode] add edit mode to redux and propogate to all <DashboardComponent />s --- superset/assets/javascripts/dashboard/index.jsx | 1 + .../javascripts/dashboard/v2/actions/editMode.js | 9 ++++++ .../dashboard/v2/components/DashboardBuilder.jsx | 16 ++++++---- .../dashboard/v2/components/DashboardGrid.jsx | 10 +++--- .../dashboard/v2/components/DashboardHeader.jsx | 10 +++--- .../dashboard/v2/components/dnd/DragDroppable.jsx | 4 +++ .../v2/components/gridComponents/Chart.jsx | 17 ++++++---- .../v2/components/gridComponents/Column.jsx | 22 ++++++++----- .../v2/components/gridComponents/Divider.jsx | 10 ++++-- .../v2/components/gridComponents/Header.jsx | 13 +++++--- .../dashboard/v2/components/gridComponents/Row.jsx | 21 +++++++----- .../dashboard/v2/components/gridComponents/Tab.jsx | 6 +++- .../v2/components/gridComponents/Tabs.jsx | 25 +++++++++------ .../gridComponents/new/DraggableNewComponent.jsx | 1 + .../v2/components/menu/WithPopoverMenu.jsx | 37 ++++++++++++++-------- .../v2/components/resizable/ResizableContainer.jsx | 14 ++++++-- .../dashboard/v2/containers/DashboardBuilder.jsx | 3 +- .../dashboard/v2/containers/DashboardComponent.jsx | 3 +- .../dashboard/v2/containers/DashboardGrid.jsx | 2 +- .../dashboard/v2/containers/DashboardHeader.jsx | 6 +++- .../javascripts/dashboard/v2/reducers/editMode.js | 11 +++++++ .../javascripts/dashboard/v2/reducers/index.js | 2 ++ .../dashboard/v2/stylesheets/builder.less | 6 ++-- .../dashboard/v2/stylesheets/components/chart.less | 4 +-- .../v2/stylesheets/components/column.less | 17 ++++++++++ .../v2/stylesheets/components/divider.less | 2 +- .../dashboard/v2/stylesheets/components/row.less | 18 +++++++++++ .../javascripts/dashboard/v2/stylesheets/grid.less | 24 +++----------- 28 files changed, 211 insertions(+), 103 deletions(-) diff --git a/superset/assets/javascripts/dashboard/index.jsx b/superset/assets/javascripts/dashboard/index.jsx index 926f6b2..f7471f5 100644 --- a/superset/assets/javascripts/dashboard/index.jsx +++ b/superset/assets/javascripts/dashboard/index.jsx @@ -25,6 +25,7 @@ const initState = { present: emptyDashboardLayout, future: [], }, + editMode: true, }; const store = createStore( diff --git a/superset/assets/javascripts/dashboard/v2/actions/editMode.js b/superset/assets/javascripts/dashboard/v2/actions/editMode.js new file mode 100644 index 0000000..0a849ea --- /dev/null +++ b/superset/assets/javascripts/dashboard/v2/actions/editMode.js @@ -0,0 +1,9 @@ +export const SET_EDIT_MODE = 'SET_EDIT_MODE'; +export function setEditMode(editMode) { + return { + type: SET_EDIT_MODE, + payload: { + editMode, + }, + }; +} diff --git a/superset/assets/javascripts/dashboard/v2/components/DashboardBuilder.jsx b/superset/assets/javascripts/dashboard/v2/components/DashboardBuilder.jsx index 2a0bfe7..fc938b1 100644 --- a/superset/assets/javascripts/dashboard/v2/components/DashboardBuilder.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/DashboardBuilder.jsx @@ -1,3 +1,4 @@ +import cx from 'classnames'; import React from 'react'; import PropTypes from 'prop-types'; import HTML5Backend from 'react-dnd-html5-backend'; @@ -18,11 +19,10 @@ import { } from '../util/constants'; const propTypes = { - editMode: PropTypes.bool, - // redux dashboardLayout: PropTypes.object.isRequired, deleteTopLevelTabs: PropTypes.func.isRequired, + editMode: PropTypes.bool.isRequired, handleComponentDrop: PropTypes.func.isRequired, }; @@ -52,7 +52,7 @@ class DashboardBuilder extends React.Component { render() { const { tabIndex } = this.state; - const { handleComponentDrop, dashboardLayout, deleteTopLevelTabs } = this.props; + const { handleComponentDrop, dashboardLayout, deleteTopLevelTabs, editMode } = this.props; const dashboardRoot = dashboardLayout[DASHBOARD_ROOT_ID]; const rootChildId = dashboardRoot.children[0]; const topLevelTabs = rootChildId !== DASHBOARD_GRID_ID && dashboardLayout[rootChildId]; @@ -64,8 +64,8 @@ class DashboardBuilder extends React.Component { const gridComponent = dashboardLayout[gridComponentId]; return ( - <div className="dashboard-v2"> - {topLevelTabs ? ( // you cannot drop on/displace tabs if they already exist + <div className={cx('dashboard-v2', editMode && 'dashboard-v2--editing')}> + {topLevelTabs || !editMode ? ( // you cannot drop on/displace tabs if they already exist <DashboardHeader /> ) : ( <DragDroppable @@ -75,6 +75,7 @@ class DashboardBuilder extends React.Component { index={0} orientation="column" onDrop={handleComponentDrop} + editMode > {({ dropIndicatorProps }) => ( <div> @@ -94,6 +95,7 @@ class DashboardBuilder extends React.Component { onClick={deleteTopLevelTabs} />, ]} + editMode={editMode} > <DashboardComponent id={topLevelTabs.id} @@ -105,12 +107,12 @@ class DashboardBuilder extends React.Component { /> </WithPopoverMenu>} - <div className="dashboard-builder"> + <div className="dashboard-content"> <DashboardGrid gridComponent={gridComponent} depth={DASHBOARD_ROOT_DEPTH + 1} /> - <BuilderComponentPane /> + {editMode && <BuilderComponentPane />} </div> </div> ); diff --git a/superset/assets/javascripts/dashboard/v2/components/DashboardGrid.jsx b/superset/assets/javascripts/dashboard/v2/components/DashboardGrid.jsx index cfe99c7..9f4cb93 100644 --- a/superset/assets/javascripts/dashboard/v2/components/DashboardGrid.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/DashboardGrid.jsx @@ -13,6 +13,7 @@ import { const propTypes = { depth: PropTypes.number.isRequired, + editMode: PropTypes.bool.isRequired, gridComponent: componentShape.isRequired, handleComponentDrop: PropTypes.func.isRequired, resizeComponent: PropTypes.func.isRequired, @@ -70,7 +71,7 @@ class DashboardGrid extends React.PureComponent { } render() { - const { gridComponent, handleComponentDrop, depth } = this.props; + const { gridComponent, handleComponentDrop, depth, editMode } = this.props; const { isResizing, rowGuideTop } = this.state; return ( @@ -99,18 +100,19 @@ class DashboardGrid extends React.PureComponent { ))} {/* render an empty drop target */} - {gridComponent.children.length === 0 && + {editMode && <DragDroppable component={gridComponent} depth={depth} parentComponent={null} - index={0} + index={gridComponent.children.length} orientation="column" onDrop={handleComponentDrop} className="empty-grid-droptarget" + editMode > {({ dropIndicatorProps }) => dropIndicatorProps && - <div {...dropIndicatorProps} />} + <div className="drop-indicator drop-indicator--top" />} </DragDroppable>} {isResizing && Array(GRID_COLUMN_COUNT).fill(null).map((_, i) => ( diff --git a/superset/assets/javascripts/dashboard/v2/components/DashboardHeader.jsx b/superset/assets/javascripts/dashboard/v2/components/DashboardHeader.jsx index 2c89f33..ca204e5 100644 --- a/superset/assets/javascripts/dashboard/v2/components/DashboardHeader.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/DashboardHeader.jsx @@ -7,8 +7,7 @@ import { componentShape } from '../util/propShapes'; import EditableTitle from '../../../components/EditableTitle'; const propTypes = { - // editMode: PropTypes.bool.isRequired, - // setEditMode: PropTypes.func.isRequired, + editMode: PropTypes.bool.isRequired, component: componentShape.isRequired, // redux @@ -17,6 +16,7 @@ const propTypes = { onRedo: PropTypes.func.isRequired, canUndo: PropTypes.bool.isRequired, canRedo: PropTypes.bool.isRequired, + setEditMode: PropTypes.func.isRequired, }; class DashboardHeader extends React.Component { @@ -27,8 +27,7 @@ class DashboardHeader extends React.Component { } toggleEditMode() { - console.log('@TODO toggleEditMode'); - // this.props.setEditMode(!this.props.editMode); + this.props.setEditMode(!this.props.editMode); } handleChangeText(nextText) { @@ -47,8 +46,7 @@ class DashboardHeader extends React.Component { } render() { - const { component, onUndo, onRedo, canUndo, canRedo } = this.props; - const editMode = true; + const { component, onUndo, onRedo, canUndo, canRedo, editMode } = this.props; return ( <div className="dashboard-header"> diff --git a/superset/assets/javascripts/dashboard/v2/components/dnd/DragDroppable.jsx b/superset/assets/javascripts/dashboard/v2/components/dnd/DragDroppable.jsx index 89664e5..775e092 100644 --- a/superset/assets/javascripts/dashboard/v2/components/dnd/DragDroppable.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/dnd/DragDroppable.jsx @@ -18,6 +18,7 @@ const propTypes = { index: PropTypes.number.isRequired, style: PropTypes.object, onDrop: PropTypes.func, + editMode: PropTypes.bool.isRequired, // from react-dnd isDragging: PropTypes.bool.isRequired, @@ -70,8 +71,11 @@ class DragDroppable extends React.Component { isDragging, isDraggingOver, style, + editMode, } = this.props; + if (!editMode) return children({}); + const { dropIndicator } = this.state; return ( diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Chart.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Chart.jsx index 7ca506d..668d268 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Chart.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Chart.jsx @@ -9,10 +9,7 @@ import ResizableContainer from '../resizable/ResizableContainer'; import WithPopoverMenu from '../menu/WithPopoverMenu'; import { componentShape } from '../../util/propShapes'; import { ROW_TYPE } from '../../util/componentTypes'; -import { - GRID_MIN_COLUMN_COUNT, - GRID_MIN_ROW_UNITS, -} from '../../util/constants'; +import { GRID_MIN_COLUMN_COUNT, GRID_MIN_ROW_UNITS } from '../../util/constants'; const propTypes = { id: PropTypes.string.isRequired, @@ -21,6 +18,7 @@ const propTypes = { parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, depth: PropTypes.number.isRequired, + editMode: PropTypes.bool.isRequired, // grid related availableColumnCount: PropTypes.number.isRequired, @@ -71,6 +69,7 @@ class Chart extends React.Component { onResize, onResizeStop, handleComponentDrop, + editMode, } = this.props; return ( @@ -82,6 +81,7 @@ class Chart extends React.Component { depth={depth} onDrop={handleComponentDrop} disableDragDrop={isFocused} + editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => ( <ResizableContainer @@ -97,16 +97,19 @@ class Chart extends React.Component { onResizeStart={onResizeStart} onResize={onResize} onResizeStop={onResizeStop} + editMode={editMode} > - <HoverMenu innerRef={dragSourceRef} position="top"> - <DragHandle position="top" /> - </HoverMenu> + {editMode && + <HoverMenu innerRef={dragSourceRef} position="top"> + <DragHandle position="top" /> + </HoverMenu>} <WithPopoverMenu onChangeFocus={this.handleChangeFocus} menuItems={[ <DeleteComponentButton onDelete={this.handleDeleteComponent} />, ]} + editMode={editMode} > <div className="dashboard-component dashboard-component-chart"> <div className="fa fa-area-chart" /> diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Column.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Column.jsx index bf33710..fe5a721 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Column.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Column.jsx @@ -24,6 +24,7 @@ const propTypes = { parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, depth: PropTypes.number.isRequired, + editMode: PropTypes.bool.isRequired, // grid related availableColumnCount: PropTypes.number.isRequired, @@ -90,6 +91,7 @@ class Column extends React.PureComponent { onResize, onResizeStop, handleComponentDrop, + editMode, } = this.props; const columnItems = columnComponent.children || []; @@ -105,6 +107,7 @@ class Column extends React.PureComponent { index={index} depth={depth} onDrop={handleComponentDrop} + editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => ( <ResizableContainer @@ -118,6 +121,7 @@ class Column extends React.PureComponent { onResizeStart={onResizeStart} onResize={onResize} onResizeStop={onResizeStop} + editMode={editMode} > <WithPopoverMenu isFocused={this.state.isFocused} @@ -130,6 +134,7 @@ class Column extends React.PureComponent { onChange={this.handleChangeBackground} />, ]} + editMode={editMode} > <div className={cx( @@ -138,14 +143,15 @@ class Column extends React.PureComponent { backgroundStyle.className, )} > - <HoverMenu innerRef={dragSourceRef} position="top"> - <DragHandle position="top" /> - <DeleteComponentButton onDelete={this.handleDeleteComponent} /> - <IconButton - onClick={this.handleChangeFocus} - className="fa fa-cog" - /> - </HoverMenu> + {editMode && + <HoverMenu innerRef={dragSourceRef} position="top"> + <DragHandle position="top" /> + <DeleteComponentButton onDelete={this.handleDeleteComponent} /> + <IconButton + onClick={this.handleChangeFocus} + className="fa fa-cog" + /> + </HoverMenu>} {columnItems.map((componentId, itemIndex) => ( <DashboardComponent diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Divider.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Divider.jsx index ff29c3f..b3010e9 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Divider.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Divider.jsx @@ -13,6 +13,7 @@ const propTypes = { depth: PropTypes.number.isRequired, parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, + editMode: PropTypes.bool.isRequired, handleComponentDrop: PropTypes.func.isRequired, deleteComponent: PropTypes.func.isRequired, }; @@ -35,6 +36,7 @@ class Divider extends React.PureComponent { parentComponent, index, handleComponentDrop, + editMode, } = this.props; return ( @@ -45,12 +47,14 @@ class Divider extends React.PureComponent { index={index} depth={depth} onDrop={handleComponentDrop} + editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => ( <div ref={dragSourceRef}> - <HoverMenu position="left"> - <DeleteComponentButton onDelete={this.handleDeleteComponent} /> - </HoverMenu> + {editMode && + <HoverMenu position="left"> + <DeleteComponentButton onDelete={this.handleDeleteComponent} /> + </HoverMenu>} <div className="dashboard-component dashboard-component-divider" /> diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Header.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Header.jsx index d8744d6..594cf6b 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Header.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Header.jsx @@ -22,6 +22,7 @@ const propTypes = { depth: PropTypes.number.isRequired, parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, + editMode: PropTypes.bool.isRequired, // redux handleComponentDrop: PropTypes.func.isRequired, @@ -79,6 +80,7 @@ class Header extends React.PureComponent { parentComponent, index, handleComponentDrop, + editMode, } = this.props; const headerStyle = headerStyleOptions.find( @@ -98,12 +100,14 @@ class Header extends React.PureComponent { depth={depth} onDrop={handleComponentDrop} disableDragDrop={isFocused} + editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => ( <div ref={dragSourceRef}> - <HoverMenu position="left"> - <DragHandle position="left" /> - </HoverMenu> + {editMode && + <HoverMenu position="left"> + <DragHandle position="left" /> + </HoverMenu>} <WithPopoverMenu onChangeFocus={this.handleChangeFocus} @@ -122,6 +126,7 @@ class Header extends React.PureComponent { />, <DeleteComponentButton onDelete={this.handleDeleteComponent} />, ]} + editMode={editMode} > <div className={cx( @@ -133,7 +138,7 @@ class Header extends React.PureComponent { > <EditableTitle title={component.meta.text} - canEdit={isFocused} + canEdit={editMode && isFocused} onSaveTitle={this.handleChangeText} showTooltip={false} /> diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Row.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Row.jsx index 99296dd..9866bc8 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Row.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Row.jsx @@ -22,6 +22,7 @@ const propTypes = { parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, depth: PropTypes.number.isRequired, + editMode: PropTypes.bool.isRequired, // grid related availableColumnCount: PropTypes.number.isRequired, @@ -90,6 +91,7 @@ class Row extends React.PureComponent { onResize, onResizeStop, handleComponentDrop, + editMode, } = this.props; const rowItems = rowComponent.children || []; @@ -106,6 +108,7 @@ class Row extends React.PureComponent { index={index} depth={depth} onDrop={handleComponentDrop} + editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => ( <WithPopoverMenu @@ -119,6 +122,7 @@ class Row extends React.PureComponent { onChange={this.handleChangeBackground} />, ]} + editMode={editMode} > <div className={cx( @@ -127,14 +131,15 @@ class Row extends React.PureComponent { backgroundStyle.className, )} > - <HoverMenu innerRef={dragSourceRef} position="left"> - <DragHandle position="left" /> - <DeleteComponentButton onDelete={this.handleDeleteComponent} /> - <IconButton - onClick={this.handleChangeFocus} - className="fa fa-cog" - /> - </HoverMenu> + {editMode && + <HoverMenu innerRef={dragSourceRef} position="left"> + <DragHandle position="left" /> + <DeleteComponentButton onDelete={this.handleDeleteComponent} /> + <IconButton + onClick={this.handleChangeFocus} + className="fa fa-cog" + /> + </HoverMenu>} {rowItems.map((componentId, itemIndex) => ( <DashboardComponent diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tab.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tab.jsx index 9b41949..218c4e7 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tab.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tab.jsx @@ -22,6 +22,7 @@ const propTypes = { renderType: PropTypes.oneOf([RENDER_TAB, RENDER_TAB_CONTENT]).isRequired, onDropOnTab: PropTypes.func, onDeleteTab: PropTypes.func, + editMode: PropTypes.bool.isRequired, // grid related availableColumnCount: PropTypes.number, @@ -127,6 +128,7 @@ export default class Tab extends React.PureComponent { parentComponent, index, depth, + editMode, } = this.props; return ( @@ -141,6 +143,7 @@ export default class Tab extends React.PureComponent { // itself, e.g. if a top-level Tab has a Tabs child, dragging the Tab into the Tabs would // reusult in circular children disableDragDrop={isFocused || depth === DASHBOARD_ROOT_DEPTH + 1} + editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => ( <div className="dragdroppable-tab" ref={dragSourceRef}> @@ -149,10 +152,11 @@ export default class Tab extends React.PureComponent { menuItems={parentComponent.children.length <= 1 ? [] : [ <DeleteComponentButton onDelete={this.handleDeleteComponent} />, ]} + editMode={editMode} > <EditableTitle title={component.meta.text} - canEdit={isFocused} + canEdit={editMode && isFocused} onSaveTitle={this.handleChangeText} showTooltip={false} /> diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tabs.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tabs.jsx index f76fa19..1f5f0c6 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tabs.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/Tabs.jsx @@ -22,7 +22,8 @@ const propTypes = { parentComponent: componentShape.isRequired, index: PropTypes.number.isRequired, depth: PropTypes.number.isRequired, - renderTabContent: PropTypes.bool, + renderTabContent: PropTypes.bool, // whether to render tabs + content or just tabs + editMode: PropTypes.bool.isRequired, // grid related availableColumnCount: PropTypes.number, @@ -130,6 +131,7 @@ class Tabs extends React.PureComponent { onResizeStop, handleComponentDrop, renderTabContent, + editMode, } = this.props; const { tabIndex: selectedTabIndex } = this.state; @@ -143,13 +145,15 @@ class Tabs extends React.PureComponent { index={index} depth={depth} onDrop={handleComponentDrop} + editMode={editMode} > {({ dropIndicatorProps: tabsDropIndicatorProps, dragSourceRef: tabsDragSourceRef }) => ( <div className="dashboard-component dashboard-component-tabs"> - <HoverMenu innerRef={tabsDragSourceRef} position="left"> - <DragHandle position="left" /> - <DeleteComponentButton onDelete={this.handleDeleteComponent} /> - </HoverMenu> + {editMode && + <HoverMenu innerRef={tabsDragSourceRef} position="left"> + <DragHandle position="left" /> + <DeleteComponentButton onDelete={this.handleDeleteComponent} /> + </HoverMenu>} <BootstrapTabs id={tabsComponent.id} @@ -200,11 +204,12 @@ class Tabs extends React.PureComponent { </BootstrapTab> ))} - {tabIds.length < MAX_TAB_COUNT && - <BootstrapTab - eventKey={NEW_TAB_INDEX} - title={<div className="fa fa-plus" />} - />} + {editMode && + tabIds.length < MAX_TAB_COUNT && + <BootstrapTab + eventKey={NEW_TAB_INDEX} + title={<div className="fa fa-plus" />} + />} </BootstrapTabs> diff --git a/superset/assets/javascripts/dashboard/v2/components/gridComponents/new/DraggableNewComponent.jsx b/superset/assets/javascripts/dashboard/v2/components/gridComponents/new/DraggableNewComponent.jsx index 778f58e..eebd6e0 100644 --- a/superset/assets/javascripts/dashboard/v2/components/gridComponents/new/DraggableNewComponent.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/gridComponents/new/DraggableNewComponent.jsx @@ -26,6 +26,7 @@ export default class DraggableNewComponent extends React.PureComponent { parentComponent={{ id: NEW_COMPONENTS_SOURCE_ID, type: NEW_COMPONENT_SOURCE_TYPE }} index={0} depth={0} + editMode > {({ dragSourceRef }) => ( <div ref={dragSourceRef} className="new-component"> diff --git a/superset/assets/javascripts/dashboard/v2/components/menu/WithPopoverMenu.jsx b/superset/assets/javascripts/dashboard/v2/components/menu/WithPopoverMenu.jsx index a1d4c0e..f213442 100644 --- a/superset/assets/javascripts/dashboard/v2/components/menu/WithPopoverMenu.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/menu/WithPopoverMenu.jsx @@ -9,6 +9,7 @@ const propTypes = { onChangeFocus: PropTypes.func, isFocused: PropTypes.bool, shouldFocus: PropTypes.func, + editMode: PropTypes.bool.isRequired, }; const defaultProps = { @@ -32,10 +33,14 @@ class WithPopoverMenu extends React.PureComponent { } componentWillReceiveProps(nextProps) { - if (nextProps.isFocused && !this.state.isFocused) { + if (nextProps.editMode && nextProps.isFocused && !this.state.isFocused) { document.addEventListener('click', this.handleClick, true); document.addEventListener('drag', this.handleClick, true); this.setState({ isFocused: true }); + } else if (this.state.isFocused && !nextProps.editMode) { + document.removeEventListener('click', this.handleClick, true); + document.removeEventListener('drag', this.handleClick, true); + this.setState({ isFocused: false }); } } @@ -49,10 +54,14 @@ class WithPopoverMenu extends React.PureComponent { } handleClick(event) { - const { onChangeFocus, shouldFocus: shouldFocusThunk } = this.props; - const shouldFocus = shouldFocusThunk(event, this.container); + const { onChangeFocus, shouldFocus: shouldFocusFunc, disableClick, editMode } = this.props; + const shouldFocus = shouldFocusFunc(event, this.container); + + if (!editMode) { + return; + } - if (shouldFocus && !this.state.isFocused) { + if (!disableClick && shouldFocus && !this.state.isFocused) { // if not focused, set focus and add a window event listener to capture outside clicks // this enables us to not set a click listener for ever item on a dashboard document.addEventListener('click', this.handleClick, true); @@ -72,26 +81,28 @@ class WithPopoverMenu extends React.PureComponent { } render() { - const { children, menuItems, disableClick } = this.props; + const { children, menuItems, editMode } = this.props; const { isFocused } = this.state; return ( <div ref={this.setRef} - onClick={!disableClick && this.handleClick} + onClick={this.handleClick} role="none" className={cx( 'with-popover-menu', - isFocused && 'with-popover-menu--focused', + editMode && isFocused && 'with-popover-menu--focused', )} > {children} - {isFocused && menuItems.length ? - <div className="popover-menu" > - {menuItems.map((node, i) => ( - <div className="menu-item" key={`menu-item-${i}`}>{node}</div> - ))} - </div> : null} + {editMode && + isFocused && + menuItems.length > 0 && + <div className="popover-menu" > + {menuItems.map((node, i) => ( + <div className="menu-item" key={`menu-item-${i}`}>{node}</div> + ))} + </div>} </div> ); } diff --git a/superset/assets/javascripts/dashboard/v2/components/resizable/ResizableContainer.jsx b/superset/assets/javascripts/dashboard/v2/components/resizable/ResizableContainer.jsx index 0b1e975..a532ff0 100644 --- a/superset/assets/javascripts/dashboard/v2/components/resizable/ResizableContainer.jsx +++ b/superset/assets/javascripts/dashboard/v2/components/resizable/ResizableContainer.jsx @@ -28,6 +28,7 @@ const propTypes = { onResizeStop: PropTypes.func, onResize: PropTypes.func, onResizeStart: PropTypes.func, + editMode: PropTypes.bool.isRequired, }; const defaultProps = { @@ -101,9 +102,9 @@ class ResizableContainer extends React.PureComponent { if (onResizeStop) { const nextWidthMultiple = - widthMultiple + Math.floor(delta.width / (widthStep + gutterWidth)); + widthMultiple + Math.round(delta.width / (widthStep + gutterWidth)); const nextHeightMultiple = - heightMultiple + Math.ceil(delta.height / heightStep); + heightMultiple + Math.round(delta.height / heightStep); onResizeStop({ id, @@ -133,6 +134,7 @@ class ResizableContainer extends React.PureComponent { minHeightMultiple, maxHeightMultiple, gutterWidth, + editMode, } = this.props; const size = { @@ -148,6 +150,14 @@ class ResizableContainer extends React.PureComponent { || undefined, }; + if (!editMode) { + return ( + <div style={{ ...size }}> + {children} + </div> + ); + } + let enableConfig = resizableConfig.notAdjustable; if (adjustableWidth && adjustableHeight) enableConfig = resizableConfig.widthAndHeight; else if (adjustableWidth) enableConfig = resizableConfig.widthOnly; diff --git a/superset/assets/javascripts/dashboard/v2/containers/DashboardBuilder.jsx b/superset/assets/javascripts/dashboard/v2/containers/DashboardBuilder.jsx index 7ed5bfc..b8d717e 100644 --- a/superset/assets/javascripts/dashboard/v2/containers/DashboardBuilder.jsx +++ b/superset/assets/javascripts/dashboard/v2/containers/DashboardBuilder.jsx @@ -7,9 +7,10 @@ import { handleComponentDrop, } from '../actions/dashboardLayout'; -function mapStateToProps({ dashboardLayout: undoableLayout }) { +function mapStateToProps({ dashboardLayout: undoableLayout, editMode }) { return { dashboardLayout: undoableLayout.present, + editMode, }; } diff --git a/superset/assets/javascripts/dashboard/v2/containers/DashboardComponent.jsx b/superset/assets/javascripts/dashboard/v2/containers/DashboardComponent.jsx index 7af3f5f..add5a6d 100644 --- a/superset/assets/javascripts/dashboard/v2/containers/DashboardComponent.jsx +++ b/superset/assets/javascripts/dashboard/v2/containers/DashboardComponent.jsx @@ -25,13 +25,14 @@ const propTypes = { handleComponentDrop: PropTypes.func.isRequired, }; -function mapStateToProps({ dashboardLayout: undoableLayout, selectedTabs }, ownProps) { +function mapStateToProps({ dashboardLayout: undoableLayout, editMode }, ownProps) { const dashboardLayout = undoableLayout.present; const { id, parentId } = ownProps; const component = dashboardLayout[id]; const props = { component, parentComponent: dashboardLayout[parentId], + editMode, }; // rows and columns need more data about their child dimensions diff --git a/superset/assets/javascripts/dashboard/v2/containers/DashboardGrid.jsx b/superset/assets/javascripts/dashboard/v2/containers/DashboardGrid.jsx index ddb2fc0..67b2396 100644 --- a/superset/assets/javascripts/dashboard/v2/containers/DashboardGrid.jsx +++ b/superset/assets/javascripts/dashboard/v2/containers/DashboardGrid.jsx @@ -14,4 +14,4 @@ function mapDispatchToProps(dispatch) { }, dispatch); } -export default connect(null, mapDispatchToProps)(DashboardGrid); +export default connect(({ editMode }) => ({ editMode }), mapDispatchToProps)(DashboardGrid); diff --git a/superset/assets/javascripts/dashboard/v2/containers/DashboardHeader.jsx b/superset/assets/javascripts/dashboard/v2/containers/DashboardHeader.jsx index 985f9ee..8855d2c 100644 --- a/superset/assets/javascripts/dashboard/v2/containers/DashboardHeader.jsx +++ b/superset/assets/javascripts/dashboard/v2/containers/DashboardHeader.jsx @@ -10,11 +10,14 @@ import { handleComponentDrop, } from '../actions/dashboardLayout'; -function mapStateToProps({ dashboardLayout: undoableLayout }) { +import { setEditMode } from '../actions/editMode'; + +function mapStateToProps({ dashboardLayout: undoableLayout, editMode }) { return { component: undoableLayout.present[DASHBOARD_HEADER_ID], canUndo: undoableLayout.past.length > 0, canRedo: undoableLayout.future.length > 0, + editMode, }; } @@ -24,6 +27,7 @@ function mapDispatchToProps(dispatch) { handleComponentDrop, onUndo: UndoActionCreators.undo, onRedo: UndoActionCreators.redo, + setEditMode, }, dispatch); } diff --git a/superset/assets/javascripts/dashboard/v2/reducers/editMode.js b/superset/assets/javascripts/dashboard/v2/reducers/editMode.js new file mode 100644 index 0000000..b1a1630 --- /dev/null +++ b/superset/assets/javascripts/dashboard/v2/reducers/editMode.js @@ -0,0 +1,11 @@ +import { SET_EDIT_MODE } from '../actions/editMode'; + +export default function editModeReducer(editMode = false, action) { + switch (action.type) { + case SET_EDIT_MODE: + return action.payload.editMode; + + default: + return editMode; + } +} diff --git a/superset/assets/javascripts/dashboard/v2/reducers/index.js b/superset/assets/javascripts/dashboard/v2/reducers/index.js index 994a1df..b824e9a 100644 --- a/superset/assets/javascripts/dashboard/v2/reducers/index.js +++ b/superset/assets/javascripts/dashboard/v2/reducers/index.js @@ -2,6 +2,7 @@ import { combineReducers } from 'redux'; import undoable, { distinctState } from 'redux-undo'; import dashboardLayout from './dashboardLayout'; +import editMode from './editMode'; const undoableLayout = undoable(dashboardLayout, { limit: 15, @@ -10,4 +11,5 @@ const undoableLayout = undoable(dashboardLayout, { export default combineReducers({ dashboardLayout: undoableLayout, + editMode, }); diff --git a/superset/assets/javascripts/dashboard/v2/stylesheets/builder.less b/superset/assets/javascripts/dashboard/v2/stylesheets/builder.less index 206d63b..3651c57 100644 --- a/superset/assets/javascripts/dashboard/v2/stylesheets/builder.less +++ b/superset/assets/javascripts/dashboard/v2/stylesheets/builder.less @@ -14,7 +14,7 @@ box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.1); /* @TODO color */ } -.dashboard-builder { +.dashboard-content { display: flex; flex-direction: row; flex-wrap: nowrap; @@ -32,12 +32,12 @@ padding-left: 8px; /* note this is added to tab-level padding, to match header */ } -.dashboard-builder .grid-container .dashboard-component-tabs { +.dashboard-content .grid-container .dashboard-component-tabs { box-shadow: none; padding-left: 0; } -.dashboard-builder > div:first-child { +.dashboard-content > div:first-child { width: 100%; flex-grow: 1; position: relative; diff --git a/superset/assets/javascripts/dashboard/v2/stylesheets/components/chart.less b/superset/assets/javascripts/dashboard/v2/stylesheets/components/chart.less index 2bdf3cc..141c3e9 100644 --- a/superset/assets/javascripts/dashboard/v2/stylesheets/components/chart.less +++ b/superset/assets/javascripts/dashboard/v2/stylesheets/components/chart.less @@ -14,8 +14,6 @@ opacity: 0.3; } -.grid-container--resizing .dashboard-component-chart, -.dashboard-builder--dragging .dashboard-component-chart, -.dashboard-component-chart:hover { +.dashboard-v2--editing .dashboard-component-chart:hover { box-shadow: inset 0 0 0 1px @gray-light; } diff --git a/superset/assets/javascripts/dashboard/v2/stylesheets/components/column.less b/superset/assets/javascripts/dashboard/v2/stylesheets/components/column.less index caf31e7..9565112 100644 --- a/superset/assets/javascripts/dashboard/v2/stylesheets/components/column.less +++ b/superset/assets/javascripts/dashboard/v2/stylesheets/components/column.less @@ -2,10 +2,27 @@ width: 100%; } +/* gutters between elements in a column */ .grid-column > :not(:only-child):not(.hover-menu):not(:last-child) { margin-bottom: 16px; } +.dashboard-v2--editing .grid-column:after { + border: 1px dashed transparent; + content: ""; + position: absolute; + width: 100%; + height: 100%; + top: 1px; + left: 0; + z-index: 1; + pointer-events: none; +} + +.dashboard-v2--editing .grid-column:hover:after { + border: 1px solid @gray-light; +} + .grid-column > .hover-menu--top { top: -20px; } diff --git a/superset/assets/javascripts/dashboard/v2/stylesheets/components/divider.less b/superset/assets/javascripts/dashboard/v2/stylesheets/components/divider.less index f1d3d86..9347a4e 100644 --- a/superset/assets/javascripts/dashboard/v2/stylesheets/components/divider.less +++ b/superset/assets/javascripts/dashboard/v2/stylesheets/components/divider.less @@ -1,6 +1,6 @@ .dashboard-component-divider { width: 100%; - padding: 24px 0; /* this is padding not margin to enable a larger mouse target */ + padding: 16px 0; /* this is padding not margin to enable a larger mouse target */ background-color: transparent; } diff --git a/superset/assets/javascripts/dashboard/v2/stylesheets/components/row.less b/superset/assets/javascripts/dashboard/v2/stylesheets/components/row.less index 30f14b8..956966d 100644 --- a/superset/assets/javascripts/dashboard/v2/stylesheets/components/row.less +++ b/superset/assets/javascripts/dashboard/v2/stylesheets/components/row.less @@ -7,10 +7,28 @@ height: fit-content; } +/* gutters between elements in a row */ .grid-row > :not(:only-child):not(:last-child):not(.hover-menu) { margin-right: 16px; } +/* hover indicator */ +.dashboard-v2--editing .grid-row:after { + border: 1px dashed transparent; + content: ""; + position: absolute; + width: 100%; + height: 100%; + top: 1px; + left: 0; + z-index: 1; + pointer-events: none; +} + +.dashboard-v2--editing .grid-row:hover:after { + border: 1px solid @gray-light; +} + .grid-row.grid-row--empty { align-items: center; /* this centers the empty note content */ height: 80px; diff --git a/superset/assets/javascripts/dashboard/v2/stylesheets/grid.less b/superset/assets/javascripts/dashboard/v2/stylesheets/grid.less index 7c55dee..45b8a42 100644 --- a/superset/assets/javascripts/dashboard/v2/stylesheets/grid.less +++ b/superset/assets/javascripts/dashboard/v2/stylesheets/grid.less @@ -9,6 +9,11 @@ flex-direction: column; } +/* gutters between rows */ +.grid-content > div:not(:only-child):not(:last-child):not(.empty-grid-droptarget) { + margin-bottom: 16px; +} + .empty-grid-droptarget { width: 100%; height: 100%; @@ -33,22 +38,3 @@ pointer-events: none; z-index: 10; } - - -.grid-container .grid-row:after, -.grid-container .grid-column:after { - border: 1px dashed transparent; - content: ""; - position: absolute; - width: 100%; - height: 100%; - top: 1px; - left: 0; - z-index: 1; - pointer-events: none; -} - -.grid-container .grid-row:hover:after, -.grid-container .grid-column:hover:after { - border: 1px solid @gray-light; -} -- To stop receiving notification emails like this one, please contact ccwilli...@apache.org.