@jetbrains/ring-ui 7.0.97 → 7.0.98

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.
@@ -150,6 +150,7 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
150
150
  getSelected(): ListDataItem<T> | null;
151
151
  defaultItemHeight(): number;
152
152
  scrollEndHandler: () => void;
153
+ scrollToActive: () => void;
153
154
  checkOverflow: () => void;
154
155
  getVisibleListHeight(maxHeight: number): number;
155
156
  private _deprecatedGenerateKeyFromContent;
@@ -119,6 +119,9 @@ export default class List extends Component {
119
119
  shouldActivateFirstItem(this.props)) {
120
120
  this.activateFirst();
121
121
  }
122
+ if (!this.props.renderOptimization) {
123
+ this.scrollToActive();
124
+ }
122
125
  }
123
126
  shouldComponentUpdate(nextProps, nextState) {
124
127
  return (Object.keys(nextProps).some(key => !Object.is(nextProps[key], this.props[key])) ||
@@ -129,18 +132,8 @@ export default class List extends Component {
129
132
  this.virtualizedList.recomputeRowHeights();
130
133
  }
131
134
  const { activeIndex } = this.state;
132
- if (!this.props.disableScrollToActive && activeIndex != null && activeIndex !== prevState.activeIndex) {
133
- if (this.virtualizedList) {
134
- this.virtualizedList.scrollToRow(activeIndex + 1);
135
- }
136
- else {
137
- const itemId = this.getId(this.props.data[activeIndex]);
138
- if (itemId) {
139
- document.getElementById(itemId)?.scrollIntoView?.({
140
- block: 'center',
141
- });
142
- }
143
- }
135
+ if (activeIndex != null && activeIndex !== prevState.activeIndex) {
136
+ this.scrollToActive();
144
137
  }
145
138
  const isActiveItemRetainedPosition = activeIndex != null ? prevProps.data[activeIndex]?.key === this.props.data[activeIndex]?.key : false;
146
139
  if ((this.props.activeIndex === null || this.props.activeIndex === undefined) &&
@@ -338,6 +331,21 @@ export default class List extends Component {
338
331
  }
339
332
  }
340
333
  });
334
+ scrollToActive = () => {
335
+ const { activeIndex } = this.state;
336
+ if (this.props.disableScrollToActive || activeIndex == null) {
337
+ return;
338
+ }
339
+ if (this.virtualizedList) {
340
+ this.virtualizedList.scrollToRow(activeIndex + 1);
341
+ }
342
+ else {
343
+ const itemId = this.getId(this.props.data[activeIndex]);
344
+ if (itemId) {
345
+ document.getElementById(itemId)?.scrollIntoView?.({ block: 'center' });
346
+ }
347
+ }
348
+ };
341
349
  checkOverflow = () => {
342
350
  if (this.inner) {
343
351
  this.setState({
@@ -449,7 +457,11 @@ export default class List extends Component {
449
457
  return props;
450
458
  };
451
459
  virtualizedListRef = (el) => {
460
+ const isFirstAssignment = el != null && this.virtualizedList == null;
452
461
  this.virtualizedList = el;
462
+ if (isFirstAssignment) {
463
+ this.scrollToActive();
464
+ }
453
465
  };
454
466
  containerRef = (el) => {
455
467
  this.container = el;
@@ -157,7 +157,7 @@ interface CaretPositionParams {
157
157
  forceSetCaret?: boolean | null | undefined;
158
158
  }
159
159
  interface HistoryEntry {
160
- query: string | null | undefined;
160
+ normalizedQuery: string;
161
161
  caret: Position | number;
162
162
  }
163
163
  /**
@@ -218,7 +218,8 @@ export default class QueryAssist extends Component<QueryAssistProps> {
218
218
  immediateState: QueryAssistChange;
219
219
  requestData?: (afterCompletion?: boolean) => void;
220
220
  ngModelStateField: string;
221
- historyStack: HistoryEntry[];
221
+ undoHistoryStack: HistoryEntry[];
222
+ redoHistoryStack: HistoryEntry[];
222
223
  mouseIsDownOnPopup?: boolean;
223
224
  handleFocusChange: (e: SyntheticEvent) => void;
224
225
  node?: HTMLElement | null;
@@ -233,9 +234,11 @@ export default class QueryAssist extends Component<QueryAssistProps> {
233
234
  handleInput: (e: Event | SyntheticEvent) => void;
234
235
  handleEnter: (e: React.KeyboardEvent) => void;
235
236
  handleTab: (e: Event) => boolean | void;
236
- setState: (state: Partial<QueryAssistState>, resolve?: () => void) => void;
237
+ setState: (state: Partial<QueryAssistState>, resolve?: () => void, undoOrRedo?: boolean) => void;
237
238
  private _pushHistory;
238
239
  undo: (e: Event) => void;
240
+ redo: (e: Event) => void;
241
+ private _undoOrRedo;
239
242
  handlePaste: (e: React.ClipboardEvent) => void;
240
243
  handleCaretMove: (e: Event | SyntheticEvent) => void;
241
244
  handleStyleRangesResponse: ({ suggestions, ...restProps }: QueryAssistResponse) => Promise<void>;
@@ -118,7 +118,7 @@ export default class QueryAssist extends Component {
118
118
  this.requestStyleRanges().catch(noop);
119
119
  }
120
120
  this.setCaretPosition();
121
- this._pushHistory(this.state);
121
+ this._pushHistory(this.state.query);
122
122
  }
123
123
  shouldComponentUpdate(props, state) {
124
124
  return (state.query !== this.state.query ||
@@ -163,8 +163,8 @@ export default class QueryAssist extends Component {
163
163
  immediateState;
164
164
  requestData;
165
165
  ngModelStateField = ngModelStateField;
166
- // An array of {query: string; caret: number}[]
167
- historyStack = [];
166
+ undoHistoryStack = [];
167
+ redoHistoryStack = [];
168
168
  mouseIsDownOnPopup;
169
169
  handleFocusChange = (e) => {
170
170
  // otherwise it's blur and false
@@ -290,6 +290,9 @@ export default class QueryAssist extends Component {
290
290
  if (this.props.autoOpen === 'force' || props.query.length > 0) {
291
291
  this.requestData?.();
292
292
  }
293
+ else {
294
+ this.handleResponse({ caret: props.caret });
295
+ }
293
296
  };
294
297
  // It's necessary to prevent new element creation before any other hooks
295
298
  handleEnter = (e) => {
@@ -313,31 +316,45 @@ export default class QueryAssist extends Component {
313
316
  }
314
317
  return true;
315
318
  };
316
- setState = (state, resolve) => {
319
+ setState = (state, resolve, undoOrRedo = false) => {
317
320
  super.setState(state, () => {
318
- this._pushHistory(state);
321
+ if (!undoOrRedo && 'query' in state) {
322
+ this._pushHistory(state.query);
323
+ }
319
324
  resolve?.();
320
325
  });
321
326
  };
322
- _pushHistory(state) {
323
- const queryIsSet = 'query' in state;
324
- const queryIsSame = this.historyStack[0]?.query === state.query;
325
- if (queryIsSet && !queryIsSame) {
326
- this.historyStack.unshift({
327
- query: state.query,
327
+ _pushHistory(query) {
328
+ const normalizedQuery = query ?? '';
329
+ if (!this.undoHistoryStack.length || this.undoHistoryStack[0].normalizedQuery !== normalizedQuery) {
330
+ this.undoHistoryStack.unshift({
331
+ normalizedQuery,
328
332
  caret: this.caret?.getPosition({ avoidFocus: true }) ?? -1,
329
333
  });
334
+ this.redoHistoryStack = [];
330
335
  }
331
336
  }
332
337
  undo = (e) => {
333
- const previous = this.historyStack.splice(0, 2)[1];
334
- if (!previous) {
338
+ this._undoOrRedo(e, false);
339
+ };
340
+ redo = (e) => {
341
+ this._undoOrRedo(e, true);
342
+ };
343
+ _undoOrRedo = (e, redo) => {
344
+ const stack = redo ? this.redoHistoryStack : this.undoHistoryStack;
345
+ const [current, previous] = stack;
346
+ const stateToApply = redo ? current : previous;
347
+ if (!stateToApply) {
335
348
  return;
336
349
  }
337
- this.setState({ query: previous.query }, () => {
338
- this.caret?.setPosition(previous.caret);
350
+ stack.shift();
351
+ e.preventDefault?.();
352
+ this.setState({ query: stateToApply.normalizedQuery }, () => {
353
+ const oppositeStack = redo ? this.undoHistoryStack : this.redoHistoryStack;
354
+ oppositeStack.unshift(current);
355
+ this.caret?.setPosition(stateToApply.caret);
339
356
  this.handleInput(e);
340
- });
357
+ }, true);
341
358
  };
342
359
  handlePaste = (e) => {
343
360
  const INSERT_COMMAND = 'insertText';
@@ -368,7 +385,7 @@ export default class QueryAssist extends Component {
368
385
  }
369
386
  };
370
387
  handleStyleRangesResponse = ({ suggestions, ...restProps }) => this.handleResponse(restProps);
371
- handleResponse = ({ query = '', caret = 0, styleRanges, suggestions = [] }, afterCompletion = false) => new Promise((resolve, reject) => {
388
+ handleResponse = ({ query = '', caret = 0, styleRanges = [], suggestions = [] }, afterCompletion = false) => new Promise((resolve, reject) => {
372
389
  if (query === this.getQuery() &&
373
390
  (caret === this.immediateState.caret || this.immediateState.caret === undefined)) {
374
391
  // Do not setState on unmounted component
@@ -665,6 +682,7 @@ export default class QueryAssist extends Component {
665
682
  'ctrl+space': this.handleCtrlSpace,
666
683
  tab: this.handleTab,
667
684
  'meta+z': this.undo,
685
+ 'meta+shift+z': this.redo,
668
686
  right: noop,
669
687
  left: noop,
670
688
  space: noop,
@@ -13,6 +13,7 @@ export interface TooltipProps extends Omit<AllHTMLAttributes<HTMLSpanElement>, '
13
13
  selfOverflowOnly?: boolean | null | undefined;
14
14
  popupProps?: Partial<PopupAttrs> | null | undefined;
15
15
  title?: ReactNode | null | undefined;
16
+ hide?: boolean | null | undefined;
16
17
  theme?: Theme | 'inherit';
17
18
  'data-test'?: string | null | undefined;
18
19
  long?: boolean | null | undefined;
@@ -21,6 +22,7 @@ export interface TooltipProps extends Omit<AllHTMLAttributes<HTMLSpanElement>, '
21
22
  * @name Tooltip
22
23
  */
23
24
  export default class Tooltip extends Component<TooltipProps> {
25
+ private static isShow;
24
26
  static defaultProps: {
25
27
  title: string;
26
28
  selfOverflowOnly: boolean;
@@ -13,6 +13,9 @@ const TooltipContext = createContext(undefined);
13
13
  * @name Tooltip
14
14
  */
15
15
  export default class Tooltip extends Component {
16
+ static isShow({ title, hide }) {
17
+ return !!title && !hide;
18
+ }
16
19
  static defaultProps = {
17
20
  title: '',
18
21
  selfOverflowOnly: false,
@@ -24,15 +27,17 @@ export default class Tooltip extends Component {
24
27
  showNestedPopup: false,
25
28
  };
26
29
  componentDidMount() {
27
- if (this.props.title) {
30
+ if (Tooltip.isShow(this.props)) {
28
31
  this.addListeners();
29
32
  }
30
33
  }
31
34
  componentDidUpdate(prevProps) {
32
- if (!prevProps.title && this.props.title) {
35
+ const prevShow = Tooltip.isShow(prevProps);
36
+ const currShow = Tooltip.isShow(this.props);
37
+ if (!prevShow && currShow) {
33
38
  this.addListeners();
34
39
  }
35
- else if (prevProps.title && !this.props.title) {
40
+ else if (prevShow && !currShow) {
36
41
  this.hidePopup();
37
42
  this.listeners.removeAll();
38
43
  }
@@ -50,10 +55,9 @@ export default class Tooltip extends Component {
50
55
  this.containerNode = el;
51
56
  };
52
57
  tryToShowPopup = () => {
53
- const { delay, title } = this.props;
54
- if (!title) {
58
+ if (!Tooltip.isShow(this.props))
55
59
  return;
56
- }
60
+ const { delay } = this.props;
57
61
  if (delay) {
58
62
  clearTimeout(this.timeout);
59
63
  this.timeout = window.setTimeout(this.showPopup, delay);
@@ -125,8 +129,8 @@ export default class Tooltip extends Component {
125
129
  this.setState({ showNestedPopup: false });
126
130
  };
127
131
  render() {
128
- const { children, 'data-test': dataTest, title, delay, theme, selfOverflowOnly, popupProps, long, ...restProps } = this.props;
129
- const ariaProps = typeof title === 'string' && !!title ? { 'aria-label': title, role: 'tooltip' } : {};
132
+ const { children, 'data-test': dataTest, title, delay, theme, selfOverflowOnly, popupProps, long, hide, ...restProps } = this.props;
133
+ const ariaProps = typeof title === 'string' && Tooltip.isShow({ title, hide }) ? { 'aria-label': title, role: 'tooltip' } : {};
130
134
  const { onNestedTooltipShow, onNestedTooltipHide } = this;
131
135
  const popup = (<Popup trapFocus={false} anchorElement={this.containerNode} hidden={!this.state.showPopup || this.state.showNestedPopup} onCloseAttempt={this.hidePopup} maxHeight={400} attached={false} onMouseOut={this.hideIfMovedOutsidePopup} top={4} dontCloseOnAnchorClick ref={this.popupRef} {...popupProps} className={classNames(styles.tooltip, { [styles.long]: long, [styles.inheritedTheme]: theme === 'inherit' }, popupProps?.className)}>
132
136
  {title}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetbrains/ring-ui",
3
- "version": "7.0.97",
3
+ "version": "7.0.98",
4
4
  "description": "JetBrains UI library",
5
5
  "author": {
6
6
  "name": "JetBrains"