@jetbrains/ring-ui 7.0.21 → 7.0.23

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.
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from 'react';
2
+ import { Size } from './avatar-size';
3
+ interface InfoAvatarProps {
4
+ size: Size;
5
+ children?: ReactNode;
6
+ }
7
+ export default function AvatarInfo({ size, children }: InfoAvatarProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import classNames from 'classnames';
3
+ import styles from './avatar.css';
4
+ import { Size } from './avatar-size';
5
+ const fontSizes = {
6
+ [Size.Size16]: 9,
7
+ [Size.Size18]: 9,
8
+ [Size.Size20]: 9,
9
+ [Size.Size24]: 11,
10
+ [Size.Size28]: 12,
11
+ [Size.Size32]: 14,
12
+ [Size.Size40]: 16,
13
+ [Size.Size48]: 16,
14
+ [Size.Size56]: 22,
15
+ };
16
+ export default function AvatarInfo({ size, children }) {
17
+ const fontSize = fontSizes[size];
18
+ return (_jsx("span", { style: { fontSize }, className: classNames(styles.avatarInfo), children: children }));
19
+ }
@@ -0,0 +1,18 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
2
+ <defs>
3
+ <filter id="inset-shadow" x="-50%" y="-50%" width="200%" height="200%">
4
+ <feComponentTransfer in="SourceAlpha">
5
+ <feFuncA type="table" tableValues="1 0" />
6
+ </feComponentTransfer>
7
+ <feGaussianBlur stdDeviation="4"/>
8
+ <feOffset dx="0" dy="0" result="offsetblur"/>
9
+ <feFlood flood-color="rgba(0, 0, 0, 0.07)"/>
10
+ <feComposite in2="offsetblur" operator="in"/>
11
+ <feComposite in="SourceAlpha" operator="in" />
12
+ <feMerge>
13
+ <feMergeNode in="SourceGraphic" />
14
+ <feMergeNode />
15
+ </feMerge>
16
+ </filter>
17
+ </defs>
18
+ </svg>
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @name Avatar
3
+ */
4
+ export declare enum Size {
5
+ Size16 = 16,
6
+ /** @deprecated */
7
+ Size18 = 18,
8
+ Size20 = 20,
9
+ Size24 = 24,
10
+ Size28 = 28,
11
+ Size32 = 32,
12
+ Size40 = 40,
13
+ /** @deprecated */
14
+ Size48 = 48,
15
+ Size56 = 56
16
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @name Avatar
3
+ */
4
+ export var Size;
5
+ (function (Size) {
6
+ Size[Size["Size16"] = 16] = "Size16";
7
+ /** @deprecated */
8
+ Size[Size["Size18"] = 18] = "Size18";
9
+ Size[Size["Size20"] = 20] = "Size20";
10
+ Size[Size["Size24"] = 24] = "Size24";
11
+ Size[Size["Size28"] = 28] = "Size28";
12
+ Size[Size["Size32"] = 32] = "Size32";
13
+ Size[Size["Size40"] = 40] = "Size40";
14
+ /** @deprecated */
15
+ Size[Size["Size48"] = 48] = "Size48";
16
+ Size[Size["Size56"] = 56] = "Size56";
17
+ })(Size || (Size = {}));
@@ -10,6 +10,25 @@
10
10
  border-radius: var(--ring-border-radius);
11
11
  }
12
12
 
13
+ .avatarShadow {
14
+ /* See https://stackoverflow.com/questions/21414925/why-doesnt-inset-box-shadow-work-over-images */
15
+
16
+ filter: url('avatar-shadow-filter.svg#inset-shadow');
17
+ }
18
+
19
+ .avatarInfo {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+
24
+ height: 100%;
25
+
26
+ color: var(--ring-secondary-color);
27
+ border-radius: inherit;
28
+
29
+ background-color: var(--ring-disabled-background-color);
30
+ }
31
+
13
32
  .subavatar {
14
33
  position: absolute;
15
34
  top: 15px;
@@ -1,19 +1,6 @@
1
- import { PureComponent, ImgHTMLAttributes } from 'react';
2
- /**
3
- * @name Avatar
4
- */
5
- export declare enum Size {
6
- /** @deprecated */
7
- Size18 = 18,
8
- Size20 = 20,
9
- Size24 = 24,
10
- Size28 = 28,
11
- Size32 = 32,
12
- Size40 = 40,
13
- /** @deprecated */
14
- Size48 = 48,
15
- Size56 = 56
16
- }
1
+ import { PureComponent, ImgHTMLAttributes, ReactNode } from 'react';
2
+ import { Size } from './avatar-size';
3
+ export { Size };
17
4
  export interface AvatarProps extends ImgHTMLAttributes<HTMLImageElement> {
18
5
  dpr: number;
19
6
  size: Size | number;
@@ -22,6 +9,7 @@ export interface AvatarProps extends ImgHTMLAttributes<HTMLImageElement> {
22
9
  round?: boolean | null | undefined;
23
10
  subavatar?: string | null | undefined;
24
11
  username?: string | null | undefined;
12
+ info?: ReactNode;
25
13
  skipParams?: boolean | null | undefined;
26
14
  }
27
15
  export default class Avatar extends PureComponent<AvatarProps> {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import figma from '@figma/code-connect';
3
+ import Avatar, { Size } from './avatar';
4
+ figma.connect(Avatar, 'https://www.figma.com/design/HY6d4uE1xxaQXCMG9fe6Y2/RingUI?m=auto&node-id=5990-522&t=v8bItK8qotmnbysK-1', {
5
+ props: {
6
+ round: figma.boolean('Round'),
7
+ size: figma.enum('Size', {
8
+ '20 px': Size.Size20,
9
+ '24 px': Size.Size24,
10
+ '28 px': Size.Size28,
11
+ '32 px': Size.Size32,
12
+ '40px': Size.Size40,
13
+ '56px': Size.Size56,
14
+ }),
15
+ username: figma.enum('Content', {
16
+ name: 'Samuel Morse',
17
+ }),
18
+ url: figma.enum('Content', {
19
+ 'color/image': 'avatar.png',
20
+ }),
21
+ },
22
+ example: props => _jsx(Avatar, { ...props }),
23
+ imports: ['import Avatar, {Size} from "@jetbrains/ring-ui/components/avatar/avatar"'],
24
+ });
@@ -7,22 +7,9 @@ import { getPixelRatio } from '../global/dom';
7
7
  import memoize from '../global/memoize';
8
8
  import styles from './avatar.css';
9
9
  import FallbackAvatar from './fallback-avatar';
10
- /**
11
- * @name Avatar
12
- */
13
- export var Size;
14
- (function (Size) {
15
- /** @deprecated */
16
- Size[Size["Size18"] = 18] = "Size18";
17
- Size[Size["Size20"] = 20] = "Size20";
18
- Size[Size["Size24"] = 24] = "Size24";
19
- Size[Size["Size28"] = 28] = "Size28";
20
- Size[Size["Size32"] = 32] = "Size32";
21
- Size[Size["Size40"] = 40] = "Size40";
22
- /** @deprecated */
23
- Size[Size["Size48"] = 48] = "Size48";
24
- Size[Size["Size56"] = 56] = "Size56";
25
- })(Size || (Size = {}));
10
+ import { Size } from './avatar-size';
11
+ import AvatarInfo from './avatar-info';
12
+ export { Size };
26
13
  const warnSize = memoize((size) => deprecate(() => { }, `Avatar: Size${size} is deprecated and will be removed in 8.0. The supported sizes are: Size20, Size24, Size28, Size32, Size40.`));
27
14
  export default class Avatar extends PureComponent {
28
15
  static defaultProps = {
@@ -41,7 +28,7 @@ export default class Avatar extends PureComponent {
41
28
  this.setState({ errorUrl: '' });
42
29
  };
43
30
  render() {
44
- const { size, url, dpr, style, round, subavatar, subavatarSize, username, skipParams, ...restProps } = this.props;
31
+ const { size, url, dpr, style, round, subavatar, subavatarSize, username, info, skipParams, ...restProps } = this.props;
45
32
  if ([Size.Size18, Size.Size48].includes(size)) {
46
33
  warnSize(size)();
47
34
  }
@@ -61,7 +48,9 @@ export default class Avatar extends PureComponent {
61
48
  ...style,
62
49
  };
63
50
  if (!url || this.state.errorUrl === url) {
64
- return (_jsx("span", { ...restProps, "data-test": "avatar", className: classNames(styles.avatar, this.props.className, { [styles.empty]: username == null }), style: styleObj, children: username != null && _jsx(FallbackAvatar, { size: size, round: round, username: username }) }));
51
+ return (_jsxs("span", { ...restProps, "data-test": "avatar", className: classNames(styles.avatar, this.props.className, {
52
+ [styles.empty]: username == null && info == null,
53
+ }), style: styleObj, children: [username != null && _jsx(FallbackAvatar, { size: size, round: round, username: username }), info != null && _jsx(AvatarInfo, { size: size, children: info })] }));
65
54
  }
66
55
  let src = url;
67
56
  if (!skipParams && !isDataURI(url)) {
@@ -82,10 +71,10 @@ export default class Avatar extends PureComponent {
82
71
  subavatarSizeString,
83
72
  };
84
73
  subavatarSrc = skipParams ? subavatar : encodeURL(urlStart, queryParams);
85
- return (_jsxs("div", { children: [_jsx("img", { ...restProps, onError: this.handleError, onLoad: this.handleSuccess, className: classNames(styles.avatar, this.props.className), style: styleObj, src: src, alt: "User avatar" }), _jsx("img", { ...restProps, "data-test": "avatar", onError: this.handleError, onLoad: this.handleSuccess, className: classNames(styles.subavatar), style: styleObjGroup, src: subavatarSrc, alt: "Subavatar" })] }));
74
+ return (_jsxs("div", { children: [_jsx("img", { ...restProps, onError: this.handleError, onLoad: this.handleSuccess, className: classNames(styles.avatar, styles.avatarShadow, this.props.className), style: styleObj, src: src, alt: "User avatar" }), _jsx("img", { ...restProps, "data-test": "avatar", onError: this.handleError, onLoad: this.handleSuccess, className: classNames(styles.subavatar), style: styleObjGroup, src: subavatarSrc, alt: "Subavatar" })] }));
86
75
  }
87
76
  else {
88
- return (_jsx("img", { ...restProps, "data-test": "avatar", onError: this.handleError, onLoad: this.handleSuccess, className: classNames(styles.avatar, this.props.className), style: styleObj, src: src, alt: "User avatar" }));
77
+ return (_jsx("img", { ...restProps, "data-test": "avatar", onError: this.handleError, onLoad: this.handleSuccess, className: classNames(styles.avatar, styles.avatarShadow, this.props.className), style: styleObj, src: src, alt: "User avatar" }));
89
78
  }
90
79
  }
91
80
  }
@@ -1,4 +1,4 @@
1
- import { Size } from './avatar';
1
+ import { Size } from './avatar-size';
2
2
  export interface FallbackAvatarProps {
3
3
  username: string;
4
4
  size: Size;
@@ -1,58 +1,159 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo } from 'react';
3
3
  import getUID from '../global/get-uid';
4
+ import { Size } from './avatar-size';
4
5
  const colorPairs = [
5
- ['#60A800', '#D5CA00'],
6
- ['#21D370', '#03E9E1'],
7
- ['#3BA1FF', '#36E97D'],
8
- ['#00C243', '#00FFFF'],
9
- ['#4BE098', '#627FFF'],
10
- ['#168BFA', '#26F7C7'],
11
- ['#9D4CFF', '#39D3C3'],
12
- ['#0A81F6', '#0ACFF6'],
13
- ['#765AF8', '#5A91F8'],
14
- ['#9E54FF', '#0ACFF6'],
15
- ['#B345F1', '#669DFF'],
16
- ['#765AF8', '#C059EE'],
17
- ['#9039D0', '#C239D0'],
18
- ['#9F2AFF', '#FD56FD'],
19
- ['#AB3AF2', '#E40568'],
20
- ['#9F2AFF', '#E9A80B'],
21
- ['#D50F6B', '#E73AE8'],
22
- ['#ED5502', '#E73AE8'],
23
- ['#ED358C', '#DBED18'],
24
- ['#ED358C', '#F9902E'],
25
- ['#FF7500', '#FFCA00'],
6
+ ['#A5E074', '#E8E093'],
7
+ ['#6AD8A2', '#79E98F'],
8
+ ['#7FBAF6', '#7BEBB2'],
9
+ ['#6DDC8C', '#97FFFF'],
10
+ ['#8EE5C0', '#A2B6FF'],
11
+ ['#73B4F5', '#78F8DC'],
12
+ ['#BA8EFF', '#78DFD6'],
13
+ ['#63AFF5', '#60E2F5'],
14
+ ['#A192F7', '#93B2F7'],
15
+ ['#B989FF', '#60E2F5'],
16
+ ['#CA7EF7', '#9BBAF8'],
17
+ ['#A192F7', '#CF94F4'],
18
+ ['#AB7EE1', '#D58AE3'],
19
+ ['#BE82FF', '#FFA0FF'],
20
+ ['#C57EF2', '#F2799E'],
21
+ ['#BE82FF', '#F2CC83'],
22
+ ['#E16C9D', '#EF8AE8'],
23
+ ['#F29766', '#EF8AE8'],
24
+ ['#F27AA9', '#EAF087'],
25
+ ['#F27AA9', '#FBBB8C'],
26
+ ['#FFAB66', '#FFEBA0'],
26
27
  ];
27
- const Sizes = {
28
- 18: {
29
- radius: 2,
28
+ const SizesSquare = {
29
+ [Size.Size16]: {
30
+ radius: 4,
31
+ text: { x: 7.5, y: 11 },
32
+ fontSize: '8px',
33
+ textAnchor: 'middle',
34
+ },
35
+ [Size.Size18]: {
36
+ radius: 4,
30
37
  text: { x: 9, y: 13 },
31
38
  fontSize: '11px',
32
39
  textAnchor: 'middle',
33
40
  },
34
- 24: {
35
- radius: 3,
36
- text: { x: 2, y: 13 },
41
+ [Size.Size20]: {
42
+ radius: 4,
43
+ text: { x: 2, y: 10 },
44
+ fontSize: '9px',
45
+ underscore: { x: 3, y: 16, width: 7, height: 1 },
46
+ },
47
+ [Size.Size24]: {
48
+ radius: 4,
49
+ text: { x: 2, y: 12 },
37
50
  fontSize: '11px',
38
- underscore: { x: 3, y: 17 },
51
+ underscore: { x: 3, y: 18.5, width: 9, height: 1.5 },
39
52
  },
40
- 32: {
41
- radius: 3,
42
- text: { x: 3, y: 17 },
53
+ [Size.Size28]: {
54
+ radius: 4,
55
+ text: { x: 2, y: 14 },
43
56
  fontSize: '13px',
57
+ underscore: { x: 3, y: 22, width: 11, height: 2 },
58
+ },
59
+ [Size.Size32]: {
60
+ radius: 4,
61
+ text: { x: 3, y: 17 },
62
+ fontSize: '15px',
44
63
  letterSpacing: 1,
45
- underscore: { x: 4, y: 22 },
64
+ underscore: { x: 4, y: 26, width: 12, height: 2 },
65
+ },
66
+ [Size.Size40]: {
67
+ radius: 4,
68
+ text: { x: 3, y: 21 },
69
+ fontSize: '19px',
70
+ letterSpacing: 1,
71
+ underscore: { x: 5, y: 32, width: 15, height: 2.5 },
72
+ },
73
+ [Size.Size48]: {
74
+ radius: 4,
75
+ text: { x: 3, y: 21 },
76
+ fontSize: '19px',
77
+ letterSpacing: 1,
78
+ underscore: { x: 5, y: 32, width: 15, height: 2.5 },
79
+ },
80
+ [Size.Size56]: {
81
+ radius: 4,
82
+ text: { x: 4, y: 28 },
83
+ fontSize: '26px',
84
+ letterSpacing: 1,
85
+ underscore: { x: 7, y: 45, width: 21, height: 3.5 },
86
+ },
87
+ };
88
+ const SizesRound = {
89
+ [Size.Size16]: {
90
+ radius: 4,
91
+ fontSize: '8px',
92
+ text: { x: '50%', y: '54%' },
93
+ textAnchor: 'middle',
94
+ dominantBaseline: 'middle',
95
+ },
96
+ [Size.Size18]: {
97
+ radius: 4,
98
+ fontSize: '11px',
99
+ text: { x: '50%', y: '54%' },
100
+ textAnchor: 'middle',
101
+ dominantBaseline: 'middle',
46
102
  },
47
- 40: {
48
- radius: 3,
49
- text: { x: 5, y: 19 },
103
+ [Size.Size20]: {
104
+ radius: 4,
105
+ fontSize: '9px',
106
+ text: { x: '50%', y: '54%' },
107
+ textAnchor: 'middle',
108
+ dominantBaseline: 'middle',
109
+ },
110
+ [Size.Size24]: {
111
+ radius: 4,
112
+ fontSize: '11px',
113
+ text: { x: '50%', y: '54%' },
114
+ textAnchor: 'middle',
115
+ dominantBaseline: 'middle',
116
+ },
117
+ [Size.Size28]: {
118
+ radius: 4,
119
+ fontSize: '13px',
120
+ text: { x: '50%', y: '54%' },
121
+ textAnchor: 'middle',
122
+ dominantBaseline: 'middle',
123
+ },
124
+ [Size.Size32]: {
125
+ radius: 4,
50
126
  fontSize: '15px',
127
+ text: { x: '50%', y: '54%' },
128
+ textAnchor: 'middle',
129
+ dominantBaseline: 'middle',
51
130
  letterSpacing: 1,
52
- underscore: { x: 6, y: 28 },
131
+ },
132
+ [Size.Size40]: {
133
+ radius: 4,
134
+ fontSize: '19px',
135
+ letterSpacing: 1,
136
+ text: { x: '50%', y: '54%' },
137
+ textAnchor: 'middle',
138
+ dominantBaseline: 'middle',
139
+ },
140
+ [Size.Size48]: {
141
+ radius: 4,
142
+ fontSize: '19px',
143
+ letterSpacing: 1,
144
+ text: { x: '50%', y: '54%' },
145
+ textAnchor: 'middle',
146
+ dominantBaseline: 'middle',
147
+ },
148
+ [Size.Size56]: {
149
+ radius: 4,
150
+ fontSize: '26px',
151
+ letterSpacing: 1,
152
+ text: { x: '50%', y: '54%' },
153
+ textAnchor: 'middle',
154
+ dominantBaseline: 'middle',
53
155
  },
54
156
  };
55
- const sizeKeys = Object.keys(Sizes).map(Number);
56
157
  function extractLetters(name) {
57
158
  const names = name
58
159
  .split(/[\s._]+/)
@@ -85,10 +186,9 @@ function hashCode(s) {
85
186
  export default function FallbackAvatar({ username, size, round }) {
86
187
  const hash = Math.abs(hashCode(username.toLowerCase()));
87
188
  const [fromColor, toColor] = colorPairs[hash % colorPairs.length];
88
- const possibleSizeKeys = sizeKeys.filter(key => key >= size);
89
- const sizeKey = possibleSizeKeys.length > 0 ? Math.min(...possibleSizeKeys) : Math.max(...sizeKeys);
90
- const sizes = Sizes[sizeKey];
189
+ const sizes = round ? SizesRound[size] : SizesSquare[size];
190
+ const underscore = sizes.underscore;
91
191
  const radius = round ? '50%' : sizes.radius;
92
192
  const gradientId = useMemo(() => getUID('gradient-'), []);
93
- return (_jsxs("svg", { viewBox: `0 0 ${sizeKey} ${sizeKey}`, xmlns: "http://www.w3.org/2000/svg", children: [_jsx("defs", { children: _jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { stopColor: fromColor, offset: "0" }), _jsx("stop", { stopColor: toColor, offset: "1" })] }) }), _jsxs("g", { children: [_jsx("rect", { fill: `url(#${gradientId})`, x: "0", y: "0", width: sizeKey, height: sizeKey, rx: radius, ry: radius }), _jsxs("text", { x: sizes.text.x, y: sizes.text.y, fontFamily: "Arial, Helvetica, sans-serif", fontSize: sizes.fontSize, letterSpacing: sizes.letterSpacing, fill: "#FFFFFF", textAnchor: sizes.textAnchor, cursor: "default", children: [_jsx("tspan", { children: extractLetters(username) }), sizes.underscore && (_jsx("tspan", { x: sizes.underscore.x, y: sizes.underscore.y, children: '_' }))] }), _jsx("title", { children: username })] })] }));
193
+ return (_jsxs("svg", { viewBox: `0 0 ${size} ${size}`, xmlns: "http://www.w3.org/2000/svg", children: [_jsx("defs", { children: _jsxs("linearGradient", { id: gradientId, x1: "1", y1: "1", x2: "0", y2: "0", children: [_jsx("stop", { stopColor: fromColor, offset: "0" }), _jsx("stop", { stopColor: toColor, offset: "0.8" })] }) }), _jsxs("g", { children: [_jsx("rect", { fill: `url(#${gradientId})`, x: "0", y: "0", width: size, height: size, rx: radius, ry: radius }), _jsx("text", { x: sizes.text.x, y: sizes.text.y, fontFamily: "var(--ring-font-family, 'Arial, Helvetica, sans-serif')", fontSize: sizes.fontSize, fontWeight: "600", letterSpacing: sizes.letterSpacing, fill: "#FFFFFF", dominantBaseline: sizes.dominantBaseline, textAnchor: sizes.textAnchor, cursor: "default", children: _jsx("tspan", { children: extractLetters(username) }) }), underscore && (_jsx("rect", { fill: "#FFFFFF", x: underscore.x, y: underscore.y, width: underscore.width, height: underscore.height })), _jsx("title", { children: username })] })] }));
94
194
  }
@@ -172,8 +172,11 @@
172
172
  }
173
173
 
174
174
  /* override link */
175
- .actionLink.actionLink {
175
+ .actionLink.actionLink.actionLink {
176
176
  transition: none;
177
+ text-decoration-line: none;
178
+
179
+ color: var(--ring-text-color);
177
180
  }
178
181
 
179
182
  .action:hover {
@@ -0,0 +1,2 @@
1
+ import { ListDataItemProps } from './consts';
2
+ export declare function getListClasses<T = unknown>({ className, disabled, hover, compact, scrolling, rgItemType, }: Readonly<ListDataItemProps<T>>): string;
@@ -0,0 +1,14 @@
1
+ import classNames from 'classnames';
2
+ import globalStyles from '../global/global.css';
3
+ import { Type } from './consts';
4
+ import styles from './list.css';
5
+ export function getListClasses({ className, disabled, hover, compact, scrolling, rgItemType, }) {
6
+ return classNames(styles.item, globalStyles.resetButton, className, {
7
+ [styles.action]: !disabled,
8
+ [styles.actionLink]: !disabled && rgItemType === Type.LINK,
9
+ [styles.hover]: hover && !disabled,
10
+ [styles.compact]: compact,
11
+ [styles.scrolling]: scrolling,
12
+ [styles.disabled]: disabled,
13
+ });
14
+ }
@@ -1,9 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { PureComponent } from 'react';
3
- import classNames from 'classnames';
4
3
  import dataTests from '../global/data-tests';
5
4
  import getEventKey from '../global/get-event-key';
6
- import styles from './list.css';
5
+ import { getListClasses } from './list__classes';
7
6
  export default class ListCustom extends PureComponent {
8
7
  handleKeyPress = (event) => {
9
8
  const key = getEventKey(event);
@@ -12,15 +11,11 @@ export default class ListCustom extends PureComponent {
12
11
  }
13
12
  };
14
13
  render() {
15
- const { scrolling, hover, className, disabled, template, rgItemType, tabIndex, onClick, onCheckboxChange, onMouseOver, onMouseUp, role, tagName, ...restProps } = this.props;
16
- const classes = classNames(styles.item, className, {
17
- [styles.action]: !disabled,
18
- [styles.hover]: hover && !disabled,
19
- [styles.scrolling]: scrolling,
20
- });
14
+ const { disabled, template, tabIndex, onClick, onMouseOver, onMouseUp, role, tagName } = this.props;
15
+ const classes = getListClasses(this.props);
21
16
  const dataTest = dataTests('ring-list-item-custom', {
22
17
  'ring-list-item-action': !disabled,
23
- }, restProps['data-test']);
18
+ }, this.props['data-test']);
24
19
  const content = typeof template === 'function' ? template(this.props) : template;
25
20
  const TagName = tagName || 'span';
26
21
  return (_jsx(TagName, { role: role || 'button', tabIndex: tabIndex, onClick: onClick, onKeyPress: this.handleKeyPress, onMouseOver: onMouseOver, onFocus: onMouseOver, onMouseUp: onMouseUp, className: classes, "data-test": dataTest, children: content }));
@@ -6,8 +6,8 @@ import Avatar, { Size as AvatarSize } from '../avatar/avatar';
6
6
  import Checkbox from '../checkbox/checkbox';
7
7
  import Icon from '../icon/icon';
8
8
  import getUID from '../global/get-uid';
9
- import globalStyles from '../global/global.css';
10
9
  import styles from './list.css';
10
+ import { getListClasses } from './list__classes';
11
11
  /**
12
12
  * @constructor
13
13
  * @extends {ReactComponent}
@@ -20,18 +20,12 @@ export default class ListItem extends PureComponent {
20
20
  stopBubbling = (e) => e.stopPropagation();
21
21
  _isString = (val) => typeof val === 'string' || val instanceof String;
22
22
  render() {
23
- const { scrolling, className, disabled, checkbox, avatar, subavatar, glyph, icon, rightGlyph, description, label, title, details, hover, rgItemType, level, tabIndex, compact, onClick, onCheckboxChange, onMouseOver, onMouseDown, onMouseUp, rightNodes, leftNodes, showGeneratedAvatar, username, labelWrapper, ...restProps } = this.props;
23
+ const { disabled, checkbox, avatar, subavatar, glyph, icon, rightGlyph, description, label, title, details, hover, level, tabIndex, onClick, onCheckboxChange, onMouseOver, onMouseDown, onMouseUp, rightNodes, leftNodes, showGeneratedAvatar, username, labelWrapper, } = this.props;
24
24
  const checkable = checkbox !== undefined;
25
25
  const shouldShowGeneratedAvatar = showGeneratedAvatar && username != null;
26
26
  const hasLeftNodes = leftNodes || glyph || avatar || shouldShowGeneratedAvatar;
27
27
  const showCheckbox = checkable && (checkbox || !hasLeftNodes || (hover && !disabled));
28
- const classes = classNames(styles.item, globalStyles.resetButton, className, {
29
- [styles.action]: !disabled,
30
- [styles.hover]: hover && !disabled,
31
- [styles.compact]: compact,
32
- [styles.scrolling]: scrolling,
33
- [styles.disabled]: disabled,
34
- });
28
+ const classes = getListClasses(this.props);
35
29
  const detailsClasses = classNames({
36
30
  [styles.details]: details,
37
31
  [styles.padded]: icon !== undefined || checkbox !== undefined || glyph !== undefined,
@@ -50,10 +44,10 @@ export default class ListItem extends PureComponent {
50
44
  computedTitle = this._isString(label) ? label : '';
51
45
  }
52
46
  const dataTest = dataTests({
53
- 'ring-list-item': (restProps['data-test'] || '').indexOf('ring-list-item') === -1,
47
+ 'ring-list-item': (this.props['data-test'] || '').indexOf('ring-list-item') === -1,
54
48
  'ring-list-item-action': !disabled,
55
49
  'ring-list-item-selected': checkbox,
56
- }, restProps['data-test']);
50
+ }, this.props['data-test']);
57
51
  const labelElement = (_jsx("span", { className: styles.label, title: computedTitle, "data-test": "ring-list-item-label", children: label }));
58
52
  return (_jsxs("div", { className: styles.itemContainer, "data-test": dataTest, children: [showCheckbox && (_jsx("div", { className: styles.checkboxContainer, children: _jsx(Checkbox, { "aria-labelledby": this.id, checked: checkbox, disabled: disabled, onChange: onCheckboxChange, onClick: this.stopBubbling }) })), _jsxs("button", { id: this.id, type: "button", tabIndex: tabIndex, onClick: onClick, onMouseOver: onMouseOver, onMouseDown: onMouseDown, onFocus: onMouseOver, onMouseUp: onMouseUp, className: classes, style: style, disabled: disabled, children: [_jsxs("div", { className: styles.top, onMouseOut: this.stopBubbling, onBlur: this.stopBubbling, children: [!showCheckbox && (_jsxs("div", { className: styles.left, children: [leftNodes, glyph && (_jsx(Icon, { className: styles.glyph, glyph: glyph, size: this.props.iconSize, suppressSizeWarning: this.props.suppressSizeWarning })), (avatar || shouldShowGeneratedAvatar) && (_jsx(Avatar, { className: styles.avatar, url: avatar, size: AvatarSize.Size20, subavatar: subavatar, username: username }))] })), labelWrapper ? labelWrapper(labelElement) : labelElement, description && (_jsx("span", { className: styles.description, "data-test": "ring-list-item-description", children: description })), _jsxs("div", { className: styles.right, children: [rightGlyph && (_jsx(Icon, { className: styles.rightGlyph, glyph: rightGlyph, suppressSizeWarning: this.props.suppressSizeWarning, size: this.props.iconSize })), icon && _jsx("div", { className: styles.icon, style: { backgroundImage: `url("${icon}")` } }), rightNodes] })] }), details && _jsx("div", { className: detailsClasses, children: details })] })] }));
59
53
  }
@@ -1,9 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { PureComponent } from 'react';
3
- import classNames from 'classnames';
4
3
  import Link, { linkHOC } from '../link/link';
5
4
  import dataTests from '../global/data-tests';
6
- import styles from './list.css';
5
+ import { getListClasses } from './list__classes';
7
6
  /**
8
7
  * @constructor
9
8
  * @extends {ReactComponent}
@@ -11,12 +10,8 @@ import styles from './list.css';
11
10
  export default class ListLink extends PureComponent {
12
11
  render() {
13
12
  const { scrolling, 'data-test': dataTest, className, label, hover, description, rgItemType, url, onCheckboxChange, disabled, LinkComponent, compact, hoverClassName, children, ...restProps } = this.props;
14
- const classes = classNames(styles.item, className, {
15
- [styles.actionLink]: !disabled,
16
- [styles.compact]: compact,
17
- [styles.scrolling]: scrolling,
18
- });
13
+ const classes = getListClasses(this.props);
19
14
  const Comp = LinkComponent ? linkHOC(LinkComponent) : Link;
20
- return (_jsx(Comp, { pseudo: !this.props.href, ...restProps, hover: hover && !disabled, className: classes, "data-test": dataTests('ring-list-link', dataTest), children: label ?? children }));
15
+ return (_jsx(Comp, { pseudo: !this.props.href, ...restProps, className: classes, "data-test": dataTests('ring-list-link', dataTest), children: label ?? children }));
21
16
  }
22
17
  }
@@ -4,6 +4,7 @@ export interface CollapsibleTabsProps {
4
4
  children: ReactElement<TabProps>[];
5
5
  selected?: string | undefined;
6
6
  onSelect?: ((key: string) => () => void) | undefined;
7
+ onLastVisibleIndexChange?: ((index: number) => void) | null | undefined;
7
8
  moreClassName?: string | null | undefined;
8
9
  moreActiveClassName?: string | null | undefined;
9
10
  morePopupClassName?: string | null | undefined;
@@ -11,6 +12,6 @@ export interface CollapsibleTabsProps {
11
12
  initialVisibleItems?: number | null | undefined;
12
13
  morePopupBeforeEnd?: ReactNode;
13
14
  }
14
- export declare const CollapsibleTabs: ({ children, selected, onSelect, moreClassName, moreActiveClassName, morePopupClassName, morePopupBeforeEnd, morePopupItemClassName, initialVisibleItems, }: CollapsibleTabsProps) => import("react/jsx-runtime").JSX.Element;
15
- declare const _default: import("react").MemoExoticComponent<({ children, selected, onSelect, moreClassName, moreActiveClassName, morePopupClassName, morePopupBeforeEnd, morePopupItemClassName, initialVisibleItems, }: CollapsibleTabsProps) => import("react/jsx-runtime").JSX.Element>;
15
+ export declare const CollapsibleTabs: ({ children, selected, onSelect, onLastVisibleIndexChange, moreClassName, moreActiveClassName, morePopupClassName, morePopupBeforeEnd, morePopupItemClassName, initialVisibleItems, }: CollapsibleTabsProps) => import("react/jsx-runtime").JSX.Element;
16
+ declare const _default: import("react").MemoExoticComponent<({ children, selected, onSelect, onLastVisibleIndexChange, moreClassName, moreActiveClassName, morePopupClassName, morePopupBeforeEnd, morePopupItemClassName, initialVisibleItems, }: CollapsibleTabsProps) => import("react/jsx-runtime").JSX.Element>;
16
17
  export default _default;
@@ -7,7 +7,7 @@ import { FakeMoreButton, MoreButton } from './collapsible-more';
7
7
  import getTabTitles from './collapsible-tab';
8
8
  const DEFAULT_DEBOUNCE_INTERVAL = 100;
9
9
  const MEASURE_TOLERANCE = 0.5;
10
- export const CollapsibleTabs = ({ children, selected, onSelect, moreClassName, moreActiveClassName, morePopupClassName, morePopupBeforeEnd, morePopupItemClassName, initialVisibleItems, }) => {
10
+ export const CollapsibleTabs = ({ children, selected, onSelect, onLastVisibleIndexChange, moreClassName, moreActiveClassName, morePopupClassName, morePopupBeforeEnd, morePopupItemClassName, initialVisibleItems, }) => {
11
11
  const [sizes, setSizes] = useState({ tabs: [], more: undefined });
12
12
  const [lastVisibleIndex, setLastVisibleIndex] = useState(null);
13
13
  const elements = { sizes, lastVisibleIndex };
@@ -74,10 +74,12 @@ export const CollapsibleTabs = ({ children, selected, onSelect, moreClassName, m
74
74
  }
75
75
  }
76
76
  }
77
- if (elements.lastVisibleIndex !== tabsToRender.length - 1) {
78
- setLastVisibleIndex(tabsToRender.length - 1);
77
+ const newLastVisibleIndex = tabsToRender.length - 1;
78
+ if (elements.lastVisibleIndex !== newLastVisibleIndex) {
79
+ setLastVisibleIndex(newLastVisibleIndex);
80
+ onLastVisibleIndexChange?.(newLastVisibleIndex);
79
81
  }
80
- }, [children, elements.lastVisibleIndex, elements.sizes, selectedIndex]);
82
+ }, [children, elements.lastVisibleIndex, elements.sizes, onLastVisibleIndexChange, selectedIndex]);
81
83
  // Prepare list of visible and hidden elements
82
84
  useEffect(() => {
83
85
  const timeout = setTimeout(() => {
@@ -8,6 +8,7 @@ export type Children = readonly (Children | null | boolean)[] | ReactElement<Tab
8
8
  export interface TabsProps extends Omit<CollapsibleTabsProps, 'onSelect' | 'children'> {
9
9
  children: Children;
10
10
  onSelect?: ((key: string) => void) | null | undefined;
11
+ onLastVisibleIndexChange?: ((index: number) => void) | null | undefined;
11
12
  className?: string | null | undefined;
12
13
  tabContainerClassName?: string | null | undefined;
13
14
  autoCollapse?: boolean | null | undefined;
@@ -23,7 +23,7 @@ export default class UserCard extends PureComponent {
23
23
  const translations = this.props.translations;
24
24
  const classes = classNames(className, {});
25
25
  const userActiveStatusClasses = classNames(styles.userActiveStatus, user.online ? styles.online : '');
26
- return (_jsx("div", { className: classes, ...restProps, children: _jsxs("div", { className: styles.userInformationContainer, children: [_jsxs("div", { className: styles.userAvatar, children: [_jsx(Avatar, { size: AvatarSize.Size56, url: user.avatarUrl, round: true }), !!avatarInfo && avatarInfo] }), _jsxs("div", { className: styles.userInformation, children: [_jsxs("div", { className: styles.userInformationGeneral, children: [_jsxs("div", { className: styles.userNameLine, children: [user.href && (_jsx(Link, { href: user.href, className: styles.userName, children: user.name })), !user.href && _jsx("span", { className: styles.userName, children: user.name }), typeof user.online === 'boolean' && (_jsx("span", { className: userActiveStatusClasses, title: user.online
26
+ return (_jsx("div", { className: classes, ...restProps, children: _jsxs("div", { className: styles.userInformationContainer, children: [_jsxs("div", { className: styles.userAvatar, children: [_jsx(Avatar, { size: AvatarSize.Size56, url: user.avatarUrl, username: user.name, round: true }), !!avatarInfo && avatarInfo] }), _jsxs("div", { className: styles.userInformation, children: [_jsxs("div", { className: styles.userInformationGeneral, children: [_jsxs("div", { className: styles.userNameLine, children: [user.href && (_jsx(Link, { href: user.href, className: styles.userName, children: user.name })), !user.href && _jsx("span", { className: styles.userName, children: user.name }), typeof user.online === 'boolean' && (_jsx("span", { className: userActiveStatusClasses, title: user.online
27
27
  ? (translations?.online ?? translate('online'))
28
28
  : (translations?.offline ?? translate('offline')) })), !!info && _jsx("span", { className: styles.userNameInfo, children: info }), user.banned && (_jsx(Tooltip, { title: user.banReason, children: _jsx(Tag, { className: styles.banLabel, children: translations?.banned ?? translate('banned') }) }))] }), _jsx("div", { className: styles.userLogin, children: user.login }), user.email && (_jsxs("span", { className: styles.userEmailWrapper, children: [_jsx(Link, { href: `mailto:${user.email}`, title: `mailto:${user.email}`, target: "_blank", className: styles.userEmail, children: user.email }), user.unverifiedEmail && (_jsx("span", { className: styles.unverifiedLabel, children: translations?.unverified ?? translate('unverified') })), _jsx(Icon, { title: translations?.copyToClipboard ?? translate('copyToClipboard'), className: styles.userCopyIcon, onClick: this.copyEmail, glyph: copyIcon, size: IconSize.Size14, suppressSizeWarning: true })] }))] }), children] })] }) }));
29
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.21",
3
+ "version": "7.0.23",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -116,8 +116,8 @@
116
116
  "@storybook/test-runner": "^0.21.0",
117
117
  "@storybook/theming": "8.4.7",
118
118
  "@testing-library/dom": "^10.4.0",
119
- "@testing-library/react": "^16.1.0",
120
- "@testing-library/user-event": "^14.5.2",
119
+ "@testing-library/react": "^16.2.0",
120
+ "@testing-library/user-event": "^14.6.1",
121
121
  "@types/chai": "^5.0.1",
122
122
  "@types/chai-as-promised": "^8.0.1",
123
123
  "@types/chai-dom": "0.0.10",
@@ -136,13 +136,13 @@
136
136
  "acorn": "^8.14.0",
137
137
  "axe-playwright": "^2.0.3",
138
138
  "babel-plugin-require-context-hook": "^1.0.0",
139
- "caniuse-lite": "^1.0.30001692",
139
+ "caniuse-lite": "^1.0.30001695",
140
140
  "chai": "^5.1.2",
141
141
  "chai-as-promised": "^8.0.1",
142
142
  "chai-dom": "^1.10.0",
143
143
  "chai-enzyme": "1.0.0-beta.1",
144
144
  "cheerio": "^1.0.0-rc.12",
145
- "chromatic": "^11.24.0",
145
+ "chromatic": "^11.25.1",
146
146
  "core-js": "^3.40.0",
147
147
  "cpy-cli": "^5.0.0",
148
148
  "dotenv-cli": "^8.0.0",
@@ -153,7 +153,7 @@
153
153
  "eslint-import-resolver-webpack": "^0.13.10",
154
154
  "eslint-plugin-import": "^2.31.0",
155
155
  "eslint-plugin-jsx-a11y": "^6.10.2",
156
- "eslint-plugin-prettier": "^5.2.2",
156
+ "eslint-plugin-prettier": "^5.2.3",
157
157
  "eslint-plugin-react": "^7.37.4",
158
158
  "eslint-plugin-react-hooks": "^5.1.0",
159
159
  "eslint-plugin-storybook": "^0.11.2",
@@ -167,7 +167,7 @@
167
167
  "jest": "~29.7.0",
168
168
  "jest-environment-jsdom": "^29.7.0",
169
169
  "jest-teamcity": "^1.12.0",
170
- "lint-staged": "^15.3.0",
170
+ "lint-staged": "^15.4.1",
171
171
  "markdown-it": "^14.1.0",
172
172
  "merge-options": "^3.0.4",
173
173
  "pinst": "^3.0.0",
@@ -178,7 +178,7 @@
178
178
  "react-test-renderer": "^19.0.0",
179
179
  "regenerator-runtime": "^0.14.1",
180
180
  "rimraf": "^6.0.1",
181
- "rollup": "^4.30.1",
181
+ "rollup": "^4.31.0",
182
182
  "rollup-plugin-clear": "^2.0.7",
183
183
  "sinon": "^19.0.2",
184
184
  "sinon-chai": "^4.0.0",
@@ -189,8 +189,8 @@
189
189
  "teamcity-service-messages": "^0.1.14",
190
190
  "terser-webpack-plugin": "^5.3.11",
191
191
  "typescript": "~5.7.3",
192
- "typescript-eslint": "^8.20.0",
193
- "vitest": "^2.1.8",
192
+ "typescript-eslint": "^8.21.0",
193
+ "vitest": "^3.0.3",
194
194
  "vitest-teamcity-reporter": "^0.3.1",
195
195
  "wallaby-webpack": "^3.9.16",
196
196
  "webpack": "^5.97.1",
@@ -1 +0,0 @@
1
- export declare const avatarDataUri: string;
@@ -1,24 +0,0 @@
1
- // Taken from https://hub.jetbrains.com/api/rest/avatar/default?username=Jet%20Brains&dpr=2&size=56
2
- export const avatarDataUri = `data:image/svg+xml,${encodeURIComponent(`
3
- <svg viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
4
- <defs>
5
- <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
6
- <stop stop-color="#D50F6B" offset="0"/>
7
- <stop stop-color="#E73AE8" offset="1"/>
8
- </linearGradient>
9
- </defs>
10
- <g>
11
- <rect fill="url(#gradient)"
12
- x="0" y="0" width="40" height="40"
13
- rx="3" ry="3"/>
14
- <text x="5" y="19"
15
- font-family="Arial, Helvetica, sans-serif"
16
- font-size="15px"
17
- letter-spacing="1"
18
- fill="#FFFFFF">
19
- <tspan>JB</tspan>
20
- <tspan x="6" y="28">_</tspan>
21
- </text>
22
- </g>
23
- </svg>
24
- `)}`;