@transferwise/components 0.0.0-experimental-f7495f3 → 0.0.0-experimental-37d8e2c

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.
Files changed (122) hide show
  1. package/build/avatar/Avatar.js +4 -1
  2. package/build/avatar/Avatar.js.map +1 -1
  3. package/build/avatar/Avatar.mjs +4 -1
  4. package/build/avatar/Avatar.mjs.map +1 -1
  5. package/build/avatar/colors/colors.js +1 -1
  6. package/build/avatar/colors/colors.js.map +1 -1
  7. package/build/avatar/colors/colors.mjs +1 -1
  8. package/build/avatar/colors/colors.mjs.map +1 -1
  9. package/build/avatarView/AvatarView.js +207 -0
  10. package/build/avatarView/AvatarView.js.map +1 -0
  11. package/build/avatarView/AvatarView.mjs +205 -0
  12. package/build/avatarView/AvatarView.mjs.map +1 -0
  13. package/build/avatarWrapper/AvatarWrapper.js +6 -9
  14. package/build/avatarWrapper/AvatarWrapper.js.map +1 -1
  15. package/build/avatarWrapper/AvatarWrapper.mjs +5 -8
  16. package/build/avatarWrapper/AvatarWrapper.mjs.map +1 -1
  17. package/build/badge/Badge.js +6 -1
  18. package/build/badge/Badge.js.map +1 -1
  19. package/build/badge/Badge.mjs +6 -1
  20. package/build/badge/Badge.mjs.map +1 -1
  21. package/build/circularButton/CircularButton.js +4 -7
  22. package/build/circularButton/CircularButton.js.map +1 -1
  23. package/build/circularButton/CircularButton.mjs +4 -7
  24. package/build/circularButton/CircularButton.mjs.map +1 -1
  25. package/build/common/circle/Circle.js +24 -1
  26. package/build/common/circle/Circle.js.map +1 -1
  27. package/build/common/circle/Circle.mjs +24 -1
  28. package/build/common/circle/Circle.mjs.map +1 -1
  29. package/build/common/initials.js +12 -0
  30. package/build/common/initials.js.map +1 -0
  31. package/build/common/initials.mjs +10 -0
  32. package/build/common/initials.mjs.map +1 -0
  33. package/build/common/notificationDot/NotificationDot.js +16 -0
  34. package/build/common/notificationDot/NotificationDot.js.map +1 -0
  35. package/build/common/notificationDot/NotificationDot.mjs +14 -0
  36. package/build/common/notificationDot/NotificationDot.mjs.map +1 -0
  37. package/build/display/Display.js +1 -1
  38. package/build/display/Display.js.map +1 -1
  39. package/build/display/Display.mjs +1 -1
  40. package/build/display/Display.mjs.map +1 -1
  41. package/build/index.js +6 -0
  42. package/build/index.js.map +1 -1
  43. package/build/index.mjs +3 -0
  44. package/build/index.mjs.map +1 -1
  45. package/build/main.css +82 -41
  46. package/build/statusIcon/StatusIcon.js +4 -2
  47. package/build/statusIcon/StatusIcon.js.map +1 -1
  48. package/build/statusIcon/StatusIcon.mjs +4 -2
  49. package/build/statusIcon/StatusIcon.mjs.map +1 -1
  50. package/build/styles/avatarGroup/AvatarGroup.css +28 -0
  51. package/build/styles/avatarView/AvatarView.css +22 -0
  52. package/build/styles/badge/Badge.css +5 -5
  53. package/build/styles/circularButton/CircularButton.css +0 -24
  54. package/build/styles/common/circle/Circle.css +17 -0
  55. package/build/styles/common/notificationDot/NotificationDot.css +10 -0
  56. package/build/styles/main.css +82 -41
  57. package/build/styles/statusIcon/StatusIcon.css +0 -12
  58. package/build/types/avatar/Avatar.d.ts +3 -0
  59. package/build/types/avatar/Avatar.d.ts.map +1 -1
  60. package/build/types/avatar/colors/colors.d.ts +1 -1
  61. package/build/types/avatar/colors/colors.d.ts.map +1 -1
  62. package/build/types/avatarGroup/AvatarGroup.d.ts +19 -0
  63. package/build/types/avatarGroup/AvatarGroup.d.ts.map +1 -0
  64. package/build/types/avatarGroup/index.d.ts +3 -0
  65. package/build/types/avatarGroup/index.d.ts.map +1 -0
  66. package/build/types/avatarView/AvatarView.d.ts +25 -0
  67. package/build/types/avatarView/AvatarView.d.ts.map +1 -0
  68. package/build/types/avatarView/index.d.ts +3 -0
  69. package/build/types/avatarView/index.d.ts.map +1 -0
  70. package/build/types/avatarWrapper/AvatarWrapper.d.ts +3 -0
  71. package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
  72. package/build/types/badge/Badge.d.ts +2 -1
  73. package/build/types/badge/Badge.d.ts.map +1 -1
  74. package/build/types/circularButton/CircularButton.d.ts.map +1 -1
  75. package/build/types/common/circle/Circle.d.ts +1 -1
  76. package/build/types/common/circle/Circle.d.ts.map +1 -1
  77. package/build/types/common/index.d.ts +1 -0
  78. package/build/types/common/initials.d.ts +2 -0
  79. package/build/types/common/initials.d.ts.map +1 -0
  80. package/build/types/common/notificationDot/NotificationDot.d.ts +8 -0
  81. package/build/types/common/notificationDot/NotificationDot.d.ts.map +1 -0
  82. package/build/types/index.d.ts +4 -1
  83. package/build/types/index.d.ts.map +1 -1
  84. package/build/types/statusIcon/StatusIcon.d.ts +6 -2
  85. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  86. package/package.json +3 -3
  87. package/src/avatar/Avatar.tsx +4 -1
  88. package/src/avatar/colors/colors.ts +4 -4
  89. package/src/avatarGroup/AvatarGroup.css +28 -0
  90. package/src/avatarGroup/AvatarGroup.less +32 -0
  91. package/src/avatarGroup/AvatarGroup.story.tsx +249 -0
  92. package/src/avatarGroup/AvatarGroup.tsx +78 -0
  93. package/src/avatarGroup/index.ts +2 -0
  94. package/src/avatarView/AvatarView.css +22 -0
  95. package/src/avatarView/AvatarView.less +24 -0
  96. package/src/avatarView/AvatarView.story.tsx +425 -0
  97. package/src/avatarView/AvatarView.tsx +217 -0
  98. package/src/avatarView/index.ts +2 -0
  99. package/src/avatarWrapper/AvatarWrapper.tsx +12 -16
  100. package/src/badge/Badge.css +5 -5
  101. package/src/badge/Badge.less +3 -3
  102. package/src/badge/Badge.tsx +10 -1
  103. package/src/circularButton/CircularButton.css +0 -24
  104. package/src/circularButton/CircularButton.less +15 -15
  105. package/src/circularButton/CircularButton.tsx +4 -5
  106. package/src/common/circle/Circle.css +17 -0
  107. package/src/common/circle/Circle.less +22 -0
  108. package/src/common/circle/Circle.tsx +33 -2
  109. package/src/common/index.js +1 -0
  110. package/src/common/initials.ts +13 -0
  111. package/src/common/notificationDot/NotificationDot.css +10 -0
  112. package/src/common/notificationDot/NotificationDot.less +7 -0
  113. package/src/common/notificationDot/NotificationDot.tsx +10 -0
  114. package/src/display/Display.tsx +1 -1
  115. package/src/index.ts +4 -0
  116. package/src/main.css +82 -41
  117. package/src/main.less +3 -0
  118. package/src/statusIcon/StatusIcon.css +0 -12
  119. package/src/statusIcon/StatusIcon.less +6 -6
  120. package/src/statusIcon/StatusIcon.story.tsx +32 -28
  121. package/src/statusIcon/StatusIcon.tsx +18 -4
  122. package/src/test-utils/avatar-img.webp +0 -0
