@jetbrains/ring-ui 7.0.79 → 7.0.81

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.
@@ -1,4 +1,4 @@
1
- import { useMemo } from 'react';
1
+ import { useState } from 'react';
2
2
  import getUID from '../global/get-uid';
3
3
  import { Size } from './avatar-size';
4
4
  const colorPairs = [
@@ -184,7 +184,7 @@ export default function FallbackAvatar({ username, size, round }) {
184
184
  const sizes = round ? SizesRound[size] : SizesSquare[size];
185
185
  const underscore = sizes.underscore;
186
186
  const radius = round ? '50%' : sizes.radius;
187
- const gradientId = useMemo(() => getUID('gradient-'), []);
187
+ const [gradientId] = useState(() => getUID('gradient-'));
188
188
  return (<svg viewBox={`0 0 ${size} ${size}`} xmlns='http://www.w3.org/2000/svg'>
189
189
  <defs>
190
190
  <linearGradient id={gradientId} x1='1' y1='1' x2='0' y2='0'>
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useRef, useContext, useMemo } from 'react';
1
+ import React, { useState, useEffect, useRef, useContext } from 'react';
2
2
  import classNames from 'classnames';
3
3
  import dataTests from '../global/data-tests';
4
4
  import { getRect } from '../global/dom';
@@ -45,15 +45,13 @@ export const CollapseContent = ({ children, minHeight = DEFAULT_HEIGHT, 'data-te
45
45
  observer.observe(contentRef.current);
46
46
  }
47
47
  }, []);
48
- const style = useMemo(() => {
49
- const calculatedDuration = duration + contentHeight * DURATION_FACTOR;
50
- return {
51
- '--duration': `${calculatedDuration}ms`,
52
- height,
53
- opacity: collapsed && !minHeight ? HIDDEN : VISIBLE,
54
- };
55
- }, [duration, contentHeight, height, collapsed, minHeight]);
56
- const fadeShouldBeVisible = useMemo(() => Boolean(minHeight && collapsed), [minHeight, collapsed]);
48
+ const calculatedDuration = duration + contentHeight * DURATION_FACTOR;
49
+ const style = {
50
+ '--duration': `${calculatedDuration}ms`,
51
+ height,
52
+ opacity: collapsed && !minHeight ? HIDDEN : VISIBLE,
53
+ };
54
+ const fadeShouldBeVisible = Boolean(minHeight && collapsed);
57
55
  const shouldRenderContent = disableAnimation ? !collapsed : !shouldHideContent;
58
56
  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}>
59
57
  <div ref={contentRef} data-test={dataTests(COLLAPSE_CONTENT_TEST_ID, dataTest)}>
@@ -1,4 +1,4 @@
1
- import { useMemo, useContext, cloneElement } from 'react';
1
+ import { useContext, cloneElement } from 'react';
2
2
  import * as React from 'react';
3
3
  import dataTests from '../global/data-tests';
4
4
  import { CollapseContext } from './collapse-context';
