mirror of
https://github.com/apache/superset.git
synced 2026-05-12 03:15:55 +00:00
perf(explore): virtualized datasource field sections (#27625)
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { CSSProperties } from 'react';
|
||||
import { css, Metric, styled, t, useTheme } from '@superset-ui/core';
|
||||
|
||||
import Icons from 'src/components/Icons';
|
||||
import DatasourcePanelDragOption from './DatasourcePanelDragOption';
|
||||
import { DndItemType } from '../DndItemType';
|
||||
import { DndItemValue } from './types';
|
||||
|
||||
export type DataSourcePanelColumn = {
|
||||
is_dttm?: boolean | null;
|
||||
description?: string | null;
|
||||
expression?: string | null;
|
||||
is_certified?: number | null;
|
||||
column_name?: string | null;
|
||||
name?: string | null;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
index: number;
|
||||
style: CSSProperties;
|
||||
data: {
|
||||
metricSlice: Metric[];
|
||||
columnSlice: DataSourcePanelColumn[];
|
||||
totalMetrics: number;
|
||||
totalColumns: number;
|
||||
width: number;
|
||||
showAllMetrics: boolean;
|
||||
onShowAllMetricsChange: (showAll: boolean) => void;
|
||||
showAllColumns: boolean;
|
||||
onShowAllColumnsChange: (showAll: boolean) => void;
|
||||
collapseMetrics: boolean;
|
||||
onCollapseMetricsChange: (collapse: boolean) => void;
|
||||
collapseColumns: boolean;
|
||||
onCollapseColumnsChange: (collapse: boolean) => void;
|
||||
};
|
||||
};
|
||||
|
||||
export const DEFAULT_MAX_COLUMNS_LENGTH = 50;
|
||||
export const DEFAULT_MAX_METRICS_LENGTH = 50;
|
||||
export const ITEM_HEIGHT = 30;
|
||||
|
||||
const Button = styled.button`
|
||||
background: none;
|
||||
border: none;
|
||||
text-decoration: underline;
|
||||
color: ${({ theme }) => theme.colors.primary.dark1};
|
||||
`;
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
text-align: center;
|
||||
padding-top: 2px;
|
||||
`;
|
||||
|
||||
const LabelWrapper = styled.div`
|
||||
${({ theme }) => css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: ${theme.typography.sizes.s}px;
|
||||
background-color: ${theme.colors.grayscale.light4};
|
||||
margin: ${theme.gridUnit * 2}px 0;
|
||||
border-radius: 4px;
|
||||
padding: 0 ${theme.gridUnit}px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: ${theme.colors.grayscale.light3};
|
||||
}
|
||||
|
||||
& > span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.option-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.metric-option {
|
||||
& > svg {
|
||||
min-width: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
& > .option-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const SectionHeaderButton = styled.button`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
padding-inline: 0px;
|
||||
`;
|
||||
|
||||
const SectionHeader = styled.span`
|
||||
${({ theme }) => `
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
line-height: 1.3;
|
||||
`}
|
||||
`;
|
||||
|
||||
const DatasourcePanelItem: React.FC<Props> = ({ index, style, data }) => {
|
||||
const {
|
||||
metricSlice: _metricSlice,
|
||||
columnSlice,
|
||||
totalMetrics,
|
||||
totalColumns,
|
||||
width,
|
||||
showAllMetrics,
|
||||
onShowAllMetricsChange,
|
||||
showAllColumns,
|
||||
onShowAllColumnsChange,
|
||||
collapseMetrics,
|
||||
onCollapseMetricsChange,
|
||||
collapseColumns,
|
||||
onCollapseColumnsChange,
|
||||
} = data;
|
||||
const metricSlice = collapseMetrics ? [] : _metricSlice;
|
||||
|
||||
const EXTRA_LINES = collapseMetrics ? 1 : 2;
|
||||
const isColumnSection = collapseMetrics
|
||||
? index >= 1
|
||||
: index > metricSlice.length + EXTRA_LINES;
|
||||
const HEADER_LINE = isColumnSection
|
||||
? metricSlice.length + EXTRA_LINES + 1
|
||||
: 0;
|
||||
const SUBTITLE_LINE = HEADER_LINE + 1;
|
||||
const BOTTOM_LINE =
|
||||
(isColumnSection ? columnSlice.length : metricSlice.length) +
|
||||
(collapseMetrics ? HEADER_LINE : SUBTITLE_LINE) +
|
||||
1;
|
||||
const collapsed = isColumnSection ? collapseColumns : collapseMetrics;
|
||||
const setCollapse = isColumnSection
|
||||
? onCollapseColumnsChange
|
||||
: onCollapseMetricsChange;
|
||||
const showAll = isColumnSection ? showAllColumns : showAllMetrics;
|
||||
const setShowAll = isColumnSection
|
||||
? onShowAllColumnsChange
|
||||
: onShowAllMetricsChange;
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
css={css`
|
||||
padding: 0 ${theme.gridUnit * 4}px;
|
||||
`}
|
||||
>
|
||||
{index === HEADER_LINE && (
|
||||
<SectionHeaderButton onClick={() => setCollapse(!collapsed)}>
|
||||
<SectionHeader>
|
||||
{isColumnSection ? t('Columns') : t('Metrics')}
|
||||
</SectionHeader>
|
||||
{collapsed ? (
|
||||
<Icons.DownOutlined iconSize="s" />
|
||||
) : (
|
||||
<Icons.UpOutlined iconSize="s" />
|
||||
)}
|
||||
</SectionHeaderButton>
|
||||
)}
|
||||
{index === SUBTITLE_LINE && !collapsed && (
|
||||
<div className="field-length">
|
||||
{isColumnSection
|
||||
? t(`Showing %s of %s`, columnSlice?.length, totalColumns)
|
||||
: t(`Showing %s of %s`, metricSlice?.length, totalMetrics)}
|
||||
</div>
|
||||
)}
|
||||
{index > SUBTITLE_LINE && index < BOTTOM_LINE && (
|
||||
<LabelWrapper
|
||||
key={
|
||||
(isColumnSection
|
||||
? columnSlice[index - SUBTITLE_LINE - 1].column_name
|
||||
: metricSlice[index - SUBTITLE_LINE - 1].metric_name) +
|
||||
String(width)
|
||||
}
|
||||
className="column"
|
||||
>
|
||||
<DatasourcePanelDragOption
|
||||
value={
|
||||
isColumnSection
|
||||
? (columnSlice[index - SUBTITLE_LINE - 1] as DndItemValue)
|
||||
: metricSlice[index - SUBTITLE_LINE - 1]
|
||||
}
|
||||
type={isColumnSection ? DndItemType.Column : DndItemType.Metric}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
)}
|
||||
{index === BOTTOM_LINE &&
|
||||
!collapsed &&
|
||||
(isColumnSection
|
||||
? totalColumns > DEFAULT_MAX_COLUMNS_LENGTH
|
||||
: totalMetrics > DEFAULT_MAX_METRICS_LENGTH) && (
|
||||
<ButtonContainer>
|
||||
<Button onClick={() => setShowAll(!showAll)}>
|
||||
{showAll ? t('Show less...') : t('Show all...')}
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatasourcePanelItem;
|
||||
Reference in New Issue
Block a user