@@ -0,0 +1,217 @@
1
+ import Badge from '../badge';
2
+ import { getInitials } from '../common';
3
+ import NotificationDot from '../common/notificationDot/NotificationDot';
4
+ import StatusIcon from '../statusIcon';
5
+ import Circle from '../common/circle';
6
+ import { HTMLAttributes, PropsWithChildren, useState } from 'react';
7
+ import { ActionOptions as ActionProps } from '../common/action/Action';
8
+ import { clsx } from 'clsx';
9
+ import { Briefcase, Profile } from '@transferwise/icons';
10
+
11
+ type InteractiveProps = Omit<ActionProps, 'text'> &
12
+ Pick<HTMLAttributes<HTMLDivElement>, 'className' | 'aria-labelledby'>;
13
+
14
+ const AvatarViewDefaultSize = 48;
15
+
16
+ export type Props = PropsWithChildren<{
17
+ size?: 16 | 24 | 32 | 40 | 48 | 56 | 72;
18
+ /** adds prebuilt notification dot via Badge component */
19
+ notification?: boolean;
20
+ /** adds custom badge to avatar */
21
+ badge?: React.ReactNode;
22
+ entity?: EntityProps;
23
+ /** changes background color to neutral and wraps component with button */
24
+ action?: InteractiveProps;
25
+ /** changes background color to neutral and adds predefined Badge */
26
+ selected?: boolean;
27
+ /** often use case to override `background-color` or `border` */
28
+ style?: React.CSSProperties;
29
+ }> &
30
+ Pick<HTMLAttributes<HTMLDivElement>, 'className' | 'style'>;
31
+
32
+ /** Non interactive avatar view has custom border width */
33
+ const mapNonInteractiveBorderWidth = {
34
+ 16: 0.667,
35
+ 24: 1,
36
+ 32: 1,
37
+ 40: 1.25,
38
+ 48: 1.5,
39
+ 56: 1.75,
40
+ 72: 2.25,
41
+ };
42
+
43
+ /** Selected state has custom border width */
44
+ const mapSelectedBorderWidth = {
45
+ 16: 1,
46
+ 24: 1,
47
+ 32: 1,
48
+ 40: 2,
49
+ 48: 2,
50
+ 56: 2,
51
+ 72: 2,
52
+ };
53
+
54
+ function AvatarView({
55
+ children = undefined,
56
+ size = AvatarViewDefaultSize,
57
+ selected,
58
+ notification,
59
+ badge,
60
+ entity,
61
+ action,
62
+ className,
63
+ style,
64
+ }: Props) {
65
+ return (
66
+ <Interactive
67
+ {...action}
68
+ className={clsx('np-avatar-view', { 'np-avatar-view-selected': selected }, className)}
69
+ >
70
+ <Badges asset={badge} avatarProps={{ size, notification, selected }}>
71
+ <Circle
72
+ size={size}
73
+ fixedSize
74
+ style={{
75
+ // @ts-expect-error CSS custom props allowed
76
+ '--avatar-view-selected-border-width': `${mapSelectedBorderWidth[size]}px`,
77
+ '--avatar-view-non-interactive-border-width': `${mapNonInteractiveBorderWidth[size]}px`,
78
+ ...style,
79
+ }}
80
+ >
81
+ {children === undefined ? <Entity {...entity} /> : children}
82
+ </Circle>
83
+ </Badges>
84
+ </Interactive>
85
+ );
86
+ }
87
+
88
+ function Interactive({
89
+ children,
90
+ onClick,
91
+ href,
92
+ target,
93
+ className,
94
+ 'aria-label': ariaLabel,
95
+ 'aria-labelledby': ariaLabelledby,
96
+ }: PropsWithChildren<InteractiveProps>) {
97
+ const elementName: React.ElementType | null = onClick ? 'button' : href ? 'a' : null;
98
+ if (elementName === null) {
99
+ // non interactive
100
+ return <div className={className}>{children}</div>;
101
+ }
102
+ const a11yProps = { ariaLabel, ariaLabelledby };
103
+ if (elementName === 'button') {
104
+ return (
105
+ <button
106
+ type="button"
107
+ className={clsx(className, 'np-avatar-view-interactive', 'btn-unstyled')}
108
+ onClick={onClick}
109
+ {...a11yProps}
110
+ >
111
+ {children}
112
+ </button>
113
+ );
114
+ }
115
+ if (elementName === 'a') {
116
+ return (
117
+ // eslint-disable-next-line react/jsx-no-target-blank
118
+ <a
119
+ href={href}
120
+ target={target}
121
+ rel={target === '_blank' ? 'noreferrer' : undefined}
122
+ className={clsx(className, 'np-avatar-view-interactive', 'text-no-decoration')}
123
+ {...a11yProps}
124
+ >
125
+ {children}
126
+ </a>
127
+ );
128
+ }
129
+ }
130
+
131
+ const mapBadgeSize = {
132
+ 16: 'sm',
133
+ 24: 'sm',
134
+ 32: 'sm',
135
+ 40: 'sm',
136
+ 48: 'sm',
137
+ 56: 'lg',
138
+ 72: 'lg',
139
+ };
140
+
141
+ /** AvatarView has a custom sizes for badge */
142
+ const mapNotificationSize = {
143
+ 16: 6,
144
+ 24: 8,
145
+ 32: 10,
146
+ 40: 10,
147
+ 48: 14,
148
+ 56: 16,
149
+ 72: 20,
150
+ };
151
+
152
+ type BadgesProps = PropsWithChildren<{
153
+ asset: React.ReactNode;
154
+ avatarProps: Pick<Props, 'selected' | 'size' | 'notification'>;
155
+ }>;
156
+
157
+ function Badges({ children, asset, avatarProps }: BadgesProps) {
158
+ const { size = AvatarViewDefaultSize, selected, notification } = avatarProps;
159
+ if (asset || selected || notification) {
160
+ if (asset && size > 16) {
161
+ return (
162
+ <Badge badge={asset} size={mapBadgeSize[size]}>
163
+ {children}
164
+ </Badge>
165
+ );
166
+ }
167
+ if (selected && size > 16) {
168
+ return (
169
+ <Badge
170
+ badge={<StatusIcon sentiment="positive" size={mapBadgeSize[size]} />}
171
+ size={mapBadgeSize[size]}
172
+ >
173
+ {children}
174
+ </Badge>
175
+ );
176
+ }
177
+ if (notification) {
178
+ return (
179
+ <Badge
180
+ badge={
181
+ <NotificationDot
182
+ // @ts-expect-error CSS custom props allowed
183
+ style={{ '--badge-notification-dot-size': `${mapNotificationSize[size]}px` }}
184
+ />
185
+ }
186
+ position="top-right"
187
+ size={mapBadgeSize[size]}
188
+ >
189
+ {children}
190
+ </Badge>
191
+ );
192
+ }
193
+ }
194
+ return children;
195
+ }
196
+
197
+ export type EntityProps = {
198
+ imageSrc?: string | null;
199
+ name?: string | null;
200
+ profileType?: 'personal' | 'business' | null;
201
+ };
202
+
203
+ function Entity({ imageSrc = null, name = null, profileType = null }: EntityProps) {
204
+ const [tryLoadImage, setTryLoadImage] = useState(true);
205
+ if (imageSrc && tryLoadImage) {
206
+ return <img src={imageSrc} alt="" onError={() => setTryLoadImage(false)} />;
207
+ }
208
+ if (profileType) {
209
+ return profileType === 'business' ? <Briefcase /> : <Profile />;
210
+ }
211
+ if (name) {
212
+ return <>{getInitials(name)}</>;
213
+ }
214
+ return <Profile />;
215
+ }
216
+
217
+ export default AvatarView;
@@ -0,0 +1,2 @@
1
+ export type { Props as AvatarViewProps } from './AvatarView';
2
+ export { default } from './AvatarView';
@@ -3,7 +3,14 @@ import { useState, useEffect } from 'react';
3
3
 
