@jetbrains/ring-ui 7.0.103 → 7.0.104

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.
@@ -58,7 +58,6 @@ export default class Profile extends PureComponent {
58
58
  {
59
59
  rgItemType: PopupMenu.ListProps.Type.LINK,
60
60
  label: translations?.profile ?? translate('profile'),
61
- target: '_self', // Full page reload in Angular
62
61
  href: profileUrl,
63
62
  LinkComponent,
64
63
  },
@@ -43,6 +43,9 @@ export interface ListProps<T = unknown> {
43
43
  maxHeight?: number | null | undefined;
44
44
  activeIndex?: number | null | undefined;
45
45
  useMouseUp?: boolean | null | undefined;
46
+ /**
47
+ * @deprecated No longer used. Visibility is detected automatically via IntersectionObserver. Will be removed in Ring UI 8.0.
48
+ */
46
49
  visible?: boolean | null | undefined;
47
50
  disableMoveOverflow?: boolean | null | undefined;
48
51
  compact?: boolean | null | undefined;
@@ -130,6 +133,8 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
130
133
  virtualizedList?: VirtualizedList | null;
131
134
  unmounted?: boolean;
132
135
  container?: HTMLElement | null;
136
+ private intersectionObserver?;
137
+ private wasVisible;
133
138
  private _bufferSize;
134
139
  sizeCacheKey: (index: number) => string | Type.ITEM | Type.MARGIN;
135
140
  private _cache;
@@ -122,6 +122,17 @@ export default class List extends Component {
122
122
  if (!this.props.renderOptimization) {
123
123
  this.scrollToActive();
124
124
  }
125
+ if (this.props.renderOptimization && this.container && typeof IntersectionObserver !== 'undefined') {
126
+ this.intersectionObserver = new IntersectionObserver(entries => {
127
+ for (const entry of entries) {
128
+ if (entry.isIntersecting && !this.wasVisible && this.virtualizedList) {
129
+ this.virtualizedList.recomputeRowHeights();
130
+ }
131
+ this.wasVisible = entry.isIntersecting;
132
+ }
133
+ });
134
+ this.intersectionObserver.observe(this.container);
135
+ }
125
136
  }
126
137
  shouldComponentUpdate(nextProps, nextState) {
127
138
  return (Object.keys(nextProps).some(key => !Object.is(nextProps[key], this.props[key])) ||
@@ -145,6 +156,7 @@ export default class List extends Component {
145
156
  this.checkOverflow();
146
157
  }
147
158
  componentWillUnmount() {
159
+ this.intersectionObserver?.disconnect();
148
160
  this.unmounted = true;
149
161
  }
150
162
  scheduleScrollListener = scheduleRAF();
@@ -157,6 +169,8 @@ export default class List extends Component {
157
169
  virtualizedList;
158
170
  unmounted;
159
171
  container;
172
+ intersectionObserver;
173
+ wasVisible = false;
160
174
  // eslint-disable-next-line no-magic-numbers
161
175
  _bufferSize = 10; // keep X items above and below of the visible area
162
176
  // reuse size cache for similar items
@@ -9,7 +9,7 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
9
9
  const trapButtonNodeRef = useRef(null);
10
10
  const previousFocusedNodeRef = useRef(null);
11
11
  const trapWithoutFocusRef = useRef(false);
12
- const mountedRef = useRef(false);
12
+ const mountedRef = useRef(undefined);
13
13
  // It's the same approach as in focus-trap-react:
14
14
  // https://github.com/focus-trap/focus-trap-react/commit/3b22fca9eebeb883edc89548850fe5a5b9d6d50e
15
15
  // We can't do it in useEffect because it's too late, some children might have already
@@ -18,16 +18,16 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
18
18
  previousFocusedNodeRef.current = document.activeElement;
19
19
  }
20
20
  useImperativeHandle(ref, () => ({ node: nodeRef.current }), []);
21
- function restoreFocus() {
21
+ function restoreFocusIfUnmounted(orElse) {
22
22
  const previousFocusedNode = previousFocusedNodeRef.current;
23
23
  if (previousFocusedNode instanceof HTMLElement &&
24
24
  previousFocusedNode.focus &&
25
- isNodeInVisiblePartOfPage(previousFocusedNode)) {
26
- // This is to prevent the focus from being restored the first time
27
- // componentWillUnmount is called in StrictMode.
28
- if (!mountedRef.current) {
29
- previousFocusedNode.focus({ preventScroll: true });
30
- }
25
+ isNodeInVisiblePartOfPage(previousFocusedNode) &&
26
+ mountedRef.current === false) {
27
+ previousFocusedNode.focus({ preventScroll: true });
28
+ }
29
+ else {
30
+ orElse?.();
31
31
  }
32
32
  }
33
33
  function focusElement(first = true) {
@@ -65,7 +65,7 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
65
65
  }
66
66
  return () => {
67
67
  if (focusBackOnClose) {
68
- restoreFocus();
68
+ restoreFocusIfUnmounted();
69
69
  }
70
70
  };
71
71
  }, [autoFocusFirst, trapDisabled, focusBackOnClose, focusFirst]);
@@ -76,7 +76,7 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
76
76
  if (focusBackOnExit) {
77
77
  const prevFocused = event.nativeEvent.relatedTarget;
78
78
  if (prevFocused && nodeRef.current && prevFocused instanceof Element && nodeRef.current.contains(prevFocused)) {
79
- restoreFocus();
79
+ restoreFocusIfUnmounted(focusLast);
80
80
  }
81
81
  }
82
82
  else {
@@ -97,6 +97,14 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
97
97
  }
98
98
  focusLast();
99
99
  }
100
+ function restoreFocusOrFocusFirst() {
101
+ if (focusBackOnExit) {
102
+ restoreFocusIfUnmounted(focusFirst);
103
+ }
104
+ else {
105
+ focusFirst();
106
+ }
107
+ }
100
108
  if (trapDisabled) {
101
109
  return (<div ref={nodeRef} {...restProps}>
102
110
  {children}
@@ -111,7 +119,7 @@ const TabTrap = forwardRef(function TabTrap({ children, trapDisabled = false, au
111
119
  <div
112
120
  // It never actually stays focused
113
121
  // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
114
- tabIndex={0} onFocus={focusBackOnExit ? restoreFocus : focusFirst} data-trap-button/>
122
+ tabIndex={0} onFocus={restoreFocusOrFocusFirst} data-trap-button/>
115
123
  </div>);
116
124
  });
117
125
  export default TabTrap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.103",
3
+ "version": "7.0.104",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"
@@ -191,7 +191,7 @@
191
191
  "vitest": "^4.0.18",
192
192
  "vitest-teamcity-reporter": "^0.4.1",
193
193
  "webpack": "^5.105.4",
194
- "webpack-cli": "^6.0.1",
194
+ "webpack-cli": "^7.0.0",
195
195
  "xmlappend": "^1.0.4"
196
196
  },
197
197
  "peerDependencies": {
@@ -243,7 +243,6 @@
243
243
  "memoize-one": "^6.0.0",
244
244
  "postcss": "^8.5.8",
245
245
  "postcss-calc": "^10.1.1",
246
- "postcss-flexbugs-fixes": "^5.0.2",
247
246
  "postcss-font-family-system-ui": "^5.0.0",
248
247
  "postcss-loader": "^8.2.1",
249
248
  "postcss-modules-values-replace": "^4.2.2",
package/postcss.config.js CHANGED
@@ -10,7 +10,6 @@ module.exports = () => {
10
10
  },
11
11
  }),
12
12
  require('postcss-font-family-system-ui')({browsers: ['last 2 versions']}),
13
- require('postcss-flexbugs-fixes')(),
14
13
  require('@jetbrains/postcss-require-hover')(),
15
14
  require('postcss-calc')({mediaQueries: true}),
16
15
  ];