import { css } from '@emotion/css'; import { useCallback, useMemo, MouseEvent, useRef, ChangeEvent } from 'react'; import { colorManipulator, GrafanaTheme2, LogRowModel, store } from '@grafana/data'; import { t } from '@grafana/i18n'; import { reportInteraction } from '@grafana/runtime'; import { IconButton, Input, useStyles2 } from '@grafana/ui'; import { copyText, handleOpenLogsContextClick } from '../../utils'; import { LOG_LINE_BODY_FIELD_NAME } from '../LogDetailsBody'; import { LogLineDetailsMode } from './LogLineDetails'; import { useLogIsPinned, useLogListContext } from './LogListContext'; import { LogListModel } from './processing'; interface Props { log: LogListModel; search: string; onSearch(newSearch: string): void; } export const LogLineDetailsHeader = ({ log, search, onSearch }: Props) => { const { closeDetails, detailsMode, displayedFields, getRowContextQuery, logOptionsStorageKey, logSupportsContext, noInteractions, setDetailsMode, onClickHideField, onClickShowField, onOpenContext, onPermalinkClick, onPinLine, onUnpinLine, wrapLogMessage, } = useLogListContext(); const pinned = useLogIsPinned(log); const styles = useStyles2(getStyles, detailsMode, wrapLogMessage); const containerRef = useRef(null); const inputRef = useRef(null); const searchUsedRef = useRef(false); const reportInteractionWrapper = useCallback( (interactionName: string, properties?: Record) => { if (noInteractions) { return; } reportInteraction(interactionName, properties); }, [noInteractions] ); const copyLogLine = useCallback(() => { copyText(log.entry, containerRef); reportInteractionWrapper('logs_log_line_details_header_copy_clicked'); }, [log.entry, reportInteractionWrapper]); const copyLinkToLogLine = useCallback(() => { onPermalinkClick?.(log); reportInteractionWrapper('logs_log_line_details_header_permalink_clicked'); }, [log, onPermalinkClick, reportInteractionWrapper]); const togglePinning = useCallback(() => { if (pinned) { onUnpinLine?.(log); } else { onPinLine?.(log); } reportInteractionWrapper('logs_log_line_details_header_pinning_clicked'); }, [log, onPinLine, onUnpinLine, pinned, reportInteractionWrapper]); const shouldlogSupportsContext = useMemo( () => (logSupportsContext ? logSupportsContext(log) : false), [log, logSupportsContext] ); const showContext = useCallback( async (event: MouseEvent) => { handleOpenLogsContextClick(event, log, getRowContextQuery, (log: LogRowModel) => onOpenContext?.(log, () => {})); reportInteractionWrapper('logs_log_line_details_header_context_clicked'); }, [log, getRowContextQuery, reportInteractionWrapper, onOpenContext] ); const showLogLineToggle = onClickHideField && onClickShowField && displayedFields.length > 0; const logLineDisplayed = displayedFields.includes(LOG_LINE_BODY_FIELD_NAME); const toggleDetailsMode = useCallback(() => { const newMode = detailsMode === 'inline' ? 'sidebar' : 'inline'; if (logOptionsStorageKey) { store.set(`${logOptionsStorageKey}.detailsMode`, newMode); } setDetailsMode(newMode); }, [detailsMode, logOptionsStorageKey, setDetailsMode]); const toggleLogLine = useCallback(() => { if (logLineDisplayed) { onClickHideField?.(LOG_LINE_BODY_FIELD_NAME); } else { onClickShowField?.(LOG_LINE_BODY_FIELD_NAME); } reportInteractionWrapper('logs_log_line_details_header_show_logline_clicked'); }, [logLineDisplayed, onClickHideField, onClickShowField, reportInteractionWrapper]); const clearSearch = useMemo( () => ( { onSearch(''); reportInteractionWrapper('logs_log_line_details_header_search_cleared'); if (inputRef.current) { inputRef.current.value = ''; } }} tooltip={t('logs.log-line-details.clear-search', 'Clear')} /> ), [onSearch, reportInteractionWrapper] ); const handleSearch = useCallback( (e: ChangeEvent) => { onSearch(e.target.value); if (!searchUsedRef.current) { reportInteractionWrapper('logs_log_line_details_header_search_used'); searchUsedRef.current = true; } }, [onSearch, reportInteractionWrapper] ); return (
{showLogLineToggle && ( )}
{onPermalinkClick && log.rowId !== undefined && log.uid && ( )} {pinned && onUnpinLine && ( )} {!pinned && onPinLine && ( )} {shouldlogSupportsContext && ( )}
); }; const getStyles = (theme: GrafanaTheme2, mode: LogLineDetailsMode, wrapLogMessage: boolean) => ({ container: css({ overflow: 'auto', height: '100%', }), scrollContainer: css({ overflow: 'auto', height: '100%', }), header: css({ alignItems: 'center', background: theme.colors.background.canvas, display: 'flex', flexDirection: !wrapLogMessage && mode === 'inline' ? 'row-reverse' : 'row', gap: theme.spacing(0.75), zIndex: theme.zIndex.navbarFixed, height: theme.spacing(5.5), marginBottom: theme.spacing(1), padding: theme.spacing(0.5, 1), position: 'sticky', top: 0, }), icons: css({ display: 'flex', gap: theme.spacing(0.75), }), copyLogButton: css({ padding: 0, height: theme.spacing(4), width: theme.spacing(2.5), overflow: 'hidden', '&:hover': { backgroundColor: colorManipulator.alpha(theme.colors.text.primary, 0.12), }, }), componentWrapper: css({ padding: theme.spacing(0, 1, 1, 1), }), });