superdesk-ui-framework 5.2.3 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -92,6 +92,10 @@
92
92
 
93
93
  .sd-avatar-content--text {
94
94
  background-color: var(--sd-colour-avatar-bg);
95
+ cursor: default;
96
+ span {
97
+ pointer-events: none;
98
+ }
95
99
  }
96
100
 
97
101
  .sd-avatar-content--dummy-img {
@@ -99,6 +103,7 @@
99
103
  background-image: url('~../../images/avatar_dummy.svg');
100
104
  background-repeat: no-repeat;
101
105
  background-size: cover;
106
+ pointer-events: none;
102
107
  }
103
108
 
104
109
  &.sd-avatar--empty-light {
@@ -121,6 +126,7 @@
121
126
  inset-inline-end: -4px;
122
127
  opacity: 1;
123
128
  color: var(--color-text-subdued);
129
+ pointer-events: none;
124
130
  [class^="icon-"], [class*=" icon-"] {
125
131
  color: inherit;
126
132
  text-shadow: -1px 1px 0 var(--sd-item__main-Bg), 1px 1px 0 var(--sd-item__main-Bg), 1px -1px 0 var(--sd-item__main-Bg), -1px -1px 0 var(--sd-item__main-Bg);
@@ -139,6 +145,7 @@
139
145
  inset-block-end: -3px;
140
146
  inset-inline-end: 3px;
141
147
  box-shadow: 0 0 0 1px var(--sd-item__main-Bg);
148
+ pointer-events: none;
142
149
  }
143
150
 
144
151
  &.sd-avatar--x-small {
@@ -329,8 +336,9 @@
329
336
  gap: 0;
330
337
  margin-inline-end: 8px;
331
338
 
332
- > .sd-avatar {
333
- margin: 0 -0.8rem 0 0;
339
+ > .sd-avatar,
340
+ > .d-contents .sd-avatar {
341
+ margin: 0 calc(-1 * var(--space--1)) 0 0;
334
342
  .sd-avatar-content {
335
343
  --avatar-shadow: var(--sd-item__main-Bg);
336
344
  box-shadow: 0 0 0 2px var(--avatar-shadow);
@@ -340,29 +348,33 @@
340
348
  }
341
349
  }
342
350
 
343
- > .sd-avatar--large {
344
- margin: 0 -1.2rem 0 0;
351
+ > .sd-avatar--large,
352
+ > .d-contents .sd-avatar--large {
353
+ margin: 0 calc(-1 * var(--space--1-5)) 0 0;
345
354
  }
346
355
 
347
356
  &.sd-avatar-group--stacked--gap-small {
348
- gap: $sd-base-increment * 0.5;
349
- > .sd-avatar {
357
+ gap: var(--gap-0-5);
358
+ > .sd-avatar,
359
+ > .d-contents .sd-avatar {
350
360
  margin: 0;
351
361
  }
352
362
  margin-inline-end: 0;
353
363
  }
354
364
 
355
365
  &.sd-avatar-group--stacked--gap-medium {
356
- gap: $sd-base-increment * 1;
357
- > .sd-avatar {
366
+ gap: var(--gap-1);
367
+ > .sd-avatar,
368
+ > .d-contents .sd-avatar {
358
369
  margin: 0;
359
370
  }
360
371
  margin-inline-end: 0;
361
372
  }
362
373
 
363
374
  &.sd-avatar-group--stacked--gap-large {
364
- gap: $sd-base-increment * 1.5;
365
- > .sd-avatar {
375
+ gap: var(--gap-1-5);
376
+ > .sd-avatar,
377
+ > .d-contents .sd-avatar {
366
378
  margin: 0;
367
379
  }
368
380
  margin-inline-end: 0;
@@ -371,14 +383,15 @@
371
383
  &.sd-avatar-group--grid {
372
384
  flex-wrap: wrap;
373
385
  justify-content: flex-start;
374
- gap: $sd-base-increment * 1.5;
386
+ gap: var(--gap-1-5);
375
387
  }
376
388
  }
377
389
 
378
390
  .avatar-popup {
379
391
  background-color: var(--color-dropdown-menu-Bg);
380
392
  border-radius: var(--b-radius--large);
381
- padding: var(--space--2);
393
+ padding-block: var(--space--2);
394
+ padding-inline-start: var(--space--1-5);
382
395
  padding-inline-end: var(--space--3);
383
396
  box-shadow: var(--sd-shadow__dropdown);
384
397
  display: flex;
@@ -386,3 +399,125 @@
386
399
  gap: var(--gap-1-5);
387
400
  overflow: auto;
388
401
  }
402
+
403
+ // Inline avatar display mode (like Material Design Chip)
404
+ .sd-avatar--inline {
405
+ display: inline-flex;
406
+ align-items: center;
407
+ gap: var(--gap-1);
408
+ vertical-align: middle;
409
+ background-color: var(--color-surface-faded);
410
+ border-radius: var(--b-radius--full);
411
+
412
+
413
+ .sd-avatar--inline__text {
414
+ color: var(--color-text);
415
+ font-size: var(--name-font-size);
416
+ line-height: 1;
417
+ white-space: nowrap;
418
+ overflow: hidden;
419
+ text-overflow: ellipsis;
420
+ // max-width: 200px;
421
+
422
+ }
423
+ &:has(.sd-avatar--x-small) {
424
+ gap: var(--gap-0-5);
425
+ .sd-avatar--inline__text {
426
+ --name-font-size: var(--text-size-xx-small);
427
+ padding-inline-end: 1em;
428
+ }
429
+ }
430
+ &:has(.sd-avatar--small) {
431
+ gap: var(--gap-0-5);
432
+ .sd-avatar--inline__text {
433
+ --name-font-size: var(--text-size-x-small);
434
+ padding-inline-end: 1em;
435
+
436
+ }
437
+ }
438
+ &:has(.sd-avatar--medium) {
439
+ gap: var(--gap-1);
440
+ .sd-avatar--inline__text {
441
+ --name-font-size: var(--text-size-small);
442
+ padding-inline-end: 1em;
443
+ }
444
+ }
445
+ &:has(.sd-avatar--large) {
446
+ gap: var(--gap-1);
447
+ .sd-avatar--inline__text {
448
+ --name-font-size: var(--text-size-small);
449
+ padding-inline-end: 1.5em;
450
+ }
451
+ }
452
+ &:has(.sd-avatar--x-large) {
453
+ gap: var(--gap-2);
454
+ .sd-avatar--inline__text {
455
+ --name-font-size: var(--text-size-medium);
456
+ padding-inline-end: 2.25em;
457
+ }
458
+ }
459
+ &:has(.sd-avatar--xx-large) {
460
+ gap: var(--gap-3);
461
+ .sd-avatar--inline__text {
462
+ --name-font-size: var(--text-size-large);
463
+ padding-inline-end: 2.25em;
464
+ }
465
+ }
466
+
467
+ // Modifier for text-start positioning (text before avatar)
468
+ &.sd-avatar--inline--text-start {
469
+ flex-direction: row-reverse;
470
+
471
+ .sd-avatar--inline__text {
472
+ padding-inline-end: 0;
473
+ padding-inline-start: 1em;
474
+ }
475
+
476
+ &:has(.sd-avatar--x-small) {
477
+ .sd-avatar--inline__text {
478
+ padding-inline-start: 1em;
479
+ }
480
+ }
481
+ &:has(.sd-avatar--small) {
482
+ .sd-avatar--inline__text {
483
+ padding-inline-start: 1em;
484
+ }
485
+ }
486
+ &:has(.sd-avatar--medium) {
487
+ .sd-avatar--inline__text {
488
+ padding-inline-start: 1em;
489
+ }
490
+ }
491
+ &:has(.sd-avatar--large) {
492
+ .sd-avatar--inline__text {
493
+ padding-inline-start: 1.5em;
494
+ }
495
+ }
496
+ &:has(.sd-avatar--x-large) {
497
+ .sd-avatar--inline__text {
498
+ padding-inline-start: 2.25em;
499
+ }
500
+ }
501
+ &:has(.sd-avatar--xx-large) {
502
+ .sd-avatar--inline__text {
503
+ padding-inline-start: 2.25em;
504
+ }
505
+ }
506
+ }
507
+ &:not(.sd-avatar--inline--text-start) {
508
+ &:has(.sd-avatar--x-small) {
509
+ &:has(.sd-avatar__icon) {
510
+ .sd-avatar--inline__text {
511
+ padding-inline-start: 0.5em;
512
+ }
513
+ }
514
+ }
515
+ &:has(.sd-avatar--small) {
516
+ &:has(.sd-avatar__icon) {
517
+ .sd-avatar--inline__text {
518
+ padding-inline-start: 0.25em;
519
+ }
520
+ }
521
+ }
522
+ }
523
+ }
@@ -422,4 +422,10 @@
422
422
  border-right-color: var(--sd-colour-background__tooltip);
423
423
  }
424
424
  }
425
- }
425
+ }
426
+
427
+ .sd-popup-positioner {
428
+ &:has(.tooltip) {
429
+ pointer-events: none;
430
+ }
431
+ }
@@ -5,15 +5,18 @@ import {InputWrapper} from './Form';
5
5
  import nextId from 'react-id-generator';
6
6
  import {IInputCommon} from './Form/InputWrapper';
7
7
  import {toasted} from './Toast';
8
+ import {Position} from './ToastMessage';
8
9
 
9
10
  interface ICopyableTextBoxProps extends IInputCommon {
10
11
  value: string;
11
12
 
12
13
  /**
13
- * Defaults to medium
14
+ * Defaults to `medium`
14
15
  */
15
16
  size?: 'medium' | 'large';
16
17
 
18
+ disableCopyButton?: boolean;
19
+ toastMessagePosition?: Position;
17
20
  'data-test-id'?: string;
18
21
  }
19
22
 
@@ -37,6 +40,7 @@ export const CopyableTextBox: React.FC<ICopyableTextBoxProps> = (props) => {
37
40
  toasted.notify(gettext('Copied to clipboard'), {
38
41
  type: 'success',
39
42
  duration: 500,
43
+ position: props.toastMessagePosition,
40
44
  });
41
45
 
42
46
  setCopied(true);
@@ -52,6 +56,8 @@ export const CopyableTextBox: React.FC<ICopyableTextBoxProps> = (props) => {
52
56
  .catch(() => {
53
57
  toasted.notify(gettext("Couldn't copy"), {
54
58
  type: 'warning',
59
+ duration: 500,
60
+ position: props.toastMessagePosition,
55
61
  });
56
62
  });
57
63
  };
@@ -88,6 +94,7 @@ export const CopyableTextBox: React.FC<ICopyableTextBoxProps> = (props) => {
88
94
  icon={copied ? 'ok' : 'copy'}
89
95
  iconOnly={true}
90
96
  onClick={handleCopy}
97
+ disabled={props.disableCopyButton}
91
98
  type="default"
92
99
  style="hollow"
93
100
  size={props.size === 'medium' ? 'normal' : (props.size ?? 'normal')}
@@ -337,6 +337,13 @@ export class DatePickerISO extends React.PureComponent<IDatePickerISO> {
337
337
  }
338
338
  }
339
339
 
340
+ function getOptionLabel(options: Array<{id: string; label?: string}>, id: string | null | undefined): string {
341
+ if (id == null) {
342
+ return '';
343
+ }
344
+ return options.find((option) => option.id === id)?.label ?? id;
345
+ }
346
+
340
347
  const MonthNavigator = ({
341
348
  value,
342
349
  options,
@@ -346,7 +353,7 @@ const MonthNavigator = ({
346
353
  kind="synchronous"
347
354
  value={[value]}
348
355
  getOptions={() => options.map((option) => ({value: option.id}))}
349
- getLabel={(option) => options[parseInt(option, 10)].label}
356
+ getLabel={(optionId) => getOptionLabel(options, optionId)}
350
357
  getId={(option) => option}
351
358
  onChange={(selected) => {
352
359
  onChange(selected[0]);
@@ -355,7 +362,7 @@ const MonthNavigator = ({
355
362
  labelHidden
356
363
  valueTemplate={(item) => (
357
364
  <div className="sd-datepicker__navigator-value sd-datepicker__navigator-month">
358
- <div>{options[parseInt(item, 10)].label}</div>
365
+ <div>{getOptionLabel(options, item)}</div>
359
366
  <Icon name="chevron-down-thin" />
360
367
  </div>
361
368
  )}
@@ -186,6 +186,7 @@ export class PopupPositioner extends React.PureComponent<IPropsPopupPositioner>
186
186
  <>
187
187
  {ReactDOM.createPortal(
188
188
  <div
189
+ className="sd-popup-positioner"
189
190
  ref={(el) => {
190
191
  this.wrapperEl = el;
191
192
  }}
@@ -1,13 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import classNames from 'classnames';
3
- import {Avatar, IPropsAvatar} from './avatar';
4
- import {AvatarWrapper} from './avatar-wrapper';
3
+ import {Avatar, IAvatarWithTooltip, IPropsAvatar} from './avatar';
5
4
  import {AvatarContentNumber} from './avatar-number';
6
5
  import {AvatarPlaceholder, IPropsAvatarPlaceholder} from './avatar-placeholder';
7
- import {Spacer} from '@sourcefabric/common';
8
6
  import {WithPopover} from '../WithPopover';
9
7
 
10
- export type IAvatarInGroup = Omit<IPropsAvatar, 'size'>;
8
+ export type IAvatarInGroup = {nameDisplay?: IAvatarWithTooltip} & Omit<IPropsAvatar, 'size' | 'nameDisplay'>;
11
9
  export type IAvatarPlaceholderInGroup = Omit<IPropsAvatarPlaceholder, 'size'>;
12
10
 
13
11
  export type IAvatarGroupItem = IAvatarInGroup | IAvatarPlaceholderInGroup;
@@ -81,32 +79,32 @@ export class AvatarGroup extends React.PureComponent<IPropsAvatarGroup> {
81
79
  <div className="avatar-popup">
82
80
  {this.props.items.map((item, index) => {
83
81
  return someHaveDisplayName ? (
84
- <Spacer h alignItems="center" gap="16" noGrow key={index}>
85
- {isAvatar(item) && item.displayName}
86
-
82
+ <div className="d-flex items-center gap-1" key={index}>
87
83
  {isAvatar(item) ? (
88
84
  <Avatar
89
- size="small"
85
+ size="medium"
90
86
  imageUrl={item.imageUrl}
91
87
  initials={item.initials}
92
88
  displayName={item.displayName}
93
89
  icon={item.icon}
94
90
  statusDot={item.statusDot}
91
+ nameDisplay={{kind: 'none'}}
95
92
  />
96
93
  ) : (
97
94
  <AvatarPlaceholder
98
95
  kind="plus-button"
99
- size="small"
96
+ size="medium"
100
97
  icon={item.icon}
101
98
  onClick={item.onClick}
102
99
  />
103
100
  )}
104
- </Spacer>
101
+ {isAvatar(item) && item.displayName}
102
+ </div>
105
103
  ) : (
106
104
  <div>
107
105
  <AvatarPlaceholder
108
106
  kind="plus-button"
109
- size="small"
107
+ size="medium"
110
108
  icon={item.icon}
111
109
  onClick={isAvatar(item) ? undefined : item.onClick}
112
110
  key={index}
@@ -129,7 +127,14 @@ export class AvatarGroup extends React.PureComponent<IPropsAvatarGroup> {
129
127
  >
130
128
  {items.slice(0, max).map((item, index) => {
131
129
  if (isAvatar(item)) {
132
- return <Avatar {...item} key={index} size={size} />;
130
+ return (
131
+ <Avatar
132
+ {...item}
133
+ key={index}
134
+ size={size}
135
+ nameDisplay={item.nameDisplay ?? {kind: 'none'}}
136
+ />
137
+ );
133
138
  } else {
134
139
  return <AvatarPlaceholder {...item} key={index} size={this.props.size} />;
135
140
  }
@@ -137,9 +142,14 @@ export class AvatarGroup extends React.PureComponent<IPropsAvatarGroup> {
137
142
 
138
143
  {itemsOverLimit > 0 && (
139
144
  <PlusButtonWrapper onToggle={onToggle}>
140
- <AvatarWrapper size={size}>
141
- <AvatarContentNumber number={`${itemsOverLimit}`} />
142
- </AvatarWrapper>
145
+ <Avatar
146
+ size={size}
147
+ imageUrl={null}
148
+ displayName=""
149
+ initials={null}
150
+ customContent={<AvatarContentNumber number={`${itemsOverLimit}`} />}
151
+ nameDisplay={{kind: 'none'}}
152
+ />
143
153
  </PlusButtonWrapper>
144
154
  )}
145
155
  </div>
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import type {IAvatarWithInlineText, IPropsAvatar} from './avatar';
3
+ import classNames from 'classnames';
4
+
5
+ interface IProps {
6
+ placement: IAvatarWithInlineText['placement'];
7
+ displayName: IPropsAvatar['displayName'];
8
+ avatarElement: JSX.Element;
9
+ }
10
+
11
+ export const AvatarWithInlineTextWrapper = ({placement, avatarElement, displayName}: IProps) => {
12
+ return (
13
+ <span
14
+ className={classNames('sd-avatar--inline', {
15
+ 'sd-avatar--inline--text-start': placement === 'start',
16
+ })}
17
+ >
18
+ {avatarElement}
19
+ <span className="sd-avatar--inline__text">{displayName}</span>
20
+ </span>
21
+ );
22
+ };
@@ -1,17 +1,37 @@
1
1
  import * as React from 'react';
2
+ import classNames from 'classnames';
2
3
  import {AvatarContentImage} from './avatar-image';
3
- import {AvatarWrapper} from './avatar-wrapper';
4
4
  import {AvatarContentText} from './avatar-text';
5
+ import {Icon} from '../Icon';
6
+ import {TooltipV2} from '../TooltipV2';
7
+ import {AvatarWithInlineTextWrapper} from './avatar-with-inline-text-wrapper';
8
+ import {assertNever} from '../../helpers';
9
+
10
+ export type IAvatarWithTooltip = {
11
+ kind: 'tooltip';
12
+
13
+ /** Defaults to `top` */
14
+ placement?: 'top' | 'left' | 'right' | 'bottom';
15
+
16
+ /** `displayName` used if not provided */
17
+ content?: string;
18
+ };
19
+
20
+ export type IAvatarWithInlineText = {
21
+ kind: 'inline';
22
+
23
+ /** Defaults to `end` */
24
+ placement?: 'start' | 'end';
25
+ };
5
26
 
6
27
  export interface IPropsAvatar {
7
- imageUrl: string | null; // nullable, but mandatory to communicate importance
8
- displayName: string;
28
+ imageUrl: string | null;
9
29
 
10
30
  /** 3 letters max */
11
- initials: string | null; // nullable, but mandatory to communicate importance
31
+ initials: string | null;
12
32
 
33
+ displayName: string;
13
34
  size: 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large';
14
-
15
35
  statusIndicator?: 'online' | 'offline';
16
36
  administratorIndicator?: boolean;
17
37
  icon?: {
@@ -21,13 +41,12 @@ export interface IPropsAvatar {
21
41
  statusDot?: {
22
42
  color?: string;
23
43
  };
24
- noAvatarPlaceholderColor?: 'subtle' | 'strong'; // defaults to strong; only applies to placeholder image
25
44
 
26
45
  /**
27
- * displayName is shown as tooltip by default
28
- * use this if you need to add additional information (it will be added on a new line)
46
+ * Defaults to `strong`.
47
+ * Color scheme for placeholder when no image is available.
29
48
  */
30
- tooltip?: string;
49
+ noAvatarPlaceholderColor?: 'subtle' | 'strong';
31
50
 
32
51
  /**
33
52
  * JSX resulting from rendering of one of the following components:
@@ -35,46 +54,100 @@ export interface IPropsAvatar {
35
54
  * AvatarContentImage
36
55
  */
37
56
  customContent?: JSX.Element;
57
+
58
+ /**
59
+ * Defaults to `tooltip`.
60
+ *
61
+ * Controls how `tooltip` is displayed:
62
+ * - `tooltip`: Shows name in a tooltip
63
+ * - `none`: No name is displayed at all
64
+ * - `inline`: Shows name inline next to avatar (like Material Design Chip)
65
+ */
66
+ nameDisplay?: IAvatarWithInlineText | IAvatarWithTooltip | {kind: 'none'};
38
67
  }
39
68
 
40
- export class Avatar extends React.PureComponent<IPropsAvatar> {
41
- render() {
42
- const {
43
- imageUrl,
44
- initials,
45
- size,
46
- statusIndicator,
47
- administratorIndicator,
48
- icon,
49
- noAvatarPlaceholderColor,
50
- displayName,
51
- customContent,
52
- statusDot,
53
- } = this.props;
54
-
55
- const tooltipCombined = [displayName, this.props.tooltip]
56
- .filter((str) => (str ?? '').trim().length > 0)
57
- .join('\n');
69
+ export const Avatar = React.memo<IPropsAvatar>((props) => {
70
+ const {
71
+ imageUrl,
72
+ initials,
73
+ size,
74
+ statusIndicator,
75
+ administratorIndicator,
76
+ icon,
77
+ noAvatarPlaceholderColor,
78
+ displayName,
79
+ customContent,
80
+ statusDot,
81
+ nameDisplay = {kind: 'tooltip'},
82
+ } = props;
58
83
 
84
+ const avatarContent = React.useMemo(() => {
85
+ if (customContent != null) {
86
+ return customContent;
87
+ } else if (imageUrl != null || initials == null) {
88
+ return <AvatarContentImage imageUrl={imageUrl} tooltipText={''} />;
89
+ } else {
90
+ return <AvatarContentText text={initials} tooltipText={''} />;
91
+ }
92
+ }, [customContent, imageUrl, initials]);
93
+
94
+ const avatarClassName = React.useMemo(
95
+ () =>
96
+ classNames('sd-avatar', {
97
+ 'sd-avatar--x-small': size === 'x-small',
98
+ 'sd-avatar--small': size === 'small',
99
+ 'sd-avatar--medium': size === 'medium',
100
+ 'sd-avatar--large': size === 'large',
101
+ 'sd-avatar--x-large': size === 'x-large',
102
+ 'sd-avatar--xx-large': size === 'xx-large',
103
+ 'sd-avatar--indicator-status--online': statusIndicator === 'online',
104
+ 'sd-avatar--indicator-status--offline': statusIndicator === 'offline',
105
+ 'sd-avatar--empty-light': noAvatarPlaceholderColor === 'subtle',
106
+ }),
107
+ [size, statusIndicator, noAvatarPlaceholderColor],
108
+ );
109
+
110
+ const avatarElement = (
111
+ <span className={avatarClassName}>
112
+ {avatarContent}
113
+
114
+ {administratorIndicator === true && <i className="icon-settings sd-avatar--indicator-admin" />}
115
+
116
+ {icon != null && (
117
+ <span className="sd-avatar__icon">
118
+ <Icon name={icon.name} color={icon.color} />
119
+ </span>
120
+ )}
121
+
122
+ {statusDot != null && (
123
+ <span style={{backgroundColor: statusDot.color}} className="sd-avatar__coverage-state" />
124
+ )}
125
+ </span>
126
+ );
127
+
128
+ const kind = nameDisplay.kind;
129
+
130
+ if (kind === 'inline') {
59
131
  return (
60
- <AvatarWrapper
61
- size={size}
62
- statusIndicator={statusIndicator ? {status: statusIndicator, tooltipText: ''} : undefined}
63
- administratorIndicator={administratorIndicator ? {enabled: true, tooltipText: ''} : undefined}
64
- icon={icon}
65
- statusDot={statusDot}
66
- noAvatarPlaceholderColor={noAvatarPlaceholderColor}
67
- >
68
- {(() => {
69
- if (customContent != null) {
70
- return customContent;
71
- } else if (imageUrl != null || initials == null) {
72
- return <AvatarContentImage imageUrl={imageUrl} tooltipText={tooltipCombined} />;
73
- } else {
74
- return <AvatarContentText text={initials} tooltipText={tooltipCombined} />;
75
- }
76
- })()}
77
- </AvatarWrapper>
132
+ <AvatarWithInlineTextWrapper
133
+ avatarElement={avatarElement}
134
+ displayName={displayName}
135
+ placement={nameDisplay.placement}
136
+ />
78
137
  );
138
+ } else if (kind === 'tooltip') {
139
+ return (
140
+ <TooltipV2 content={nameDisplay.content ?? displayName} placement={nameDisplay.placement}>
141
+ {({attributes}) => (
142
+ <span className="d-contents" {...attributes}>
143
+ {avatarElement}
144
+ </span>
145
+ )}
146
+ </TooltipV2>
147
+ );
148
+ } else if (kind === 'none') {
149
+ return avatarElement;
150
+ } else {
151
+ assertNever(kind);
79
152
  }
80
- }
153
+ });