@jetbrains/ring-ui 7.0.31 → 7.0.33

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.
@@ -4,5 +4,6 @@ interface InfoAvatarProps {
4
4
  size: Size;
5
5
  children?: ReactNode;
6
6
  }
7
+ export declare const fontSizes: Record<Size, number>;
7
8
  export default function AvatarInfo({ size, children }: InfoAvatarProps): import("react/jsx-runtime").JSX.Element;
8
9
  export {};
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import classNames from 'classnames';
3
3
  import styles from './avatar.css';
4
4
  import { Size } from './avatar-size';
5
- const fontSizes = {
5
+ export const fontSizes = {
6
6
  [Size.Size16]: 9,
7
7
  [Size.Size18]: 9,
8
8
  [Size.Size20]: 9,
@@ -5,11 +5,13 @@
5
5
  object-fit: cover;
6
6
  object-position: center;
7
7
 
8
- /* This is a "graceful degradation" fallback, while the real value is controlled by JS */
9
-
10
8
  border-radius: var(--ring-border-radius);
11
9
  }
12
10
 
11
+ .round {
12
+ border-radius: 50%;
13
+ }
14
+
13
15
  .avatarShadow {
14
16
  /* See https://stackoverflow.com/questions/21414925/why-doesnt-inset-box-shadow-work-over-images */
15
17
 
@@ -26,7 +28,7 @@
26
28
  color: var(--ring-secondary-color);
27
29
  border-radius: inherit;
28
30
 
29
- background-color: var(--ring-disabled-background-color);
31
+ background-color: var(--ring-tag-background-color);
30
32
  }
31
33
 
32
34
  .subavatar {
@@ -1,24 +1,45 @@
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
- });
1
+ // url=https://www.figma.com/design/HY6d4uE1xxaQXCMG9fe6Y2/RingUI?node-id=5990-522
2
+ const figma = require('figma');
3
+
4
+ const instance = figma.selectedInstance;
5
+
6
+ const round = instance.getBoolean('Round');
7
+ const size = parseInt(instance.getString('Size'), 10);
8
+ const content = instance.getString('Content');
9
+
10
+ const props = [];
11
+ const DEFAULT_SIZE = 20;
12
+ const isDefaultSize = size === DEFAULT_SIZE;
13
+ const imports = [`import Avatar${isDefaultSize ? '' : ', {Size}'} from '@jetbrains/ring-ui/components/avatar/avatar'`];
14
+ if (!isDefaultSize) {
15
+ props.push(`size={Size.Size${size}}`);
16
+ }
17
+ switch (content) {
18
+ case 'color/image':
19
+ props.push('url="avatar.png"');
20
+ break;
21
+ case 'name':
22
+ props.push('username="Samuel Morse"');
23
+ break;
24
+ case 'label':
25
+ props.push('info="5"');
26
+ break;
27
+ case 'icon':
28
+ imports.push(
29
+ "import Icon from '@jetbrains/ring-ui/components/icon/icon'",
30
+ "import warningIcon from '@jetbrains/icons/warning.svg'",
31
+ );
32
+ props.push('info={<Icon glyph={warningIcon} />}');
33
+ break;
34
+ default:
35
+ }
36
+ if (round) {
37
+ props.push('round');
38
+ }
39
+
40
+ export default {
41
+ id: 'avatar',
42
+ example: figma.code`${imports.join('\n')}
43
+
44
+ <Avatar ${props.map(prop => `${prop} `).join('')}/>`,
45
+ };
@@ -34,9 +34,7 @@ export default class Avatar extends PureComponent {
34
34
  }
35
35
  const sizeString = `${size}px`;
36
36
  const subavatarSizeString = `${subavatarSize}px`;
