| /** |
| * 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 { |
| forwardRef, |
| ReactNode, |
| useContext, |
| useEffect, |
| useRef, |
| useState, |
| } from 'react'; |
| import { css, getExtensionsRegistry, styled, t } from '@superset-ui/core'; |
| import { useUiConfig } from 'src/components/UiConfigContext'; |
| import { Tooltip } from 'src/components/Tooltip'; |
| import { useSelector } from 'react-redux'; |
| import EditableTitle from 'src/components/EditableTitle'; |
| import SliceHeaderControls from 'src/dashboard/components/SliceHeaderControls'; |
| import { SliceHeaderControlsProps } from 'src/dashboard/components/SliceHeaderControls/types'; |
| import FiltersBadge from 'src/dashboard/components/FiltersBadge'; |
| import Icons from 'src/components/Icons'; |
| import { RootState } from 'src/dashboard/types'; |
| import { getSliceHeaderTooltip } from 'src/dashboard/util/getSliceHeaderTooltip'; |
| import { DashboardPageIdContext } from 'src/dashboard/containers/DashboardPage'; |
| |
| const extensionsRegistry = getExtensionsRegistry(); |
| |
| type SliceHeaderProps = SliceHeaderControlsProps & { |
| updateSliceName?: (arg0: string) => void; |
| editMode?: boolean; |
| annotationQuery?: object; |
| annotationError?: object; |
| sliceName?: string; |
| filters: object; |
| handleToggleFullSize: () => void; |
| formData: object; |
| width: number; |
| height: number; |
| }; |
| |
| const annotationsLoading = t('Annotation layers are still loading.'); |
| const annotationsError = t('One or more annotation layers failed loading.'); |
| const CrossFilterIcon = styled(Icons.ApartmentOutlined)` |
| ${({ theme }) => ` |
| cursor: default; |
| color: ${theme.colors.primary.base}; |
| line-height: 1.8; |
| `} |
| `; |
| |
| const ChartHeaderStyles = styled.div` |
| ${({ theme }) => css` |
| font-size: ${theme.typography.sizes.l}px; |
| font-weight: ${theme.typography.weights.bold}; |
| margin-bottom: ${theme.gridUnit}px; |
| display: flex; |
| max-width: 100%; |
| align-items: flex-start; |
| min-height: 0; |
| |
| & > .header-title { |
| overflow: hidden; |
| text-overflow: ellipsis; |
| max-width: 100%; |
| flex-grow: 1; |
| display: -webkit-box; |
| -webkit-line-clamp: 2; |
| -webkit-box-orient: vertical; |
| |
| & > span.antd5-tooltip-open { |
| display: inline; |
| } |
| } |
| |
| & > .header-controls { |
| display: flex; |
| align-items: center; |
| height: 24px; |
| |
| & > * { |
| margin-left: ${theme.gridUnit * 2}px; |
| } |
| } |
| |
| .dropdown.btn-group { |
| pointer-events: none; |
| vertical-align: top; |
| & > * { |
| pointer-events: auto; |
| } |
| } |
| |
| .dropdown-toggle.btn.btn-default { |
| background: none; |
| border: none; |
| box-shadow: none; |
| } |
| |
| .dropdown-menu.dropdown-menu-right { |
| top: ${theme.gridUnit * 5}px; |
| } |
| |
| .divider { |
| margin: ${theme.gridUnit}px 0; |
| } |
| |
| .refresh-tooltip { |
| display: block; |
| height: ${theme.gridUnit * 4}px; |
| margin: ${theme.gridUnit}px 0; |
| color: ${theme.colors.text.label}; |
| } |
| `} |
| `; |
| |
| const SliceHeader = forwardRef<HTMLDivElement, SliceHeaderProps>( |
| ( |
| { |
| forceRefresh = () => ({}), |
| updateSliceName = () => ({}), |
| toggleExpandSlice = () => ({}), |
| logExploreChart = () => ({}), |
| logEvent, |
| exportCSV = () => ({}), |
| exportXLSX = () => ({}), |
| editMode = false, |
| annotationQuery = {}, |
| annotationError = {}, |
| cachedDttm = null, |
| updatedDttm = null, |
| isCached = [], |
| isExpanded = false, |
| sliceName = '', |
| supersetCanExplore = false, |
| supersetCanShare = false, |
| supersetCanCSV = false, |
| exportPivotCSV, |
| exportFullCSV, |
| exportFullXLSX, |
| slice, |
| componentId, |
| dashboardId, |
| addSuccessToast, |
| addDangerToast, |
| handleToggleFullSize, |
| isFullSize, |
| chartStatus, |
| formData, |
| width, |
| height, |
| }, |
| ref, |
| ) => { |
| const SliceHeaderExtension = extensionsRegistry.get( |
| 'dashboard.slice.header', |
| ); |
| const uiConfig = useUiConfig(); |
| const dashboardPageId = useContext(DashboardPageIdContext); |
| const [headerTooltip, setHeaderTooltip] = useState<ReactNode | null>(null); |
| const headerRef = useRef<HTMLDivElement>(null); |
| // TODO: change to indicator field after it will be implemented |
| const crossFilterValue = useSelector<RootState, any>( |
| state => state.dataMask[slice?.slice_id]?.filterState?.value, |
| ); |
| const isCrossFiltersEnabled = useSelector<RootState, boolean>( |
| ({ dashboardInfo }) => dashboardInfo.crossFiltersEnabled, |
| ); |
| |
| const canExplore = !editMode && supersetCanExplore; |
| |
| useEffect(() => { |
| const headerElement = headerRef.current; |
| if (canExplore) { |
| setHeaderTooltip(getSliceHeaderTooltip(sliceName)); |
| } else if ( |
| headerElement && |
| (headerElement.scrollWidth > headerElement.offsetWidth || |
| headerElement.scrollHeight > headerElement.offsetHeight) |
| ) { |
| setHeaderTooltip(sliceName ?? null); |
| } else { |
| setHeaderTooltip(null); |
| } |
| }, [sliceName, width, height, canExplore]); |
| |
| const exploreUrl = `/explore/?dashboard_page_id=${dashboardPageId}&slice_id=${slice.slice_id}`; |
| |
| return ( |
| <ChartHeaderStyles data-test="slice-header" ref={ref}> |
| <div className="header-title" ref={headerRef}> |
| <Tooltip title={headerTooltip}> |
| <EditableTitle |
| title={ |
| sliceName || |
| (editMode |
| ? '---' // this makes an empty title clickable |
| : '') |
| } |
| canEdit={editMode} |
| onSaveTitle={updateSliceName} |
| showTooltip={false} |
| url={canExplore ? exploreUrl : undefined} |
| /> |
| </Tooltip> |
| {!!Object.values(annotationQuery).length && ( |
| <Tooltip |
| id="annotations-loading-tooltip" |
| placement="top" |
| title={annotationsLoading} |
| > |
| <i |
| role="img" |
| aria-label={annotationsLoading} |
| className="fa fa-refresh warning" |
| /> |
| </Tooltip> |
| )} |
| {!!Object.values(annotationError).length && ( |
| <Tooltip |
| id="annotation-errors-tooltip" |
| placement="top" |
| title={annotationsError} |
| > |
| <i |
| role="img" |
| aria-label={annotationsError} |
| className="fa fa-exclamation-circle danger" |
| /> |
| </Tooltip> |
| )} |
| </div> |
| <div className="header-controls"> |
| {!editMode && ( |
| <> |
| {SliceHeaderExtension && ( |
| <SliceHeaderExtension |
| sliceId={slice.slice_id} |
| dashboardId={dashboardId} |
| /> |
| )} |
| {crossFilterValue && ( |
| <Tooltip |
| placement="top" |
| title={t( |
| 'This chart applies cross-filters to charts whose datasets contain columns with the same name.', |
| )} |
| > |
| <CrossFilterIcon iconSize="m" /> |
| </Tooltip> |
| )} |
| {!uiConfig.hideChartControls && ( |
| <FiltersBadge chartId={slice.slice_id} /> |
| )} |
| {!uiConfig.hideChartControls && ( |
| <SliceHeaderControls |
| slice={slice} |
| isCached={isCached} |
| isExpanded={isExpanded} |
| cachedDttm={cachedDttm} |
| updatedDttm={updatedDttm} |
| toggleExpandSlice={toggleExpandSlice} |
| forceRefresh={forceRefresh} |
| logExploreChart={logExploreChart} |
| logEvent={logEvent} |
| exportCSV={exportCSV} |
| exportPivotCSV={exportPivotCSV} |
| exportFullCSV={exportFullCSV} |
| exportXLSX={exportXLSX} |
| exportFullXLSX={exportFullXLSX} |
| supersetCanExplore={supersetCanExplore} |
| supersetCanShare={supersetCanShare} |
| supersetCanCSV={supersetCanCSV} |
| componentId={componentId} |
| dashboardId={dashboardId} |
| addSuccessToast={addSuccessToast} |
| addDangerToast={addDangerToast} |
| handleToggleFullSize={handleToggleFullSize} |
| isFullSize={isFullSize} |
| isDescriptionExpanded={isExpanded} |
| chartStatus={chartStatus} |
| formData={formData} |
| exploreUrl={exploreUrl} |
| crossFiltersEnabled={isCrossFiltersEnabled} |
| /> |
| )} |
| </> |
| )} |
| </div> |
| </ChartHeaderStyles> |
| ); |
| }, |
| ); |
| |
| export default SliceHeader; |