mirror of
https://github.com/apache/superset.git
synced 2026-09-01 13:01:33 +00:00
160 lines
4.3 KiB
TypeScript
160 lines
4.3 KiB
TypeScript
/**
|
|
* 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 { useCallback, useMemo, useState } from 'react';
|
|
import { List } from 'react-window';
|
|
import { FlattenedItem, Folder } from './types';
|
|
import DatasourcePanelItem from './DatasourcePanelItem';
|
|
|
|
const BORDER_WIDTH = 2;
|
|
const HEADER_ITEM_HEIGHT = 50;
|
|
const METRIC_OR_COLUMN_ITEM_HEIGHT = 32;
|
|
const SUBTITLE_ITEM_HEIGHT = 32;
|
|
const DIVIDER_ITEM_HEIGHT = 16;
|
|
|
|
const flattenFolderStructure = (
|
|
foldersToFlatten: Folder[],
|
|
collapsedFolderIds: Set<string>,
|
|
depth = 0,
|
|
folderMap: Map<string, Folder> = new Map(),
|
|
): { flattenedItems: FlattenedItem[]; folderMap: Map<string, Folder> } => {
|
|
const flattenedItems: FlattenedItem[] = [];
|
|
|
|
foldersToFlatten.forEach((folder, idx) => {
|
|
folderMap.set(folder.id, folder);
|
|
|
|
flattenedItems.push({
|
|
type: 'header',
|
|
folderId: folder.id,
|
|
depth,
|
|
height: HEADER_ITEM_HEIGHT,
|
|
});
|
|
|
|
if (!collapsedFolderIds.has(folder.id)) {
|
|
flattenedItems.push({
|
|
type: 'subtitle',
|
|
folderId: folder.id,
|
|
depth,
|
|
height: SUBTITLE_ITEM_HEIGHT,
|
|
totalItems: folder.totalItems,
|
|
showingItems: folder.showingItems,
|
|
});
|
|
folder.items.forEach(item => {
|
|
flattenedItems.push({
|
|
type: 'item',
|
|
folderId: folder.id,
|
|
depth,
|
|
item,
|
|
height: METRIC_OR_COLUMN_ITEM_HEIGHT,
|
|
});
|
|
});
|
|
|
|
if (folder.subFolders && folder.subFolders.length > 0) {
|
|
const { flattenedItems: subItems } = flattenFolderStructure(
|
|
folder.subFolders,
|
|
collapsedFolderIds,
|
|
depth + 1,
|
|
folderMap,
|
|
);
|
|
|
|
flattenedItems.push(...subItems);
|
|
}
|
|
}
|
|
if (depth === 0 && idx !== foldersToFlatten.length - 1) {
|
|
flattenedItems.push({
|
|
type: 'divider',
|
|
folderId: folder.id,
|
|
depth,
|
|
height: DIVIDER_ITEM_HEIGHT,
|
|
});
|
|
}
|
|
});
|
|
|
|
return { flattenedItems, folderMap };
|
|
};
|
|
|
|
interface DatasourceItemsProps {
|
|
width: number;
|
|
height: number;
|
|
folders: Folder[];
|
|
}
|
|
export const DatasourceItems = ({
|
|
width,
|
|
height,
|
|
folders,
|
|
}: DatasourceItemsProps) => {
|
|
const [collapsedFolderIds, setCollapsedFolderIds] = useState<Set<string>>(
|
|
new Set(
|
|
folders.filter(folder => folder.isCollapsed).map(folder => folder.id),
|
|
),
|
|
);
|
|
|
|
const { flattenedItems, folderMap } = useMemo(
|
|
() => flattenFolderStructure(folders, collapsedFolderIds),
|
|
[folders, collapsedFolderIds],
|
|
);
|
|
|
|
const handleToggleCollapse = useCallback((folderId: string) => {
|
|
setCollapsedFolderIds(prevIds => {
|
|
const newIds = new Set(prevIds);
|
|
if (newIds.has(folderId)) {
|
|
newIds.delete(folderId);
|
|
} else {
|
|
newIds.add(folderId);
|
|
}
|
|
return newIds;
|
|
});
|
|
}, []);
|
|
|
|
// No manual cache-reset is needed: `rowHeight` is a useCallback tied to
|
|
// `flattenedItems`, so react-window's internal size cache recomputes
|
|
// automatically whenever its reference changes.
|
|
const rowHeight = useCallback(
|
|
(index: number) => flattenedItems[index].height,
|
|
[flattenedItems],
|
|
);
|
|
|
|
const rowProps = useMemo(
|
|
() => ({
|
|
flattenedItems,
|
|
folderMap,
|
|
width,
|
|
onToggleCollapse: handleToggleCollapse,
|
|
collapsedFolderIds,
|
|
}),
|
|
[
|
|
flattenedItems,
|
|
folderMap,
|
|
width,
|
|
handleToggleCollapse,
|
|
collapsedFolderIds,
|
|
],
|
|
);
|
|
|
|
return (
|
|
<List
|
|
style={{ width: width - BORDER_WIDTH, height, maxHeight: height }}
|
|
rowHeight={rowHeight}
|
|
rowCount={flattenedItems.length}
|
|
rowProps={rowProps}
|
|
rowComponent={DatasourcePanelItem}
|
|
overscanCount={5}
|
|
/>
|
|
);
|
|
};
|