@jetbrains/ring-ui 7.0.22 → 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> {
@@ -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
  }
@@ -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.22",
3
+ "version": "7.0.23",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -117,7 +117,7 @@
117
117
  "@storybook/theming": "8.4.7",
118
118
  "@testing-library/dom": "^10.4.0",
119
119
  "@testing-library/react": "^16.2.0",
120
- "@testing-library/user-event": "^14.6.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",
@@ -142,7 +142,7 @@
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.25.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",
@@ -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": "^3.0.2",
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
- `)}`;