未验证 提交 288f6bb8 编写于 作者: K Kamil Gabryjelski 提交者: Ville Brofeldt

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: NEvan Rusackas <evan@preset.io>
上级 507302d6
......@@ -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'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册