@jetbrains/ring-ui 7.0.101 → 7.0.102

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.
@@ -130,7 +130,7 @@
130
130
  }
131
131
 
132
132
  .inline {
133
- --ring-button-text-color: var(--ring-action-link-color);
133
+ --ring-button-text-color: var(--ring-link-color);
134
134
  --ring-button-background-color: transparent;
135
135
 
136
136
  display: inline;
@@ -0,0 +1,138 @@
1
+ @import '../global/variables.css';
2
+
3
+ .expand .collapseRoot {
4
+ display: flex;
5
+ flex-direction: column;
6
+ }
7
+
8
+ .expand {
9
+ display: flex;
10
+ flex-direction: column;
11
+
12
+ box-sizing: border-box;
13
+
14
+ border: 1px solid var(--ring-line-color);
15
+ border-radius: var(--ring-border-radius-large);
16
+ outline: none;
17
+ background: var(--ring-content-background-color);
18
+ }
19
+
20
+ .hovered {
21
+ box-shadow: var(--ring-popup-shadow);
22
+ }
23
+
24
+ .expanded {
25
+ box-shadow: var(--ring-popup-shadow);
26
+ }
27
+
28
+ .focused {
29
+ box-shadow: 0 0 0 2px var(--ring-border-hover-color), var(--ring-popup-shadow);
30
+ }
31
+
32
+ .header {
33
+ display: flex;
34
+ align-items: center;
35
+ gap: calc(var(--ring-unit) * 1.5);
36
+
37
+ height: 28px;
38
+ }
39
+
40
+ .headerButton {
41
+ display: block;
42
+
43
+ box-sizing: border-box;
44
+ width: 100%;
45
+ padding: calc(var(--ring-unit) * 1.5);
46
+
47
+ cursor: pointer;
48
+ text-align: left;
49
+
50
+ color: inherit;
51
+
52
+ border: 0;
53
+ background: transparent;
54
+
55
+ font: inherit;
56
+ }
57
+
58
+ .headerButton:focus-visible {
59
+ outline: none;
60
+ }
61
+
62
+ .headerStatic {
63
+ display: block;
64
+
65
+ padding: calc(var(--ring-unit) * 1.5);
66
+ }
67
+
68
+ .headerContent {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--ring-unit);
72
+
73
+ flex: 1;
74
+
75
+ min-width: 0;
76
+ }
77
+
78
+ .avatarGroup {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: var(--ring-unit);
82
+
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ .title {
87
+ color: var(--ring-text-color);
88
+
89
+ font-size: var(--ring-font-size);
90
+ font-weight: var(--ring-font-weight-bold);
91
+ line-height: var(--ring-line-height);
92
+ }
93
+
94
+ .subtitleGroup {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: var(--ring-unit);
98
+
99
+ min-width: 0;
100
+ }
101
+
102
+ .subtitle {
103
+ color: var(--ring-secondary-color);
104
+
105
+ font-size: var(--ring-font-size);
106
+ line-height: var(--ring-line-height);
107
+ }
108
+
109
+ .subtitleChevron {
110
+ color: var(--ring-secondary-color);
111
+ }
112
+
113
+ .toggle {
114
+ display: flex;
115
+ align-items: center;
116
+
117
+ flex-shrink: 0;
118
+ justify-content: center;
119
+
120
+ width: calc(var(--ring-unit) * 3);
121
+ height: calc(var(--ring-unit) * 3);
122
+
123
+ border-radius: calc(var(--ring-unit) / 2);
124
+ background: var(--ring-secondary-background-color);
125
+ }
126
+
127
+ .toggleIcon {
128
+ color: var(--ring-secondary-color);
129
+ }
130
+
131
+ .body {
132
+ padding: 0 calc(var(--ring-unit) * 2) calc(var(--ring-unit) * 1.5);
133
+
134
+ color: var(--ring-text-color);
135
+
136
+ font-size: var(--ring-font-size);
137
+ line-height: var(--ring-line-height);
138
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ export interface CollapsibleGroupProps {
3
+ avatar?: React.ReactNode;
4
+ title: React.ReactNode;
5
+ subtitle?: React.ReactNode;
6
+ children?: React.ReactNode;
7
+ className?: string | null | undefined;
8
+ defaultExpanded?: boolean;
9
+ expanded?: boolean | null | undefined;
10
+ onChange?: (expanded: boolean) => void;
11
+ disableAnimation?: boolean;
12
+ interactive?: boolean;
13
+ 'data-test'?: string | null | undefined;
14
+ }
15
+ declare const CollapsibleGroup: React.ForwardRefExoticComponent<CollapsibleGroupProps & React.RefAttributes<HTMLDivElement>>;
16
+ export default CollapsibleGroup;
@@ -0,0 +1,73 @@
1
+ import React, { forwardRef, useContext, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import chevronRightIcon from '@jetbrains/icons/chevron-12px-right';
4
+ import chevronDownIcon from '@jetbrains/icons/chevron-12px-down';
5
+ import Icon from '../icon/icon';
6
+ import Collapse from '../collapse/collapse';
7
+ import CollapseContent from '../collapse/collapse-content';
8
+ import { CollapseContext } from '../collapse/collapse-context';
9
+ import styles from './collapsible-group.css';
10
+ function CollapsibleGroupHeaderContent({ avatar, titleContent, subtitle }) {
11
+ const { collapsed } = useContext(CollapseContext);
12
+ return (<span className={styles.header}>
13
+ <span className={styles.headerContent}>
14
+ <span className={styles.avatarGroup}>
15
+ {avatar}
16
+ <span className={styles.title}>{titleContent}</span>
17
+ </span>
18
+ {subtitle ? (<span className={styles.subtitleGroup}>
19
+ <Icon className={styles.subtitleChevron} glyph={chevronRightIcon} aria-hidden/>
20
+ <span className={styles.subtitle}>{subtitle}</span>
21
+ </span>) : null}
22
+ </span>
23
+ <span className={styles.toggle} aria-hidden>
24
+ <Icon className={styles.toggleIcon} glyph={collapsed ? chevronRightIcon : chevronDownIcon}/>
25
+ </span>
26
+ </span>);
27
+ }
28
+ function CollapsibleGroupHeader({ avatar, titleContent, subtitle, ...buttonProps }) {
29
+ const { setCollapsed, collapsed, id } = useContext(CollapseContext);
30
+ return (<button type='button' {...buttonProps} className={styles.headerButton} onClick={setCollapsed} aria-controls={`collapse-content-${id}`} aria-expanded={!collapsed}>
31
+ <CollapsibleGroupHeaderContent avatar={avatar} titleContent={titleContent} subtitle={subtitle}/>
32
+ </button>);
33
+ }
34
+ function CollapsibleGroupHeaderStatic({ avatar, titleContent, subtitle }) {
35
+ return (<span className={styles.headerStatic}>
36
+ <CollapsibleGroupHeaderContent avatar={avatar} titleContent={titleContent} subtitle={subtitle}/>
37
+ </span>);
38
+ }
39
+ const CollapsibleGroup = forwardRef(({ avatar, title, subtitle, children, className, defaultExpanded = false, expanded = null, onChange = () => { }, disableAnimation = false, interactive = true, 'data-test': dataTest, }, ref) => {
40
+ const [innerExpanded, setInnerExpanded] = useState(defaultExpanded);
41
+ const [hovered, setHovered] = useState(false);
42
+ const [focused, setFocused] = useState(false);
43
+ const isExpanded = expanded ?? innerExpanded;
44
+ const handleChange = (collapsed) => {
45
+ const nextExpanded = !collapsed;
46
+ if (expanded == null) {
47
+ setInnerExpanded(nextExpanded);
48
+ }
49
+ onChange(nextExpanded);
50
+ };
51
+ const onBlur = (event) => {
52
+ const nextTarget = event.relatedTarget;
53
+ if (nextTarget instanceof Node && event.currentTarget.contains(nextTarget)) {
54
+ return;
55
+ }
56
+ setFocused(false);
57
+ };
58
+ const classes = classNames(styles.expand, className, {
59
+ [styles.hovered]: hovered,
60
+ [styles.expanded]: isExpanded,
61
+ [styles.focused]: focused,
62
+ });
63
+ return (<div ref={ref} className={classes} data-test={dataTest}>
64
+ <Collapse defaultCollapsed={!defaultExpanded} collapsed={expanded == null ? null : !expanded} onChange={handleChange} disableAnimation={disableAnimation} className={styles.collapseRoot}>
65
+ {interactive ? (<CollapsibleGroupHeader avatar={avatar} titleContent={title} subtitle={subtitle} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} onFocus={() => setFocused(true)} onBlur={onBlur}/>) : (<CollapsibleGroupHeaderStatic avatar={avatar} titleContent={title} subtitle={subtitle}/>)}
66
+ <CollapseContent>
67
+ <div className={styles.body}>{children}</div>
68
+ </CollapseContent>
69
+ </Collapse>
70
+ </div>);
71
+ });
72
+ CollapsibleGroup.displayName = 'CollapsibleGroup';
73
+ export default CollapsibleGroup;
@@ -36,8 +36,8 @@
36
36
  --ring-icon-hover-color: rgb(var(--ring-icon-hover-components)); /* #5a5d6b */
37
37
  --ring-main-components: 51, 105, 214;
38
38
  --ring-main-color: rgb(var(--ring-main-components)); /* #3369D6 */
39
- --ring-action-link-components: 46, 85, 163;
40
- --ring-action-link-color: rgb(var(--ring-action-link-components)); /* #2E55A3 */ /* TODO: remove in 8.0 in favor of --ring-link-color */
39
+ --ring-action-link-components: var(--ring-link-components);
40
+ --ring-action-link-color: var(--ring-link-color); /* #315FBD */ /* TODO: remove in 8.0 in favor of --ring-link-color */
41
41
  --ring-main-hover-components: 49, 95, 189;
42
42
  --ring-main-hover-color: rgb(var(--ring-main-hover-components)); /* #315FBD */
43
43
  --ring-main-success-components: 31, 128, 57;
@@ -29,8 +29,6 @@
29
29
  --ring-border-accent-color: rgb(var(--ring-border-accent-components)); /* #366ACF */
30
30
  --ring-main-components: 54, 106, 207;
31
31
  --ring-main-color: rgb(var(--ring-main-components)); /* #366ACF */
32
- --ring-action-link-components: 153, 187, 255;
33
- --ring-action-link-color: rgb(var(--ring-action-link-components)); /* #99BBFF */ /* TODO: remove in 8.0 in favor of --ring-link-color */
34
32
  --ring-main-hover-components: 55, 95, 173;
35
33
  --ring-main-hover-color: rgb(var(--ring-main-hover-components)); /* #375FAD */
36
34
  --ring-main-success-components: 78, 128, 82;
@@ -0,0 +1,13 @@
1
+ .chevronButton {
2
+ padding: 0 2px;
3
+
4
+ transition: none;
5
+
6
+ color: inherit;
7
+ }
8
+
9
+ .chevronIcon {
10
+ transition: none;
11
+
12
+ color: inherit;
13
+ }
@@ -0,0 +1,6 @@
1
+ import { type ButtonHTMLAttributes } from 'react';
2
+ import { type ControlsHeight } from '../global/configuration';
3
+ export default function ChevronButton({ className, disabled, height, ...props }: {
4
+ disabled?: boolean | null | undefined;
5
+ height?: ControlsHeight | undefined;
6
+ } & ButtonHTMLAttributes<HTMLButtonElement>): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import chevronDownIcon from '@jetbrains/icons/chevron-down';
2
+ import classNames from 'classnames';
3
+ import Button from '../button/button';
4
+ import styles from './chevron-button.css';
5
+ export default function ChevronButton({ className, disabled, height, ...props }) {
6
+ return (<Button title='Toggle options popup' className={classNames(styles.chevronButton, className)} iconClassName={styles.chevronIcon} icon={chevronDownIcon} disabled={disabled ?? false} height={height} {...props}/>);
7
+ }
@@ -231,17 +231,3 @@
231
231
  min-width: calc(var(--ring-unit) * 30);
232
232
  max-width: calc(var(--ring-unit) * 40);
233
233
  }
234
-
235
- .chevron.chevron {
236
- padding: 0 2px;
237
-
238
- transition: none;
239
-
240
- color: inherit;
241
- }
242
-
243
- .chevronIcon.chevronIcon {
244
- transition: none;
245
-
246
- color: inherit;
247
- }
@@ -2,7 +2,6 @@
2
2
  import { Component, Fragment, } from 'react';
3
3
  import * as React from 'react';
4
4
  import classNames from 'classnames';
5
- import chevronDownIcon from '@jetbrains/icons/chevron-down';
6
5
  import closeIcon from '@jetbrains/icons/close-12px';
7
6
  import { dequal } from 'dequal';
8
7
  import { Anchor } from '../dropdown/dropdown';
@@ -23,6 +22,7 @@ import { createComposedRef } from '../global/compose-refs';
23
22
  import { isArray } from '../global/typescript-utils';
24
23
  import { ControlsHeight, ControlsHeightContext } from '../global/controls-height';
25
24
  import SelectPopup from './select-popup';
25
+ import ChevronButton from './chevron-button';
26
26
  import inputStyles from '../input/input.css';
27
27
  import styles from './select.css';
28
28
  /**
@@ -770,7 +770,7 @@ export default class Select extends Component {
770
770
  icons.push(<Button title='Clear selection' data-test='ring-clear-select' className={styles.clearIcon} key='close' disabled={this.props.disabled} onClick={this.clear} height={height} icon={closeIcon}/>);
771
771
  }
772
772
  if (!hideArrow) {
773
- icons.push(<Button title='Toggle options popup' className={styles.chevron} iconClassName={styles.chevronIcon} icon={chevronDownIcon} key='hide' disabled={this.props.disabled} height={height} onClick={this._clickHandler}/>);
773
+ icons.push(<ChevronButton key='chevron' disabled={this.props.disabled} height={height} onClick={this._clickHandler}/>);
774
774
  }
775
775
  return icons;
776
776
  }
@@ -34,6 +34,8 @@
34
34
  --ring-input-padding-start: var(--ring-input-padding-inline);
35
35
  --ring-input-padding-end: var(--ring-input-padding-inline);
36
36
 
37
+ position: relative;
38
+
37
39
  display: flex;
38
40
  flex-direction: row;
39
41
  flex-wrap: wrap;
@@ -42,7 +44,7 @@
42
44
 
43
45
  margin: 0;
44
46
  padding-top: var(--ring-input-padding-block);
45
- padding-right: var(--ring-input-padding-end);
47
+ padding-right: calc(var(--ring-input-padding-end) + var(--ring-line-height));
46
48
  padding-bottom: var(--ring-input-padding-block);
47
49
  padding-left: var(--ring-input-padding-start);
48
50
 
@@ -57,12 +59,13 @@
57
59
  background: transparent;
58
60
 
59
61
  font: inherit;
62
+ row-gap: calc(var(--ring-unit) / 2);
60
63
 
61
64
  caret-color: var(--ring-main-color);
62
65
 
63
66
  [dir='rtl'] & {
64
67
  padding-right: var(--ring-input-padding-start);
65
- padding-left: var(--ring-input-padding-end);
68
+ padding-left: calc(var(--ring-input-padding-end) + var(--ring-line-height));
66
69
  }
67
70
 
68
71
  &.error {
@@ -73,6 +76,11 @@
73
76
  transition: none;
74
77
 
75
78
  border-color: var(--ring-border-hover-color);
79
+
80
+ /* stylelint-disable-next-line selector-max-specificity */
81
+ .tagInputChevronButton {
82
+ color: var(--ring-main-color);
83
+ }
76
84
  }
