@jetbrains/ring-ui 7.0.7 → 7.0.9
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.
- package/README.md +1 -0
- package/components/collapse/collapse-content.d.ts +1 -2
- package/components/collapse/collapse-content.js +20 -13
- package/components/collapse/collapse.d.ts +2 -0
- package/components/collapse/collapse.js +8 -7
- package/components/list/list.css +17 -3
- package/components/list/list.d.ts +0 -6
- package/components/list/list.js +1 -37
- package/components/list/list__item.js +1 -1
- package/components/popup/popup.css +4 -0
- package/components/popup/popup.d.ts +1 -0
- package/components/popup/popup.js +2 -1
- package/components/popup-menu/popup-menu.d.ts +1 -0
- package/components/popup-menu/popup-menu.js +1 -0
- package/components/select/select__popup.js +1 -1
- package/components/tabs/collapsible-more.d.ts +1 -1
- package/components/tabs/collapsible-more.js +9 -7
- package/components/tabs/collapsible-tab.js +1 -2
- package/components/tabs/collapsible-tabs.d.ts +1 -1
- package/components/tabs/dumb-tabs.d.ts +2 -5
- package/components/tabs/dumb-tabs.js +5 -8
- package/package.json +27 -27
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ This collection of UI components aims to provide all the necessary building bloc
|
|
|
8
8
|
## Try now
|
|
9
9
|
* Try the [codesandbox](https://codesandbox.io/p/sandbox/ring-ui-7-0-demo-z6v6ym), based on `create-react-app` tooling, to see and try the UI components
|
|
10
10
|
* Check out [list of examples](https://jetbrains.github.io/ring-ui/master/index.html) for each component
|
|
11
|
+
* Check out [Ring UI Design Guidelines](http://www.jetbrains.com/help/ring-ui)
|
|
11
12
|
|
|
12
13
|
## Installation
|
|
13
14
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useRef, useContext, useMemo } from 'react';
|
|
2
|
-
import * as React from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef, useContext, useMemo } from 'react';
|
|
3
2
|
import classNames from 'classnames';
|
|
4
3
|
import dataTests from '../global/data-tests';
|
|
5
4
|
import { getRect } from '../global/dom';
|
|
@@ -20,19 +19,26 @@ export const CollapseContent = ({ children, minHeight = DEFAULT_HEIGHT, 'data-te
|
|
|
20
19
|
const contentRef = useRef(null);
|
|
21
20
|
const initialContentHeight = useRef(minHeight);
|
|
22
21
|
const contentHeight = useRef(DEFAULT_HEIGHT);
|
|
23
|
-
const [dimensions, setDimensions] = useState({
|
|
24
|
-
width: 0,
|
|
25
|
-
height: 0,
|
|
26
|
-
});
|
|
22
|
+
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
|
27
23
|
const [height, setHeight] = useState(toPx(minHeight));
|
|
28
|
-
const [showFade, setShowFade] = useState(
|
|
24
|
+
const [showFade, setShowFade] = useState(collapsed);
|
|
25
|
+
const [shouldHideContent, setShouldHideContent] = useState(collapsed && minHeight <= DEFAULT_HEIGHT);
|
|
29
26
|
useEffect(() => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
setShowFade(true);
|
|
27
|
+
function onTransitionEnd() {
|
|
28
|
+
if (initialContentHeight.current <= DEFAULT_HEIGHT) {
|
|
29
|
+
setShouldHideContent(collapsed);
|
|
30
|
+
}
|
|
35
31
|
}
|
|
32
|
+
const container = containerRef.current;
|
|
33
|
+
container?.addEventListener('transitionend', onTransitionEnd);
|
|
34
|
+
return () => {
|
|
35
|
+
container?.removeEventListener('transitionend', onTransitionEnd);
|
|
36
|
+
};
|
|
37
|
+
}, [collapsed]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
setShowFade(collapsed);
|
|
40
|
+
if (!collapsed)
|
|
41
|
+
setShouldHideContent(false);
|
|
36
42
|
}, [collapsed]);
|
|
37
43
|
useEffect(() => {
|
|
38
44
|
if (contentRef.current) {
|
|
@@ -63,9 +69,10 @@ export const CollapseContent = ({ children, minHeight = DEFAULT_HEIGHT, 'data-te
|
|
|
63
69
|
};
|
|
64
70
|
}, [duration, height, collapsed, minHeight]);
|
|
65
71
|
const fadeShouldBeVisible = useMemo(() => Boolean(minHeight && showFade), [minHeight, showFade]);
|
|
72
|
+
const shouldRenderContent = disableAnimation ? !collapsed : !shouldHideContent;
|
|
66
73
|
return (<div ref={containerRef} id={`collapse-content-${id}`} data-test={dataTests(COLLAPSE_CONTENT_CONTAINER_TEST_ID)} className={classNames(styles.container, { [styles.transition]: !disableAnimation })} style={style}>
|
|
67
74
|
<div ref={contentRef} data-test={dataTests(COLLAPSE_CONTENT_TEST_ID, dataTest)}>
|
|
68
|
-
{children}
|
|
75
|
+
{shouldRenderContent ? children : null}
|
|
69
76
|
</div>
|
|
70
77
|
{fadeShouldBeVisible && <div className={styles.fade}/>}
|
|
71
78
|
</div>);
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { useCallback, useId, useState } from 'react';
|
|
1
|
+
import { useCallback, useId, useMemo, useState } from 'react';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { CollapseContext } from './collapse-context';
|
|
4
4
|
import { BASE_ANIMATION_DURATION } from './consts';
|
|
5
5
|
/**
|
|
6
6
|
* @name Collapse
|
|
7
7
|
*/
|
|
8
|
-
export const Collapse = ({ children, duration = BASE_ANIMATION_DURATION, disableAnimation = false, className = '', onChange = () => { }, }) => {
|
|
9
|
-
const [
|
|
8
|
+
export const Collapse = ({ children, duration = BASE_ANIMATION_DURATION, disableAnimation = false, className = '', onChange = () => { }, defaultCollapsed = true, collapsed = null, }) => {
|
|
9
|
+
const [innerCollapsed, setInnerCollapsed] = useState(defaultCollapsed);
|
|
10
10
|
const id = useId();
|
|
11
|
+
const finalCollapsedValue = useMemo(() => collapsed ?? innerCollapsed, [innerCollapsed, collapsed]);
|
|
11
12
|
const setCollapsed = useCallback(() => {
|
|
12
|
-
|
|
13
|
-
onChange(!
|
|
14
|
-
}, [
|
|
13
|
+
setInnerCollapsed(!finalCollapsedValue);
|
|
14
|
+
onChange(!finalCollapsedValue);
|
|
15
|
+
}, [setInnerCollapsed, onChange, finalCollapsedValue]);
|
|
15
16
|
return (<div className={className}>
|
|
16
17
|
<CollapseContext.Provider value={{
|
|
17
|
-
collapsed,
|
|
18
|
+
collapsed: finalCollapsedValue,
|
|
18
19
|
setCollapsed,
|
|
19
20
|
duration,
|
|
20
21
|
disableAnimation,
|
package/components/list/list.css
CHANGED
|
@@ -48,20 +48,24 @@
|
|
|
48
48
|
|
|
49
49
|
box-sizing: border-box;
|
|
50
50
|
|
|
51
|
-
width: 100
|
|
51
|
+
width: calc(100% - 2 * var(--ring-unit));
|
|
52
52
|
|
|
53
53
|
text-align: left;
|
|
54
54
|
vertical-align: bottom;
|
|
55
55
|
white-space: nowrap;
|
|
56
56
|
text-decoration: none;
|
|
57
57
|
|
|
58
|
+
border-radius: var(--ring-border-radius);
|
|
59
|
+
|
|
58
60
|
outline: none;
|
|
59
61
|
|
|
60
62
|
font-size: var(--ring-font-size);
|
|
63
|
+
|
|
64
|
+
margin-inline: var(--ring-unit);
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
.item.item {
|
|
64
|
-
padding: 3px
|
|
68
|
+
padding: 3px var(--ring-unit) 5px;
|
|
65
69
|
|
|
66
70
|
line-height: calc(var(--ring-unit) * 3);
|
|
67
71
|
}
|
|
@@ -160,12 +164,17 @@
|
|
|
160
164
|
|
|
161
165
|
/* Override :last-child */
|
|
162
166
|
.hint.hint {
|
|
167
|
+
width: 100%;
|
|
168
|
+
|
|
163
169
|
margin-bottom: 0;
|
|
164
170
|
|
|
165
171
|
border-top: 1px solid var(--ring-line-color);
|
|
172
|
+
border-radius: 0;
|
|
166
173
|
background-color: var(--ring-sidebar-background-color);
|
|
167
174
|
|
|
168
175
|
font-size: var(--ring-font-size-smaller);
|
|
176
|
+
margin-inline: 0;
|
|
177
|
+
padding-inline: calc(2 * var(--ring-unit));
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
.action {
|
|
@@ -179,7 +188,12 @@
|
|
|
179
188
|
transition: none;
|
|
180
189
|
}
|
|
181
190
|
|
|
182
|
-
.hover:not(.error) {
|
|
191
|
+
.item:hover:not(.error) {
|
|
192
|
+
background-color: var(--ring-hover-background-color);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* TODO rename .hover to .selected in 8.0 */
|
|
196
|
+
.item.hover:not(.error) {
|
|
183
197
|
background-color: var(--ring-selected-background-color);
|
|
184
198
|
}
|
|
185
199
|
|
|
@@ -67,7 +67,6 @@ export interface ListState<T = unknown> {
|
|
|
67
67
|
needScrollToActive: boolean;
|
|
68
68
|
scrolling: boolean;
|
|
69
69
|
hasOverflow: boolean;
|
|
70
|
-
disabledHover: boolean;
|
|
71
70
|
scrolledToBottom: boolean;
|
|
72
71
|
}
|
|
73
72
|
interface RenderVirtualizedInnerParams extends Partial<WindowScrollerChildProps> {
|
|
@@ -109,7 +108,6 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
|
|
|
109
108
|
componentDidUpdate(prevProps: ListProps<T>): void;
|
|
110
109
|
componentWillUnmount(): void;
|
|
111
110
|
scheduleScrollListener: (cb: () => void) => void;
|
|
112
|
-
scheduleHoverListener: (cb: () => void) => void;
|
|
113
111
|
static isItemType: typeof isItemType;
|
|
114
112
|
static ListHint: typeof ListHint;
|
|
115
113
|
static ListProps: {
|
|
@@ -129,7 +127,6 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
|
|
|
129
127
|
virtualizedList?: VirtualizedList | null;
|
|
130
128
|
unmounted?: boolean;
|
|
131
129
|
container?: HTMLElement | null;
|
|
132
|
-
hoverHandler: (arg: number) => () => void;
|
|
133
130
|
private _bufferSize;
|
|
134
131
|
sizeCacheKey: (index: number) => string | Type.ITEM | Type.MARGIN;
|
|
135
132
|
private _cache;
|
|
@@ -142,15 +139,12 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
|
|
|
142
139
|
downHandler: (e: KeyboardEvent) => void;
|
|
143
140
|
homeHandler: (e: KeyboardEvent) => void;
|
|
144
141
|
endHandler: (e: KeyboardEvent) => void;
|
|
145
|
-
onDocumentMouseMove: () => void;
|
|
146
|
-
onDocumentKeyDown: (e: KeyboardEvent) => void;
|
|
147
142
|
moveHandler(index: number, retryCallback: (e: KeyboardEvent) => void, e: KeyboardEvent): void;
|
|
148
143
|
mouseHandler: () => void;
|
|
149
144
|
scrollHandler: () => void;
|
|
150
145
|
enterHandler: (event: KeyboardEvent, shortcut?: string) => boolean;
|
|
151
146
|
getFirst(): ListDataItem<T> | undefined;
|
|
152
147
|
getSelected(): ListDataItem<T> | null;
|
|
153
|
-
clearSelected: () => void;
|
|
154
148
|
defaultItemHeight(): number;
|
|
155
149
|
scrollEndHandler: () => void;
|
|
156
150
|
checkOverflow: () => void;
|
package/components/list/list.js
CHANGED
|
@@ -77,7 +77,6 @@ export default class List extends Component {
|
|
|
77
77
|
needScrollToActive: false,
|
|
78
78
|
scrolling: false,
|
|
79
79
|
hasOverflow: false,
|
|
80
|
-
disabledHover: false,
|
|
81
80
|
scrolledToBottom: false,
|
|
82
81
|
};
|
|
83
82
|
static getDerivedStateFromProps(nextProps, prevState) {
|
|
@@ -110,8 +109,6 @@ export default class List extends Component {
|
|
|
110
109
|
return nextState;
|
|
111
110
|
}
|
|
112
111
|
componentDidMount() {
|
|
113
|
-
document.addEventListener('mousemove', this.onDocumentMouseMove);
|
|
114
|
-
document.addEventListener('keydown', this.onDocumentKeyDown, true);
|
|
115
112
|
if (this.props.activeIndex == null && shouldActivateFirstItem(this.props)) {
|
|
116
113
|
this.activateFirst();
|
|
117
114
|
}
|
|
@@ -133,11 +130,8 @@ export default class List extends Component {
|
|
|
133
130
|
}
|
|
134
131
|
componentWillUnmount() {
|
|
135
132
|
this.unmounted = true;
|
|
136
|
-
document.removeEventListener('mousemove', this.onDocumentMouseMove);
|
|
137
|
-
document.removeEventListener('keydown', this.onDocumentKeyDown, true);
|
|
138
133
|
}
|
|
139
134
|
scheduleScrollListener = scheduleRAF();
|
|
140
|
-
scheduleHoverListener = scheduleRAF();
|
|
141
135
|
static isItemType = isItemType;
|
|
142
136
|
static ListHint = ListHint;
|
|
143
137
|
static ListProps = {
|
|
@@ -147,18 +141,6 @@ export default class List extends Component {
|
|
|
147
141
|
virtualizedList;
|
|
148
142
|
unmounted;
|
|
149
143
|
container;
|
|
150
|
-
hoverHandler = memoize((index) => () => this.scheduleHoverListener(() => {
|
|
151
|
-
if (this.state.disabledHover) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (this.container) {
|
|
155
|
-
this.setState({
|
|
156
|
-
activeIndex: index,
|
|
157
|
-
activeItem: this.props.data[index],
|
|
158
|
-
needScrollToActive: false,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}));
|
|
162
144
|
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
163
145
|
_bufferSize = 10; // keep X items above and below of the visible area
|
|
164
146
|
// reuse size cache for similar items
|
|
@@ -260,17 +242,6 @@ export default class List extends Component {
|
|
|
260
242
|
endHandler = (e) => {
|
|
261
243
|
this.moveHandler(this.props.data.length - 1, this.upHandler, e);
|
|
262
244
|
};
|
|
263
|
-
onDocumentMouseMove = () => {
|
|
264
|
-
if (this.state.disabledHover) {
|
|
265
|
-
this.setState({ disabledHover: false });
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
onDocumentKeyDown = (e) => {
|
|
269
|
-
const metaKeys = [16, 17, 18, 19, 20, 91]; // eslint-disable-line @typescript-eslint/no-magic-numbers
|
|
270
|
-
if (!this.state.disabledHover && !metaKeys.includes(e.keyCode)) {
|
|
271
|
-
this.setState({ disabledHover: true });
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
245
|
moveHandler(index, retryCallback, e) {
|
|
275
246
|
let correctedIndex;
|
|
276
247
|
if (this.props.data.length === 0 || !this.hasActivatableItems()) {
|
|
@@ -330,12 +301,6 @@ export default class List extends Component {
|
|
|
330
301
|
getSelected() {
|
|
331
302
|
return this.state.activeIndex != null ? this.props.data[this.state.activeIndex] : null;
|
|
332
303
|
}
|
|
333
|
-
clearSelected = () => {
|
|
334
|
-
this.setState({
|
|
335
|
-
activeIndex: null,
|
|
336
|
-
needScrollToActive: false,
|
|
337
|
-
});
|
|
338
|
-
};
|
|
339
304
|
defaultItemHeight() {
|
|
340
305
|
return this.props.compact ? Dimension.COMPACT_ITEM_HEIGHT : Dimension.ITEM_HEIGHT;
|
|
341
306
|
}
|
|
@@ -404,7 +369,6 @@ export default class List extends Component {
|
|
|
404
369
|
if (itemProps.hoverClassName != null && itemProps.hover) {
|
|
405
370
|
itemProps.className = classNames(itemProps.className, itemProps.hoverClassName);
|
|
406
371
|
}
|
|
407
|
-
itemProps.onMouseOver = this.hoverHandler(realIndex);
|
|
408
372
|
itemProps.tabIndex = -1;
|
|
409
373
|
itemProps.scrolling = isScrolling;
|
|
410
374
|
const selectHandler = this.selectHandler(realIndex);
|
|
@@ -528,7 +492,7 @@ export default class List extends Component {
|
|
|
528
492
|
const classes = classNames(styles.list, this.props.className);
|
|
529
493
|
return (<>
|
|
530
494
|
<ActiveItemContext.Updater value={this.getId(this.state.activeItem)} skipUpdate={this.props.hidden || !isActivatable(this.state.activeItem)}/>
|
|
531
|
-
<div id={this.props.id} ref={this.containerRef} className={classes} onMouseOut={this.props.onMouseOut} onBlur={this.props.onMouseOut}
|
|
495
|
+
<div id={this.props.id} ref={this.containerRef} className={classes} onMouseOut={this.props.onMouseOut} onBlur={this.props.onMouseOut} data-test="ring-list">
|
|
532
496
|
{this.props.shortcuts && (<Shortcuts map={this.props.shortcutsMap ? { ...this.shortcutsMap, ...this.props.shortcutsMap } : this.shortcutsMap} scope={this.shortcutsScope}/>)}
|
|
533
497
|
{this.props.renderOptimization
|
|
534
498
|
? this.renderVirtualized(maxHeight, rowCount)
|
|
@@ -12,7 +12,7 @@ import styles from './list.css';
|
|
|
12
12
|
* @extends {ReactComponent}
|
|
13
13
|
*/
|
|
14
14
|
const RING_UNIT = 8;
|
|
15
|
-
const DEFAULT_PADDING =
|
|
15
|
+
const DEFAULT_PADDING = 8;
|
|
16
16
|
const CHECKBOX_WIDTH = 28;
|
|
17
17
|
export default class ListItem extends PureComponent {
|
|
18
18
|
id = getUID('list-item-');
|
|
@@ -31,6 +31,7 @@ export interface BasePopupProps {
|
|
|
31
31
|
legacy: boolean;
|
|
32
32
|
withTail?: boolean;
|
|
33
33
|
tailOffset?: number;
|
|
34
|
+
largeBorderRadius?: boolean;
|
|
34
35
|
anchorElement?: HTMLElement | null | undefined;
|
|
35
36
|
target?: string | Element | null | undefined;
|
|
36
37
|
className?: string | null | undefined;
|
|
@@ -256,12 +256,13 @@ export default class Popup extends PureComponent {
|
|
|
256
256
|
esc: this._onEscPress,
|
|
257
257
|
};
|
|
258
258
|
render() {
|
|
259
|
-
const { className, style, hidden, attached, keepMounted, client, onMouseDown, onMouseUp, onMouseOver, onMouseOut, onContextMenu, 'data-test': dataTest, } = this.props;
|
|
259
|
+
const { className, style, hidden, attached, keepMounted, client, onMouseDown, onMouseUp, onMouseOver, onMouseOut, onContextMenu, 'data-test': dataTest, largeBorderRadius, } = this.props;
|
|
260
260
|
const showing = this.state.display === Display.SHOWING;
|
|
261
261
|
const classes = classNames(className, styles.popup, {
|
|
262
262
|
[styles.attached]: attached,
|
|
263
263
|
[styles.hidden]: hidden,
|
|
264
264
|
[styles.showing]: showing,
|
|
265
|
+
[styles.largeBorderRadius]: largeBorderRadius,
|
|
265
266
|
});
|
|
266
267
|
const direction = (this.state.direction || '').toLowerCase().replace(/[_]/g, '-');
|
|
267
268
|
return (<PopupTargetContext.Consumer>
|
|
@@ -27,6 +27,7 @@ export default class PopupMenu<T = unknown> extends Popup<PopupMenuProps<T>> {
|
|
|
27
27
|
static defaultProps: {
|
|
28
28
|
renderOptimization: boolean;
|
|
29
29
|
closeOnSelect: boolean;
|
|
30
|
+
largeBorderRadius: boolean;
|
|
30
31
|
shortcuts: boolean;
|
|
31
32
|
hidden: boolean;
|
|
32
33
|
onOutsideClick(): void;
|
|
@@ -328,7 +328,7 @@ export default class SelectPopup extends PureComponent {
|
|
|
328
328
|
const list = this.getList(this.props.ringPopupTarget || ringPopupTarget);
|
|
329
329
|
const bottomLine = this.getBottomLine();
|
|
330
330
|
const hasContent = filterWithTags || selectAll || list || bottomLine || toolbar || topbar;
|
|
331
|
-
return (<Popup trapFocus={false} ref={this.popupRef} hidden={hidden || !hasContent} attached={isInputMode} className={classes} dontCloseOnAnchorClick anchorElement={anchorElement} minWidth={minWidth} onCloseAttempt={onCloseAttempt} onOutsideClick={onOutsideClick} directions={directions} top={top} left={left} offset={offset} onMouseDown={this.mouseDownHandler} target={this.props.ringPopupTarget} autoCorrectTopOverflow={false} style={style}>
|
|
331
|
+
return (<Popup trapFocus={false} ref={this.popupRef} hidden={hidden || !hasContent} attached={isInputMode} className={classes} dontCloseOnAnchorClick anchorElement={anchorElement} minWidth={minWidth} onCloseAttempt={onCloseAttempt} onOutsideClick={onOutsideClick} directions={directions} top={top} left={left} offset={offset} onMouseDown={this.mouseDownHandler} target={this.props.ringPopupTarget} autoCorrectTopOverflow={false} style={style} largeBorderRadius>
|
|
332
332
|
<div dir={dir}>
|
|
333
333
|
{!hidden && filter && <Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>}
|
|
334
334
|
{topbar}
|
|
@@ -10,7 +10,7 @@ export declare const AnchorLink: ({ hasActiveChildren, moreClassName, moreActive
|
|
|
10
10
|
export interface MoreButtonProps {
|
|
11
11
|
items: ReactElement<TabProps>[];
|
|
12
12
|
selected?: string | undefined;
|
|
13
|
-
onSelect
|
|
13
|
+
onSelect?: ((key: string) => () => void) | null | undefined;
|
|
14
14
|
moreClassName?: string | null | undefined;
|
|
15
15
|
moreActiveClassName?: string | null | undefined;
|
|
16
16
|
morePopupClassName?: string | null | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo,
|
|
1
|
+
import { memo, useMemo } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { Directions } from '../popup/popup.consts';
|
|
4
4
|
import PopupMenu, { ListProps } from '../popup-menu/popup-menu';
|
|
@@ -15,13 +15,15 @@ export const AnchorLink = ({ hasActiveChildren, moreClassName, moreActiveClassNa
|
|
|
15
15
|
};
|
|
16
16
|
const morePopupDirections = [Directions.BOTTOM_CENTER, Directions.BOTTOM_LEFT, Directions.BOTTOM_RIGHT];
|
|
17
17
|
export const MoreButton = memo(({ items, selected, onSelect, moreClassName, moreActiveClassName, morePopupClassName, morePopupItemClassName, morePopupBeforeEnd, }) => {
|
|
18
|
-
const onSelectHandler =
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const onSelectHandler = useMemo(() => onSelect != null
|
|
19
|
+
? (listItem) => {
|
|
20
|
+
if (listItem.disabled === true || listItem.custom === true) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const cb = onSelect(String(listItem.key));
|
|
24
|
+
cb();
|
|
21
25
|
}
|
|
22
|
-
|
|
23
|
-
cb();
|
|
24
|
-
}, [onSelect]);
|
|
26
|
+
: undefined, [onSelect]);
|
|
25
27
|
const hasActiveChild = useMemo(() => items.some(item => item.props.alwaysHidden && item.props.id === selected), [items, selected]);
|
|
26
28
|
const data = useMemo(() => {
|
|
27
29
|
const popupItems = getTabTitles({
|
|
@@ -3,8 +3,7 @@ import classNames from 'classnames';
|
|
|
3
3
|
import styles from './tabs.css';
|
|
4
4
|
import TabLink from './tab-link';
|
|
5
5
|
import { CustomItem } from './custom-item';
|
|
6
|
-
function
|
|
7
|
-
const TabTitle = React.memo(function TabTitle({ selected, child, handleSelect = noop, collapsed = false, tabIndex, }) {
|
|
6
|
+
const TabTitle = React.memo(function TabTitle({ selected, child, handleSelect, collapsed = false, tabIndex, }) {
|
|
8
7
|
if (child == null || typeof child !== 'object' || child.type === CustomItem) {
|
|
9
8
|
return child;
|
|
10
9
|
}
|
|
@@ -3,7 +3,7 @@ import { TabProps } from './tab';
|
|
|
3
3
|
export interface CollapsibleTabsProps {
|
|
4
4
|
children: ReactElement<TabProps>[];
|
|
5
5
|
selected?: string | undefined;
|
|
6
|
-
onSelect
|
|
6
|
+
onSelect?: ((key: string) => () => void) | undefined;
|
|
7
7
|
moreClassName?: string | null | undefined;
|
|
8
8
|
moreActiveClassName?: string | null | undefined;
|
|
9
9
|
morePopupClassName?: string | null | undefined;
|
|
@@ -7,17 +7,14 @@ export { CustomItem };
|
|
|
7
7
|
export type Children = readonly (Children | null | boolean)[] | ReactElement<TabProps> | null | boolean;
|
|
8
8
|
export interface TabsProps extends Omit<CollapsibleTabsProps, 'onSelect' | 'children'> {
|
|
9
9
|
children: Children;
|
|
10
|
-
onSelect
|
|
10
|
+
onSelect?: ((key: string) => void) | null | undefined;
|
|
11
11
|
className?: string | null | undefined;
|
|
12
12
|
tabContainerClassName?: string | null | undefined;
|
|
13
13
|
autoCollapse?: boolean | null | undefined;
|
|
14
14
|
'data-test'?: string | null | undefined;
|
|
15
15
|
}
|
|
16
16
|
declare class Tabs extends PureComponent<TabsProps> {
|
|
17
|
-
|
|
18
|
-
onSelect(): void;
|
|
19
|
-
};
|
|
20
|
-
handleSelect: (arg: string) => () => void;
|
|
17
|
+
handleSelect: (arg: string) => () => void | undefined;
|
|
21
18
|
getTabTitle: (child: ReactElement<TabProps>, i: number) => React.JSX.Element;
|
|
22
19
|
render(): React.JSX.Element;
|
|
23
20
|
}
|
|
@@ -9,29 +9,26 @@ import CollapsibleTabs from './collapsible-tabs';
|
|
|
9
9
|
import { CustomItem } from './custom-item';
|
|
10
10
|
export { CustomItem };
|
|
11
11
|
class Tabs extends PureComponent {
|
|
12
|
-
|
|
13
|
-
onSelect() { },
|
|
14
|
-
};
|
|
15
|
-
handleSelect = memoize((key) => () => this.props.onSelect(key));
|
|
12
|
+
handleSelect = memoize((key) => () => this.props.onSelect?.(key));
|
|
16
13
|
getTabTitle = (child, i) => {
|
|
17
14
|
if (child == null || typeof child !== 'object' || child.type === CustomItem) {
|
|
18
15
|
return child;
|
|
19
16
|
}
|
|
20
|
-
const { selected } = this.props;
|
|
17
|
+
const { selected, onSelect } = this.props;
|
|
21
18
|
const { title, titleProps, id, disabled, href, className, activeClassName } = child.props;
|
|
22
19
|
const key = id || String(i);
|
|
23
20
|
const isSelected = key === selected;
|
|
24
21
|
const titleClasses = classNames(styles.title, className, isSelected && activeClassName, {
|
|
25
22
|
[styles.selected]: isSelected,
|
|
26
23
|
});
|
|
27
|
-
return (<TabLink title={title} isSelected={isSelected} key={key} href={href} className={titleClasses} disabled={disabled} onPlainLeftClick={this.handleSelect(key)} {...titleProps}/>);
|
|
24
|
+
return (<TabLink title={title} isSelected={isSelected} key={key} href={href} className={titleClasses} disabled={disabled} onPlainLeftClick={onSelect != null ? this.handleSelect(key) : undefined} {...titleProps}/>);
|
|
28
25
|
};
|
|
29
26
|
render() {
|
|
30
|
-
const { className, tabContainerClassName, children, selected, autoCollapse, 'data-test': dataTest, ...restProps } = this.props;
|
|
27
|
+
const { className, tabContainerClassName, children, selected, autoCollapse, 'data-test': dataTest, onSelect, ...restProps } = this.props;
|
|
31
28
|
const classes = classNames(styles.tabs, className);
|
|
32
29
|
const childrenArray = React.Children.toArray(children).filter(Boolean);
|
|
33
30
|
return (<div className={classes} data-test={dataTests('ring-dumb-tabs', dataTest)}>
|
|
34
|
-
{autoCollapse === true ? (<CollapsibleTabs {...restProps} onSelect={this.handleSelect} selected={selected}>
|
|
31
|
+
{autoCollapse === true ? (<CollapsibleTabs {...restProps} onSelect={onSelect != null ? this.handleSelect : undefined} selected={selected}>
|
|
35
32
|
{childrenArray}
|
|
36
33
|
</CollapsibleTabs>) : (<div className={styles.titles}>{childrenArray.map(this.getTabTitle)}</div>)}
|
|
37
34
|
<div className={classNames(tabContainerClassName)}>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jetbrains/ring-ui",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.9",
|
|
4
4
|
"description": "JetBrains UI library",
|
|
5
5
|
"author": "JetBrains",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -94,18 +94,18 @@
|
|
|
94
94
|
"@rollup/plugin-json": "^6.1.0",
|
|
95
95
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
96
96
|
"@rollup/plugin-replace": "^6.0.1",
|
|
97
|
-
"@storybook/addon-a11y": "8.4.
|
|
98
|
-
"@storybook/addon-docs": "8.4.
|
|
99
|
-
"@storybook/addon-essentials": "8.4.
|
|
100
|
-
"@storybook/addon-themes": "^8.4.
|
|
101
|
-
"@storybook/components": "8.4.
|
|
97
|
+
"@storybook/addon-a11y": "8.4.5",
|
|
98
|
+
"@storybook/addon-docs": "8.4.5",
|
|
99
|
+
"@storybook/addon-essentials": "8.4.5",
|
|
100
|
+
"@storybook/addon-themes": "^8.4.5",
|
|
101
|
+
"@storybook/components": "8.4.5",
|
|
102
102
|
"@storybook/csf": "^0.1.11",
|
|
103
|
-
"@storybook/manager-api": "8.4.
|
|
104
|
-
"@storybook/preview-api": "8.4.
|
|
105
|
-
"@storybook/react": "8.4.
|
|
106
|
-
"@storybook/react-webpack5": "8.4.
|
|
107
|
-
"@storybook/test-runner": "^0.
|
|
108
|
-
"@storybook/theming": "8.4.
|
|
103
|
+
"@storybook/manager-api": "8.4.5",
|
|
104
|
+
"@storybook/preview-api": "8.4.5",
|
|
105
|
+
"@storybook/react": "8.4.5",
|
|
106
|
+
"@storybook/react-webpack5": "8.4.5",
|
|
107
|
+
"@storybook/test-runner": "^0.20.0",
|
|
108
|
+
"@storybook/theming": "8.4.5",
|
|
109
109
|
"@testing-library/dom": "^10.4.0",
|
|
110
110
|
"@testing-library/react": "^16.0.1",
|
|
111
111
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -121,22 +121,22 @@
|
|
|
121
121
|
"@types/sinon": "^17.0.3",
|
|
122
122
|
"@types/sinon-chai": "^4.0.0",
|
|
123
123
|
"@types/webpack-env": "^1.18.5",
|
|
124
|
-
"@vitejs/plugin-react": "^4.3.
|
|
125
|
-
"@vitest/eslint-plugin": "^1.1.
|
|
124
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
125
|
+
"@vitest/eslint-plugin": "^1.1.12",
|
|
126
126
|
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
|
|
127
127
|
"acorn": "^8.14.0",
|
|
128
128
|
"axe-playwright": "^2.0.3",
|
|
129
129
|
"babel-plugin-require-context-hook": "^1.0.0",
|
|
130
|
-
"caniuse-lite": "^1.0.
|
|
130
|
+
"caniuse-lite": "^1.0.30001684",
|
|
131
131
|
"chai": "^5.1.2",
|
|
132
|
-
"chai-as-promised": "^8.0.
|
|
132
|
+
"chai-as-promised": "^8.0.1",
|
|
133
133
|
"chai-dom": "^1.10.0",
|
|
134
134
|
"chai-enzyme": "1.0.0-beta.1",
|
|
135
135
|
"cheerio": "^1.0.0-rc.12",
|
|
136
136
|
"core-js": "^3.39.0",
|
|
137
137
|
"cpy-cli": "^5.0.0",
|
|
138
138
|
"enzyme": "^3.11.0",
|
|
139
|
-
"eslint": "^9.
|
|
139
|
+
"eslint": "^9.15.0",
|
|
140
140
|
"eslint-config-prettier": "^9.1.0",
|
|
141
141
|
"eslint-formatter-jslint-xml": "^8.40.0",
|
|
142
142
|
"eslint-import-resolver-webpack": "^0.13.9",
|
|
@@ -145,13 +145,13 @@
|
|
|
145
145
|
"eslint-plugin-prettier": "^5.2.1",
|
|
146
146
|
"eslint-plugin-react": "^7.37.2",
|
|
147
147
|
"eslint-plugin-react-hooks": "^5.0.0",
|
|
148
|
-
"eslint-plugin-storybook": "^0.11.
|
|
148
|
+
"eslint-plugin-storybook": "^0.11.1",
|
|
149
149
|
"events": "^3.3.0",
|
|
150
150
|
"glob": "^11.0.0",
|
|
151
151
|
"globals": "^15.12.0",
|
|
152
152
|
"html-webpack-plugin": "^5.6.3",
|
|
153
153
|
"http-server": "^14.1.1",
|
|
154
|
-
"husky": "^9.1.
|
|
154
|
+
"husky": "^9.1.7",
|
|
155
155
|
"identity-obj-proxy": "^3.0.0",
|
|
156
156
|
"jest": "~29.7.0",
|
|
157
157
|
"jest-environment-jsdom": "^29.7.0",
|
|
@@ -160,26 +160,26 @@
|
|
|
160
160
|
"markdown-it": "^14.1.0",
|
|
161
161
|
"merge-options": "^3.0.4",
|
|
162
162
|
"pinst": "^3.0.0",
|
|
163
|
-
"prettier": "^3.
|
|
163
|
+
"prettier": "^3.4.1",
|
|
164
164
|
"raw-loader": "^4.0.2",
|
|
165
165
|
"react": "^18.3.1",
|
|
166
166
|
"react-dom": "^18.3.1",
|
|
167
167
|
"react-test-renderer": "^18.3.1",
|
|
168
168
|
"regenerator-runtime": "^0.14.1",
|
|
169
169
|
"rimraf": "^6.0.1",
|
|
170
|
-
"rollup": "^4.27.
|
|
170
|
+
"rollup": "^4.27.4",
|
|
171
171
|
"rollup-plugin-clear": "^2.0.7",
|
|
172
172
|
"sinon": "^19.0.2",
|
|
173
173
|
"sinon-chai": "^4.0.0",
|
|
174
174
|
"storage-mock": "^2.1.0",
|
|
175
|
-
"storybook": "8.4.
|
|
176
|
-
"stylelint": "^16.
|
|
175
|
+
"storybook": "8.4.5",
|
|
176
|
+
"stylelint": "^16.11.0",
|
|
177
177
|
"svg-inline-loader": "^0.8.2",
|
|
178
178
|
"teamcity-service-messages": "^0.1.14",
|
|
179
179
|
"terser-webpack-plugin": "^5.3.10",
|
|
180
180
|
"typescript": "~5.6.3",
|
|
181
|
-
"typescript-eslint": "^8.
|
|
182
|
-
"vitest": "^2.1.
|
|
181
|
+
"typescript-eslint": "^8.16.0",
|
|
182
|
+
"vitest": "^2.1.6",
|
|
183
183
|
"vitest-teamcity-reporter": "^0.3.1",
|
|
184
184
|
"wallaby-webpack": "^3.9.16",
|
|
185
185
|
"webpack": "^5.96.1",
|
|
@@ -209,11 +209,11 @@
|
|
|
209
209
|
"@babel/core": "^7.26.0",
|
|
210
210
|
"@babel/preset-typescript": "^7.26.0",
|
|
211
211
|
"@jetbrains/babel-preset-jetbrains": "^2.4.0",
|
|
212
|
-
"@jetbrains/icons": "^5.
|
|
212
|
+
"@jetbrains/icons": "^5.3.0",
|
|
213
213
|
"@jetbrains/postcss-require-hover": "^0.1.2",
|
|
214
214
|
"@types/combokeys": "^2.4.9",
|
|
215
215
|
"@types/element-resize-detector": "^1.1.6",
|
|
216
|
-
"@types/react-virtualized": "9.
|
|
216
|
+
"@types/react-virtualized": "9.22.0",
|
|
217
217
|
"@types/util-deprecate": "^1.0.4",
|
|
218
218
|
"babel-loader": "9.2.1",
|
|
219
219
|
"babel-plugin-transform-define": "^2.1.4",
|