4
4
  import Avatar, { AvatarProps, AvatarType } from '../avatar';
5
5
  import Badge, { BadgeProps } from '../badge';
6
- import { ProfileType, ProfileTypePersonal, ProfileTypeBusiness, Size, Sentiment } from '../common';
6
+ import {
7
+ ProfileType,
8
+ ProfileTypePersonal,
9
+ ProfileTypeBusiness,
10
+ Size,
11
+ Sentiment,
12
+ getInitials,
13
+ } from '../common';
7
14
  import StatusIcon from '../statusIcon/StatusIcon';
8
15
 
9
16
  interface OptionalBadgeProps extends Omit<BadgeProps, 'badge'> {
@@ -68,6 +75,9 @@ export type AvatarWrapperProps = {
68
75
  }
69
76
  );
70
77
 
78
+ /**
79
+ * @deprecated Use `AvatarView` component instead
80
+ */
71
81
  const AvatarWrapper = ({
72
82
  url,
73
83
  'aria-label': ariaLabel,
@@ -109,7 +119,7 @@ const AvatarWrapper = ({
109
119
  return {
110
120
  type: AvatarType.INITIALS,
111
121
  children: <>{getInitials(name)}</>,
112
- backgroundColorSeed: profileId?.toString(),
122
+ backgroundColorSeed: name?.toString(),
113
123
  ...updatedAvatarProps,
114
124
  };
115
125
  }
@@ -133,18 +143,4 @@ const AvatarWrapper = ({
133
143
  );
134
144
  };
135
145
 
136
- function getInitials(name: string) {
137
- const allInitials = name
138
- .split(' ')
139
- .map((part) => part[0])
140
- .join('')
141
- .toUpperCase();
142
-
143
- if (allInitials.length === 1) {
144
- return allInitials[0];
145
- }
146
-
147
- return allInitials[0] + allInitials.slice(-1);
148
- }
149
-
150
146
  export default AvatarWrapper;
@@ -14,16 +14,16 @@
14
14
  --badge-border-color: rgba(0, 0, 0, 0.08);
15
15
  }
16
16
  .tw-badge > .tw-badge__children {
17
- -webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
18
- mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
17
+ -webkit-mask-image: radial-gradient(circle at var(--badge-mask-position) calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
18
+ mask-image: radial-gradient(circle at var(--badge-mask-position) calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
19
19
  }
20
20
  [dir="rtl"] .tw-badge > .tw-badge__children {
21
- -webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
22
- mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
21
+ -webkit-mask-image: radial-gradient(circle at var(--badge-mask-position) calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
22
+ mask-image: radial-gradient(circle at var(--badge-mask-position) calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
23
23
  }
24
24
  .tw-badge > .tw-badge__content {
25
25
  position: absolute;
26
- bottom: 0;
26
+ bottom: var(--badge-bottom-position);
27
27
  right: 0;
28
28
  box-sizing: border-box;
29
29
  text-align: center;
@@ -33,7 +33,7 @@
33
33
 
34
34
  mask-image:
35
35
  radial-gradient(
36
- circle at top calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)),
36
+ circle at var(--badge-mask-position) calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)),
37
37
  transparent 0,
38
38
  transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
39
39
  black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
@@ -42,7 +42,7 @@
42
42
  [dir="rtl"] & {
43
43
  mask-image:
44
44
  radial-gradient(
45
- circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)),
45
+ circle at var(--badge-mask-position) calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)),
46
46
  transparent 0,
47
47
  transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
48
48
  black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
@@ -52,7 +52,7 @@
52
52
 
53
53
  & > &__content {
54
54
  position: absolute;
55
- bottom: 0;
55
+ bottom: var(--badge-bottom-position);
56
56
  .right(0);
57
57
 
58
58
  box-sizing: border-box;
@@ -27,6 +27,7 @@ export type BadgeProps = {
27
27
  size?: SizeSmall | DeprecatedSizes | SizeLarge;
28
28
  border?: ThemeDark | ThemeLight;
29
29
  'aria-label'?: string;
30
+ position?: 'top-right' | 'bottom-right';
30
31
  } & CommonProps;
31
32
 
32
33
  const mapLegacySize = {
@@ -43,6 +44,7 @@ const Badge = ({
43
44
  border = Theme.LIGHT,
44
45
  'aria-label': ariaLabel,
45
46
  children,
47
+ position = 'bottom-right',
46
48
  }: BadgeProps) => {
47
49
  // medium is deprecated, so we map it to small
48
50
  const size = sizeProp === Size.MEDIUM ? Size.SMALL : sizeProp;
@@ -56,7 +58,14 @@ const Badge = ({
56
58
  );
57
59
 
58
60
  return (
59
- <div aria-label={ariaLabel} className={classes}>
61
+ <div
62
+ aria-label={ariaLabel}
63
+ className={classes}
64
+ style={{
65
+ '--badge-mask-position': position === 'top-right' ? 'bottom' : 'top',
66
+ '--badge-bottom-position': position === 'top-right' ? 'calc(var(--badge-size) * 2)' : 0,
67
+ }}
68
+ >
60
69
  <div className="tw-badge__children">{children}</div>
61
70
  <Circle size={mapLegacySize[size]} fixedSize className="tw-badge__content">
62
71
  {badge}
@@ -6,30 +6,6 @@
6
6
  cursor: pointer;
7
7
  position: relative;
8
8
  }
9
- .np-circular-btn .tw-icon {
10
- position: absolute;
11
- top: 16px;
12
- top: var(--size-16);
13
- pointer-events: none;
14
- color: white;
15
- transition: color 0.15s ease-in-out;
16
- width: 100%;
17
- left: 0;
18
- }
19
- [dir="rtl"] .np-circular-btn .tw-icon {
20
- right: 0;
21
- left: auto;
22
- left: initial;
23
- }
24
- @media (max-width: 320px) {
25
- .np-circular-btn .tw-icon {
26
- top: 8px;
27
- top: var(--size-8);
28
- }
29
- }
30
- .np-circular-btn .tw-icon > svg {
31
- margin: 0 auto;
32
- }
33
9
  .np-theme-personal .np-circular-btn {
34
10
  /* stylelint-disable-next-line no-duplicate-selectors */
35
11
  }
@@ -9,23 +9,23 @@
9
9
  cursor: pointer;
10
10
  position: relative;
11
11
 
12
- .tw-icon {
13
- position: absolute;
14
- top: var(--size-16);
15
- pointer-events: none;
16
- color: white;
17
- transition: color 0.15s ease-in-out;
18
- width: 100%;
19
- .left(0);
12
+ // .tw-icon {
13
+ // position: absolute;
14
+ // top: var(--size-16);
15
+ // pointer-events: none;
16
+ // color: white;
17
+ // transition: color 0.15s ease-in-out;
18
+ // width: 100%;
19
+ // .left(0);
20
20
 
21
- @media (--screen-400-zoom) {
22
- top: var(--size-8);
23
- }
21
+ // @media (--screen-400-zoom) {
22
+ // top: var(--size-8);
23
+ // }
24
24
 
25
- > svg {
26
- margin: 0 auto;
27
- }
28
- }
25
+ // > svg {
26
+ // margin: 0 auto;
27
+ // }
28
+ // }
29
29
 
30
30
  .np-theme-personal & {
31
31
  /* stylelint-disable-next-line no-duplicate-selectors */
@@ -28,14 +28,12 @@ const CircularButton = ({
28
28
  }: CircularButtonProps) => {
29
29
  const classes = clsx('btn np-btn', 'm-b-1', typeClassMap[type], priorityClassMap[priority]);
30
30
 
31
- const iconElement = Number(icon.props.size) !== 24 ? cloneElement(icon, { size: 24 }) : icon;
32
-
33
31
  const isTinyViewport = useMedia(`(max-width: ${Breakpoint.ZOOM_400}px)`);
34
32
 
35
33
  return (
36
34
  <label className={clsx('np-circular-btn', priority, type, disabled && 'disabled', className)}>
37
35
  <Circle
38
- as="input"
36
+ as="button"
39
37
  // @ts-expect-error it's input[type=button] element
40
38
  type="button"
41
39
  size={isTinyViewport ? 64 : 56}
@@ -43,8 +41,9 @@ const CircularButton = ({
43
41
  className={classes}
44
42
  disabled={disabled}
45
43
  {...rest}
46
- />
47
- {iconElement}
44
+ >
45
+ {icon}
46
+ </Circle>
48
47
  <Body as="span" className="np-circular-btn__label" type={Typography.BODY_DEFAULT_BOLD}>
49
48
  {children}
50
49
  </Body>
@@ -4,4 +4,21 @@
4
4
  width: var(--circle-size);
5
5
  height: var(--circle-size);
6
6
  flex-shrink: 0;
7
+ font-size: var(--circle-font-size);
8
+ font-weight: 600;
9
+ font-weight: var(--font-weight-semi-bold);
10
+ line-height: 1;
11
+ }
12
+ .np-circle img {
13
+ border-radius: 9999px;
14
+ border-radius: var(--radius-full);
15
+ width: 100%;
16
+ height: 100%;
17
+ }
18
+ .np-circle .np-display {
19
+ font-size: var(--circle-font-size);
20
+ }
21
+ .np-circle .tw-icon > svg {
22
+ height: var(--circle-icon-size);
23
+ width: var(--circle-icon-size);
7
24
  }
@@ -3,4 +3,26 @@
3
3
  width: var(--circle-size);
4
4
  height: var(--circle-size);
5
5
  flex-shrink: 0;
6
+
7
+ img {
8
+ border-radius: var(--radius-full);
9
+ width: 100%;
10
+ height: 100%;
11
+ }
12
+
13
+ // circle like components set custom typography styles for default (Inter) font
14
+ font-size: var(--circle-font-size);
15
+ font-weight: var(--font-weight-semi-bold);
16
+ line-height: 1;
17
+
18
+ // circle like components set custom typography styles for Wise Sand font
19
+ .np-display {
20
+ font-size: var(--circle-font-size);
21
+ }
22
+
23
+ // circle like components have custom icon sizes
24
+ .tw-icon > svg {
25
+ height: var(--circle-icon-size);
26
+ width: var(--circle-icon-size);
27
+ }
6
28
  }
@@ -1,7 +1,7 @@
1
1
  import { HTMLAttributes, forwardRef } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
 
4
- export type ShapeSize = 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72;
4
+ export type ShapeSize = 16 | 24 | 32 | 40 | 48 | 56 | 72;
5
5
 
6
6
  export type Props = {
7
7
  /**
@@ -19,6 +19,32 @@ export type Props = {
19
19
  fixedSize?: boolean;
20
20
  } & HTMLAttributes<HTMLDivElement>;
21
21
 
22
+ /**
23
+ * circle like components has custom sizes for icons
24
+ */
25
+ const mapIconSize = {
26
+ 16: 12,
27
+ 24: 16,
28
+ 32: 18,
29
+ 40: 20,
30
+ 48: 24,
31
+ 56: 28,
32
+ 72: 36,
33
+ };
34
+
35
+ /**
36
+ * circle like components have custom font styles
37
+ */
38
+ const mapFontSize = {
39
+ 16: 8,
40
+ 24: 12,
41
+ 32: 14,
42
+ 40: 18,
43
+ 48: 22,
44
+ 56: 26,
45
+ 72: 30,
46
+ };
47
+
22
48
  const Circle = forwardRef(function Circle(
23
49
  {
24
50
  as: Element = 'div',
@@ -35,7 +61,12 @@ const Circle = forwardRef(function Circle(
35
61
  <Element
36
62
  {...props}
37
63
  ref={ref}
38
- style={{ ...style, '--circle-size': fixedSize ? `${size}px` : `var(--size-${size})` }}
64
+ style={{
65
+ ...style,
66
+ '--circle-size': fixedSize ? `${size}px` : `var(--size-${size})`,
67
+ '--circle-icon-size': `${mapIconSize[size]}px`,
68
+ '--circle-font-size': `${mapFontSize[size]}px`,
69
+ }}
39
70
  className={clsx('np-circle', 'd-flex align-items-center justify-content-center', className)}
40
71
  >
41
72
  {children}
@@ -23,3 +23,4 @@ export { MarkdownNodeType } from './propsValues/markdownNodeType';
23
23
  export { FileType } from './fileType';
24
24
  export * from './locale';
25
25
  export * from './commonProps';
26
+ export * from './initials';
@@ -0,0 +1,13 @@
1
+ export function getInitials(name: string) {
2
+ const allInitials = name
3
+ .split(' ')
4
+ .map((part) => part[0])
5
+ .join('')
6
+ .toUpperCase();
7
+
8
+ if (allInitials.length === 1) {
9
+ return allInitials[0];
10
+ }
11
+
12
+ return allInitials[0] + allInitials.slice(-1);
13
+ }
@@ -0,0 +1,10 @@
1
+ .np-notification-dot {
2
+ --badge-notification-dot-size: 14px;
3
+ width: 14px;
4
+ width: var(--badge-notification-dot-size);
5
+ height: 14px;
6
+ height: var(--badge-notification-dot-size);
7
+ background-color: var(--color-sentiment-negative);
8
+ border-radius: 9999px;
9
+ border-radius: var(--radius-full);
10
+ }
@@ -0,0 +1,7 @@
1
+ .np-notification-dot {
2
+ --badge-notification-dot-size: 14px;
3
+ width: var(--badge-notification-dot-size);
4
+ height: var(--badge-notification-dot-size);
5
+ background-color: var(--color-sentiment-negative);
6
+ border-radius: var(--radius-full);
7
+ }
@@ -0,0 +1,10 @@
1
+ import { HTMLAttributes } from 'react';
2
+
3
+ type Props = Pick<HTMLAttributes<HTMLDivElement>, 'className' | 'style'>;
4
+
5
+ /**
6
+ * @deprecated make this element part pre build badges
7
+ */
8
+ export default function NotificationDot(props: Props) {
9
+ return <div {...props} className="np-notification-dot" />;
10
+ }
@@ -18,7 +18,7 @@ function Display({
18
18
  id,
19
19
  }: Props) {
20
20
  return (
21
- <Heading id={id} className={clsx(`np-text-${type}`, 'text-primary', className)}>
21
+ <Heading id={id} className={clsx('np-display', `np-text-${type}`, 'text-primary', className)}>
22
22
  {children}
23
23
  </Heading>
24
24
  );
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ export type { ActionOptionProps } from './actionOption';
8
8
  export type { SelectOptionProps, SelectOptionValue, SelectOptiopsSection } from './selectOption';
9
9
  export type { AlertAction, AlertProps, AlertType } from './alert';
10
10
  export type { AvatarProps } from './avatar';
11
+ export type { AvatarViewProps } from './avatarView';
11
12
  export type { BadgeProps } from './badge';
12
13
  export type { CardProps } from './card';
13
14
  export type { CarouselProps } from './carousel';
@@ -102,6 +103,7 @@ export { default as ActionOption } from './actionOption';
102
103
  export { default as SelectOption } from './selectOption';
103
104
  export { default as Alert } from './alert';
104
105
  export { default as Avatar } from './avatar';
106
+ export { default as AvatarView } from './avatarView';
105
107
  export { default as AvatarWrapper } from './avatarWrapper';
106
108
  export { default as Badge } from './badge';
107
109
  export { default as Body } from './body';
@@ -243,7 +245,9 @@ export {
243
245
  getLangFromLocale,
244
246
  isBrowser,
245
247
  isServerSide,
248
+ getInitials,
246
249
  } from './common';
250
+ export { getAvatarColorFromSeed } from './avatar/colors';
247
251
 
248
252
  /**
249
253
  * Translations