77
85
 
78
86
  &:focus-within {
@@ -102,3 +110,17 @@
102
110
  line-height: calc(var(--ring-line-height) - 2px);
103
111
  }
104
112
  }
113
+
114
+ .tagInputChevronButton {
115
+ position: absolute;
116
+ right: 0;
117
+
118
+ height: var(--ring-line-height);
119
+
120
+ color: var(--ring-secondary-color);
121
+
122
+ [dir='rtl'] & {
123
+ right: auto;
124
+ left: 0;
125
+ }
126
+ }
@@ -12,13 +12,14 @@ import { Size } from '../input/input';
12
12
  import { ControlsHeightContext } from '../global/controls-height';
13
13
  import getUID from '../global/get-uid';
14
14
  import ControlLabel from '../control-label/control-label';
15
+ import ChevronButton from '../select/chevron-button';
15
16
  import inputStyles from '../input/input.css';
16
17
  import styles from './tags-input.css';
17
18
  function noop() { }
18
19
  /**
19
20
  * @name Tags Input
20
21
  */
21
- const POPUP_VERTICAL_SHIFT = 2;
22
+ const POPUP_VERTICAL_SHIFT = -2;
22
23
  export default class TagsInput extends PureComponent {
23
24
  static defaultProps = {
24
25
  dataSource: noop,
@@ -118,7 +119,9 @@ export default class TagsInput extends PureComponent {
118
119
  }, this.focusInput);
119
120
  }
