mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat: Resizable dataset and controls panels on Explore view (#12411)
* Implement resizable panels on explore view * Optimize chart rendering while resizing * Make dataset column narrower Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
committed by
GitHub
parent
d8f4443c10
commit
ed53b0090e
21
superset-frontend/package-lock.json
generated
21
superset-frontend/package-lock.json
generated
@@ -39186,6 +39186,11 @@
|
||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"lodash.topath": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
|
||||
@@ -44123,6 +44128,11 @@
|
||||
"performance-now": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"raf-schd": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz",
|
||||
"integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ=="
|
||||
},
|
||||
"railroad-diagrams": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
|
||||
@@ -46500,6 +46510,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-resize-detector": {
|
||||
"version": "6.0.1-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.0.1-rc.1.tgz",
|
||||
"integrity": "sha512-r+UZtJottPZaW/2CKAyb4Vgpi6KROsXBH890UChK7mB8DSFf8nEvwqpvE9akfd8wQOGi0cXekcGLHzYp9FiscA==",
|
||||
"requires": {
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"raf-schd": "^4.0.2",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"react-loadable": "^5.5.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-resize-detector": "^6.0.1-rc.1",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-search-input": "^0.11.3",
|
||||
"react-select": "^3.1.0",
|
||||
|
||||
@@ -141,6 +141,24 @@ const DatasourceContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const LabelContainer = styled.div`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
& > span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.option-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.metric-option > .option-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
const DataSourcePanel = ({
|
||||
datasource,
|
||||
controls: { datasource: datasourceControl },
|
||||
@@ -200,9 +218,9 @@ const DataSourcePanel = ({
|
||||
{t(`Showing %s of %s`, metricSlice.length, metrics.length)}
|
||||
</div>
|
||||
{metricSlice.map(m => (
|
||||
<div key={m.metric_name} className="column">
|
||||
<LabelContainer key={m.metric_name} className="column">
|
||||
<MetricOption metric={m} showType />
|
||||
</div>
|
||||
</LabelContainer>
|
||||
))}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
@@ -213,9 +231,9 @@ const DataSourcePanel = ({
|
||||
{t(`Showing %s of %s`, columnSlice.length, columns.length)}
|
||||
</div>
|
||||
{columnSlice.map(col => (
|
||||
<div key={col.column_name} className="column">
|
||||
<LabelContainer key={col.column_name} className="column">
|
||||
<ColumnOption column={col} showType />
|
||||
</div>
|
||||
</LabelContainer>
|
||||
))}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Split from 'react-split';
|
||||
import { ParentSize } from '@vx/responsive';
|
||||
import { styled, useTheme } from '@superset-ui/core';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useResizeDetector } from 'react-resize-detector';
|
||||
import { chartPropShape } from 'src/dashboard/util/propShapes';
|
||||
import ChartContainer from 'src/chart/ChartContainer';
|
||||
import ConnectedExploreChartHeader from './ExploreChartHeader';
|
||||
@@ -55,6 +54,7 @@ const propTypes = {
|
||||
const GUTTER_SIZE_FACTOR = 1.25;
|
||||
|
||||
const CHART_PANEL_PADDING = 30;
|
||||
const HEADER_PADDING = 15;
|
||||
|
||||
const INITIAL_SIZES = [90, 10];
|
||||
const MIN_SIZES = [300, 50];
|
||||
@@ -104,20 +104,32 @@ const ExploreChartPanel = props => {
|
||||
const gutterMargin = theme.gridUnit * GUTTER_SIZE_FACTOR;
|
||||
const gutterHeight = theme.gridUnit * GUTTER_SIZE_FACTOR;
|
||||
|
||||
const panelHeadingRef = useRef(null);
|
||||
const { height: hHeight, ref: headerRef } = useResizeDetector({
|
||||
refreshMode: 'debounce',
|
||||
refreshRate: 300,
|
||||
});
|
||||
const { width: chartWidth, ref: chartRef } = useResizeDetector({
|
||||
refreshMode: 'debounce',
|
||||
refreshRate: 300,
|
||||
});
|
||||
const [splitSizes, setSplitSizes] = useState(INITIAL_SIZES);
|
||||
|
||||
const calcSectionHeight = useCallback(
|
||||
percent => {
|
||||
const headerHeight = props.standalone
|
||||
? 0
|
||||
: panelHeadingRef?.current?.offsetHeight ?? 50;
|
||||
let headerHeight;
|
||||
if (props.standalone) {
|
||||
headerHeight = 0;
|
||||
} else if (hHeight) {
|
||||
headerHeight = hHeight + HEADER_PADDING;
|
||||
} else {
|
||||
headerHeight = 50;
|
||||
}
|
||||
const containerHeight = parseInt(props.height, 10) - headerHeight;
|
||||
return (
|
||||
(containerHeight * percent) / 100 - (gutterHeight / 2 + gutterMargin)
|
||||
);
|
||||
},
|
||||
[gutterHeight, gutterMargin, props.height, props.standalone],
|
||||
[gutterHeight, gutterMargin, props.height, props.standalone, hHeight],
|
||||
);
|
||||
|
||||
const [tableSectionHeight, setTableSectionHeight] = useState(
|
||||
@@ -132,15 +144,11 @@ const ExploreChartPanel = props => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const recalcSizes = debounce(() => recalcPanelSizes(splitSizes), 200);
|
||||
|
||||
window.addEventListener('resize', recalcSizes);
|
||||
return () => window.removeEventListener('resize', recalcSizes);
|
||||
}, [props.standalone, recalcPanelSizes, splitSizes]);
|
||||
recalcPanelSizes(splitSizes);
|
||||
}, [recalcPanelSizes, splitSizes]);
|
||||
|
||||
const onDragEnd = sizes => {
|
||||
setSplitSizes(sizes);
|
||||
recalcPanelSizes(sizes);
|
||||
};
|
||||
|
||||
const onCollapseChange = openPanelName => {
|
||||
@@ -154,42 +162,46 @@ const ExploreChartPanel = props => {
|
||||
];
|
||||
}
|
||||
setSplitSizes(splitSizes);
|
||||
recalcPanelSizes(splitSizes);
|
||||
};
|
||||
|
||||
const renderChart = () => {
|
||||
const renderChart = useCallback(() => {
|
||||
const { chart } = props;
|
||||
const newHeight = calcSectionHeight(splitSizes[0]) - CHART_PANEL_PADDING;
|
||||
return (
|
||||
<ParentSize>
|
||||
{({ width }) =>
|
||||
width > 0 && (
|
||||
<ChartContainer
|
||||
width={Math.floor(width)}
|
||||
height={newHeight}
|
||||
annotationData={chart.annotationData}
|
||||
chartAlert={chart.chartAlert}
|
||||
chartStackTrace={chart.chartStackTrace}
|
||||
chartId={chart.id}
|
||||
chartStatus={chart.chartStatus}
|
||||
triggerRender={props.triggerRender}
|
||||
datasource={props.datasource}
|
||||
errorMessage={props.errorMessage}
|
||||
formData={props.form_data}
|
||||
onQuery={props.onQuery}
|
||||
owners={props?.slice?.owners}
|
||||
queriesResponse={chart.queriesResponse}
|
||||
refreshOverlayVisible={props.refreshOverlayVisible}
|
||||
setControlValue={props.actions.setControlValue}
|
||||
timeout={props.timeout}
|
||||
triggerQuery={chart.triggerQuery}
|
||||
vizType={props.vizType}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</ParentSize>
|
||||
chartWidth > 0 && (
|
||||
<ChartContainer
|
||||
width={Math.floor(chartWidth)}
|
||||
height={newHeight}
|
||||
annotationData={chart.annotationData}
|
||||
chartAlert={chart.chartAlert}
|
||||
chartStackTrace={chart.chartStackTrace}
|
||||
chartId={chart.id}
|
||||
chartStatus={chart.chartStatus}
|
||||
triggerRender={props.triggerRender}
|
||||
datasource={props.datasource}
|
||||
errorMessage={props.errorMessage}
|
||||
formData={props.form_data}
|
||||
onQuery={props.onQuery}
|
||||
owners={props?.slice?.owners}
|
||||
queriesResponse={chart.queriesResponse}
|
||||
refreshOverlayVisible={props.refreshOverlayVisible}
|
||||
setControlValue={props.actions.setControlValue}
|
||||
timeout={props.timeout}
|
||||
triggerQuery={chart.triggerQuery}
|
||||
vizType={props.vizType}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
}, [calcSectionHeight, chartWidth, props, splitSizes]);
|
||||
|
||||
const panelBody = useMemo(
|
||||
() => (
|
||||
<div className="panel-body" ref={chartRef}>
|
||||
{renderChart()}
|
||||
</div>
|
||||
),
|
||||
[chartRef, renderChart],
|
||||
);
|
||||
|
||||
if (props.standalone) {
|
||||
// dom manipulation hack to get rid of the boostrap theme's body background
|
||||
@@ -222,14 +234,12 @@ const ExploreChartPanel = props => {
|
||||
[dimension]: `calc(${elementSize}% - ${gutterSize + gutterMargin}px)`,
|
||||
});
|
||||
|
||||
const panelBody = <div className="panel-body">{renderChart()}</div>;
|
||||
|
||||
return (
|
||||
<Styles
|
||||
className="panel panel-default chart-container"
|
||||
style={{ height: props.height }}
|
||||
>
|
||||
<div className="panel-heading" ref={panelHeadingRef}>
|
||||
<div className="panel-heading" ref={headerRef}>
|
||||
{header}
|
||||
</div>
|
||||
{props.vizType === 'filter_box' ? (
|
||||
|
||||
@@ -23,6 +23,7 @@ import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { styled, t, supersetTheme, css } from '@superset-ui/core';
|
||||
import { debounce } from 'lodash';
|
||||
import { Resizable } from 're-resizable';
|
||||
|
||||
import { useDynamicPluginContext } from 'src/components/DynamicPlugins';
|
||||
import { Global } from '@emotion/core';
|
||||
@@ -81,10 +82,8 @@ const Styles = styled.div`
|
||||
border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
|
||||
.explore-column {
|
||||
display: flex;
|
||||
flex: 0 0 ${({ theme }) => theme.gridUnit * 95}px;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => 2 * theme.gridUnit}px 0;
|
||||
max-width: ${({ theme }) => theme.gridUnit * 95}px;
|
||||
max-height: 100%;
|
||||
}
|
||||
.data-source-selection {
|
||||
@@ -404,7 +403,11 @@ function ExploreViewContainer(props) {
|
||||
dashboardId={props.dashboardId}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
<Resizable
|
||||
defaultSize={{ width: 300 }}
|
||||
minWidth={300}
|
||||
maxWidth="33%"
|
||||
enable={{ right: true }}
|
||||
className={
|
||||
isCollapsed ? 'no-show' : 'explore-column data-source-selection'
|
||||
}
|
||||
@@ -430,7 +433,7 @@ function ExploreViewContainer(props) {
|
||||
controls={props.controls}
|
||||
actions={props.actions}
|
||||
/>
|
||||
</div>
|
||||
</Resizable>
|
||||
{isCollapsed ? (
|
||||
<div
|
||||
className="sidebar"
|
||||
@@ -452,7 +455,13 @@ function ExploreViewContainer(props) {
|
||||
<Icon name="dataset-physical" width={16} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="col-sm-3 explore-column controls-column">
|
||||
<Resizable
|
||||
defaultSize={{ width: 320 }}
|
||||
minWidth={320}
|
||||
maxWidth="33%"
|
||||
enable={{ right: true }}
|
||||
className="col-sm-3 explore-column controls-column"
|
||||
>
|
||||
<QueryAndSaveBtns
|
||||
canAdd={!!(props.can_add || props.can_overwrite)}
|
||||
onQuery={onQuery}
|
||||
@@ -470,7 +479,7 @@ function ExploreViewContainer(props) {
|
||||
datasource_type={props.datasource_type}
|
||||
isDatasourceMetaLoading={props.isDatasourceMetaLoading}
|
||||
/>
|
||||
</div>
|
||||
</Resizable>
|
||||
<div
|
||||
className={`main-explore-content ${
|
||||
isCollapsed ? 'col-sm-9' : 'col-sm-7'
|
||||
|
||||
Reference in New Issue
Block a user