import { css } from '@emotion/css'; import React, { lazy, PureComponent, RefObject, Suspense } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { DataSourceInstanceSettings, RawTimeRange } from '@grafana/data'; import { config, DataSourcePicker, reportInteraction } from '@grafana/runtime'; import { defaultIntervals, PageToolbar, RefreshPicker, SetInterval, ToolbarButton, ButtonGroup } from '@grafana/ui'; import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate'; import { contextSrv } from 'app/core/core'; import { createAndCopyShortLink } from 'app/core/utils/shortLinks'; import { AccessControlAction } from 'app/types'; import { ExploreId } from 'app/types/explore'; import { StoreState } from 'app/types/store'; import { DashNavButton } from '../dashboard/components/DashNav/DashNavButton'; import { getTimeSrv } from '../dashboard/services/TimeSrv'; import { updateFiscalYearStartMonthForSession, updateTimeZoneForSession } from '../profile/state/reducers'; import { getFiscalYearStartMonth, getTimeZone } from '../profile/state/selectors'; import { ExploreTimeControls } from './ExploreTimeControls'; import { LiveTailButton } from './LiveTailButton'; import { changeDatasource } from './state/datasource'; import { splitClose, splitOpen, maximizePaneAction, evenPaneResizeAction } from './state/main'; import { cancelQueries, runQueries } from './state/query'; import { isSplit } from './state/selectors'; import { syncTimes, changeRefreshInterval } from './state/time'; import { LiveTailControls } from './useLiveTailControls'; const AddToDashboard = lazy(() => import('./AddToDashboard').then(({ AddToDashboard }) => ({ default: AddToDashboard })) ); const getStyles = (exploreId: ExploreId, isLargerExploreId: boolean) => { return { rotateIcon: css({ '> div > svg': { transform: (exploreId === 'left' && isLargerExploreId) || (exploreId === 'right' && !isLargerExploreId) ? 'rotate(180deg)' : 'none', }, }), }; }; interface OwnProps { exploreId: ExploreId; onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void; topOfViewRef: RefObject; } type Props = OwnProps & ConnectedProps; class UnConnectedExploreToolbar extends PureComponent { onChangeDatasource = async (dsSettings: DataSourceInstanceSettings) => { const { changeDatasource, exploreId } = this.props; changeDatasource(exploreId, dsSettings.uid, { importQueries: true }); }; onRunQuery = (loading = false) => { const { runQueries, cancelQueries, exploreId } = this.props; if (loading) { return cancelQueries(exploreId); } else { return runQueries(exploreId); } }; onChangeRefreshInterval = (item: string) => { const { changeRefreshInterval, exploreId } = this.props; changeRefreshInterval(exploreId, item); }; onChangeTimeSync = () => { const { syncTimes, exploreId } = this.props; syncTimes(exploreId); }; onCopyShortLink = async () => { await createAndCopyShortLink(window.location.href); reportInteraction('grafana_explore_shortened_link_clicked'); }; onOpenSplitView = () => { const { split } = this.props; split(); reportInteraction('grafana_explore_split_view_opened', { origin: 'menu' }); }; onCloseSplitView = () => { const { closeSplit, exploreId } = this.props; closeSplit(exploreId); reportInteraction('grafana_explore_split_view_closed'); }; renderRefreshPicker = (showSmallTimePicker: boolean) => { const { loading, refreshInterval, isLive } = this.props; let refreshPickerText: string | undefined = loading ? 'Cancel' : 'Run query'; let refreshPickerTooltip = undefined; let refreshPickerWidth = '108px'; if (showSmallTimePicker) { refreshPickerTooltip = refreshPickerText; refreshPickerText = undefined; refreshPickerWidth = '35px'; } return ( this.onRunQuery(loading)} noIntervalPicker={isLive} primary={true} width={refreshPickerWidth} /> ); }; renderActions = () => { const { splitted, isLive, exploreId, range, timeZone, fiscalYearStartMonth, onChangeTime, syncedTimes, onChangeTimeZone, onChangeFiscalYearStartMonth, isPaused, hasLiveOption, containerWidth, largerExploreId, } = this.props; const showSmallTimePicker = splitted || containerWidth < 1210; const isLargerExploreId = largerExploreId === exploreId; const styles = getStyles(exploreId, isLargerExploreId); const showExploreToDashboard = contextSrv.hasAccess(AccessControlAction.DashboardsCreate, contextSrv.isEditor) || contextSrv.hasAccess(AccessControlAction.DashboardsWrite, contextSrv.isEditor); const onClickResize = () => { if (isLargerExploreId) { this.props.evenPaneResizeAction(); } else { this.props.maximizePaneAction({ exploreId: exploreId }); } }; return [ !splitted ? ( Split ) : ( Close ), showExploreToDashboard && ( ), !isLive && ( ), this.renderRefreshPicker(showSmallTimePicker), hasLiveOption && ( {(c) => { const controls = { ...c, start: () => { reportInteraction('grafana_explore_logs_live_tailing_clicked', { datasourceType: this.props.datasourceType, }); c.start(); }, }; return ( ); }} ), ].filter(Boolean); }; render() { const { datasourceMissing, exploreId, splitted, containerWidth, topOfViewRef, refreshInterval, loading } = this.props; const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false; const isTopnav = config.featureToggles.topnav; const shareButton = ( ); const getDataSourcePicker = () => !datasourceMissing && ( ); const toolbarLeftItems = [ // We only want to show the shortened link button in the left Toolbar if topnav is not enabled as with topnav enabled it sits next to the brecrumbs !isTopnav && exploreId === ExploreId.left && shareButton, getDataSourcePicker(), ].filter(Boolean); return (
{refreshInterval && } {isTopnav && (
]} />
)} {this.renderActions()}
); } } const mapStateToProps = (state: StoreState, { exploreId }: OwnProps) => { const { syncedTimes, largerExploreId } = state.explore; const exploreItem = state.explore[exploreId]!; const { datasourceInstance, datasourceMissing, range, refreshInterval, loading, isLive, isPaused, containerWidth } = exploreItem; const hasLiveOption = !!datasourceInstance?.meta?.streaming; return { datasourceMissing, datasourceRef: datasourceInstance?.getRef(), datasourceType: datasourceInstance?.type, loading, range, timeZone: getTimeZone(state.user), fiscalYearStartMonth: getFiscalYearStartMonth(state.user), splitted: isSplit(state), refreshInterval, hasLiveOption, isLive, isPaused, syncedTimes, containerWidth, largerExploreId, }; }; const mapDispatchToProps = { changeDatasource, changeRefreshInterval, cancelQueries, runQueries, closeSplit: splitClose, split: splitOpen, syncTimes, onChangeTimeZone: updateTimeZoneForSession, onChangeFiscalYearStartMonth: updateFiscalYearStartMonthForSession, maximizePaneAction, evenPaneResizeAction, }; const connector = connect(mapStateToProps, mapDispatchToProps); export const ExploreToolbar = connector(UnConnectedExploreToolbar);