mirror of
https://github.com/apache/superset.git
synced 2026-05-07 08:54:23 +00:00
Working on filters
This commit is contained in:
@@ -60,6 +60,12 @@ function UIFilters(
|
||||
filter.current?.clearFilter?.();
|
||||
});
|
||||
},
|
||||
clearFilterById: (id: string) => {
|
||||
const index = filters.findIndex(f => f.id === id);
|
||||
if (index >= 0) {
|
||||
filterRefs[index]?.current?.clearFilter?.();
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
|
||||
@@ -264,6 +264,11 @@ export interface ListViewProps<T extends object = any> {
|
||||
columnsForWrapText?: string[];
|
||||
enableBulkTag?: boolean;
|
||||
bulkTagResourceName?: string;
|
||||
/** Optional ref exposed to callers for programmatic filter control. */
|
||||
filtersRef?: React.RefObject<{
|
||||
clearFilters: () => void;
|
||||
clearFilterById: (id: string) => void;
|
||||
}>;
|
||||
}
|
||||
|
||||
export function ListView<T extends object = any>({
|
||||
@@ -290,6 +295,7 @@ export function ListView<T extends object = any>({
|
||||
columnsForWrapText,
|
||||
enableBulkTag = false,
|
||||
bulkTagResourceName,
|
||||
filtersRef,
|
||||
addSuccessToast,
|
||||
addDangerToast,
|
||||
}: ListViewProps<T>) {
|
||||
@@ -337,7 +343,17 @@ export function ListView<T extends object = any>({
|
||||
});
|
||||
}
|
||||
|
||||
const filterControlsRef = useRef<{ clearFilters: () => void }>(null);
|
||||
const filterControlsRef = useRef<{
|
||||
clearFilters: () => void;
|
||||
clearFilterById: (id: string) => void;
|
||||
}>(null);
|
||||
|
||||
// Wire the optional external filtersRef to our internal filterControlsRef.
|
||||
useEffect(() => {
|
||||
if (filtersRef) {
|
||||
(filtersRef as any).current = filterControlsRef.current;
|
||||
}
|
||||
});
|
||||
|
||||
const handleClearFilterControls = useCallback(() => {
|
||||
if (query.filters) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
FeatureFlag,
|
||||
} from '@superset-ui/core';
|
||||
import { styled, useTheme, css } from '@apache-superset/core/theme';
|
||||
import { FunctionComponent, useState, useMemo, useCallback, Key } from 'react';
|
||||
import { FunctionComponent, useState, useMemo, useCallback, useRef, Key } from 'react';
|
||||
import type { CellProps } from 'react-table';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import rison from 'rison';
|
||||
@@ -194,7 +194,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [lastFetchConfig, setLastFetchConfig] =
|
||||
useState<ListViewFetchDataConfig | null>(null);
|
||||
|
||||
const currentSourceFilter = useMemo(() => {
|
||||
const sourceTypeFilter = lastFetchConfig?.filters.find(
|
||||
filter => filter.id === 'source_type',
|
||||
@@ -209,6 +208,84 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
return (sourceTypeFilter?.value as string | undefined) ?? '';
|
||||
}, [lastFetchConfig]);
|
||||
|
||||
// Track the current type and connection filter values so cascade-clear logic
|
||||
// can inspect them when a different filter changes.
|
||||
const currentTypeFilter = useRef<unknown>(undefined);
|
||||
const currentConnectionFilter = useRef<unknown>(undefined);
|
||||
|
||||
// Ref wired to ListView's filter controls for programmatic per-filter clearing.
|
||||
const filtersRef = useRef<{
|
||||
clearFilters: () => void;
|
||||
clearFilterById: (id: string) => void;
|
||||
}>(null);
|
||||
|
||||
/**
|
||||
* Cascade-clear incompatible filters when one filter changes.
|
||||
*
|
||||
* Rules:
|
||||
* - Selecting a DB connection → clear "Semantic View" type
|
||||
* - Selecting a SL connection → clear "Physical" / "Virtual" type
|
||||
* - Selecting Physical/Virtual type → clear any SL connection
|
||||
* - Selecting Semantic View type → clear any DB connection
|
||||
* - Selecting Source=Database → clear SL connection + Semantic View type
|
||||
* - Selecting Source=Semantic Layer → clear DB connection + Physical/Virtual type
|
||||
*/
|
||||
const cascadeClear = useCallback(
|
||||
(
|
||||
changed: 'source' | 'type' | 'connection',
|
||||
newValue: unknown,
|
||||
) => {
|
||||
if (!isFeatureEnabled(FeatureFlag.SemanticLayers)) return;
|
||||
|
||||
const isSlConnection = (v: unknown) =>
|
||||
typeof v === 'string' && v.startsWith('sl:');
|
||||
const isDbConnection = (v: unknown) =>
|
||||
v !== undefined && v !== null && v !== '' && !isSlConnection(v);
|
||||
const isSemanticViewType = (v: unknown) => v === 'semantic_view';
|
||||
const isPhysicalVirtualType = (v: unknown) =>
|
||||
v === true || v === false;
|
||||
|
||||
if (changed === 'connection') {
|
||||
if (isSlConnection(newValue) && isPhysicalVirtualType(currentTypeFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('sql');
|
||||
}
|
||||
if (isDbConnection(newValue) && isSemanticViewType(currentTypeFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('sql');
|
||||
}
|
||||
}
|
||||
|
||||
if (changed === 'type') {
|
||||
if (isSemanticViewType(newValue) && isDbConnection(currentConnectionFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('database');
|
||||
}
|
||||
if (isPhysicalVirtualType(newValue) && isSlConnection(currentConnectionFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('database');
|
||||
}
|
||||
}
|
||||
|
||||
if (changed === 'source') {
|
||||
const src = newValue as string;
|
||||
if (src === 'database') {
|
||||
if (isSemanticViewType(currentTypeFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('sql');
|
||||
}
|
||||
if (isSlConnection(currentConnectionFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('database');
|
||||
}
|
||||
}
|
||||
if (src === 'semantic_layer') {
|
||||
if (isPhysicalVirtualType(currentTypeFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('sql');
|
||||
}
|
||||
if (isDbConnection(currentConnectionFilter.current)) {
|
||||
filtersRef.current?.clearFilterById('database');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* Fetches "Data connection" filter options — a combined list of databases
|
||||
* and semantic layers.
|
||||
@@ -333,7 +410,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
otherFilters.push({
|
||||
col: 'database',
|
||||
opr: databaseFilter.operator,
|
||||
value: raw,
|
||||
value: raw as string | number,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -859,6 +936,9 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
{ label: t('Database'), value: 'database' },
|
||||
{ label: t('Semantic Layer'), value: 'semantic_layer' },
|
||||
],
|
||||
onFilterUpdate: (option: any) => {
|
||||
cascadeClear('source', option?.value);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
@@ -889,6 +969,10 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
? [{ label: t('Semantic View'), value: 'semantic_view' }]
|
||||
: []),
|
||||
],
|
||||
onFilterUpdate: (option: any) => {
|
||||
currentTypeFilter.current = option?.value;
|
||||
cascadeClear('type', option?.value);
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
@@ -915,6 +999,10 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
fetchSelects: fetchConnectionOptions,
|
||||
paginate: true,
|
||||
dropdownStyle: { minWidth: WIDER_DROPDOWN_WIDTH },
|
||||
onFilterUpdate: (option: any) => {
|
||||
currentConnectionFilter.current = option?.value;
|
||||
cascadeClear('connection', option?.value);
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: t('Schema'),
|
||||
@@ -1381,6 +1469,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
|
||||
pageSize={PAGE_SIZE}
|
||||
fetchData={fetchData}
|
||||
filters={filterTypes}
|
||||
filtersRef={filtersRef}
|
||||
loading={loading}
|
||||
initialSort={initialSort}
|
||||
bulkActions={bulkActions}
|
||||
|
||||
Reference in New Issue
Block a user