120
121
  clickHandler = (event) => {
121
- if (event.target !== this.node && event.target.parentElement !== this.node) {
122
+ if (event.target !== this.node &&
123
+ event.target.parentElement !== this.node &&
124
+ !this.node?.querySelector(`.${styles.tagInputChevronButton}`)?.contains(event.target)) {
122
125
  return;
123
126
  }
124
127
  // eslint-disable-next-line no-underscore-dangle
@@ -241,7 +244,8 @@ export default class TagsInput extends PureComponent {
241
244
  </ControlLabel>)}
242
245
 
243
246
  <TagsList tags={tags} activeIndex={activeIndex} disabled={disabled} canNotBeEmpty={canNotBeEmpty} handleRemove={this.handleRemove} className={classNames(styles.tagsList, error != null && styles.error)} handleClick={this.handleClick} customTagComponent={this.props.customTagComponent}>
244
- <Select id={this.id} ref={this.selectRef} size={Select.Size.AUTO} type={Select.Type.INPUT_WITHOUT_CONTROLS} inputPlaceholder={this.props.placeholder} data={this.state.suggestions} className={classNames(styles.tagsSelect)} onSelect={this.addTag} onFocus={this._focusHandler} onBlur={this._blurHandler} renderOptimization={this.props.renderOptimization} add={allowAddNewTags ? { prefix: 'Add new tag' } : undefined} onAdd={allowAddNewTags ? this.handleTagCreation : undefined} filter={filter} maxHeight={this.props.maxPopupHeight} minWidth={this.props.minPopupWidth} top={POPUP_VERTICAL_SHIFT} loading={this.state.loading} onFilter={this.loadSuggestions} onBeforeOpen={this.loadSuggestions} onKeyDown={this.handleKeyDown} disabled={this.props.disabled} loadingMessage={this.props.loadingMessage} notFoundMessage={this.props.notFoundMessage} hint={this.props.hint} error={error}/>
247
+ <ChevronButton className={styles.tagInputChevronButton}/>
248
+ <Select id={this.id} ref={this.selectRef} size={Select.Size.AUTO} type={Select.Type.INPUT_WITHOUT_CONTROLS} inputPlaceholder={this.props.placeholder} data={this.state.suggestions} className={classNames(styles.tagsSelect)} onSelect={this.addTag} onFocus={this._focusHandler} onBlur={this._blurHandler} renderOptimization={this.props.renderOptimization} add={allowAddNewTags ? { prefix: 'Add new tag' } : undefined} onAdd={allowAddNewTags ? this.handleTagCreation : undefined} filter={filter} maxHeight={this.props.maxPopupHeight} minWidth={this.props.minPopupWidth} top={POPUP_VERTICAL_SHIFT} loading={this.state.loading} onFilter={this.loadSuggestions} onBeforeOpen={this.loadSuggestions} onKeyDown={this.handleKeyDown} disabled={this.props.disabled} loadingMessage={this.props.loadingMessage} notFoundMessage={this.props.notFoundMessage} hint={this.props.hint} error={error} targetElement={this.node}/>
245
249
  </TagsList>
246
250
  </div>);
247
251
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.101",
3
+ "version": "7.0.102",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"