|
|
|
|
@ -11,7 +11,7 @@ import { |
|
|
|
|
LinkModel, |
|
|
|
|
} from '@grafana/data'; |
|
|
|
|
import { SortOrder, TooltipDisplayMode } from '@grafana/schema'; |
|
|
|
|
import { HorizontalGroup, LinkButton, useStyles2 } from '@grafana/ui'; |
|
|
|
|
import { LinkButton, useStyles2, VerticalGroup } from '@grafana/ui'; |
|
|
|
|
|
|
|
|
|
export interface Props { |
|
|
|
|
data?: DataFrame; // source data
|
|
|
|
|
@ -39,18 +39,19 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode, he |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const displayValues: Array<[string, unknown, string]> = []; |
|
|
|
|
const links: Record<string, Array<LinkModel<Field>>> = {}; |
|
|
|
|
const links: Array<LinkModel<Field>> = []; |
|
|
|
|
const linkLookup = new Set<string>(); |
|
|
|
|
|
|
|
|
|
for (const f of orderedVisibleFields) { |
|
|
|
|
const v = f.values.get(rowIndex); |
|
|
|
|
const disp = f.display ? f.display(v) : { text: `${v}`, numeric: +v }; |
|
|
|
|
if (f.getLinks) { |
|
|
|
|
f.getLinks({ calculatedValue: disp, valueRowIndex: rowIndex }).forEach((link) => { |
|
|
|
|
const key = getFieldDisplayName(f, data); |
|
|
|
|
if (!links[key]) { |
|
|
|
|
links[key] = []; |
|
|
|
|
const key = `${link.title}/${link.href}`; |
|
|
|
|
if (!linkLookup.has(key)) { |
|
|
|
|
links.push(link); |
|
|
|
|
linkLookup.add(key); |
|
|
|
|
} |
|
|
|
|
links[key].push(link); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -61,6 +62,29 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode, he |
|
|
|
|
displayValues.sort((a, b) => arrayUtils.sortValues(sortOrder)(a[1], b[1])); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const renderLinks = () => |
|
|
|
|
links.length > 0 && ( |
|
|
|
|
<tr> |
|
|
|
|
<td colSpan={2}> |
|
|
|
|
<VerticalGroup> |
|
|
|
|
{links.map((link, i) => ( |
|
|
|
|
<LinkButton |
|
|
|
|
key={i} |
|
|
|
|
icon={'external-link-alt'} |
|
|
|
|
target={link.target} |
|
|
|
|
href={link.href} |
|
|
|
|
onClick={link.onClick} |
|
|
|
|
fill="text" |
|
|
|
|
style={{ width: '100%' }} |
|
|
|
|
> |
|
|
|
|
{link.title} |
|
|
|
|
</LinkButton> |
|
|
|
|
))} |
|
|
|
|
</VerticalGroup> |
|
|
|
|
</td> |
|
|
|
|
</tr> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<div className={styles.wrapper}> |
|
|
|
|
{header && ( |
|
|
|
|
@ -74,45 +98,22 @@ export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder, mode, he |
|
|
|
|
displayValues.map((v, i) => ( |
|
|
|
|
<tr key={`${i}/${rowIndex}`} className={i === columnIndex ? styles.highlight : ''}> |
|
|
|
|
<th>{v[0]}:</th> |
|
|
|
|
<td>{renderWithLinks(v[0], v[2], links)}</td> |
|
|
|
|
<td>{v[2]}</td> |
|
|
|
|
</tr> |
|
|
|
|
))} |
|
|
|
|
{mode === TooltipDisplayMode.Single && columnIndex && ( |
|
|
|
|
<tr key={`${columnIndex}/${rowIndex}`}> |
|
|
|
|
<th>{displayValues[columnIndex][0]}:</th> |
|
|
|
|
<td>{renderWithLinks(displayValues[columnIndex][0], displayValues[columnIndex][2], links)}</td> |
|
|
|
|
<td>{displayValues[columnIndex][2]}</td> |
|
|
|
|
</tr> |
|
|
|
|
)} |
|
|
|
|
{renderLinks()} |
|
|
|
|
</tbody> |
|
|
|
|
</table> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const renderWithLinks = (key: string, val: string, links: Record<string, Array<LinkModel<Field>>>) => |
|
|
|
|
links[key] ? ( |
|
|
|
|
<HorizontalGroup> |
|
|
|
|
<> |
|
|
|
|
{val} |
|
|
|
|
{links[key].map((link, i) => ( |
|
|
|
|
<LinkButton |
|
|
|
|
key={i} |
|
|
|
|
icon={'external-link-alt'} |
|
|
|
|
target={link.target} |
|
|
|
|
href={link.href} |
|
|
|
|
onClick={link.onClick} |
|
|
|
|
fill="text" |
|
|
|
|
style={{ width: '100%' }} |
|
|
|
|
> |
|
|
|
|
{link.title} |
|
|
|
|
</LinkButton> |
|
|
|
|
))} |
|
|
|
|
</> |
|
|
|
|
</HorizontalGroup> |
|
|
|
|
) : ( |
|
|
|
|
<>{val}</> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const getStyles = (theme: GrafanaTheme2) => { |
|
|
|
|
const bg = theme.isDark ? theme.v1.palette.dark2 : theme.v1.palette.white; |
|
|
|
|
const headerBg = theme.isDark ? theme.v1.palette.dark9 : theme.v1.palette.gray5; |
|
|
|
|
|