/** * 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 { CSSProperties, ReactNode, useCallback } from 'react'; import { css, styled, t, useCSSTextTruncation, useTheme, } from '@superset-ui/core'; import { Icons } from 'src/components/Icons'; import { Tooltip } from 'src/components/Tooltip'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { DndItemValue, FlattenedItem, Folder } from './types'; 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.colorPrimaryText}; `; const ButtonContainer = styled.div` text-align: center; padding-top: 2px; `; const LabelWrapper = styled.div` ${({ theme }) => css` color: ${theme.colors.grayscale.dark1}; overflow: hidden; text-overflow: ellipsis; font-size: ${theme.fontSizeSM}px; background-color: ${theme.colors.grayscale.light4}; margin: ${theme.gridUnit * 2}px 0; border-radius: ${theme.borderRadius}px; 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.sizeUnit * 4}px; } & > .option-label { overflow: hidden; text-overflow: ellipsis; } } `} `; const SectionHeaderButton = styled.button` border: none; background: transparent; width: 100%; height: 100%; padding-inline: 0; `; const SectionHeaderTextContainer = styled.div` display: flex; justify-content: space-between; align-items: center; width: 100%; `; const SectionHeader = styled.span` ${({ theme }) => ` font-size: ${theme.fontSize}px; line-height: 1.3; `} `; const Box = styled.div` ${({ theme }) => ` border: 1px ${theme.colors.grayscale.light4} solid; border-radius: ${theme.sizeUnit}px; font-size: ${theme.fontSizeSM}px; padding: ${theme.sizeUnit}px; color: ${theme.colors.grayscale.light1}; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 1; -webkit-box-orient: vertical; `} `; const Divider = styled.div` ${({ theme }) => css` height: 16px; border-bottom: 1px solid ${theme.colors.grayscale.light3}; `} `; export interface DatasourcePanelItemProps { index: number; style: CSSProperties; data: { flattenedItems: FlattenedItem[]; folderMap: Map; width: number; onToggleCollapse: (folderId: string) => void; collapsedFolderIds: Set; }; } const DatasourcePanelItem = ({ index, style, data, }: DatasourcePanelItemProps) => { const { flattenedItems, folderMap, width, onToggleCollapse, collapsedFolderIds, } = data; const item = flattenedItems[index]; const theme = useTheme(); const [labelRef, labelIsTruncated] = useCSSTextTruncation({ isVertical: true, isHorizontal: false, }); const getTooltipNode = useCallback( (folder: Folder) => { let tooltipNode: ReactNode | null = null; if (labelIsTruncated) { tooltipNode = (
{t('Name')}: {folder.name}
); } if (folder.description) { tooltipNode = (
{tooltipNode}
{t('Description')}: {folder.description}
); } return tooltipNode; }, [labelIsTruncated], ); if (!item) return null; const folder = folderMap.get(item.folderId); if (!folder) return null; const indentation = item.depth * theme.gridUnit * 4; return (
{item.type === 'header' && ( onToggleCollapse(folder.id)}> {folder.name} {collapsedFolderIds.has(folder.id) ? ( ) : ( )} )} {item.type === 'subtitle' && (
{t(`Showing %s of %s items`, item.showingItems, item.totalItems)}
)} {item.type === 'item' && item.item && ( )} {item.type === 'divider' && ( )}
); }; export default DatasourcePanelItem;