blob: bed610a09392ae4225598dbdaf511b0318892123 [file] [log] [blame]
/**
* 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 { useMemo, useState } from 'react';
import { useTheme, t } from '@superset-ui/core';
import { GenericDataType } from '@apache-superset/core/api/core';
import {
COLUMN_NAME_ALIASES,
ControlComponentProps,
} from '@superset-ui/chart-controls';
import { Icons } from '@superset-ui/core/components';
import ColumnConfigItem from './ColumnConfigItem';
import {
ColumnConfigInfo,
ColumnConfig,
ColumnConfigFormLayout,
} from './types';
import { DEFAULT_CONFIG_FORM_LAYOUT } from './constants';
import ControlHeader from '../../ControlHeader';
export type ColumnConfigControlProps<T extends ColumnConfig> =
ControlComponentProps<Record<string, T>> & {
columnsPropsObject?: {
colnames: string[];
coltypes: GenericDataType[];
childColumnMap?: Record<string, boolean>;
timeComparisonColumnMap?: Record<string, boolean>;
};
configFormLayout?: ColumnConfigFormLayout;
appliedColumnNames?: string[];
width?: number | string;
height?: number | string;
};
/**
* Max number of columns to show by default.
*/
const MAX_NUM_COLS = 10;
/**
* Add per-column config to queried results.
*/
export default function ColumnConfigControl<T extends ColumnConfig>({
columnsPropsObject,
appliedColumnNames = [],
value,
onChange,
configFormLayout = DEFAULT_CONFIG_FORM_LAYOUT,
width,
height,
...props
}: ColumnConfigControlProps<T>) {
const { colnames: _colnames, coltypes: _coltypes } = columnsPropsObject || {};
let colnames: string[] = [];
let coltypes: GenericDataType[] = [];
if (appliedColumnNames.length === 0) {
colnames = _colnames || [];
coltypes = _coltypes || [];
} else {
const appliedCol = new Set(appliedColumnNames);
_colnames?.forEach((col, idx) => {
if (appliedCol.has(col)) {
colnames.push(col);
coltypes.push(_coltypes?.[idx] as GenericDataType);
}
});
}
const theme = useTheme();
const columnConfigs = useMemo(() => {
const configs: Record<string, ColumnConfigInfo> = {};
colnames?.forEach((col, idx) => {
configs[col] = {
name: COLUMN_NAME_ALIASES[col] || col,
type: coltypes?.[idx],
config: value?.[col] || {},
isChildColumn: columnsPropsObject?.childColumnMap?.[col] ?? false,
isTimeComparisonColumn:
columnsPropsObject?.timeComparisonColumnMap?.[col] ?? false,
};
});
return configs;
}, [value, colnames, coltypes, columnsPropsObject?.childColumnMap]);
const [showAllColumns, setShowAllColumns] = useState(false);
const getColumnInfo = (col: string) => columnConfigs[col] || {};
const setColumnConfig = (col: string, config: T) => {
if (onChange) {
// Only keep configs for known columns
const validConfigs: Record<string, T> =
colnames && value
? Object.fromEntries(
Object.entries(value).filter(([key]) => colnames.includes(key)),
)
: { ...value };
onChange({
...validConfigs,
[col]: config,
});
}
};
if (!colnames || colnames.length === 0) return null;
const needShowMoreButton = colnames.length > MAX_NUM_COLS + 2;
const cols =
needShowMoreButton && !showAllColumns
? colnames.slice(0, MAX_NUM_COLS)
: colnames;
const columnsWithChildInfo = cols.map(col => getColumnInfo(col));
return (
<>
<ControlHeader {...props} />
<div
css={{
border: `1px solid ${theme.colorBorder}`,
borderRadius: theme.borderRadius,
}}
>
{columnsWithChildInfo.map(col => (
<ColumnConfigItem
key={col.name}
column={col}
onChange={config => setColumnConfig(col.name, config as T)}
configFormLayout={
col.isTimeComparisonColumn
? ({
[col.type ?? GenericDataType.String]: [
{
tab: 'General',
children: [
['customColumnName'],
['displayTypeIcon'],
['visible'],
],
},
...(configFormLayout?.[
col.type ?? GenericDataType.String
] ?? []),
],
} as ColumnConfigFormLayout)
: configFormLayout
}
width={width}
height={height}
/>
))}
{needShowMoreButton && (
<div
role="button"
tabIndex={-1}
css={{
padding: theme.sizeUnit * 2,
textAlign: 'center',
cursor: 'pointer',
fontSize: theme.fontSizeXS,
color: theme.colorTextLabel,
':hover': {
backgroundColor: theme.colorFillContentHover,
},
}}
onClick={() => setShowAllColumns(!showAllColumns)}
>
{showAllColumns ? (
<>
<Icons.UpOutlined /> &nbsp; {t('Show less columns')}
</>
) : (
<>
<Icons.DownOutlined /> &nbsp; {t('Show all columns')}
</>
)}
</div>
)}
</div>
</>
);
}