@@ -9,12 +9,7 @@ import { COLLAPSE_CONTROL_TEST_ID } from './consts';
9
9
  export const CollapseControl = ({ children, 'data-test': dataTest }) => {
10
10
  const { setCollapsed, collapsed, id } = useContext(CollapseContext);
11
11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- const child = useMemo(() => {
13
- if (typeof children === 'function') {
14
- return children(collapsed);
15
- }
16
- return children;
17
- }, [children, collapsed]);
12
+ const child = typeof children === 'function' ? children(collapsed) : children;
18
13
  return (<p data-test={dataTests(COLLAPSE_CONTROL_TEST_ID, dataTest)}>
19
14
  {/* eslint-disable-next-line */}
20
15
  {cloneElement(child, {
@@ -1,4 +1,4 @@
1
- import { useCallback, useId, useMemo, useState } from 'react';
1
+ import { useId, 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';
@@ -8,11 +8,11 @@ import { BASE_ANIMATION_DURATION } from './consts';
8
8
  export const Collapse = ({ children, duration = BASE_ANIMATION_DURATION, disableAnimation = false, className = '', onChange = () => { }, defaultCollapsed = true, collapsed = null, }) => {
9
9
  const [innerCollapsed, setInnerCollapsed] = useState(defaultCollapsed);
10
10
  const id = useId();
11
- const finalCollapsedValue = useMemo(() => collapsed ?? innerCollapsed, [innerCollapsed, collapsed]);
12
- const setCollapsed = useCallback(() => {
11
+ const finalCollapsedValue = collapsed ?? innerCollapsed;
12
+ const setCollapsed = () => {
13
13
  setInnerCollapsed(!finalCollapsedValue);
14
14
  onChange(!finalCollapsedValue);
15
- }, [setInnerCollapsed, onChange, finalCollapsedValue]);
15
+ };
16
16
  return (<div className={className}>
17
17
  <CollapseContext.Provider value={{
18
18
  collapsed: finalCollapsedValue,
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo, useRef } from 'react';
1
+ import { useEffect, useRef } from 'react';
2
2
  import { addMonths } from 'date-fns/addMonths';
3
3
  import { getDay } from 'date-fns/getDay';
4
4
  import { getDaysInMonth } from 'date-fns/getDaysInMonth';
@@ -37,15 +37,12 @@ export default function Months(props) {
37
37
  const { scrollDate } = props;
38
38
  const monthDate = scrollDate instanceof Date ? scrollDate : new Date(scrollDate);
39
39
  const monthStart = startOfMonth(monthDate);
40
- const months = useMemo(() => {
41
- let month = subMonths(monthStart, MONTHSBACK);
42
- const result = [month];
43
- for (let i = 0; i < MONTHSBACK * DOUBLE; i++) {
44
- month = addMonths(month, 1);
45
- result.push(month);
46
- }
47
- return result;
48
- }, [monthStart]);
40
+ let month = subMonths(monthStart, MONTHSBACK);
41
+ const months = [month];
42
+ for (let i = 0; i < MONTHSBACK * DOUBLE; i++) {
43
+ month = addMonths(month, 1);
44
+ months.push(month);
45
+ }
49
46
  const currentSpeed = scrollSpeed(scrollDate);
50
47
  const pxToDate = linearFunction(0, Number(scrollDate), currentSpeed);
51
48
  const offset = pxToDate.x(Number(monthStart)); // is a negative number
@@ -4,6 +4,10 @@
4
4
  display: inline-block;
5
5
  }
6
6
 
7
+ .clickInterceptor {
8
+ display: contents;
9
+ }
10
+
7
11
  .anchor.anchor {
8
12
  margin-inline: -3px;
9
13
  padding-inline: 3px;
@@ -125,10 +125,12 @@ export default class Dropdown extends Component {
125
125
  onContextMenu: hoverMode ? this.handlePopupInteraction : undefined,
126
126
  dontCloseOnAnchorClick: true,
127
127
  };
128
- return (<div data-test={dataTests('ring-dropdown', dataTest)} {...restProps} onClick={clickMode ? this.onClick : undefined}
129
- // anchorElement should be a `button` or an `a`
130
- role='presentation' onMouseEnter={hoverMode ? this.onMouseEnter : undefined} onMouseLeave={hoverMode ? this.onMouseLeave : undefined} className={classes}>
131
- {anchorElement}
128
+ return (<div data-test={dataTests('ring-dropdown', dataTest)} {...restProps} onMouseEnter={hoverMode ? this.onMouseEnter : undefined} onMouseLeave={hoverMode ? this.onMouseLeave : undefined} className={classes}>
129
+ {clickMode ? (<div data-test='ring-dropdown-anchor-click-interceptor' className={styles.clickInterceptor} onClick={this.onClick}
130
+ // anchorElement should be a `button` or an `a`
131
+ role='presentation'>
132
+ {anchorElement}
133
+ </div>) : (anchorElement)}
132
134
  {typeof children === 'function'
133
135
  ? children(childProps)
134
136
  : cloneElement(children, childProps)}
@@ -1,4 +1,4 @@
1
- import { forwardRef, useMemo, cloneElement, } from 'react';
1
+ import { forwardRef, cloneElement, useState, } from 'react';
2
2
  import List, { ActiveItemContext } from '../list/list';
3
3
  import Dropdown from '../dropdown/dropdown';
4
4
  import PopupMenu from '../popup-menu/popup-menu';
@@ -7,13 +7,13 @@ import Anchor from '../dropdown/anchor';
7
7
  import { isArray } from '../global/typescript-utils';
8
8
  const defaultAriaLabel = 'Dropdown menu';
9
9
  function DropdownAnchorWrapper({ anchor, pinned, active, activeListItemId, listId, ...restProps }) {
10
- const anchorAriaProps = useMemo(() => ({
10
+ const anchorAriaProps = {
11
11
  ...(listId ? { 'aria-haspopup': true } : {}),
12
12
  ...(activeListItemId ? { 'aria-activedescendant': activeListItemId, 'aria-owns': listId } : {}),
13
13
  ...(active ? { 'aria-expanded': true } : {}),
14
- }), [active, activeListItemId, listId]);
15
- const anchorProps = useMemo(() => ({ active, pinned, ...restProps, ...anchorAriaProps }), [pinned, active, restProps, anchorAriaProps]);
16
- const anchorComponentProps = useMemo(() => ({ ...anchorProps, pinned: `${anchorProps.pinned}` }), [anchorProps]);
14
+ };
15
+ const anchorProps = { active, pinned, ...restProps, ...anchorAriaProps };
16
+ const anchorComponentProps = { ...anchorProps, pinned: `${anchorProps.pinned}` };
17
17
  if (typeof anchor === 'string') {
18
18
  return <Anchor {...anchorComponentProps}>{anchor}</Anchor>;
19
19
  }
@@ -32,8 +32,9 @@ function renderDropdownMenuChildren({ children, popupMenuProps }) {
32
32
  return (popupProps) => children({ ...popupProps, ...popupMenuProps });
33
33
  }
34
34
  const DropdownMenu = forwardRef(function DropdownMenu({ id, anchor, ariaLabel, data, onSelect, menuProps, children, ...restDropdownProps }, forwardedRef) {
35
- const listId = useMemo(() => id || getUID('dropdown-menu-list'), [id]);
36
- const popupMenuProps = useMemo(() => ({
35
+ const [uid] = useState(() => getUID('dropdown-menu-list'));
36
+ const listId = id || uid;
37
+ const popupMenuProps = {
37
38
  ref: forwardedRef,
38
39
  id: listId,
39
40
  ariaLabel: ariaLabel || defaultAriaLabel,
@@ -42,7 +43,7 @@ const DropdownMenu = forwardRef(function DropdownMenu({ id, anchor, ariaLabel, d
42
43
  data,
43
44
  onSelect,
44
45
  ...menuProps,
45
- }), [ariaLabel, data, forwardedRef, listId, menuProps, onSelect]);
46
+ };
46
47
  return (<ActiveItemContext.Provider>
47
48
  <Dropdown anchor={({ pinned, active, ...restAnchorProps }) => (<ActiveItemContext.ValueContext.Consumer>
48
49
  {activeItemId => (<DropdownAnchorWrapper anchor={anchor} pinned={pinned} active={active} activeListItemId={activeItemId} listId={listId} {...restAnchorProps}/>)}
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import * as React from 'react';
3
3
  import classNames from 'classnames';
4
4
  import Heading, { Levels } from '../heading/heading';
@@ -6,6 +6,7 @@ import Button from '../button/button';
6
6
  import { Size } from '../input/input';
7
7
  import getUID from '../global/get-uid';
8
8
  import Shortcuts from '../shortcuts/shortcuts';
9
+ import useEventCallback from '../global/use-event-callback';
9
10
  import inputStyles from '../input/input.css';
10
11
  import styles from './editable-heading.css';
11
12
  export { Levels };
@@ -28,16 +29,13 @@ export const EditableHeading = (props) => {
28
29
  const isSaveDisabled = !isSavingPossible || !children || children.trim() === '' || hasError || isSaving;
29
30
  const isCancelDisabled = isSaving;
30
31
  const isShortcutsDisabled = !isInFocus || isSaving;
31
- const shortcutsMap = React.useMemo(() => {
32
- const map = {};
33
- if (!isSaveDisabled) {
34
- map.enter = onSave;
35
- }
36
- if (isCancelDisabled) {
37
- map.esc = onCancel;
38
- }
39
- return map;
40
- }, [isSaveDisabled, isCancelDisabled, onSave, onCancel]);
32
+ const shortcutsMap = {};
33
+ if (!isSaveDisabled) {
34
+ shortcutsMap.enter = onSave;
35
+ }
36
+ if (isCancelDisabled) {
37
+ shortcutsMap.esc = onCancel;
38
+ }
41
39
  const classes = classNames(styles.editableHeading, className, {
42
40
  [styles.fullSize]: isEditing && size === Size.FULL,
43
41
  [styles.isEditing]: isEditing,
@@ -48,61 +46,61 @@ export const EditableHeading = (props) => {
48
46
  });
49
47
  const headingClasses = classNames(styles.heading, headingClassName, styles[`size${size}`]);
50
48
  const inputClasses = classNames('ring-js-shortcuts', styles.input, styles.textarea, { [styles.textareaNotOverflow]: !isOverflow }, inputStyles[`size${size}`], styles[`level${level}`], inputClassName);
51
- const stretch = useCallback((el) => {
49
+ const stretch = (el) => {
52
50
  if (!el || !el.style) {
53
51
  return;
54
52
  }
55
53
  el.style.height = '0';
56
54
  const { paddingTop, paddingBottom } = window.getComputedStyle(el);
57
55
  el.style.height = `${el.scrollHeight - parseFloat(paddingTop) - parseFloat(paddingBottom)}px`;
58
- }, []);
59
- const checkValue = useCallback((el) => {
56
+ };
57
+ const checkValue = (el) => {
60
58
  if (multiline && el && el.scrollHeight >= el.clientHeight) {
61
59
  stretch(el);
62
60
  }
63
- }, [stretch, multiline]);
64
- const checkOverflow = useCallback((el) => {
61
+ };
62
+ const checkOverflow = (el) => {
65
63
  const scrollHeight = el.scrollHeight || 0;
66
64
  const clientHeight = el.clientHeight || 0;
67
65
  const scrollTop = el.scrollTop || 0;
68
66
  setIsScrolledToBottom(scrollHeight - clientHeight <= scrollTop);
69
67
  setIsOverflow(scrollHeight > clientHeight);
70
- }, [setIsScrolledToBottom]);
71
- const onHeadingMouseDown = React.useCallback(() => {
68
+ };
69
+ const onHeadingMouseDown = () => {
72
70
  setIsMouseDown(true);
73
- }, []);
74
- const onMouseMove = React.useCallback(() => {
71
+ };
72
+ const onMouseMove = useEventCallback(() => {
75
73
  if (!isMouseDown) {
76
74
  return;
77
75
  }
78
76
  setIsInSelectionMode(true);
79
- }, [isMouseDown]);
80
- const onMouseUp = React.useCallback(() => {
77
+ });
78
+ const onMouseUp = useEventCallback(() => {
81
79
  if (isMouseDown && !isInSelectionMode && !disabled) {
82
80
  onEdit();
83
81
  }
84
82
  setIsMouseDown(false);
85
83
  setIsInSelectionMode(false);
86
- }, [isMouseDown, isInSelectionMode, disabled, onEdit]);
87
- const onInputFocus = React.useCallback((e) => {
84
+ });
85
+ const onInputFocus = (e) => {
88
86
  setIsInFocus(true);
89
87
  checkValue(e.target);
90
88
  checkOverflow(e.target);
91
89
  onFocus?.(e);
92
- }, [onFocus, checkOverflow, checkValue]);
93
- const onInputChange = React.useCallback((e) => {
90
+ };
91
+ const onInputChange = (e) => {
94
92
  checkValue(e.target);
95
93
  checkOverflow(e.target);
96
94
  onChange?.(e);
97
- }, [onChange, checkOverflow, checkValue]);
98
- const onInputScroll = React.useCallback((e) => {
95
+ };
96
+ const onInputScroll = (e) => {
99
97
  checkOverflow(e.target);
100
98
  onScroll?.(e);
101
- }, [onScroll, checkOverflow]);
102
- const onInputBlur = React.useCallback((e) => {
99
+ };
100
+ const onInputBlur = (e) => {
103
101
  setIsInFocus(false);
104
102
  onBlur?.(e);
105
- }, [onBlur]);
103
+ };
106
104
  useEffect(() => {
107
105
  window.addEventListener('mousemove', onMouseMove);
108
106
  window.addEventListener('mouseup', onMouseUp);
@@ -6,6 +6,7 @@ declare enum Theme {
6
6
  }
7
7
  export declare const ThemeContext: import("react").Context<{
8
8
  theme: Theme.LIGHT | Theme.DARK;
9
+ passToPopups?: boolean;
9
10
  }>;
10
11
  export declare const GLOBAL_DARK_CLASS_NAME = "ring-ui-theme-dark";
11
12
  export declare function useTheme(): Theme.LIGHT | Theme.DARK;
@@ -1,9 +1,5 @@
1
- import { useMemo, useState, useEffect, forwardRef, useContext, createContext, } from 'react';
1
+ import { useState, useEffect, forwardRef, createContext, } from 'react';
2
2
  import classNames from 'classnames';
3
- import { createPortal } from 'react-dom';
4
- import { PopupTarget, PopupTargetContext } from '../popup/popup.target';
5
- import { getPopupContainer } from '../popup/popup';
6
- import getUID from './get-uid';
7
3
  import defaultStyles from './variables.css';
8
4
  import styles from './variables_dark.css';
9
5
  var Theme;
@@ -12,7 +8,9 @@ var Theme;
12
8
  Theme["LIGHT"] = "light";
13
9
  Theme["DARK"] = "dark";
14
10
  })(Theme || (Theme = {}));
15
- export const ThemeContext = createContext({ theme: Theme.LIGHT });
11
+ export const ThemeContext = createContext({
12
+ theme: Theme.LIGHT,
13
+ });
16
14
  export const GLOBAL_DARK_CLASS_NAME = 'ring-ui-theme-dark';
17
15
  const darkMatcher = window.matchMedia('(prefers-color-scheme: dark)');
18
16
  export function useTheme() {
@@ -52,27 +50,23 @@ export function applyTheme(theme, container) {
52
50
  const DefaultWrapper = forwardRef(function Wrapper(props, ref) {
53
51
  return <div {...props} ref={ref}/>;
54
52
  });
55
- export const ThemeProvider = forwardRef(function ThemeProvider({ theme = Theme.AUTO, className, passToPopups, children, WrapperComponent = DefaultWrapper, target, ...restProps }, ref) {
53
+ function ThemeProviderInner({ theme = Theme.AUTO, className, passToPopups, children, WrapperComponent = DefaultWrapper, target, wrapperRef, ...restProps }) {
56
54
  const systemTheme = useTheme();
57
55
  const resolvedTheme = theme === Theme.AUTO ? systemTheme : theme;
58
- const id = useMemo(() => getUID('popups-with-theme-'), []);
59
- const themeValue = useMemo(() => ({ theme: resolvedTheme }), [resolvedTheme]);
56
+ const themeValue = { theme: resolvedTheme, passToPopups };
60
57
  useEffect(() => {
61
58
  if (target) {
62
59
  applyTheme(resolvedTheme, target);
63
60
  }
64
61
  }, [resolvedTheme, target]);
65
62
  const themeClasses = useThemeClasses(theme);
66
- const parentTarget = useContext(PopupTargetContext);
67
63
  return (<ThemeContext.Provider value={themeValue}>
68
- <WrapperComponent ref={ref} className={target ? undefined : classNames(className, themeClasses)} {...restProps}>
69
- {passToPopups ? (<PopupTarget id={id}>
70
- {popupTarget => (<>
71
- {children}
72
- {createPortal(<div className={themeClasses}>{popupTarget}</div>, (parentTarget && getPopupContainer(parentTarget)) || document.body)}
73
- </>)}
74
- </PopupTarget>) : (children)}
64
+ <WrapperComponent ref={wrapperRef} className={target ? undefined : classNames(className, themeClasses)} {...restProps}>
65
+ {children}
75
66
  </WrapperComponent>
76
67
  </ThemeContext.Provider>);
68
+ }
69
+ export const ThemeProvider = forwardRef(function ThemeProvider(props, ref) {
70
+ return <ThemeProviderInner {...props} wrapperRef={ref}/>;
77
71
  });
78
72
  export default Theme;
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useLayoutEffect, useRef } from 'react';
2
+ // TODO deprecate in favor of useEffectEvent after dropping support for React < 19.2
2
3
  export default function useEventCallback(fn) {
3
4
  const ref = useRef(null);
4
5
  useLayoutEffect(() => {
@@ -17,30 +17,9 @@ import position from './position';
17
17
  import { DEFAULT_DIRECTIONS, Dimension, Directions, Display, MaxHeight, MinWidth } from './popup.consts';
18
18
  import { PopupTargetContext, PopupTarget } from './popup.target';
19
19
  import { setCSSAnchorPositioning, supportsCSSAnchorPositioning } from './position-css';
20
+ import { ThemeContext, WithThemeClasses } from '../global/theme';
20
21
  import styles from './popup.css';
21
22
  export { PopupTargetContext, PopupTarget };
22
- const isPossibleClientSideNavigation = (event) => {
23
- const target = event.target;
24
- const link = target.closest('a');
25
- // Taken from https://github.com/nanostores/router/blob/80a333db4cf0789fda21a02715ebabca15192642/index.js#L58-L69
26
- return (link &&
27
- event.button === 0 && // Left mouse button
28
- link.target !== '_blank' && // Not for new tab
29
- link.origin === location.origin && // Not external link
30
- link.rel !== 'external' && // Not external link
31
- link.target !== '_self' && // Now manually disabled
32
- !link.download && // Not download link
33
- !event.altKey && // Not download link by user
34
- !event.metaKey && // Not open in new tab by user
35
- !event.ctrlKey && // Not open in new tab by user
36
- !event.shiftKey && // Not open in new window by user
37
- !event.defaultPrevented);
38
- };
39
- const stop = (event) => {
40
- if (!isPossibleClientSideNavigation(event)) {
41
- event.stopPropagation();
42
- }
43
- };
44
23
  export const getPopupContainer = (target) => typeof target === 'string' ? document.querySelector(`[data-portaltarget=${target}]`) : target;
45
24
  /**
46
25
  * @constructor
@@ -301,35 +280,35 @@ export default class Popup extends PureComponent {
301
280
  render() {
302
281
  const { className, style, hidden, attached, keepMounted, client, onMouseDown, onMouseUp, onMouseOver, onMouseOut, onContextMenu, 'data-test': dataTest, largeBorderRadius, } = this.props;
303
282
  const showing = this.state.display === Display.SHOWING;
304
- const classes = classNames(className, styles.popup, {
305
- [styles.cssAnchoredPopup]: this.shouldUseCssPositioning(),
306
- [styles.attached]: attached,
307
- [styles.hidden]: hidden,
308
- [styles.showing]: showing,
309
- [styles.largeBorderRadius]: largeBorderRadius,
310
- });
311
283
  const direction = (this.state.direction || '').toLowerCase().replace(/[_]/g, '-');
312
- return (<PopupTargetContext.Consumer>
313
- {value => {
314
- this.ringPopupTarget = value;
315
- return (<span
316
- // prevent bubbling through portal
317
- onClick={stop}
318
- // This handler only blocks bubbling through React portal
319
- role='presentation' ref={this.portalRef}>
320
- {this.shouldUseShortcuts() && <Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>}
284
+ return (<ThemeContext.Consumer>
285
+ {theme => (<WithThemeClasses theme={theme.theme}>
286
+ {themeClasses => (<PopupTargetContext.Consumer>
287
+ {value => {
288
+ this.ringPopupTarget = value;
289
+ const classes = classNames(className, theme.passToPopups ? themeClasses : null, styles.popup, {
290
+ [styles.cssAnchoredPopup]: this.shouldUseCssPositioning(),
291
+ [styles.attached]: attached,
292
+ [styles.hidden]: hidden,
293
+ [styles.showing]: showing,
294
+ [styles.largeBorderRadius]: largeBorderRadius,
295
+ });
296
+ return (<span ref={this.portalRef}>
297
+ {this.shouldUseShortcuts() && <Shortcuts map={this.shortcutsMap} scope={this.shortcutsScope}/>}
321
298
 
322
- {client !== false &&
323
- (keepMounted || !hidden) &&
324
- createPortal(<PopupTarget id={this.uid} ref={this.containerRef} onMouseOver={onMouseOver} onFocus={onMouseOver} onMouseOut={onMouseOut} onBlur={onMouseOut} onContextMenu={onContextMenu}>
325
- <div data-test={dataTests('ring-popup', dataTest)} data-test-shown={!hidden && !showing} data-test-direction={direction} ref={this.popupRef} className={classes} style={style} onMouseDown={onMouseDown} onMouseUp={onMouseUp}
326
- // mouse handlers are used to track clicking on inner elements
327
- role='presentation'>
328
- {this.getInternalContent()}
329
- </div>
330
- </PopupTarget>, this.getContainer() || document.body)}
331
- </span>);
332
- }}
333
- </PopupTargetContext.Consumer>);
299
+ {client !== false &&
300
+ (keepMounted || !hidden) &&
301
+ createPortal(<PopupTarget id={this.uid} ref={this.containerRef} onMouseOver={onMouseOver} onFocus={onMouseOver} onMouseOut={onMouseOut} onBlur={onMouseOut} onContextMenu={onContextMenu}>
302
+ <div data-test={dataTests('ring-popup', dataTest)} data-test-shown={!hidden && !showing} data-test-direction={direction} ref={this.popupRef} className={classes} style={style} onMouseDown={onMouseDown} onMouseUp={onMouseUp}
303
+ // mouse handlers are used to track clicking on inner elements
304
+ role='presentation'>
305
+ {this.getInternalContent()}
306
+ </div>
307
+ </PopupTarget>, this.getContainer() || document.body)}
308
+ </span>);
309
+ }}
310
+ </PopupTargetContext.Consumer>)}
311
+ </WithThemeClasses>)}
312
+ </ThemeContext.Consumer>);
334
313
  }
335
314
  }
@@ -1,22 +1,23 @@
1
- import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import { Fragment, useEffect, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import classNames from 'classnames';
4
4
  import { isArray } from '../global/typescript-utils';
5
5
  import Shortcuts from '../shortcuts/shortcuts';
6
6
  import getUID from '../global/get-uid';
7
7
  import { adjustValues, calculateMarks, calculateValue, HUNDRED, toPercent, toRange, validateValue } from './slider.utils';
8
+ import useEventCallback from '../global/use-event-callback';
8
9
  import styles from './slider.css';
9
10
  export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1, disabled, marks, showTicks, showTag, className, renderTag, onChange, }) => {
10
11
  const ref = useRef(null);
11
12
  const previouslyDragged = useRef(false);
12
13
  const [values, setValues] = useState(defaultValue ?? min);
13
- const validValues = useMemo(() => toRange(value ?? values, min, max), [max, min, value, values]);
14
+ const validValues = toRange(value ?? values, min, max);
14
15
  const validStep = step < 0 ? 0 : step;
15
16
  const isRange = isArray(defaultValue ?? value);
16
17
  const [isDragging, setIsDragging] = useState(false);
17
18
  const [draggedIndex, setDraggedIndex] = useState(-1);
18
19
  const [shortcutsScope] = useState(getUID('ring-slider-'));
19
- const markValues = useMemo(() => {
20
+ const markValues = (() => {
20
21
  if (isArray(marks)) {
21
22
  return marks.map(mark => ({ ...mark, value: validateValue(mark.value, min, max) }));
22
23
  }
@@ -24,54 +25,51 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
24
25
  return calculateMarks(min, max, validStep);
25
26
  }
26
27
  return [];
27
- }, [marks, max, min, validStep]);
28
- const tickMarks = useMemo(() => {
28
+ })();
29
+ const tickMarks = (() => {
29
30
  if (showTicks) {
30
31
  return markValues.length ? markValues : calculateMarks(min, max, validStep);
31
32
  }
32
33
  return [];
33
- }, [max, min, markValues, showTicks, validStep]);
34
- const trackStart = useMemo(() => toPercent(isRange ? Math.min(...validValues) : min, min, max), [isRange, max, min, validValues]);
35
- const trackLength = useMemo(() => toPercent(Math.max(...validValues), min, max) - trackStart, [max, min, trackStart, validValues]);
36
- const handleValueChange = useCallback((nextValues) => {
34
+ })();
35
+ const trackStart = toPercent(isRange ? Math.min(...validValues) : min, min, max);
36
+ const trackLength = toPercent(Math.max(...validValues), min, max) - trackStart;
37
+ const handleValueChange = (nextValues) => {
37
38
  setValues(nextValues);
38
39
  onChange?.(isRange ? nextValues : nextValues[0]);
39
- }, [isRange, onChange]);
40
- const shortcutsMap = useMemo(() => {
41
- const setValueAndSwap = (nextValue, index) => {
42
- const nextValues = [...validValues];
43
- nextValues[index] = nextValue;
44
- if (nextValues[0] > nextValues[1]) {
45
- const previousValue = nextValues[index];
46
- nextValues.reverse();
47
- const thumb = ref.current?.querySelector(`[role="slider"][data-index="${nextValues.indexOf(previousValue)}"]`);
48
- thumb?.focus();
49
- }
50
- handleValueChange(nextValues);
51
- };
52
- const getIndex = (target) => Number(target?.getAttribute('data-index'));
53
- const map = {};
54
- if (!disabled) {
55
- map.left = map.down = ({ target }) => {
56
- const index = getIndex(target);
57
- setValueAndSwap(Math.max(min, validValues[index] - validStep), index);
58
- };
59
- map.right = map.up = ({ target }) => {
60
- const index = getIndex(target);
61
- setValueAndSwap(Math.min(max, validValues[index] + validStep), index);
62
- };
63
- map.home = ({ target }) => {
64
- const index = getIndex(target);
65
- setValueAndSwap(min, index);
66
- };
67
- map.end = ({ target }) => {
68
- const index = getIndex(target);
69
- setValueAndSwap(max, index);
70
- };
40
+ };
41
+ const setValueAndSwap = (nextValue, index) => {
42
+ const nextValues = [...validValues];
43
+ nextValues[index] = nextValue;
44
+ if (nextValues[0] > nextValues[1]) {
45
+ const previousValue = nextValues[index];
46
+ nextValues.reverse();
47
+ const thumb = ref.current?.querySelector(`[role="slider"][data-index="${nextValues.indexOf(previousValue)}"]`);
48
+ thumb?.focus();
71
49
  }
72
- return map;
73
- }, [disabled, handleValueChange, max, min, validStep, validValues]);
74
- const handleMouseDown = useCallback((e) => {
50
+ handleValueChange(nextValues);
51
+ };
52
+ const getIndex = (target) => Number(target?.getAttribute('data-index'));
53
+ const shortcutsMap = {};
54
+ if (!disabled) {
55
+ shortcutsMap.left = shortcutsMap.down = ({ target }) => {
56
+ const index = getIndex(target);
57
+ setValueAndSwap(Math.max(min, validValues[index] - validStep), index);
58
+ };
59
+ shortcutsMap.right = shortcutsMap.up = ({ target }) => {
60
+ const index = getIndex(target);
61
+ setValueAndSwap(Math.min(max, validValues[index] + validStep), index);
62
+ };
63
+ shortcutsMap.home = ({ target }) => {
64
+ const index = getIndex(target);
65
+ setValueAndSwap(min, index);
66
+ };
67
+ shortcutsMap.end = ({ target }) => {
68
+ const index = getIndex(target);
69
+ setValueAndSwap(max, index);
70
+ };
71
+ }
72
+ const handleMouseDown = (e) => {
75
73
  e.stopPropagation();
76
74
  if (disabled) {
77
75
  return;
@@ -87,8 +85,8 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
87
85
  }
88
86
  setIsDragging(true);
89
87
  previouslyDragged.current = false;
90
- }, [disabled, isRange, max, min, validStep, validValues]);
91
- const handleMouseUp = useCallback(({ pageX }) => {
88
+ };
89
+ const handleMouseUp = useEventCallback(({ pageX }) => {
92
90
  const nextValues = adjustValues(validValues, ref, draggedIndex, pageX, max, min, validStep);
93
91
  if (nextValues[0] > nextValues[1]) {
94
92
  nextValues.reverse();
@@ -97,15 +95,15 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
97
95
  setDraggedIndex(-1);
98
96
  setIsDragging(false);
99
97
  previouslyDragged.current = true;
100
- }, [validValues, draggedIndex, handleValueChange, max, min, validStep]);
101
- const handleMouseMove = useCallback(({ pageX }) => {
98
+ });
99
+ const handleMouseMove = useEventCallback(({ pageX }) => {
102
100
  const nextValues = adjustValues(validValues, ref, draggedIndex, pageX, max, min, validStep);
103
101
  if (nextValues[0] > nextValues[1]) {
104
102
  nextValues.reverse();
105
103
  setDraggedIndex(prevState => (prevState === 0 ? 1 : 0));
106
104
  }
107
105
  handleValueChange(nextValues);
108
- }, [validValues, draggedIndex, max, min, validStep, handleValueChange]);
106
+ });
109
107
  useEffect(() => {
110
108
  if (disabled) {
111
109
  return undefined;
@@ -122,7 +120,7 @@ export const Slider = ({ defaultValue, value, min = 0, max = HUNDRED, step = 1,
122
120
  window.removeEventListener('mousemove', handleMouseMove);
123
121
  window.removeEventListener('mouseup', handleMouseUp);
124
122
  };
125
- }, [isDragging, handleMouseMove, handleMouseUp, disabled]);
123
+ }, [isDragging, disabled, handleMouseMove, handleMouseUp]);
126
124
  return (<div ref={ref} role='presentation' // contains interactive elements
127
125
  className={classNames(styles.slider, className, {
128
126
  [styles.disabled]: disabled,
@@ -1,4 +1,4 @@
1
- import { memo, useMemo } from 'react';
1
+ import { memo } 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,7 +15,7 @@ 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 = useMemo(() => (onSelect
18
+ const onSelectHandler = onSelect
19
19
  ? (listItem) => {
20
20
  if (listItem.disabled === true || listItem.custom === true) {
21
21
  return;
@@ -23,37 +23,34 @@ export const MoreButton = memo(({ items, selected, onSelect, moreClassName, more
23
23
  const cb = onSelect(String(listItem.key));
24
24
  cb();
25
25
  }
26
- : undefined), [onSelect]);
27
- const hasActiveChild = useMemo(() => items.some(item => item.props.alwaysHidden && item.props.id === selected), [items, selected]);
28
- const data = useMemo(() => {
29
- const popupItems = getTabTitles({
30
- items,
31
- selected,
32
- collapsed: true,
33
- }).map(tab => {
34
- const disabled = tab.props.disabled === true;
35
- const custom = tab.props.child.type === CustomItem;
36
- return {
37
- template: tab,
38
- key: tab.key,
39
- rgItemType: ListProps.Type.CUSTOM,
40
- className: morePopupItemClassName,
41
- disabled,
42
- custom,
43
- };
26
+ : undefined;
27
+ const hasActiveChild = items.some(item => item.props.alwaysHidden && item.props.id === selected);
28
+ const data = getTabTitles({
29
+ items,
30
+ selected,
31
+ collapsed: true,
32
+ }).map(tab => {
33
+ const disabled = tab.props.disabled === true;
34
+ const custom = tab.props.child.type === CustomItem;
35
+ return {
36
+ template: tab,
37
+ key: tab.key,
38
+ rgItemType: ListProps.Type.CUSTOM,
39
+ className: morePopupItemClassName,
40
+ disabled,
41
+ custom,
42
+ };
43
+ });
44
+ if (morePopupBeforeEnd) {
45
+ data.push({
46
+ template: morePopupBeforeEnd,
47
+ key: 'before-end-content',
48
+ className: styles.morePopupBeforeEnd,
49
+ rgItemType: ListProps.Type.CUSTOM,
44
50
  });
45
- if (morePopupBeforeEnd) {
46
- popupItems.push({
47
- template: morePopupBeforeEnd,
48
- key: 'before-end-content',
49
- className: styles.morePopupBeforeEnd,
50
- rgItemType: ListProps.Type.CUSTOM,
51
- });
52
- }
53
- return popupItems;
54
- }, [items, morePopupBeforeEnd, morePopupItemClassName, selected]);
55
- const popupAnchor = useMemo(() => (<AnchorLink moreClassName={moreClassName} moreActiveClassName={moreActiveClassName} hasActiveChildren={hasActiveChild}/>), [hasActiveChild, moreActiveClassName, moreClassName]);
56
- const popup = useMemo(() => (<PopupMenu directions={morePopupDirections} className={morePopupClassName} onSelect={onSelectHandler} data={data}/>), [data, morePopupClassName, onSelectHandler]);
51
+ }
52
+ const popupAnchor = (<AnchorLink moreClassName={moreClassName} moreActiveClassName={moreActiveClassName} hasActiveChildren={hasActiveChild}/>);
53
+ const popup = (<PopupMenu directions={morePopupDirections} className={morePopupClassName} onSelect={onSelectHandler} data={data}/>);
57
54
  if (items.length === 0) {
58
55
  return null;
59
56
  }
@@ -1,4 +1,4 @@
1
- import { useState, useRef, useMemo, useCallback, useEffect, memo } from 'react';
1
+ import { useState, useRef, useCallback, useEffect, memo } from 'react';
2
2
  import classNames from 'classnames';
3
3
  import fastdom from 'fastdom';
4
4
  import { FakeMoreButton, MoreButton } from './collapsible-more';
@@ -15,24 +15,22 @@ export const CollapsibleTabs = ({ children, selected, onSelect, onLastVisibleInd
15
15
  hidden: [],
16
16
  });
17
17
  const measureRef = useRef(null);
18
- const selectedIndex = useMemo(() => children.filter(tab => tab.props.alwaysHidden !== true).findIndex(tab => tab.props.id === selected) ?? null, [children, selected]);
19
- const visibleElements = useMemo(() => {
20
- let items;
21
- if (preparedElements.ready) {
22
- items = preparedElements.visible;
23
- }
24
- else {
25
- items = initialVisibleItems
26
- ? children.filter(item => item.props.alwaysHidden !== true).slice(0, initialVisibleItems)
27
- : [];
28
- }
29
- return getTabTitles({
30
- items,
31
- selected,
32
- onSelect,
33
- });
34
- }, [initialVisibleItems, children, preparedElements.ready, preparedElements.visible, onSelect, selected]);
35
- const hiddenElements = useMemo(() => {
18
+ const selectedIndex = children.filter(tab => tab.props.alwaysHidden !== true).findIndex(tab => tab.props.id === selected) ?? null;
19
+ let items;
20
+ if (preparedElements.ready) {
21
+ items = preparedElements.visible;
22
+ }
23
+ else {
24
+ items = initialVisibleItems
25
+ ? children.filter(item => item.props.alwaysHidden !== true).slice(0, initialVisibleItems)
26
+ : [];
27
+ }
28
+ const visibleElements = getTabTitles({
29
+ items,
30
+ selected,
31
+ onSelect,
32
+ });
33
+ const hiddenElements = (() => {
36
34
  if (preparedElements.ready) {
37
35
  return preparedElements.hidden;
38
36
  }
@@ -40,7 +38,7 @@ export const CollapsibleTabs = ({ children, selected, onSelect, onLastVisibleInd
40
38
  return children.filter(item => !visibleElements.some(visibleItem => visibleItem.props.child === item));
41
39
  }
42
40
  return [];
43
- }, [children, preparedElements.hidden, preparedElements.ready, visibleElements, initialVisibleItems]);
41
+ })();
44
42
  const adjustTabs = useCallback((entry) => {
45
43
  const containerWidth = entry.contentRect.width;
46
44
  const { tabs: tabsSizes, more = 0 } = elements.sizes;
@@ -119,10 +117,8 @@ export const CollapsibleTabs = ({ children, selected, onSelect, onLastVisibleInd
119
117
  };
120
118
  }, [children, elements.lastVisibleIndex, preparedElements, selected, selectedIndex]);
121
119
  // Get list of all possibly visible elements to render in a measure container
122
- const childrenToMeasure = useMemo(() => {
123
- const items = children.filter(tab => tab.props.alwaysHidden !== true);
124
- return getTabTitles({ items, tabIndex: -1 });
125
- }, [children]);
120
+ const childItems = children.filter(tab => tab.props.alwaysHidden !== true);
121
+ const childrenToMeasure = getTabTitles({ items: childItems, tabIndex: -1 });
126
122
  // Initial measure for tabs and more button sizes
127
123
  useEffect(() => {
128
124
  if (measureRef.current === null) {
@@ -1,14 +1,14 @@
1
- import { forwardRef, useCallback, useImperativeHandle, useRef, useState, } from 'react';
1
+ import { forwardRef, useImperativeHandle, useRef, useState, } from 'react';
2
2
  import classNames from 'classnames';
3
3
  import attachmentIcon from '@jetbrains/icons/attachment';
4
4
  import Icon from '../icon';
5
5
  import styles from './upload.css';
6
6
  const defaultRenderIcon = () => <Icon className={styles.attachmentIcon} glyph={attachmentIcon}/>;
7
- export const Upload = forwardRef(function Upload({ children, className, onFilesSelected, onFilesRejected, validate = () => true, variant = 'empty', multiple, renderIcon = defaultRenderIcon, accept, disabled, }, ref) {
7
+ function UploadInner({ children, className, onFilesSelected, onFilesRejected, validate = () => true, variant = 'empty', multiple, renderIcon = defaultRenderIcon, accept, disabled, forwardedRef, }) {
8
8
  const fileInputRef = useRef(null);
9
9
  const [dragOver, setDragOver] = useState(false);
10
- useImperativeHandle(ref, () => ({ openFilePicker: () => fileInputRef.current?.click() }), []);
11
- const handleSelectedFiles = useCallback((files) => {
10
+ useImperativeHandle(forwardedRef, () => ({ openFilePicker: () => fileInputRef.current?.click() }), []);
11
+ const handleSelectedFiles = (files) => {
12
12
  if (!files.length) {
13
13
  return;
14
14
  }
@@ -18,16 +18,16 @@ export const Upload = forwardRef(function Upload({ children, className, onFilesS
18
18
  return;
19
19
  }
20
20
  onFilesSelected(files);
21
- }, [onFilesRejected, onFilesSelected, validate]);
22
- const onDragEnter = useCallback(() => setDragOver(true), []);
23
- const onDragOver = useCallback(e => e.preventDefault(), []);
24
- const onDragLeave = useCallback(() => setDragOver(false), []);
25
- const onInputChange = useCallback(() => {
21
+ };
22
+ const onDragEnter = () => setDragOver(true);
23
+ const onDragOver = e => e.preventDefault();
24
+ const onDragLeave = () => setDragOver(false);
25
+ const onInputChange = () => {
26
26
  setDragOver(false);
27
27
  if (fileInputRef.current?.files) {
28
28
  handleSelectedFiles(Array.from(fileInputRef.current.files));
29
29
  }
30
- }, [handleSelectedFiles]);
30
+ };
31
31
  return (<div className={classNames(className, styles.upload, {
32
32
  [styles.disabled]: disabled,
33
33
  [styles.dragOver]: dragOver,
@@ -38,5 +38,8 @@ export const Upload = forwardRef(function Upload({ children, className, onFilesS
38
38
  {renderIcon()}
39
39
  {children}
40
40
  </div>);
41
+ }
42
+ export const Upload = forwardRef(function Upload(props, ref) {
43
+ return <UploadInner {...props} forwardedRef={ref}/>;
41
44
  });
42
45
  export default Upload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.79",
3
+ "version": "7.0.81",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -93,43 +93,43 @@
93
93
  "@babel/eslint-parser": "^7.28.5",
94
94
  "@csstools/css-parser-algorithms": "^3.0.4",
95
95
  "@csstools/stylelint-no-at-nest-rule": "^4.0.0",
96
- "@eslint/compat": "^1.4.1",
97
- "@eslint/eslintrc": "^3.2.0",
96
+ "@eslint/compat": "^2.0.0",
97
+ "@eslint/eslintrc": "^3.3.3",
98
98
  "@eslint/js": "^9.39.1",
99
- "@figma/code-connect": "^1.3.8",
99
+ "@figma/code-connect": "^1.3.11",
100
100
  "@jetbrains/eslint-config": "^6.0.5",
101
101
  "@jetbrains/logos": "3.0.0-canary.734b213.0",
102
102
  "@jetbrains/rollup-css-plugin": "./packages/rollup-css-plugin",
103
103
  "@jetbrains/stylelint-config": "^4.0.2",
104
- "@primer/octicons": "^19.20.0",
104
+ "@primer/octicons": "^19.21.0",
105
105
  "@rollup/plugin-babel": "^6.1.0",
106
106
  "@rollup/plugin-json": "^6.1.0",
107
107
  "@rollup/plugin-node-resolve": "^16.0.3",
108
108
  "@rollup/plugin-replace": "^6.0.3",
109
- "@storybook/addon-a11y": "10.0.6",
110
- "@storybook/addon-docs": "^10.0.6",
111
- "@storybook/addon-themes": "^10.0.6",
109
+ "@storybook/addon-a11y": "10.1.2",
110
+ "@storybook/addon-docs": "^10.1.2",
111
+ "@storybook/addon-themes": "^10.1.2",
112
112
  "@storybook/csf": "^0.1.13",
113
- "@storybook/react-webpack5": "10.0.6",
114
- "@storybook/test-runner": "^0.24.1",
113
+ "@storybook/react-webpack5": "10.1.2",
114
+ "@storybook/test-runner": "^0.24.2",
115
115
  "@testing-library/dom": "^10.4.1",
116
116
  "@testing-library/react": "^16.3.0",
117
117
  "@testing-library/user-event": "^14.6.1",
118
118
  "@types/chai-as-promised": "^8.0.2",
119
119
  "@types/chai-dom": "1.11.3",
120
120
  "@types/markdown-it": "^14.1.2",
121
- "@types/react": "^19.2.2",
122
- "@types/react-dom": "^19.2.2",
121
+ "@types/react": "^19.2.7",
122
+ "@types/react-dom": "^19.2.3",
123
123
  "@types/webpack-env": "^1.18.8",
124
- "@vitejs/plugin-react": "^5.0.4",
125
- "@vitest/eslint-plugin": "^1.3.23",
124
+ "@vitejs/plugin-react": "^5.1.1",
125
+ "@vitest/eslint-plugin": "^1.5.1",
126
126
  "acorn": "^8.15.0",
127
127
  "babel-plugin-require-context-hook": "^1.0.0",
128
- "caniuse-lite": "^1.0.30001754",
128
+ "caniuse-lite": "^1.0.30001757",
129
129
  "chai-as-promised": "^8.0.2",
130
130
  "chai-dom": "^1.12.1",
131
131
  "cheerio": "^1.1.2",
132
- "core-js": "^3.46.0",
132
+ "core-js": "^3.47.0",
133
133
  "cpy-cli": "^6.0.0",
134
134
  "dotenv-cli": "^11.0.0",
135
135
  "eslint": "^9.39.1",
@@ -142,42 +142,42 @@
142
142
  "eslint-plugin-prettier": "^5.5.4",
143
143
  "eslint-plugin-react": "^7.37.5",
144
144
  "eslint-plugin-react-hooks": "^7.0.1",
145
- "eslint-plugin-storybook": "^10.0.6",
145
+ "eslint-plugin-storybook": "^10.1.2",
146
146
  "eslint-plugin-unicorn": "^62.0.0",
147
147
  "events": "^3.3.0",
148
- "glob": "^11.1.0",
148
+ "glob": "^13.0.0",
149
149
  "globals": "^16.5.0",
150
- "html-webpack-plugin": "^5.6.4",
150
+ "html-webpack-plugin": "^5.6.5",
151
151
  "http-server": "^14.1.1",
152
152
  "husky": "^9.1.7",
153
153
  "identity-obj-proxy": "^3.0.0",
154
154
  "jest": "~30.2.0",
155
155
  "jest-environment-jsdom": "^30.2.0",
156
156
  "jest-teamcity": "^1.12.0",
157
- "lint-staged": "^16.2.6",
157
+ "lint-staged": "^16.2.7",
158
158
  "markdown-it": "^14.1.0",
159
159
  "merge-options": "^3.0.4",
160
160
  "pinst": "^3.0.0",
161
- "prettier": "^3.6.2",
161
+ "prettier": "^3.7.3",
162
162
  "raw-loader": "^4.0.2",
163
163
  "react": "^19.2.0",
164
164
  "react-dom": "^19.2.0",
165
165
  "regenerator-runtime": "^0.14.1",
166
- "rimraf": "^6.1.0",
167
- "rollup": "^4.53.1",
166
+ "rimraf": "^6.1.2",
167
+ "rollup": "^4.53.3",
168
168
  "rollup-plugin-clear": "^2.0.7",
169
169
  "storage-mock": "^2.1.0",
170
- "storybook": "10.0.6",
171
- "stylelint": "^16.25.0",
170
+ "storybook": "10.1.2",
171
+ "stylelint": "^16.26.1",
172
172
  "stylelint-config-sass-guidelines": "^12.1.0",
173
173
  "svg-inline-loader": "^0.8.2",
174
174
  "teamcity-service-messages": "^0.1.14",
175
175
  "terser-webpack-plugin": "^5.3.14",
176
176
  "typescript": "~5.9.3",
177
- "typescript-eslint": "^8.46.3",
178
- "vitest": "^3.2.4",
179
- "vitest-teamcity-reporter": "^0.3.1",
180
- "webpack": "^5.102.1",
177
+ "typescript-eslint": "^8.48.0",
178
+ "vitest": "^4.0.14",
179
+ "vitest-teamcity-reporter": "^0.4.1",
180
+ "webpack": "^5.103.0",
181
181
  "webpack-cli": "^6.0.1"
182
182
  },
183
183
  "peerDependencies": {
@@ -212,12 +212,12 @@
212
212
  "babel-loader": "10.0.0",
213
213
  "babel-plugin-react-compiler": "^1.0.0",
214
214
  "babel-plugin-transform-define": "^2.1.4",
215
- "browserslist": "^4.27.0",
215
+ "browserslist": "^4.28.0",
216
216
  "change-case": "^4.1.1",
217
217
  "classnames": "^2.5.1",
218
218
  "combokeys": "^3.0.1",
219
219
  "css-loader": "^7.1.2",
220
- "csstype": "^3.1.3",
220
+ "csstype": "^3.2.1",
221
221
  "date-fns": "^4.1.0",
222
222
  "dequal": "^2.0.3",
223
223
  "element-resize-detector": "^1.2.4",
@@ -240,7 +240,7 @@
240
240
  "react-waypoint": "^10.3.0",
241
241
  "scrollbar-width": "^3.1.1",
242
242
  "simply-uuid": "^1.0.1",
243
- "sniffr": "^1.3.2",
243
+ "sniffr": "^1.4.0",
244
244
  "style-inject": "^0.3.0",
245
245
  "style-loader": "~4.0.0",
246
246
  "url-loader": "^4.1.1",