功能点
路径
superset-frontend\src\dashboard\components\nativeFilters\FiltersConfigModal\FiltersConfigForm\DependencyList.tsx
/**
* 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, { useState } from 'react';
import { styled, t } from '@superset-ui/core';
import Icons from 'src/components/Icons';
import { Select } from 'src/components';
import { CollapsibleControl } from './CollapsibleControl';
import { INPUT_WIDTH } from './constants';
interface DependencyListProps {
availableFilters: {
label: string;
value: string;
type: string | undefined;
}[];
dependencies: string[];
onDependenciesChange: (dependencies: string[]) => void;
getDependencySuggestion: () => string;
children?: JSX.Element;
}
const MainPanel = styled.div`
display: flex;
flex-direction: column;
`;
const AddFilter = styled.div`
${({ theme }) => `
display: inline-flex;
flex-direction: row;
align-items: center;
cursor: pointer;
color: ${theme.colors.primary.base};
&:hover {
color: ${theme.colors.primary.dark1};
}
`}
`;
const DeleteFilter = styled(Icons.Trash)`
${({ theme }) => `
cursor: pointer;
margin-left: ${theme.gridUnit * 2}px;
color: ${theme.colors.grayscale.base};
&:hover {
color: ${theme.colors.grayscale.dark1};
}
`}
`;
const RowPanel = styled.div`
${({ theme }) => `
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: ${theme.gridUnit}px;
& > div {
width: ${INPUT_WIDTH}px;
}
`}
`;
const Label = styled.div`
text-transform: uppercase;
font-size: ${({ theme }) => theme.typography.sizes.s}px;
color: ${({ theme }) => theme.colors.grayscale.base};
margin-bottom: ${({ theme }) => theme.gridUnit}px;
`;
const Row = ({
availableFilters,
selection,
onChange,
onDelete,
}: {
availableFilters: { label: string; value: string }[];
selection: string;
onChange: (id: string, value: string) => void;
onDelete: (id: string) => void;
}) => {
let value = availableFilters.find(e => e.value === selection);
let options = availableFilters;
if (!value) {
value = { label: t('(deleted or invalid type)'), value: selection };
options = [value, ...options];
}
return (
);
};
const List = ({
availableFilters = [],
dependencies = [],
onDependenciesChange,
}: DependencyListProps) => {
const [rows, setRows] = useState(dependencies);
const updateRows = (newRows: string[]) => {
setRows(newRows);
onDependenciesChange(newRows);
};
const onAdd = () => {
const filter = availableFilters.find(
availableFilter => !rows.includes(availableFilter.value),
);
if (filter) {
const newRows = [...rows];
newRows.push(filter.value);
updateRows(newRows);
}
};
const onChange = (id: string, value: string) => {
const indexOf = rows.findIndex(row => row === id);
const newRows = [...rows];
newRows[indexOf] = value;
updateRows(newRows);
};
const onDelete = (id: string) => {
const newRows = [...rows];
newRows.splice(rows.indexOf(id), 1);
updateRows(newRows);
};
if (availableFilters.length === 0) {
return {t('No available filters.')};
}
return (
<>
{rows.map(row => (
e.value === row || !rows.includes(e.value),
)}
onChange={onChange}
onDelete={onDelete}
/>
))}
{availableFilters.length > rows.length && (
{t('Add filter')}
)}
>
);
};
const DependencyList = ({
availableFilters = [],
dependencies = [],
onDependenciesChange,
getDependencySuggestion,
children,
}: DependencyListProps) => {
const hasAvailableFilters = availableFilters.length > 0;
const hasDependencies = dependencies.length > 0;
const onCheckChanged = (value: boolean) => {
const newDependencies: string[] = [];
if (value && !hasDependencies && hasAvailableFilters) {
newDependencies.push(getDependencySuggestion());
}
onDependenciesChange(newDependencies);
};
return (
{hasDependencies && }
{children}
);
};
export default DependencyList;
export const DependencyList: React.FC = ({
dependencies = [],
onRemove,
onAdd,
getFilterTitle,
}) => {
// ... 组件实现
}
这个组件接收依赖列表、删除和添加依赖的回调函数,以及获取过滤器标题的函数作为props。
{dependencies.map(dependency => (
{getFilterTitle(dependency)}
onRemove(dependency)}
/>
))}
这段代码遍历依赖列表,为每个依赖项渲染一个包含标题和删除图标的项目。
{t('Add Dependent')}
这部分代码渲染了一个"添加依赖"按钮,点击时触发onAdd回调。
文件中使用了多个样式化组件(如StyledItem, StyledItemContent等)来定制组件的外观。
使用t函数进行文本国际化,支持多语言
type DependencyListProps = {
dependencies?: string[];
onRemove: (id: string) => void;
onAdd: () => void;
getFilterTitle: (id: string) => string;
};
这个DependencyList组件是实现"Values are dependent on other filters"逻辑的重要部分。它提供了一个用户界面,允许配置和管理过滤器之间的依赖关系。通过这个组件,用户可以直观地看到和修改过滤器的依赖结构,从而实现动态且相互关联的过滤系统