superdesk-ui-framework 3.0.21 → 3.0.24

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.
@@ -1,5 +1,5 @@
1
1
  .sd-avatar {
2
- display: inline-block;
2
+ display: block;
3
3
  position: relative; // required for absolutely positioned indicators
4
4
 
5
5
  &.sd-avatar--indicator-status--offline {
@@ -339,3 +339,14 @@
339
339
  gap: $sd-base-increment * 1.5;
340
340
  }
341
341
  }
342
+
343
+ .avatar-popup {
344
+ background-color: var(--color-dropdown-menu-Bg);
345
+ border-radius: 5px;
346
+ padding: 1.5rem;
347
+ box-shadow: var(--sd-shadow__dropdown);
348
+ display: flex;
349
+ flex-direction: column;
350
+ gap: 4px;
351
+ overflow: auto;
352
+ }
@@ -324,7 +324,10 @@ $sd-icon-font: (
324
324
  &.color--white {
325
325
  color: hsla(214, 13%, 90, 1) !important;
326
326
  }
327
-
327
+
328
+ &.scale--1\.5x {
329
+ --icon-base-size: 24px
330
+ }
328
331
  &.scale--2x {
329
332
  --icon-base-size: 32px
330
333
  }
@@ -5,7 +5,7 @@ interface IProps {
5
5
  size?: 'small' | 'big'; // defaults to 'small'
6
6
  type?: 'default' | 'primary' | 'success' | 'warning' | 'alert' | 'highlight' | 'light' | 'white';
7
7
  className?: string;
8
- scale?: '2x' | '3x' | '4x';
8
+ scale?: '1.5x' | '2x' | '3x' | '4x';
9
9
  ariaHidden?: boolean;
10
10
  color?: string;
11
11
  }
@@ -123,7 +123,7 @@ class PopupPositioner extends React.PureComponent<IPropsPopupPositioner> {
123
123
  ref={(el) => {
124
124
  this.wrapperEl = el;
125
125
  }}
126
- style={{zIndex: this.props.zIndex ?? 1, position: 'absolute', left: '-100vw'}}
126
+ style={{zIndex: this.props.zIndex ?? 1, position: 'absolute', left: '-100vw', display: 'flex'}}
127
127
  >
128
128
  {this.props.children}
129
129
  </div>
@@ -4,6 +4,8 @@ import {Avatar, IPropsAvatar} from './avatar';
4
4
  import {AvatarWrapper} from './avatar-wrapper';
5
5
  import {AvatarContentNumber} from './avatar-number';
6
6
  import {AvatarPlaceholder, IPropsAvatarPlaceholder} from './avatar-placeholder';
7
+ import { Spacer } from '../Spacer';
8
+ import {WithPopover} from '../WithPopover';
7
9
 
8
10
  export type IAvatarInGroup = Omit<IPropsAvatar, 'size'>;
9
11
  export type IAvatarPlaceholderInGroup = Omit<IPropsAvatarPlaceholder, 'size'>;
@@ -21,6 +23,13 @@ export interface IPropsAvatarGroup {
21
23
  * if exceeded, "+1"/"+2"/"+n" button will be shown
22
24
  */
23
25
  max?: number | 'show-all';
26
+
27
+ zIndex?: number;
28
+
29
+ // unless a custom onClick handler is passed
30
+ // a popover would get shown when maximum number
31
+ // of avatars is exceeded.
32
+ onClick?(): void;
24
33
  }
25
34
 
26
35
  function isAvatar(item: IAvatarInGroup | IAvatarPlaceholderInGroup): item is IAvatarInGroup {
@@ -46,41 +55,117 @@ export class AvatarGroup extends React.PureComponent<IPropsAvatarGroup> {
46
55
  })();
47
56
  const itemsOverLimit = items.length - max;
48
57
 
58
+ const PlusButtonWrapper: React.ComponentType<{onToggle(event: HTMLElement): void}> = ({children, onToggle}) => {
59
+ if (this.props.onClick == null) {
60
+ return (
61
+ <button
62
+ style={{padding: 0}}
63
+ onClick={(event) => {
64
+ if (this.props.onClick == null) {
65
+ onToggle(event.target as HTMLElement);
66
+ }
67
+ }}
68
+ >
69
+ {children}
70
+ </button>
71
+ );
72
+ } else {
73
+ return <>{children}</>;
74
+ }
75
+ };
76
+
77
+ const someHaveDisplayName = this.props.items.some((item) => isAvatar(item) && item.displayName.length > 0);
78
+
49
79
  return (
50
- <div
51
- className={classNames(
52
- 'sd-avatar-group',
53
- 'sd-avatar-group--stacked',
54
- `sd-avatar-group--stacked--gap-${gap}`,
55
- )}
56
- role='group'
57
- >
58
- {
59
- items.slice(0, max).map((item, index) => {
60
- if (isAvatar(item)) {
80
+ <WithPopover
81
+ zIndex={this.props.zIndex ?? 101}
82
+ placement='bottom-end'
83
+ component={() => (
84
+ <div className="avatar-popup">
85
+ {this.props.items.map((item, index) => {
61
86
  return (
62
- <Avatar {...item} key={index} size={size} />
63
- );
64
- } else {
65
- return (
66
- <AvatarPlaceholder
67
- {...item}
68
- key={index}
69
- size={this.props.size}
70
- />
87
+ someHaveDisplayName
88
+ ? <Spacer h alignItems='center' gap='16' noGrow key={index}>
89
+ {
90
+ isAvatar(item)
91
+ && item.displayName
92
+ }
93
+
94
+ {
95
+ isAvatar(item)
96
+ ? (
97
+ <Avatar
98
+ size='small'
99
+ imageUrl={item.imageUrl}
100
+ initials={item.initials}
101
+ displayName={item.displayName}
102
+ icon={item.icon}
103
+ />
104
+ )
105
+ : (
106
+ <AvatarPlaceholder
107
+ kind='plus-button'
108
+ size='small'
109
+ icon={item.icon}
110
+ onClick={item.onClick}
111
+ />
112
+ )
113
+ }
114
+ </Spacer>
115
+ : <div>
116
+ <AvatarPlaceholder
117
+ kind='plus-button'
118
+ size='small'
119
+ icon={item.icon}
120
+ onClick={isAvatar(item) ? undefined : item.onClick}
121
+ key={index}
122
+ />
123
+ </div>
71
124
  );
125
+ })}
126
+ </div>
127
+ )}
128
+ >
129
+ {(onToggle) => (
130
+ <div
131
+ className={classNames(
132
+ 'sd-avatar-group',
133
+ 'sd-avatar-group--stacked',
134
+ `sd-avatar-group--stacked--gap-${gap}`,
135
+ )}
136
+ role='group'
137
+ onClick={this.props.onClick}
138
+ >
139
+ {
140
+ items.slice(0, max).map((item, index) => {
141
+ if (isAvatar(item)) {
142
+ return (
143
+ <Avatar {...item} key={index} size={size} />
144
+ );
145
+ } else {
146
+ return (
147
+ <AvatarPlaceholder
148
+ {...item}
149
+ key={index}
150
+ size={this.props.size}
151
+ />
152
+ );
153
+ }
154
+ })
72
155
  }
73
- })
74
- }
75
156
 
76
- {
77
- itemsOverLimit > 0 && (
78
- <AvatarWrapper size={size} isEmpty={false}>
79
- <AvatarContentNumber number={`${itemsOverLimit}`} />
80
- </AvatarWrapper>
81
- )
82
- }
83
- </div>
157
+ {
158
+ itemsOverLimit > 0 && (
159
+ <PlusButtonWrapper onToggle={onToggle}>
160
+ <AvatarWrapper size={size} isEmpty={false}>
161
+ <AvatarContentNumber number={`${itemsOverLimit}`} />
162
+ </AvatarWrapper>
163
+ </PlusButtonWrapper>
164
+ )
165
+ }
166
+ </div>
167
+ )}
168
+ </WithPopover>
84
169
  );
85
170
  }
86
171
  }
@@ -5,7 +5,7 @@ import {AvatarContentText} from './avatar-text';
5
5
 
6
6
  export interface IPropsAvatar {
7
7
  imageUrl: string | null; // nullable, but mandatory to communicate importance
8
- tooltip: string | null; // nullable, but mandatory to communicate importance
8
+ displayName: string;
9
9
 
10
10
  /** 3 letters max */
11
11
  initials: string | null; // nullable, but mandatory to communicate importance
@@ -22,7 +22,7 @@ export interface IPropsAvatar {
22
22
 
23
23
  export class Avatar extends React.PureComponent<IPropsAvatar> {
24
24
  render() {
25
- const {imageUrl, initials, size, statusIndicator, administratorIndicator, icon, tooltip} = this.props;
25
+ const {imageUrl, initials, size, statusIndicator, administratorIndicator, icon, displayName} = this.props;
26
26
 
27
27
  return (
28
28
  <AvatarWrapper
@@ -35,10 +35,10 @@ export class Avatar extends React.PureComponent<IPropsAvatar> {
35
35
  {
36
36
  imageUrl != null || initials == null
37
37
  ? (
38
- <AvatarContentImage imageUrl={imageUrl} tooltipText={tooltip ?? undefined} />
38
+ <AvatarContentImage imageUrl={imageUrl} tooltipText={displayName} />
39
39
  )
40
40
  : (
41
- <AvatarContentText text={initials} tooltipText={tooltip ?? undefined} />
41
+ <AvatarContentText text={initials} tooltipText={displayName} />
42
42
  )
43
43
  }
44
44