AnnotationListItem.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { css } from '@emotion/css';
  2. import React, { FC, MouseEvent } from 'react';
  3. import { AnnotationEvent, DateTimeInput, GrafanaTheme2, PanelProps } from '@grafana/data';
  4. import { Card, TagList, Tooltip, useStyles2 } from '@grafana/ui';
  5. import { AnnoOptions } from './types';
  6. interface Props extends Pick<PanelProps<AnnoOptions>, 'options'> {
  7. annotation: AnnotationEvent;
  8. formatDate: (date: DateTimeInput, format?: string) => string;
  9. onClick: (annotation: AnnotationEvent) => void;
  10. onAvatarClick: (annotation: AnnotationEvent) => void;
  11. onTagClick: (tag: string, remove?: boolean) => void;
  12. }
  13. export const AnnotationListItem: FC<Props> = ({
  14. options,
  15. annotation,
  16. formatDate,
  17. onClick,
  18. onAvatarClick,
  19. onTagClick,
  20. }) => {
  21. const styles = useStyles2(getStyles);
  22. const { showUser, showTags, showTime } = options;
  23. const { text, login, email, avatarUrl, tags, time, timeEnd } = annotation;
  24. const onItemClick = () => {
  25. onClick(annotation);
  26. };
  27. const onLoginClick = () => {
  28. onAvatarClick(annotation);
  29. };
  30. const showAvatar = login && showUser;
  31. const showTimeStamp = time && showTime;
  32. const showTimeStampEnd = timeEnd && timeEnd !== time && showTime;
  33. return (
  34. <Card className={styles.card} onClick={onItemClick}>
  35. <Card.Heading>
  36. <span>{text}</span>
  37. </Card.Heading>
  38. {showTimeStamp && (
  39. <Card.Description className={styles.timestamp}>
  40. <TimeStamp formatDate={formatDate} time={time!} />
  41. {showTimeStampEnd && (
  42. <>
  43. <span className={styles.time}>-</span>
  44. <TimeStamp formatDate={formatDate} time={timeEnd!} />{' '}
  45. </>
  46. )}
  47. </Card.Description>
  48. )}
  49. {showAvatar && (
  50. <Card.Meta className={styles.meta}>
  51. <Avatar email={email} login={login!} avatarUrl={avatarUrl} onClick={onLoginClick} />
  52. </Card.Meta>
  53. )}
  54. {showTags && tags && (
  55. <Card.Tags>
  56. <TagList tags={tags} onClick={(tag) => onTagClick(tag, false)} />
  57. </Card.Tags>
  58. )}
  59. </Card>
  60. );
  61. };
  62. interface AvatarProps {
  63. login: string;
  64. onClick: () => void;
  65. avatarUrl?: string;
  66. email?: string;
  67. }
  68. const Avatar: FC<AvatarProps> = ({ onClick, avatarUrl, login, email }) => {
  69. const styles = useStyles2(getStyles);
  70. const onAvatarClick = (e: MouseEvent) => {
  71. e.stopPropagation();
  72. onClick();
  73. };
  74. const tooltipContent = (
  75. <span>
  76. Created by:
  77. <br /> {email}
  78. </span>
  79. );
  80. return (
  81. <Tooltip content={tooltipContent} theme="info" placement="top">
  82. <button onClick={onAvatarClick} className={styles.avatar} aria-label={`Created by ${email}`}>
  83. <img src={avatarUrl} alt="avatar icon" />
  84. </button>
  85. </Tooltip>
  86. );
  87. };
  88. interface TimeStampProps {
  89. time: number;
  90. formatDate: (date: DateTimeInput, format?: string) => string;
  91. }
  92. const TimeStamp: FC<TimeStampProps> = ({ time, formatDate }) => {
  93. const styles = useStyles2(getStyles);
  94. return (
  95. <span className={styles.time}>
  96. <span>{formatDate(time)}</span>
  97. </span>
  98. );
  99. };
  100. function getStyles(theme: GrafanaTheme2) {
  101. return {
  102. card: css({
  103. gridTemplateAreas: `"Heading Description Meta Tags"`,
  104. gridTemplateColumns: 'auto 1fr auto auto',
  105. padding: theme.spacing(1),
  106. margin: theme.spacing(0.5),
  107. width: 'inherit',
  108. }),
  109. meta: css({
  110. margin: 0,
  111. position: 'relative',
  112. justifyContent: 'end',
  113. }),
  114. timestamp: css({
  115. margin: 0,
  116. alignSelf: 'center',
  117. }),
  118. time: css({
  119. marginLeft: theme.spacing(1),
  120. marginRight: theme.spacing(1),
  121. fontSize: theme.typography.bodySmall.fontSize,
  122. color: theme.colors.text.secondary,
  123. }),
  124. avatar: css({
  125. border: 'none',
  126. background: 'inherit',
  127. margin: 0,
  128. padding: theme.spacing(0.5),
  129. img: {
  130. borderRadius: '50%',
  131. width: theme.spacing(2),
  132. height: theme.spacing(2),
  133. },
  134. }),
  135. };
  136. }