@douyinfe/semi-ui 2.7.0 → 2.8.0-beta.1

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.
Files changed (48) hide show
  1. package/_base/_story/index.stories.js +2 -6
  2. package/_portal/_story/portal.stories.js +1 -5
  3. package/_utils/hooks/usePrevFocus.ts +1 -0
  4. package/_utils/index.ts +29 -1
  5. package/datePicker/_story/v2/FixDefaultPickerValue.jsx +31 -0
  6. package/datePicker/_story/v2/InsetInput.jsx +1 -1
  7. package/datePicker/_story/v2/index.js +1 -0
  8. package/datePicker/monthsGrid.tsx +3 -13
  9. package/dist/css/semi.css +30 -21
  10. package/dist/css/semi.min.css +1 -1
  11. package/dist/umd/semi-ui.js +608 -284
  12. package/dist/umd/semi-ui.js.map +1 -1
  13. package/dist/umd/semi-ui.min.js +1 -1
  14. package/dist/umd/semi-ui.min.js.map +1 -1
  15. package/input/_story/input.stories.js +10 -1
  16. package/inputNumber/_story/inputNumber.stories.js +4 -0
  17. package/lib/cjs/_utils/hooks/usePrevFocus.js +1 -0
  18. package/lib/cjs/_utils/index.d.ts +3 -1
  19. package/lib/cjs/_utils/index.js +25 -1
  20. package/lib/cjs/datePicker/monthsGrid.js +11 -19
  21. package/lib/cjs/modal/useModal/HookModal.js +2 -0
  22. package/lib/cjs/notification/useNotification/index.js +1 -1
  23. package/lib/cjs/popover/index.d.ts +18 -3
  24. package/lib/cjs/popover/index.js +53 -23
  25. package/lib/cjs/tooltip/index.d.ts +22 -4
  26. package/lib/cjs/tooltip/index.js +65 -27
  27. package/lib/es/_utils/hooks/usePrevFocus.js +2 -0
  28. package/lib/es/_utils/index.d.ts +3 -1
  29. package/lib/es/_utils/index.js +18 -0
  30. package/lib/es/datePicker/monthsGrid.js +11 -19
  31. package/lib/es/modal/useModal/HookModal.js +2 -0
  32. package/lib/es/notification/useNotification/index.js +2 -1
  33. package/lib/es/popover/index.d.ts +18 -3
  34. package/lib/es/popover/index.js +52 -23
  35. package/lib/es/tooltip/index.d.ts +22 -4
  36. package/lib/es/tooltip/index.js +65 -27
  37. package/modal/_story/modal.stories.js +93 -1
  38. package/modal/useModal/HookModal.tsx +1 -0
  39. package/notification/_story/useNotification/index.jsx +21 -7
  40. package/notification/useNotification/index.tsx +1 -1
  41. package/package.json +9 -9
  42. package/popover/_story/popover.stories.js +75 -1
  43. package/popover/index.tsx +24 -8
  44. package/select/__test__/select.test.js +16 -0
  45. package/table/_story/v2/FixedMemoryLeak/index.jsx +33 -0
  46. package/table/_story/v2/index.js +2 -1
  47. package/toast/_story/toast.stories.js +41 -0
  48. package/tooltip/index.tsx +72 -22
package/tooltip/index.tsx CHANGED
@@ -3,7 +3,7 @@ import React, { isValidElement, cloneElement } from 'react';
3
3
  import ReactDOM from 'react-dom';
4
4
  import classNames from 'classnames';
5
5
  import PropTypes from 'prop-types';
6
- import { throttle, noop, get, omit, each, isEmpty } from 'lodash';
6
+ import { throttle, noop, get, omit, each, isEmpty, isFunction } from 'lodash';
7
7
 
8
8
  import { BASE_CLASS_PREFIX } from '@douyinfe/semi-foundation/base/constants';
9
9
  import warning from '@douyinfe/semi-foundation/utils/warning';
