UNPKG

@grafana/ui

Version:
1 lines 15.5 kB
{"version":3,"file":"VizTooltipRow.mjs","sources":["../../../../src/components/VizTooltip/VizTooltipRow.tsx"],"sourcesContent":["import { css } from '@emotion/css';\nimport clsx from 'clsx';\nimport { type CSSProperties, type ReactNode, useEffect, useRef, useState } from 'react';\nimport * as React from 'react';\n\nimport { type GrafanaTheme2 } from '@grafana/data';\n\nimport { useStyles2 } from '../../themes/ThemeContext';\nimport { InlineToast } from '../InlineToast/InlineToast';\nimport { Tooltip } from '../Tooltip/Tooltip';\n\nimport { ColorIndicatorPosition, VizTooltipColorIndicator } from './VizTooltipColorIndicator';\nimport { VizTooltipColorPlacement, type VizTooltipItem } from './types';\n\ninterface VizTooltipRowProps extends Omit<VizTooltipItem, 'value'> {\n /** The formatted value to display. Widened from `VizTooltipItem.value` to also accept numbers, null, and ReactNode. */\n value: string | number | null | ReactNode;\n /** CSS `justify-content` value for the row layout. Defaults to `'start'`. */\n justify?: string;\n /** Whether this row corresponds to the closest/hovered series — renders the label in bold. */\n isActive?: boolean;\n /** Right margin applied to the value cell, used to leave room for overlay elements such as the close button. */\n marginRight?: string;\n /**\n * Whether the tooltip is currently pinned (locked open by the user).\n * When pinned, label and value become clickable to copy their text to the clipboard.\n * Defaults to `false`.\n */\n isPinned?: boolean;\n /** When true the value cell becomes vertically scrollable up to a fixed max-height. */\n showValueScroll?: boolean;\n /** When true the color indicator is rendered hollow, signalling the field is not shown in the visualization. */\n isHiddenFromViz?: boolean;\n}\n\nenum LabelValueTypes {\n label = 'label',\n value = 'value',\n}\n\nconst SUCCESSFULLY_COPIED_TEXT = 'Copied to clipboard';\nconst SHOW_SUCCESS_DURATION = 2 * 1000;\nconst HORIZONTAL_PX_PER_CHAR = 7;\nconst CAN_COPY = Boolean(navigator.clipboard && window.isSecureContext);\n\n/** @alpha */\nexport const VizTooltipRow = ({\n label,\n value,\n color,\n colorIndicator,\n colorPlacement = VizTooltipColorPlacement.first,\n justify,\n isActive = false,\n marginRight,\n isPinned = false,\n lineStyle,\n showValueScroll,\n isHiddenFromViz,\n}: VizTooltipRowProps) => {\n const styles = useStyles2(getStyles, justify, marginRight);\n\n const innerValueScrollStyle: CSSProperties = showValueScroll\n ? {\n maxHeight: 55,\n whiteSpace: 'wrap',\n wordBreak: 'break-word',\n overflowY: 'auto',\n }\n : {\n whiteSpace: 'pre-line',\n wordBreak: 'break-word',\n lineHeight: 1.2,\n };\n\n const [showLabelTooltip, setShowLabelTooltip] = useState(false);\n\n const [copiedText, setCopiedText] = useState<Record<string, string> | null>(null);\n const [showCopySuccess, setShowCopySuccess] = useState(false);\n\n const labelRef = useRef<null | HTMLDivElement>(null);\n const valueRef = useRef<null | HTMLDivElement>(null);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n if (showCopySuccess) {\n timeoutId = setTimeout(() => {\n setShowCopySuccess(false);\n }, SHOW_SUCCESS_DURATION);\n }\n\n return () => {\n window.clearTimeout(timeoutId);\n };\n }, [showCopySuccess]);\n\n const copyToClipboard = async (text: string, type: LabelValueTypes) => {\n if (!CAN_COPY) {\n fallbackCopyToClipboard(text, type);\n return;\n }\n\n try {\n await navigator.clipboard.writeText(text);\n setCopiedText({ [`${type}`]: text });\n setShowCopySuccess(true);\n } catch (error) {\n setCopiedText(null);\n }\n };\n\n const fallbackCopyToClipboard = (text: string, type: LabelValueTypes) => {\n // Use a fallback method for browsers/contexts that don't support the Clipboard API.\n const textarea = document.createElement('textarea');\n labelRef.current?.appendChild(textarea);\n textarea.value = text;\n textarea.focus();\n textarea.select();\n try {\n const successful = document.execCommand('copy');\n if (successful) {\n setCopiedText({ [`${type}`]: text });\n setShowCopySuccess(true);\n }\n } catch (err) {\n console.error('Unable to copy to clipboard', err);\n }\n\n textarea.remove();\n };\n\n const onMouseEnterLabel = (event: React.MouseEvent<HTMLDivElement>) => {\n if (event.currentTarget.offsetWidth < event.currentTarget.scrollWidth) {\n setShowLabelTooltip(true);\n }\n };\n\n const onMouseLeaveLabel = () => setShowLabelTooltip(false);\n\n // if label is > 50% window width, try to put label/value pairs on new lines\n if (label.length * HORIZONTAL_PX_PER_CHAR > window.innerWidth / 2) {\n label = label.replaceAll('{', '{\\n ').replaceAll('}', '\\n}').replaceAll(', ', ',\\n ');\n }\n\n return (\n <div className={styles.contentWrapper}>\n {color && colorPlacement === VizTooltipColorPlacement.first && (\n <div className={styles.colorWrapper}>\n <VizTooltipColorIndicator\n color={color}\n colorIndicator={colorIndicator}\n lineStyle={lineStyle}\n isHollow={isHiddenFromViz}\n />\n </div>\n )}\n {label && (\n <div className={styles.labelWrapper}>\n {!isPinned ? (\n <div className={clsx(styles.label, isActive ? styles.activeSeries : '')}>{label}</div>\n ) : (\n <>\n <Tooltip content={label} interactive={false} show={showLabelTooltip}>\n <>\n {showCopySuccess && copiedText?.label && (\n <InlineToast placement=\"top\" referenceElement={labelRef.current}>\n {SUCCESSFULLY_COPIED_TEXT}\n </InlineToast>\n )}\n {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}\n <div\n className={clsx(styles.label, isActive ? styles.activeSeries : '', CAN_COPY ? styles.copy : '')}\n onMouseEnter={onMouseEnterLabel}\n onMouseLeave={onMouseLeaveLabel}\n onClick={() => copyToClipboard(label, LabelValueTypes.label)}\n ref={labelRef}\n >\n {label}\n </div>\n </>\n </Tooltip>\n </>\n )}\n </div>\n )}\n\n <div className={styles.valueWrapper}>\n {color && colorPlacement === VizTooltipColorPlacement.leading && (\n <VizTooltipColorIndicator\n color={color}\n colorIndicator={colorIndicator}\n position={ColorIndicatorPosition.Leading}\n lineStyle={lineStyle}\n />\n )}\n\n {!isPinned ? (\n <div className={styles.value} style={innerValueScrollStyle}>\n {value}\n </div>\n ) : (\n <>\n {showCopySuccess && copiedText?.value && (\n <InlineToast placement=\"top\" referenceElement={valueRef.current}>\n {SUCCESSFULLY_COPIED_TEXT}\n </InlineToast>\n )}\n {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}\n <div\n className={clsx(styles.value, CAN_COPY ? styles.copy : '')}\n style={innerValueScrollStyle}\n onClick={() => copyToClipboard(value ? value.toString() : '', LabelValueTypes.value)}\n ref={valueRef}\n >\n {value}\n </div>\n </>\n )}\n\n {color && colorPlacement === VizTooltipColorPlacement.trailing && (\n <VizTooltipColorIndicator\n color={color}\n colorIndicator={colorIndicator}\n position={ColorIndicatorPosition.Trailing}\n lineStyle={lineStyle}\n />\n )}\n </div>\n </div>\n );\n};\n\nconst getStyles = (theme: GrafanaTheme2, justify = 'start', marginRight?: string) => ({\n contentWrapper: css({\n display: 'flex',\n maxWidth: '100%',\n alignItems: 'start',\n justifyContent: justify,\n columnGap: theme.spacing(0.75),\n }),\n label: css({ display: 'inline' }),\n value: css({\n fontWeight: 500,\n textOverflow: 'ellipsis',\n overflow: 'hidden',\n }),\n colorWrapper: css({\n alignSelf: 'center',\n flexShrink: 0,\n }),\n labelWrapper: css({\n flexGrow: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n color: theme.colors.text.secondary,\n fontWeight: 400,\n }),\n valueWrapper: css({\n display: 'flex',\n alignItems: 'center',\n flexShrink: 0,\n alignSelf: 'center',\n marginRight,\n }),\n activeSeries: css({\n fontWeight: theme.typography.fontWeightBold,\n color: theme.colors.text.maxContrast,\n }),\n copy: css({\n cursor: 'pointer',\n }),\n});\n"],"names":["LabelValueTypes"],"mappings":"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"}