| /** |
| * 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 { |
| ReactNode, |
| SyntheticEvent, |
| MouseEventHandler as ReactMouseEventHandler, |
| } from 'react'; |
| import { styled, css, SupersetTheme, t } from '@superset-ui/core'; |
| import { Empty } from 'src/components'; |
| import Button from 'src/components/Button'; |
| |
| export enum EmptyStateSize { |
| Small, |
| Medium, |
| Big, |
| } |
| |
| export interface EmptyStateSmallProps { |
| title?: ReactNode; |
| description?: ReactNode; |
| image?: ReactNode; |
| } |
| |
| export interface EmptyStateProps extends EmptyStateSmallProps { |
| buttonText?: ReactNode; |
| buttonAction?: ReactMouseEventHandler<HTMLElement>; |
| className?: string; |
| } |
| |
| export interface ImageContainerProps { |
| image: ReactNode; |
| size: EmptyStateSize; |
| } |
| |
| const EmptyStateContainer = styled.div` |
| ${({ theme }) => css` |
| display: flex; |
| flex-direction: column; |
| width: 100%; |
| height: 100%; |
| align-items: center; |
| justify-content: center; |
| padding: ${theme.gridUnit * 4}px; |
| text-align: center; |
| |
| & .ant-empty-image svg { |
| width: auto; |
| } |
| |
| & a, |
| & span[role='button'] { |
| color: inherit; |
| text-decoration: underline; |
| &:hover { |
| color: ${theme.colors.grayscale.base}; |
| } |
| } |
| `} |
| `; |
| |
| const TextContainer = styled.div``; |
| |
| const Title = styled.p` |
| ${({ theme }) => css` |
| font-size: ${theme.typography.sizes.m}px; |
| color: ${theme.colors.grayscale.light1}; |
| margin: ${theme.gridUnit * 2}px 0 0 0; |
| font-weight: ${theme.typography.weights.bold}; |
| `} |
| `; |
| |
| const BigTitle = styled(Title)` |
| ${({ theme }) => css` |
| font-size: ${theme.typography.sizes.l}px; |
| color: ${theme.colors.grayscale.light1}; |
| margin-top: ${theme.gridUnit * 4}px; |
| `} |
| `; |
| |
| const Description = styled.p` |
| ${({ theme }) => css` |
| font-size: ${theme.typography.sizes.s}px; |
| color: ${theme.colors.grayscale.light1}; |
| margin: ${theme.gridUnit * 2}px 0 0 0; |
| `} |
| `; |
| |
| const BigDescription = styled(Description)` |
| ${({ theme }) => css` |
| font-size: ${theme.typography.sizes.m}px; |
| `} |
| `; |
| |
| const SmallDescription = styled(Description)` |
| ${({ theme }) => css` |
| margin-top: ${theme.gridUnit}px; |
| line-height: 1.2; |
| `} |
| `; |
| |
| const ActionButton = styled(Button)` |
| ${({ theme }) => css` |
| margin-top: ${theme.gridUnit * 4}px; |
| z-index: 1; |
| `} |
| `; |
| |
| const getImage = (image: string | ReactNode) => |
| typeof image === 'string' ? `/static/assets/images/${image}` : image; |
| |
| const getImageHeight = (size: EmptyStateSize) => { |
| switch (size) { |
| case EmptyStateSize.Small: |
| return { height: '50px' }; |
| case EmptyStateSize.Medium: |
| return { height: '80px' }; |
| case EmptyStateSize.Big: |
| return { height: '150px' }; |
| default: |
| return { height: '50px' }; |
| } |
| }; |
| |
| const ImageContainer = ({ image, size }: ImageContainerProps) => ( |
| <Empty |
| description={false} |
| image={getImage(image)} |
| imageStyle={getImageHeight(size)} |
| /> |
| ); |
| |
| const handleMouseDown = (e: SyntheticEvent) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| }; |
| |
| export const EmptyStateBig = ({ |
| title, |
| image, |
| description, |
| buttonAction, |
| buttonText, |
| className, |
| }: EmptyStateProps) => ( |
| <EmptyStateContainer className={className}> |
| {image && <ImageContainer image={image} size={EmptyStateSize.Big} />} |
| <TextContainer |
| css={(theme: SupersetTheme) => css` |
| max-width: ${theme.gridUnit * 150}px; |
| `} |
| > |
| <BigTitle>{title}</BigTitle> |
| {description && <BigDescription>{description}</BigDescription>} |
| {buttonAction && buttonText && ( |
| <ActionButton |
| buttonStyle="primary" |
| onClick={buttonAction} |
| onMouseDown={handleMouseDown} |
| > |
| {buttonText} |
| </ActionButton> |
| )} |
| </TextContainer> |
| </EmptyStateContainer> |
| ); |
| |
| export const EmptyStateMedium = ({ |
| title, |
| image, |
| description, |
| buttonAction, |
| buttonText, |
| }: EmptyStateProps) => ( |
| <EmptyStateContainer> |
| {image && <ImageContainer image={image} size={EmptyStateSize.Medium} />} |
| <TextContainer |
| css={(theme: SupersetTheme) => css` |
| max-width: ${theme.gridUnit * 100}px; |
| `} |
| > |
| <Title>{title}</Title> |
| {description && <Description>{description}</Description>} |
| {buttonText && buttonAction && ( |
| <ActionButton |
| buttonStyle="primary" |
| onClick={buttonAction} |
| onMouseDown={handleMouseDown} |
| > |
| {buttonText} |
| </ActionButton> |
| )} |
| </TextContainer> |
| </EmptyStateContainer> |
| ); |
| |
| export const EmptyStateSmall = ({ |
| title, |
| image, |
| description, |
| }: EmptyStateSmallProps) => ( |
| <EmptyStateContainer> |
| {image && <ImageContainer image={image} size={EmptyStateSize.Small} />} |
| <TextContainer |
| css={(theme: SupersetTheme) => css` |
| max-width: ${theme.gridUnit * 75}px; |
| `} |
| > |
| <Title>{title}</Title> |
| {description && <SmallDescription>{description}</SmallDescription>} |
| </TextContainer> |
| </EmptyStateContainer> |
| ); |
| |
| const TRANSLATIONS = { |
| NO_DATABASES_MATCH_TITLE: t('No databases match your search'), |
| NO_DATABASES_AVAILABLE_TITLE: t('There are no databases available'), |
| MANAGE_YOUR_DATABASES_TEXT: t('Manage your databases'), |
| HERE_TEXT: t('here'), |
| }; |
| |
| export const emptyStateComponent = (emptyResultsWithSearch: boolean) => ( |
| <EmptyStateSmall |
| image="empty.svg" |
| title={ |
| emptyResultsWithSearch |
| ? TRANSLATIONS.NO_DATABASES_MATCH_TITLE |
| : TRANSLATIONS.NO_DATABASES_AVAILABLE_TITLE |
| } |
| description={ |
| <p> |
| {TRANSLATIONS.MANAGE_YOUR_DATABASES_TEXT}{' '} |
| <a href="/databaseview/list">{TRANSLATIONS.HERE_TEXT}</a> |
| </p> |
| } |
| /> |
| ); |