@@ -17,7 +17,7 @@ import '@douyinfe/semi-foundation/tooltip/tooltip.scss';
17
17
 
18
18
  import BaseComponent, { BaseProps } from '../_base/baseComponent';
19
19
  import { isHTMLElement } from '../_base/reactUtils';
20
- import { stopPropagation } from '../_utils';
20
+ import { getActiveElement, getFocusableElements, stopPropagation } from '../_utils';
21
21
  import Portal from '../_portal/index';
22
22
  import ConfigContext from '../configProvider/context';
23
23
  import TriangleArrow from './TriangleArrow';
@@ -36,6 +36,12 @@ export interface ArrowBounding {
36
36
  height?: number;
37
37
  }
38
38
 
39
+ export interface RenderContentProps {
40
+ initialFocusRef?: React.RefObject<HTMLElement>;
41
+ }
42
+
43
+ export type RenderContent = (props: RenderContentProps) => React.ReactNode;
44
+
39
45
  export interface TooltipProps extends BaseProps {
40
46
  children?: React.ReactNode;
41
47
  motion?: Motion;
@@ -49,7 +55,7 @@ export interface TooltipProps extends BaseProps {
49
55
  clickToHide?: boolean;
50
56
  visible?: boolean;
51
57
  style?: React.CSSProperties;
52
- content?: React.ReactNode;
58
+ content?: React.ReactNode | RenderContent;
53
59
  prefixCls?: string;
54
60
  onVisibleChange?: (visible: boolean) => void;
55
61
  onClickOutSide?: (e: React.MouseEvent) => void;
@@ -65,6 +71,10 @@ export interface TooltipProps extends BaseProps {
65
71
  stopPropagation?: boolean;
66
72
  clickTriggerToHide?: boolean;
67
73
  wrapperClassName?: string;
74
+ closeOnEsc?: boolean;
75
+ guardFocus?: boolean;
76
+ returnFocusOnClose?: boolean;
77
+ onEscKeyDown?: (e: React.KeyboardEvent) => void;
68
78
  }
69
79
  interface TooltipState {
70
80
  visible: boolean;
@@ -108,7 +118,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
108
118
  clickTriggerToHide: PropTypes.bool,
109
119
  visible: PropTypes.bool,
110
120
  style: PropTypes.object,
111
- content: PropTypes.node,
121
+ content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
112
122
  prefixCls: PropTypes.string,
113
123
  onVisibleChange: PropTypes.func,
114
124
  onClickOutSide: PropTypes.func,
@@ -123,6 +133,8 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
123
133
  // private
124
134
  role: PropTypes.string,
125
135
  wrapWhenSpecial: PropTypes.bool, // when trigger has special status such as "disabled" or "loading", wrap span
136
+ guardFocus: PropTypes.bool,
137
+ returnFocusOnClose: PropTypes.bool,
126
138
  };
127
139
 
128
140
  static defaultProps = {
@@ -143,11 +155,16 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
143
155
  showArrow: true,
144
156
  wrapWhenSpecial: true,
145
157
  zIndex: numbers.DEFAULT_Z_INDEX,
158
+ closeOnEsc: false,
159
+ guardFocus: false,
160
+ returnFocusOnClose: false,
161
+ onEscKeyDown: noop,
146
162
  };
147
163
 
148
164
  eventManager: Event;
149
165
  triggerEl: React.RefObject<unknown>;
150
- containerEl: React.RefObject<unknown>;
166
+ containerEl: React.RefObject<HTMLDivElement>;
167
+ initialFocusRef: React.RefObject<HTMLElement>;
151
168
  clickOutsideHandler: any;
152
169
  resizeHandler: any;
153
170
  isWrapped: boolean;
@@ -155,6 +172,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
155
172
  scrollHandler: any;
156
173
  getPopupContainer: () => HTMLElement;
157
174
  containerPosition: string;
175
+ foundation: TooltipFoundation;
158
176
 
159
177
  constructor(props: TooltipProps) {
160
178
  super(props);
@@ -180,6 +198,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
180
198
  this.eventManager = new Event();
181
199
  this.triggerEl = React.createRef();
182
200
  this.containerEl = React.createRef();
201
+ this.initialFocusRef = React.createRef();
183
202
  this.clickOutsideHandler = null;
184
203
  this.resizeHandler = null;
185
204
  this.isWrapped = false; // Identifies whether a span element is wrapped
@@ -197,7 +216,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
197
216
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
198
217
  // @ts-ignore
199
218
  off: (...args: any[]) => this.eventManager.off(...args),
200
- insertPortal: (content: string, { position, ...containerStyle }: { position: Position }) => {
219
+ insertPortal: (content: TooltipProps['content'], { position, ...containerStyle }: { position: Position }) => {
201
220
  this.setState(
202
221
  {
203
222
  isInsert: true,
@@ -223,6 +242,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
223
242
  click: 'onClick',
224
243
  focus: 'onFocus',
225
244
  blur: 'onBlur',
245
+ keydown: 'onKeyDown'
226
246
  }),
227
247
  registerTriggerEvent: (triggerEventSet: Record<string, any>) => {
228
248
  this.setState({ triggerEventSet });
@@ -236,12 +256,8 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
236
256
  // eslint-disable-next-line
237
257
  // It may be a React component or an html element
238
258
  // There is no guarantee that triggerE l.current can get the real dom, so call findDOMNode to ensure that you can get the real dom
239
- let triggerDOM = this.triggerEl.current;
240
- if (!isHTMLElement(this.triggerEl.current)) {
241
- const realDomNode = ReactDOM.findDOMNode(this.triggerEl.current as React.ReactInstance);
242
- (this.triggerEl as any).current = realDomNode;
243
- triggerDOM = realDomNode;
244
- }
259
+ const triggerDOM = this.adapter.getTriggerNode();
260
+ (this.triggerEl as any).current = triggerDOM;
245
261
  return triggerDOM && (triggerDOM as Element).getBoundingClientRect();
246
262
  },
247
263
  // Gets the outer size of the specified container
@@ -302,7 +318,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
302
318
  } else {
303
319
  willUpdateStates.visible = visible;
304
320
  }
305
- this.setState(willUpdateStates as TooltipState, () => {
321
+ this.mounted && this.setState(willUpdateStates as TooltipState, () => {
306
322
  cb();
307
323
  });
308
324
  },
@@ -317,7 +333,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
317
333
  let el = this.triggerEl && this.triggerEl.current;
318
334
  let popupEl = this.containerEl && this.containerEl.current;
319
335
  el = ReactDOM.findDOMNode(el as React.ReactInstance);
320
- popupEl = ReactDOM.findDOMNode(popupEl as React.ReactInstance);
336
+ popupEl = ReactDOM.findDOMNode(popupEl as React.ReactInstance) as HTMLDivElement;
321
337
  if (
322
338
  (el && !(el as any).contains(e.target) && popupEl && !(popupEl as any).contains(e.target)) ||
323
339
  this.props.clickTriggerToHide
@@ -363,10 +379,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
363
379
  if (!this.mounted) {
364
380
  return false;
365
381
  }
366
- let triggerDOM = this.triggerEl.current;
367
- if (!isHTMLElement(this.triggerEl.current)) {
368
- triggerDOM = ReactDOM.findDOMNode(this.triggerEl.current as React.ReactInstance);
369
- }
382
+ const triggerDOM = this.adapter.getTriggerNode();
370
383
  const isRelativeScroll = e.target.contains(triggerDOM);
371
384
  if (isRelativeScroll) {
372
385
  const scrollPos = { x: e.target.scrollLeft, y: e.target.scrollTop };
@@ -392,6 +405,29 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
392
405
  }
393
406
  },
394
407
  getContainerPosition: () => this.containerPosition,
408
+ getContainer: () => this.containerEl && this.containerEl.current,
409
+ getTriggerNode: () => {
410
+ let triggerDOM = this.triggerEl.current;
411
+ if (!isHTMLElement(this.triggerEl.current)) {
412
+ triggerDOM = ReactDOM.findDOMNode(this.triggerEl.current as React.ReactInstance);
413
+ }
414
+ return triggerDOM as Element;
415
+ },
416
+ getFocusableElements: (node: HTMLDivElement) => {
417
+ return getFocusableElements(node);
418
+ },
419
+ getActiveElement: () => {
420
+ return getActiveElement();
421
+ },
422
+ setInitialFocus: () => {
423
+ const focusRefNode = get(this, 'initialFocusRef.current');
424
+ if (focusRefNode && 'focus' in focusRefNode) {
425
+ focusRefNode.focus();
426
+ }
427
+ },
428
+ notifyEscKeydown: (event: React.KeyboardEvent) => {
429
+ this.props.onEscKeyDown(event);
430
+ }
395
431
  };
396
432
  }
397
433
 
@@ -491,9 +527,21 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
491
527
  }
492
528
  };
493
529
 
530
+ handlePortalInnerKeyDown = (e: React.KeyboardEvent) => {
531
+ this.foundation.handleContainerKeydown(e);
532
+ }
533
+
534
+ renderContentNode = (content: TooltipProps['content']) => {
535
+ const contentProps = {
536
+ initialFocusRef: this.initialFocusRef
537
+ };
538
+ return !isFunction(content) ? content : content(contentProps);
539
+ };
540
+
494
541
  renderPortal = () => {
495
542
  const { containerStyle = {}, visible, portalEventSet, placement, transitionState, id, isPositionUpdated } = this.state;
496
543
  const { prefixCls, content, showArrow, style, motion, role, zIndex } = this.props;
544
+ const contentNode = this.renderContentNode(content);
497
545
  const { className: propClassName } = this.props;
498
546
  const direction = this.context.direction;
499
547
  const className = classNames(propClassName, {
@@ -524,7 +572,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
524
572
  x-placement={placement}
525
573
  id={id}
526
574
  >
527
- {content}
575
+ {contentNode}
528
576
  {icon}
529
577
  </div>
530
578
  ) :
@@ -533,7 +581,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
533
581
  </TooltipTransition>
534
582
  ) : (
535
583
  <div className={className} {...portalEventSet} x-placement={placement} style={{ visibility: motion ? undefined : 'visible', ...style }}>
536
- {content}
584
+ {contentNode}
537
585
  {icon}
538
586
  </div>
539
587
  );
@@ -546,6 +594,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
546
594
  style={portalInnerStyle}
547
595
  ref={this.setContainerEl}
548
596
  onClick={this.handlePortalInnerClick}
597
+ onKeyDown={this.handlePortalInnerKeyDown}
549
598
  >
550
599
  {inner}
551
600
  </div>
@@ -587,7 +636,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
587
636
 
588
637
  render() {
589
638
  const { isInsert, triggerEventSet, visible, id } = this.state;
590
- const { wrapWhenSpecial, role } = this.props;
639
+ const { wrapWhenSpecial, role, trigger } = this.props;
591
640
  let { children } = this.props;
592
641
  const childrenStyle = { ...get(children, 'props.style') };
593
642
  const extraStyle: React.CSSProperties = {};
@@ -648,6 +697,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
648
697
  ref.current = node;
649
698
  }
650
699
  },
700
+ tabIndex: trigger === 'hover' ? 0 : undefined, // a11y keyboard
651
701
  });
652
702
 
653
703
  // If you do not add a layer of div, in order to bind the events and className in the tooltip, you need to cloneElement children, but this time it may overwrite the children's original ref reference
@@ -661,4 +711,4 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
661
711
  }
662
712
  }
663
713
 
664
- export { Position };
714
+ export { Position };