37
- const borderRadius = size <= Size.Size18 ? 'var(--ring-border-radius-small)' : 'var(--ring-border-radius)';
38
37
  const styleObj = {
39
- borderRadius: round ? '50%' : borderRadius,
40
38
  height: sizeString,
41
39
  width: sizeString,
42
40
  ...style,
@@ -47,8 +45,11 @@ export default class Avatar extends PureComponent {
47
45
  width: subavatarSizeString,
48
46
  ...style,
49
47
  };
48
+ const classes = classNames(styles.avatar, this.props.className, {
49
+ [styles.round]: round,
50
+ });
50
51
  if (!url || this.state.errorUrl === url) {
51
- return (_jsxs("span", { ...restProps, "data-test": "avatar", className: classNames(styles.avatar, this.props.className, {
52
+ return (_jsxs("span", { ...restProps, "data-test": "avatar", className: classNames(classes, {
52
53
  [styles.empty]: username == null && info == null,
53
54
  }), style: styleObj, children: [username != null && _jsx(FallbackAvatar, { size: size, round: round, username: username }), info != null && _jsx(AvatarInfo, { size: size, children: info })] }));
54
55
  }
@@ -71,10 +72,10 @@ export default class Avatar extends PureComponent {
71
72
  subavatarSizeString,
72
73
  };
73
74
  subavatarSrc = skipParams ? subavatar : encodeURL(urlStart, queryParams);
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" })] }));
75
+ return (_jsxs("div", { children: [_jsx("img", { ...restProps, onError: this.handleError, onLoad: this.handleSuccess, className: classNames(classes, styles.avatarShadow), 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" })] }));
75
76
  }
76
77
  else {
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" }));
78
+ return (_jsx("img", { ...restProps, "data-test": "avatar", onError: this.handleError, onLoad: this.handleSuccess, className: classNames(classes, styles.avatarShadow), style: styleObj, src: src, alt: "User avatar" }));
78
79
  }
79
80
  }
80
81
  }
@@ -1,37 +1,31 @@
1
1
  .size20 {
2
2
  --ring-avatar-stack-offset: 12px;
3
3
  --ring-avatar-stack-mask-image: url('avatar-stack.mask-20.svg');
4
- --ring-avatar-stack-extra-font-size: 9px
5
4
  }
6
5
 
7
6
  .size24 {
8
7
  --ring-avatar-stack-offset: 16px;
9
8
  --ring-avatar-stack-mask-image: url('avatar-stack.mask-24.svg');
10
- --ring-avatar-stack-extra-font-size: 11px;
11
9
  }
12
10
 
13
11
  .size28 {
14
12
  --ring-avatar-stack-offset: 18px;
15
13
  --ring-avatar-stack-mask-image: url('avatar-stack.mask-28.svg');
16
- --ring-avatar-stack-extra-font-size: var(--ring-font-size-smaller);
17
14
  }
18
15
 
19
16
  .size32 {
20
17
  --ring-avatar-stack-offset: 20px;
21
18
  --ring-avatar-stack-mask-image: url('avatar-stack.mask-32.svg');
22
- --ring-avatar-stack-extra-font-size: var(--ring-font-size);
23
19
  }
24
20
 
25
21
  .size40 {
26
22
  --ring-avatar-stack-offset: 26px;
27
23
  --ring-avatar-stack-mask-image: url('avatar-stack.mask-40.svg');
28
- --ring-avatar-stack-extra-font-size: var(--ring-font-size-larger);
29
24
  }
30
25
 
31
26
  .size56 {
32
27
  --ring-avatar-stack-offset: 42px;
33
28
  --ring-avatar-stack-mask-image: url('avatar-stack.mask-56.svg');
34
- --ring-avatar-stack-extra-font-size: 22px;
35
29
  }
36
30
 
37
31
  .avatarStack {
@@ -43,29 +37,23 @@
43
37
  .item {
44
38
  position: relative;
45
39
 
46
- margin-left: calc(var(--ring-avatar-stack-offset) * -1);
40
+ margin: -1px;
41
+ margin-left: calc(var(--ring-avatar-stack-offset) * -1 - 1px);
42
+ padding: 1px;
47
43
 
48
44
  transition: transform var(--ring-fast-ease);
49
45
  mask-image: var(--ring-avatar-stack-mask-image);
50
- mask-position: calc(var(--ring-avatar-stack-offset) - 4px) -1px;
46
+ mask-position: calc(var(--ring-avatar-stack-offset) - 3px) 0;
51
47
  mask-repeat: no-repeat;
52
48
 
49
+ border-radius: calc(var(--ring-border-radius) + 1px);
53
50
 
54
- /* make the gaps hoverable */
55
- &::before {
56
- position: absolute;
57
- top: 0;
58
- right: -1px;
59
- bottom: 0;
60
- left: -1px;
61
-
62
- content: '';
63
- }
51
+ background-color: var(--ring-content-background-color);
64
52
  }
65
53
 
66
54
  .item:nth-child(1) {
67
55
  --ring-avatar-stack-index: 0;
68
- --ring-avatar-stack-offset: 0;
56
+ --ring-avatar-stack-offset: 0px;
69
57
 
70
58
  mask: none;
71
59
  }
@@ -80,38 +68,27 @@
80
68
 
81
69
  .extra {
82
70
  composes: item;
83
-
84
- color: var(--ring-secondary-color);
85
-
86
- border-radius: var(--ring-border-radius);
87
-
88
- background-color: var(--ring-disabled-background-color);
89
-
90
- font-size: var(--ring-avatar-stack-extra-font-size);
91
71
  }
92
72
 
93
- .extraText {
73
+ .extraButton {
94
74
  composes: resetButton from "../global/global.css";
95
75
 
96
- display: block;
97
-
98
- width: 100%;
99
- height: 100%;
100
-
101
76
  cursor: pointer;
102
- transition: opacity var(--ring-fast-ease);
103
- text-align: center;
104
-
105
- opacity: 0;
106
77
 
107
78
  border-radius: var(--ring-border-radius);
108
79
  }
109
80
 
110
- .extraText:focus-visible {
81
+ .extraButton:focus-visible {
111
82
  outline: none;
112
83
  box-shadow: 0 0 0 2px var(--ring-border-hover-color);
113
84
  }
114
85
 
86
+ .extraText {
87
+ transition: opacity var(--ring-fast-ease);
88
+
89
+ opacity: 0;
90
+ }
91
+
115
92
  .hovered .extraText,
116
93
  .avatarStack:hover .extraText,
117
94
  .avatarStack:focus-within .extraText {
@@ -2,11 +2,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Children, useState } from 'react';
3
3
  import classNames from 'classnames';
4
4
  import DropdownMenu from '../dropdown-menu/dropdown-menu';
5
- import { Size } from '../avatar/avatar';
5
+ import Avatar, { Size } from '../avatar/avatar';
6
+ import { fontSizes } from '../avatar/avatar-info';
6
7
  import styles from './avatar-stack.css';
7
8
  export default function AvatarStack({ children, className, size = Size.Size20, extraItems, dropdownMenuProps, ...restProps }) {
8
9
  const [dropdownOpen, setDropdownOpen] = useState(false);
9
10
  return (_jsxs("div", { className: classNames(styles.avatarStack, className, styles[`size${size}`], {
10
11
  [styles.hovered]: dropdownOpen,
11
- }), ...restProps, children: [Children.map(children, (child, index) => (_jsx("div", { className: styles.item, style: { '--ring-avatar-stack-index': index }, children: child }))), extraItems?.length ? (_jsx(DropdownMenu, { hoverMode: true, hoverShowTimeOut: 10, onShow: () => setDropdownOpen(true), onHide: () => setDropdownOpen(false), className: styles.extra, style: { width: size, height: size, '--ring-avatar-stack-index': Children.count(children) }, anchor: _jsx("button", { type: "button", className: styles.extraText, children: `+${extraItems.length}` }), data: extraItems, menuProps: { offset: 4, ...dropdownMenuProps?.menuProps }, ...dropdownMenuProps })) : null] }));
12
+ }), ...restProps, style: { height: size, ...restProps.style }, children: [Children.map(children, (child, index) => (_jsx("div", { className: styles.item, style: { '--ring-avatar-stack-index': index }, children: child }))), extraItems?.length ? (_jsx(DropdownMenu, { hoverMode: true, hoverShowTimeOut: 10, onShow: () => setDropdownOpen(true), onHide: () => setDropdownOpen(false), className: styles.extra, style: {
13
+ width: size,
14
+ height: size,
15
+ '--ring-avatar-stack-index': Children.count(children),
16
+ fontSize: fontSizes[size],
17
+ }, anchor: _jsx("button", { type: "button", className: styles.extraButton, children: _jsx(Avatar, { size: size, info: _jsx("span", { className: styles.extraText, children: `+${extraItems.length}` }) }) }), data: extraItems, menuProps: { offset: 4, ...dropdownMenuProps?.menuProps }, ...dropdownMenuProps })) : null] }));
12
18
  }
@@ -1,3 +1,3 @@
1
- <svg width="56px" height="56px" xmlns="http://www.w3.org/2000/svg">
2
- <path fill="black" d="M 0 0 a 5 5 0 0 1 5 5 V 51 a 5 5 0 0 1 -5 5 H 56 V 0"/>
1
+ <svg width="58px" height="58px" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill="black" d="M 0 0 a 5 5 0 0 1 5 5 V 53 a 5 5 0 0 1 -5 5 H 58 V 0"/>
3
3
  </svg>
@@ -1,70 +1,95 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import figma from '@figma/code-connect';
3
- import addIcon from '@jetbrains/icons/add';
4
- import add12pxIcon from '@jetbrains/icons/add-12px';
5
- import downloadIcon from '@jetbrains/icons/download';
6
- import download12pxIcon from '@jetbrains/icons/download-12px';
7
- import { ControlsHeight } from '../global/controls-height';
8
- import Button from './button';
9
- figma.connect(Button, 'https://www.figma.com/design/HY6d4uE1xxaQXCMG9fe6Y2/RingUI?node-id=9954%3A528', {
10
- props: {
11
- height: figma.enum('Size', {
12
- S: ControlsHeight.S,
13
- M: ControlsHeight.M,
14
- L: ControlsHeight.L,
15
- }),
16
- children: figma.enum('Type', {
17
- Label: 'Button',
18
- 'L-Icon-Label': 'Button',
19
- 'R-Icon-Label': 'Button',
20
- }),
21
- icon: figma.enum('Type', {
22
- 'L-Icon-Label': figma.enum('Size', {
23
- S: figma.enum('Variant', {
24
- Main: download12pxIcon,
25
- Green: download12pxIcon,
26
- 'Red solid': download12pxIcon,
27
- Gray: download12pxIcon,
28
- Outlined: download12pxIcon,
29
- 'Red outlined': download12pxIcon,
30
- Ghost: download12pxIcon,
31
- Text: downloadIcon,
32
- }),
33
- M: downloadIcon,
34
- L: downloadIcon,
35
- }),
36
- Icon: figma.enum('Size', {
37
- S: figma.enum('Variant', {
38
- Main: add12pxIcon,
39
- Green: add12pxIcon,
40
- 'Red solid': add12pxIcon,
41
- Gray: add12pxIcon,
42
- Outlined: add12pxIcon,
43
- 'Red outlined': add12pxIcon,
44
- Ghost: add12pxIcon,
45
- Text: addIcon,
46
- }),
47
- M: addIcon,
48
- L: addIcon,
49
- }),
50
- }),
51
- dropdown: figma.enum('Type', { 'R-Icon-Label': true }),
52
- disabled: figma.enum('State', { Disabled: true }),
53
- primary: figma.enum('Variant', { Main: true }),
54
- success: figma.enum('Variant', { Green: true }),
55
- error: figma.enum('Variant', { 'Red solid': true }),
56
- secondary: figma.enum('Variant', { Gray: true }),
57
- danger: figma.enum('Variant', { 'Red outlined': true }),
58
- ghost: figma.enum('Variant', { Ghost: true }),
59
- inline: figma.enum('Variant', { Text: true }),
60
- },
61
- example: ({ children, ...restProps }) => _jsx(Button, { ...restProps, children: children }),
62
- imports: [
63
- 'import Button from "@jetbrains/ring-ui/components/button/button"',
64
- 'import {ControlsHeight} from "@jetbrains/ring-ui/components/global/controls-height"',
65
- 'import addIcon from "@jetbrains/icons/add"',
66
- 'import add12pxIcon from "@jetbrains/icons/add-12px"',
67
- 'import downloadIcon from "@jetbrains/icons/download"',
68
- 'import download12pxIcon from "@jetbrains/icons/download-12px"',
69
- ],
70
- });
1
+ const figma = require('figma');
2
+
3
+ const instance = figma.selectedInstance;
4
+
5
+ const size = instance.getString('Size');
6
+ const type = instance.getString('Type');
7
+ const state = instance.getString('State');
8
+ const variant = instance.getString('Variant');
9
+
10
+ const imports = ["import Button from '@jetbrains/ring-ui/components/button/button'"];
11
+ const props = [];
12
+ const DEFAULT_SIZE = 'M';
13
+ const isDefaultSize = size === DEFAULT_SIZE;
14
+
15
+ if (!isDefaultSize) {
16
+ imports.push("import {ControlsHeight} from '@jetbrains/ring-ui/components/global/controls-height'");
17
+ props.push(`height={ControlsHeight.${size}}`);
18
+ }
19
+
20
+ switch (variant) {
21
+ case 'Red outlined':
22
+ props.push('danger');
23
+ break;
24
+ case 'Main':
25
+ props.push('primary');
26
+ break;
27
+ case 'Green':
28
+ props.push('success');
29
+ break;
30
+ case 'Red solid':
31
+ props.push('error');
32
+ break;
33
+ case 'Gray':
34
+ props.push('secondary');
35
+ break;
36
+ case 'Ghost':
37
+ props.push('ghost');
38
+ break;
39
+ case 'Text':
40
+ props.push('inline');
41
+ break;
42
+ default:
43
+ }
44
+
45
+ let children = 'Button';
46
+ const use12pxIcon = size === 'S' && variant !== 'Text';
47
+ let useButtonGroup = false;
48
+ switch (type) {
49
+ case 'R-Icon-Label':
50
+ props.push('dropdown');
51
+ break;
52
+ case 'L-Icon-Label':
53
+ imports.push(`import downloadIcon from '@jetbrains/icons/download${use12pxIcon ? '-12px' : ''}'`);
54
+ props.push('icon={downloadIcon}');
55
+ break;
56
+ case 'Icon':
57
+ children = null;
58
+ imports.push(`import addIcon from '@jetbrains/icons/add${use12pxIcon ? '-12px' : ''}'`);
59
+ props.push('icon={addIcon}');
60
+ break;
61
+ case 'Split':
62
+ imports.push("import ButtonGroup from '@jetbrains/ring-ui/components/button-group/button-group'");
63
+ imports.push(`import chevronDownIcon from '@jetbrains/icons/chevron-down${use12pxIcon ? '-12px' : ''}'`);
64
+ useButtonGroup = true;
65
+ break;
66
+ default:
67
+ }
68
+
69
+ switch (state) {
70
+ case 'Disabled':
71
+ props.push('disabled');
72
+ break;
73
+ default:
74
+ }
75
+
76
+ const joinedProps = props.map(prop => `${prop} `).join('');
77
+ // prettier-ignore
78
+ const button = children ?
79
+ `<Button ${joinedProps}>
80
+ ${children}
81
+ </Button>` : `<Button ${joinedProps}/>`;
82
+
83
+ export default {
84
+ id: 'button',
85
+ example: figma.code`${imports.join('\n')}
86
+
87
+ ${
88
+ // prettier-ignore
89
+ useButtonGroup ?
90
+ `<ButtonGroup>
91
+ ${button}
92
+ <Button ${joinedProps} icon={chevronDownIcon} />
93
+ </ButtonGroup>` : button
94
+ }`,
95
+ };
@@ -29,6 +29,7 @@ export interface InputBaseProps {
29
29
  onClear?: ((e: React.MouseEvent<HTMLButtonElement>) => void) | null | undefined;
30
30
  icon?: string | ComponentType | null | undefined;
31
31
  height?: ControlsHeight | undefined;
32
+ beforeInput?: ReactNode;
32
33
  afterInput?: ReactNode;
33
34
  translations?: InputTranslations | null | undefined;
34
35
  }
@@ -93,7 +93,7 @@ export class Input extends PureComponent {
93
93
  // Modifiers
94
94
  size, multiline, borderless,
95
95
  // Props
96
- label, labelType, error, help, className, inputClassName, children, value, onClear, disabled, inputRef, onChange, enableShortcuts, id, placeholder, icon, translations, height = this.context, afterInput, ...restProps } = this.props;
96
+ label, labelType, error, help, className, inputClassName, children, value, onClear, disabled, inputRef, onChange, enableShortcuts, id, placeholder, icon, translations, height = this.context, beforeInput, afterInput, ...restProps } = this.props;
97
97
  const { empty } = this.state;
98
98
  const clearable = !!onClear;
99
99
  const classes = classNames(className, styles.outerContainer, [styles[`size${size}`]], [styles[`height${height}`]], {
@@ -116,7 +116,7 @@ export class Input extends PureComponent {
116
116
  'aria-label': typeof label === 'string' && label ? label : placeholder,
117
117
  'data-enabled-shortcuts': Array.isArray(enableShortcuts) ? enableShortcuts.join(',') : null,
118
118
  };
119
- return (_jsx(I18nContext.Consumer, { children: ({ translate }) => (_jsxs("div", { className: classes, "data-test": "ring-input", children: [label && (_jsx(ControlLabel, { htmlFor: this.getId(), disabled: disabled, type: labelType, children: label })), _jsxs("div", { className: styles.container, children: [icon && _jsx(Icon, { glyph: icon, className: styles.icon }), multiline ? (_jsx("textarea", { onChange: this.handleTextareaChange, rows: 1, ...commonProps, ...restProps })) : (_jsx("input", { onChange: this.handleInputChange, ...commonProps, ...restProps })), clearable && !disabled && (_jsx(Button, { title: translations?.clear ?? translate('clear'), "data-test": "ring-input-clear", className: styles.clear, icon: closeIcon, onClick: this.clear })), afterInput] }), error ? (_jsx("div", { className: styles.errorText, children: error })) : (help && _jsx(ControlHelp, { className: styles.helpText, children: help }))] })) }));
119
+ return (_jsx(I18nContext.Consumer, { children: ({ translate }) => (_jsxs("div", { className: classes, "data-test": "ring-input", children: [label && (_jsx(ControlLabel, { htmlFor: this.getId(), disabled: disabled, type: labelType, children: label })), _jsxs("div", { className: styles.container, children: [icon && _jsx(Icon, { glyph: icon, className: styles.icon }), beforeInput, multiline ? (_jsx("textarea", { onChange: this.handleTextareaChange, rows: 1, ...commonProps, ...restProps })) : (_jsx("input", { onChange: this.handleInputChange, ...commonProps, ...restProps })), clearable && !disabled && (_jsx(Button, { title: translations?.clear ?? translate('clear'), "data-test": "ring-input-clear", className: styles.clear, icon: closeIcon, onClick: this.clear })), afterInput] }), error ? (_jsx("div", { className: styles.errorText, children: error })) : (help && _jsx(ControlHelp, { className: styles.helpText, children: help }))] })) }));
120
120
  }
121
121
  }
122
122
  export default Input;
@@ -50,6 +50,7 @@ export interface TableProps<T extends SelectionItem> extends FocusSensorAddProps
50
50
  stickyHeaderOffset?: string | undefined;
51
51
  renderEmpty?: (() => ReactNode) | null | undefined;
52
52
  RowComponent: typeof Row;
53
+ customLoader?: ((loaderClassName?: string) => ReactNode) | null | undefined;
53
54
  }
54
55
  /**
55
56
  * Interactive table with selection and keyboard navigation support.
@@ -109,7 +109,7 @@ export class Table extends PureComponent {
109
109
  window.scrollTo(scrollX, scrollY);
110
110
  };
111
111
  render() {
112
- const { data, selection, columns, caption, getItemKey, selectable, focused, isItemSelectable, getItemLevel, getItemClassName, getMetaColumnClassName, getItemDataTest, draggable, alwaysShowDragHandle, dragHandleTitle, loading, onSort, sortKey, sortOrder, loaderClassName, stickyHeader, stickyHeaderOffset, isItemCollapsible, isParentCollapsible, isItemCollapsed, onItemCollapse, onItemExpand, isDisabledSelectionVisible, getCheckboxTooltip, onItemDoubleClick, onItemClick, renderEmpty, RowComponent, } = this.props;
112
+ const { data, selection, columns, caption, getItemKey, selectable, focused, isItemSelectable, getItemLevel, getItemClassName, getMetaColumnClassName, getItemDataTest, draggable, alwaysShowDragHandle, dragHandleTitle, loading, onSort, sortKey, sortOrder, loaderClassName, stickyHeader, stickyHeaderOffset, isItemCollapsible, isParentCollapsible, isItemCollapsed, onItemCollapse, onItemExpand, isDisabledSelectionVisible, getCheckboxTooltip, onItemDoubleClick, onItemClick, renderEmpty, RowComponent, customLoader, } = this.props;
113
113
  // NOTE: Do not construct new object per render because it causes all rows rerendering
114
114
  const columnsArray = typeof columns === 'function' ? columns(null) : columns;
115
115
  const headerProps = {
@@ -152,7 +152,7 @@ export class Table extends PureComponent {
152
152
  const row = (_createElement(RowComponent, { innerRef: ref, level: getItemLevel(value), item: value, showFocus: selection.isFocused(value), autofocus: selection.isFocused(value), focused: focused && selection.isFocused(value), selectable: selectable && isItemSelectable(value), selected: selectable && selection.isSelected(value), onFocus: this.onRowFocus, onSelect: this.onRowSelect, onDoubleClick: onItemDoubleClick, onClick: onItemClick, collapsible: isItemCollapsible(value), parentCollapsible: isParentCollapsible(value), collapsed: isItemCollapsed(value), onCollapse: onItemCollapse, onExpand: onItemExpand, showDisabledSelection: isDisabledSelectionVisible(value), checkboxTooltip: getCheckboxTooltip(value), className: classNames(getItemClassName(value), { [style.draggingRow]: isDragged }), metaColumnClassName: getMetaColumnClassName(value), draggable: draggable, alwaysShowDragHandle: alwaysShowDragHandle, dragHandleTitle: dragHandleTitle, columns: columns, "data-test": getItemDataTest(value), ...restProps, key: restProps.key ?? getItemKey(value) }));
153
153
  return isDragged ? (_jsx("table", { style: { ...props.style }, className: style.draggingTable, children: _jsx("tbody", { children: row }) })) : (row);
154
154
  };
155
- return (_jsxs("div", { className: wrapperClasses, "data-test": "ring-table-wrapper", ref: this.props.innerRef, children: [focused && _jsx(Shortcuts, { map: this.props.shortcutsMap, scope: this.state.shortcutsScope }), _jsx("div", { role: "presentation", onMouseDown: this.onMouseDown, children: draggable ? (_jsx(List, { values: data, renderList: renderList, renderItem: renderItem, onChange: this.onSortEnd })) : (renderList({ children: data.map((value, index) => renderItem({ value, index })) })) }), loading && (_jsx("div", { className: style.loadingOverlay, children: _jsx(Loader, { className: loaderClassName }) }))] }));
155
+ return (_jsxs("div", { className: wrapperClasses, "data-test": "ring-table-wrapper", ref: this.props.innerRef, children: [focused && _jsx(Shortcuts, { map: this.props.shortcutsMap, scope: this.state.shortcutsScope }), _jsx("div", { role: "presentation", onMouseDown: this.onMouseDown, children: draggable ? (_jsx(List, { values: data, renderList: renderList, renderItem: renderItem, onChange: this.onSortEnd })) : (renderList({ children: data.map((value, index) => renderItem({ value, index })) })) }), loading && (_jsx("div", { className: style.loadingOverlay, children: customLoader ? customLoader(loaderClassName) : _jsx(Loader, { className: loaderClassName }) }))] }));
156
156
  }
157
157
  }
158
158
  const getContainer = () => disableHoverHOC(selectionShortcutsHOC(focusSensorHOC(Table)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.31",
3
+ "version": "7.0.33",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -90,7 +90,7 @@
90
90
  "@babel/plugin-syntax-import-assertions": "^7.26.0",
91
91
  "@csstools/css-parser-algorithms": "^3.0.4",
92
92
  "@csstools/stylelint-no-at-nest-rule": "^4.0.0",
93
- "@eslint/compat": "^1.2.6",
93
+ "@eslint/compat": "^1.2.7",
94
94
  "@eslint/eslintrc": "^3.2.0",
95
95
  "@eslint/js": "^9.20.0",
96
96
  "@figma/code-connect": "^1.3.1",
@@ -103,18 +103,18 @@
103
103
  "@rollup/plugin-json": "^6.1.0",
104
104
  "@rollup/plugin-node-resolve": "^16.0.0",
105
105
  "@rollup/plugin-replace": "^6.0.2",
106
- "@storybook/addon-a11y": "8.5.6",
107
- "@storybook/addon-docs": "8.5.6",
108
- "@storybook/addon-essentials": "8.5.6",
109
- "@storybook/addon-themes": "^8.5.6",
110
- "@storybook/components": "8.5.6",
106
+ "@storybook/addon-a11y": "8.5.8",
107
+ "@storybook/addon-docs": "8.5.8",
108
+ "@storybook/addon-essentials": "8.5.8",
109
+ "@storybook/addon-themes": "^8.5.8",
110
+ "@storybook/components": "8.5.8",
111
111
  "@storybook/csf": "^0.1.13",
112
- "@storybook/manager-api": "8.5.6",
113
- "@storybook/preview-api": "8.5.6",
114
- "@storybook/react": "8.5.6",
115
- "@storybook/react-webpack5": "8.5.6",
116
- "@storybook/test-runner": "^0.21.1",
117
- "@storybook/theming": "8.5.6",
112
+ "@storybook/manager-api": "8.5.8",
113
+ "@storybook/preview-api": "8.5.8",
114
+ "@storybook/react": "8.5.8",
115
+ "@storybook/react-webpack5": "8.5.8",
116
+ "@storybook/test-runner": "^0.21.3",
117
+ "@storybook/theming": "8.5.8",
118
118
  "@testing-library/dom": "^10.4.0",
119
119
  "@testing-library/react": "^16.2.0",
120
120
  "@testing-library/user-event": "^14.6.1",
@@ -127,7 +127,7 @@
127
127
  "@types/markdown-it": "^14.1.2",
128
128
  "@types/react": "^18.3.12",
129
129
  "@types/react-dom": "^18.3.1",
130
- "@types/sinon": "^17.0.3",
130
+ "@types/sinon": "^17.0.4",
131
131
  "@types/sinon-chai": "^4.0.0",
132
132
  "@types/webpack-env": "^1.18.8",
133
133
  "@vitejs/plugin-react": "^4.3.4",
@@ -147,7 +147,7 @@
147
147
  "cpy-cli": "^5.0.0",
148
148
  "dotenv-cli": "^8.0.0",
149
149
  "enzyme": "^3.11.0",
150
- "eslint": "^9.20.1",
150
+ "eslint": "^9.21.0",
151
151
  "eslint-config-prettier": "^10.0.1",
152
152
  "eslint-formatter-jslint-xml": "^8.40.0",
153
153
  "eslint-import-resolver-webpack": "^0.13.10",
@@ -159,7 +159,7 @@
159
159
  "eslint-plugin-storybook": "^0.11.3",
160
160
  "events": "^3.3.0",
161
161
  "glob": "^11.0.1",
162
- "globals": "^15.15.0",
162
+ "globals": "^16.0.0",
163
163
  "html-webpack-plugin": "^5.6.3",
164
164
  "http-server": "^14.1.1",
165
165
  "husky": "^9.1.7",
@@ -171,7 +171,7 @@
171
171
  "markdown-it": "^14.1.0",
172
172
  "merge-options": "^3.0.4",
173
173
  "pinst": "^3.0.0",
174
- "prettier": "^3.5.1",
174
+ "prettier": "^3.5.2",
175
175
  "raw-loader": "^4.0.2",
176
176
  "react": "^18.3.1",
177
177
  "react-dom": "^18.3.1",
@@ -183,14 +183,14 @@
183
183
  "sinon": "^19.0.2",
184
184
  "sinon-chai": "^4.0.0",
185
185
  "storage-mock": "^2.1.0",
186
- "storybook": "8.5.6",
186
+ "storybook": "8.5.8",
187
187
  "stylelint": "^16.14.1",
188
188
  "svg-inline-loader": "^0.8.2",
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.24.1",
193
- "vitest": "^3.0.5",
192
+ "typescript-eslint": "^8.25.0",
193
+ "vitest": "^3.0.7",
194
194
  "vitest-teamcity-reporter": "^0.3.1",
195
195
  "wallaby-webpack": "^3.9.16",
196
196
  "webpack": "^5.98.0",
@@ -220,11 +220,11 @@
220
220
  "@babel/core": "^7.26.9",
221
221
  "@babel/preset-typescript": "^7.26.0",
222
222
  "@jetbrains/babel-preset-jetbrains": "^2.4.0",
223
- "@jetbrains/icons": "^5.6.0",
223
+ "@jetbrains/icons": "^5.7.0",
224
224
  "@jetbrains/postcss-require-hover": "^0.1.3",
225
225
  "@types/combokeys": "^2.4.9",
226
226
  "@types/element-resize-detector": "^1.1.6",
227
- "@types/react-virtualized": "9.22.0",
227
+ "@types/react-virtualized": "9.22.2",
228
228
  "@types/util-deprecate": "^1.0.4",
229
229
  "babel-loader": "9.2.1",
230
230
  "babel-plugin-transform-define": "^2.1.4",
@@ -244,13 +244,13 @@
244
244
  "highlight.js": "^10.7.2",
245
245
  "just-debounce-it": "^3.2.0",
246
246
  "memoize-one": "^6.0.0",
247
- "postcss": "^8.5.2",
247
+ "postcss": "^8.5.3",
248
248
  "postcss-calc": "^10.1.1",
249
249
  "postcss-flexbugs-fixes": "^5.0.2",
250
250
  "postcss-font-family-system-ui": "^5.0.0",
251
251
  "postcss-loader": "^8.1.1",
252
252
  "postcss-modules-values-replace": "^4.2.0",
253
- "postcss-preset-env": "^10.1.4",
253
+ "postcss-preset-env": "^10.1.5",
254
254
  "react-movable": "^3.4.0",
255
255
  "react-virtualized": "^9.22.6",
256
256
  "react-waypoint": "^10.3.0",
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,30 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import figma from '@figma/code-connect';
3
- import chevronDownIcon from '@jetbrains/icons/chevron-down';
4
- import Button from '../button/button';
5
- import { ControlsHeight } from '../global/controls-height';
6
- import ButtonGroup from './button-group';
7
- figma.connect(ButtonGroup, 'https://www.figma.com/design/HY6d4uE1xxaQXCMG9fe6Y2/RingUI?node-id=9954%3A528', {
8
- props: {
9
- height: figma.enum('Size', {
10
- S: ControlsHeight.S,
11
- M: ControlsHeight.M,
12
- L: ControlsHeight.L,
13
- }),
14
- disabled: figma.enum('State', { Disabled: true }),
15
- primary: figma.enum('Variant', { Main: true }),
16
- success: figma.enum('Variant', { Green: true }),
17
- error: figma.enum('Variant', { 'Red solid': true }),
18
- secondary: figma.enum('Variant', { Gray: true }),
19
- danger: figma.enum('Variant', { 'Red outlined': true }),
20
- ghost: figma.enum('Variant', { Ghost: true }),
21
- inline: figma.enum('Variant', { Text: true }),
22
- },
23
- variant: { Type: 'Split' },
24
- example: props => (_jsxs(ButtonGroup, { children: [_jsx(Button, { ...props, children: 'Button' }), _jsx(Button, { ...props, icon: chevronDownIcon })] })),
25
- imports: [
26
- 'import Button from "@jetbrains/ring-ui/components/button/button"',
27
- 'import {ControlsHeight} from "@jetbrains/ring-ui/components/global/controls-height"',
28
- 'import chevronDownIcon from "@jetbrains/icons/chevron-down"',
29
- ],
30
- });