@douyinfe/semi-foundation 2.14.0 → 2.15.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 (91) hide show
  1. package/cascader/cascader.scss +20 -0
  2. package/cascader/foundation.ts +21 -0
  3. package/cascader/variables.scss +2 -0
  4. package/checkbox/checkbox.scss +9 -9
  5. package/checkbox/checkboxFoundation.ts +1 -0
  6. package/datePicker/foundation.ts +32 -8
  7. package/datePicker/monthsGridFoundation.ts +1 -2
  8. package/dropdown/dropdown.scss +4 -0
  9. package/dropdown/foundation.ts +38 -1
  10. package/dropdown/menuFoundation.ts +77 -0
  11. package/lib/cjs/cascader/cascader.css +15 -0
  12. package/lib/cjs/cascader/cascader.scss +20 -0
  13. package/lib/cjs/cascader/foundation.d.ts +6 -0
  14. package/lib/cjs/cascader/foundation.js +23 -0
  15. package/lib/cjs/cascader/variables.scss +2 -0
  16. package/lib/cjs/checkbox/checkbox.css +5 -5
  17. package/lib/cjs/checkbox/checkbox.scss +9 -9
  18. package/lib/cjs/checkbox/checkboxFoundation.d.ts +1 -0
  19. package/lib/cjs/datePicker/foundation.d.ts +7 -0
  20. package/lib/cjs/datePicker/foundation.js +38 -7
  21. package/lib/cjs/datePicker/monthsGridFoundation.js +1 -4
  22. package/lib/cjs/dropdown/dropdown.css +4 -0
  23. package/lib/cjs/dropdown/dropdown.scss +4 -0
  24. package/lib/cjs/dropdown/foundation.d.ts +4 -0
  25. package/lib/cjs/dropdown/foundation.js +48 -0
  26. package/lib/cjs/dropdown/menuFoundation.d.ts +9 -0
  27. package/lib/cjs/dropdown/menuFoundation.js +119 -0
  28. package/lib/cjs/list/list.css +1 -1
  29. package/lib/cjs/list/list.scss +1 -1
  30. package/lib/cjs/list/variables.scss +2 -1
  31. package/lib/cjs/modal/modalFoundation.d.ts +1 -0
  32. package/lib/cjs/rating/foundation.d.ts +13 -0
  33. package/lib/cjs/rating/foundation.js +123 -35
  34. package/lib/cjs/rating/rating.css +14 -5
  35. package/lib/cjs/rating/rating.scss +21 -8
  36. package/lib/cjs/rating/variables.scss +4 -0
  37. package/lib/cjs/tabs/foundation.js +28 -6
  38. package/lib/cjs/tooltip/foundation.js +39 -9
  39. package/lib/cjs/tree/foundation.d.ts +1 -0
  40. package/lib/cjs/treeSelect/foundation.js +9 -1
  41. package/lib/cjs/utils/FocusHandle.d.ts +1 -0
  42. package/lib/cjs/utils/FocusHandle.js +6 -1
  43. package/lib/cjs/utils/a11y.d.ts +9 -0
  44. package/lib/cjs/utils/a11y.js +123 -0
  45. package/lib/es/cascader/cascader.css +15 -0
  46. package/lib/es/cascader/cascader.scss +20 -0
  47. package/lib/es/cascader/foundation.d.ts +6 -0
  48. package/lib/es/cascader/foundation.js +23 -0
  49. package/lib/es/cascader/variables.scss +2 -0
  50. package/lib/es/checkbox/checkbox.css +5 -5
  51. package/lib/es/checkbox/checkbox.scss +9 -9
  52. package/lib/es/checkbox/checkboxFoundation.d.ts +1 -0
  53. package/lib/es/datePicker/foundation.d.ts +7 -0
  54. package/lib/es/datePicker/foundation.js +37 -7
  55. package/lib/es/datePicker/monthsGridFoundation.js +1 -3
  56. package/lib/es/dropdown/dropdown.css +4 -0
  57. package/lib/es/dropdown/dropdown.scss +4 -0
  58. package/lib/es/dropdown/foundation.d.ts +4 -0
  59. package/lib/es/dropdown/foundation.js +45 -0
  60. package/lib/es/dropdown/menuFoundation.d.ts +9 -0
  61. package/lib/es/dropdown/menuFoundation.js +99 -0
  62. package/lib/es/list/list.css +1 -1
  63. package/lib/es/list/list.scss +1 -1
  64. package/lib/es/list/variables.scss +2 -1
  65. package/lib/es/modal/modalFoundation.d.ts +1 -0
  66. package/lib/es/rating/foundation.d.ts +13 -0
  67. package/lib/es/rating/foundation.js +116 -32
  68. package/lib/es/rating/rating.css +14 -5
  69. package/lib/es/rating/rating.scss +21 -8
  70. package/lib/es/rating/variables.scss +4 -0
  71. package/lib/es/tabs/foundation.js +30 -6
  72. package/lib/es/tooltip/foundation.js +38 -9
  73. package/lib/es/tree/foundation.d.ts +1 -0
  74. package/lib/es/treeSelect/foundation.js +9 -1
  75. package/lib/es/utils/FocusHandle.d.ts +1 -0
  76. package/lib/es/utils/FocusHandle.js +6 -1
  77. package/lib/es/utils/a11y.d.ts +9 -0
  78. package/lib/es/utils/a11y.js +101 -0
  79. package/list/list.scss +1 -1
  80. package/list/variables.scss +2 -1
  81. package/modal/modalFoundation.ts +1 -0
  82. package/package.json +2 -2
  83. package/rating/foundation.ts +90 -31
  84. package/rating/rating.scss +21 -8
  85. package/rating/variables.scss +4 -0
  86. package/tabs/foundation.ts +9 -6
  87. package/tooltip/foundation.ts +16 -8
  88. package/tree/foundation.ts +1 -0
  89. package/treeSelect/foundation.ts +5 -1
  90. package/utils/FocusHandle.ts +3 -1
  91. package/utils/a11y.ts +105 -0
@@ -79,6 +79,7 @@ class TabsFoundation<P = Record<string, any>, S = Record<string, any>> extends B
79
79
  }
80
80
 
81
81
  handleKeyDown = (event: any, itemKey: string, closable: boolean) => {
82
+ const { preventScroll } = this.getProps();
82
83
  const tabs = [...event.target.parentNode.childNodes].filter(item => {
83
84
  return get(item, 'attributes.data-tabkey.value', '').includes('semiTab') && get(item, 'attributes.aria-disabled.value', '') !== "true";
84
85
  });
@@ -100,11 +101,11 @@ class TabsFoundation<P = Record<string, any>, S = Record<string, any>> extends B
100
101
  this.handlePrevent(event);
101
102
  break;
102
103
  case "Home":
103
- tabs[0].focus(); // focus first tab
104
+ tabs[0].focus({ preventScroll }); // focus first tab
104
105
  this.handlePrevent(event);
105
106
  break;
106
107
  case "End":
107
- tabs[tabs.length - 1].focus(); // focus last tab
108
+ tabs[tabs.length - 1].focus({ preventScroll }); // focus last tab
108
109
  this.handlePrevent(event);
109
110
  break;
110
111
  }
@@ -128,18 +129,20 @@ class TabsFoundation<P = Record<string, any>, S = Record<string, any>> extends B
128
129
  }
129
130
 
130
131
  handleDeleteKeyDown(event:any, tabs: HTMLElement[], itemKey: string, closable: boolean): void {
132
+ const { preventScroll } = this.getProps();
131
133
  if (closable) {
132
134
  this.handleTabDelete(itemKey);
133
135
  const index = tabs.indexOf(event.target);
134
136
  // Move focus to next element after deletion
135
137
  // If the element is the last removable tab, focus to its previous tab
136
138
  if (tabs.length !== 1 ){
137
- tabs[index + 1 >= tabs.length ? index - 1 : index + 1].focus();
139
+ tabs[index + 1 >= tabs.length ? index - 1 : index + 1].focus({ preventScroll });
138
140
  }
139
141
  }
140
142
  }
141
143
 
142
144
  switchTabOnArrowPress(event: any, tabs: HTMLElement[]): void {
145
+ const { preventScroll } = this.getProps();
143
146
  const index = tabs.indexOf(event.target);
144
147
 
145
148
  const direction = {
@@ -152,11 +155,11 @@ class TabsFoundation<P = Record<string, any>, S = Record<string, any>> extends B
152
155
  if (direction[event.key]) {
153
156
  if (index !== undefined) {
154
157
  if (tabs[index + direction[event.key]]) {
155
- tabs[index+ direction[event.key]].focus();
158
+ tabs[index+ direction[event.key]].focus({ preventScroll });
156
159
  } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
157
- tabs[tabs.length - 1].focus(); // focus last tab
160
+ tabs[tabs.length - 1].focus({ preventScroll }); // focus last tab
158
161
  } else if (event.key === "ArrowRight" || event.key == "ArrowDown") {
159
- tabs[0].focus(); // focus first tab
162
+ tabs[0].focus({ preventScroll }); // focus first tab
160
163
  }
161
164
  }
162
165
  }
@@ -6,6 +6,7 @@ import { DOMRectLikeType } from '../utils/dom';
6
6
  import BaseFoundation, { DefaultAdapter } from '../base/foundation';
7
7
  import { ArrayElement } from '../utils/type';
8
8
  import { strings } from './constants';
9
+ import { handlePrevent } from '../utils/a11y';
9
10
 
10
11
  const REGS = {
11
12
  TOP: /top/i,
@@ -862,6 +863,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
862
863
 
863
864
  switch (event && event.key) {
864
865
  case "Escape":
866
+ handlePrevent(event);
865
867
  closeOnEsc && this._handleEscKeyDown(event);
866
868
  break;
867
869
  case "ArrowUp":
@@ -885,11 +887,11 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
885
887
  * 因此 returnFocusOnClose 只支持 click trigger
886
888
  */
887
889
  _focusTrigger() {
888
- const { trigger, returnFocusOnClose } = this.getProps();
889
- if (returnFocusOnClose && trigger === 'click') {
890
+ const { trigger, returnFocusOnClose, preventScroll } = this.getProps();
891
+ if (returnFocusOnClose && trigger !== 'custom') {
890
892
  const triggerNode = this._adapter.getTriggerNode();
891
893
  if (triggerNode && 'focus' in triggerNode) {
892
- triggerNode.focus();
894
+ triggerNode.focus({ preventScroll });
893
895
  }
894
896
  }
895
897
  }
@@ -897,37 +899,43 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
897
899
  _handleEscKeyDown(event: any) {
898
900
  const { trigger } = this.getProps();
899
901
  if (trigger !== 'custom') {
900
- this.hide();
902
+ // Move the focus into the trigger first and then close the pop-up layer
903
+ // to avoid the problem of opening the pop-up layer again when the focus returns to the trigger in the case of hover and focus
901
904
  this._focusTrigger();
905
+ this.hide();
902
906
  }
903
907
  this._adapter.notifyEscKeydown(event);
904
908
  }
905
909
 
906
910
  _handleContainerTabKeyDown(focusableElements: any[], event: any) {
911
+ const { preventScroll } = this.getProps();
907
912
  const activeElement = this._adapter.getActiveElement();
908
913
  const isLastCurrentFocus = focusableElements[focusableElements.length - 1] === activeElement;
909
914
  if (isLastCurrentFocus) {
910
- focusableElements[0].focus();
915
+ focusableElements[0].focus({ preventScroll });
911
916
  event.preventDefault(); // prevent browser default tab move behavior
912
917
  }
913
918
  }
914
919
 
915
920
  _handleContainerShiftTabKeyDown(focusableElements: any[], event: any) {
921
+ const { preventScroll } = this.getProps();
916
922
  const activeElement = this._adapter.getActiveElement();
917
923
  const isFirstCurrentFocus = focusableElements[0] === activeElement;
918
924
  if (isFirstCurrentFocus) {
919
- focusableElements[focusableElements.length - 1].focus();
925
+ focusableElements[focusableElements.length - 1].focus({ preventScroll });
920
926
  event.preventDefault(); // prevent browser default tab move behavior
921
927
  }
922
928
  }
923
929
 
924
930
  _handleTriggerArrowDownKeydown(focusableElements: any[], event: any) {
925
- focusableElements[0].focus();
931
+ const { preventScroll } = this.getProps();
932
+ focusableElements[0].focus({ preventScroll });
926
933
  event.preventDefault(); // prevent browser default scroll behavior
927
934
  }
928
935
 
929
936
  _handleTriggerArrowUpKeydown(focusableElements: any[], event: any) {
930
- focusableElements[focusableElements.length - 1].focus();
937
+ const { preventScroll } = this.getProps();
938
+ focusableElements[focusableElements.length - 1].focus({ preventScroll });
931
939
  event.preventDefault(); // prevent browser default scroll behavior
932
940
  }
933
941
  }
@@ -219,6 +219,7 @@ export interface BasicTreeProps {
219
219
  onContextMenu?: (e: any, node: BasicTreeNodeData) => void;
220
220
  onSearch?: (sunInput: string) => void;
221
221
  onSelect?: (selectedKeys: string, selected: boolean, selectedNode: BasicTreeNodeData) => void;
222
+ preventScroll?: boolean;
222
223
  renderDraggingNode?: (nodeInstance: HTMLElement, node: BasicTreeNodeData) => HTMLElement;
223
224
  renderFullLabel?: (renderFullLabelProps: BasicRenderFullLabelProps) => any;
224
225
  renderLabel?: (label?: any, treeNode?: BasicTreeNodeData) => any;
@@ -429,13 +429,17 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
429
429
 
430
430
  handleClick(e: any) {
431
431
  const isDisabled = this._isDisabled();
432
- const { isOpen } = this.getStates();
432
+ const { isOpen, inputValue } = this.getStates();
433
+ const { searchPosition } = this.getProps();
433
434
  if (isDisabled) {
434
435
  return;
435
436
  } else if (!isOpen) {
436
437
  this.open();
437
438
  this._notifyFocus(e);
438
439
  } else if (isOpen) {
440
+ if (searchPosition === 'trigger' && inputValue) {
441
+ return;
442
+ }
439
443
  this.close(e);
440
444
  }
441
445
  }
@@ -7,6 +7,7 @@ type FocusRedirectListener = (element: HTMLElement) => boolean;
7
7
  interface HandleOptions {
8
8
  enable?: boolean
9
9
  onFocusRedirectListener?: FocusRedirectListener | FocusRedirectListener[]
10
+ preventScroll?: boolean;
10
11
  }
11
12
 
12
13
  /*
@@ -85,7 +86,8 @@ class FocusTrapHandle {
85
86
  }
86
87
 
87
88
  private focusElement = (element: HTMLElement, event: KeyboardEvent) => {
88
- element?.focus();
89
+ const { preventScroll } = this.options;
90
+ element?.focus({ preventScroll });
89
91
  event.preventDefault(); // prevent browser default tab move behavior
90
92
  }
91
93
 
package/utils/a11y.ts CHANGED
@@ -1,4 +1,109 @@
1
+ import { get } from "lodash";
2
+
1
3
  export function handlePrevent(event: any) {
2
4
  event.stopPropagation();
3
5
  event.preventDefault();
6
+ }
7
+
8
+ export function isPrintableCharacter(string: string): RegExpMatchArray {
9
+ return string.length === 1 && string.match(/\S/);
10
+ }
11
+
12
+ // set focus to the target item in item list
13
+ export function setFocusToItem(itemNodes: HTMLElement[], targetItem: HTMLElement): void {
14
+ for (let i = 0; i < itemNodes.length; i++) {
15
+ if (itemNodes[i] === targetItem) {
16
+ itemNodes[i].tabIndex = 0;
17
+ itemNodes[i].focus();
18
+ } else {
19
+ itemNodes[i].tabIndex = -1;
20
+ }
21
+ }
22
+ }
23
+
24
+ // set focus to the first item in item list
25
+ export function setFocusToFirstItem(itemNodes: HTMLElement[]): void {
26
+ itemNodes.length > 0 && setFocusToItem(itemNodes, itemNodes[0]);
27
+ }
28
+
29
+ // set focus to the last item in item list
30
+ export function setFocusToLastItem(itemNodes: HTMLElement[]): void {
31
+ itemNodes.length > 0 && setFocusToItem(itemNodes, itemNodes[itemNodes.length-1]);
32
+ }
33
+
34
+ // set focus to the previous item in item list
35
+ export function setFocusToPreviousMenuItem (itemNodes: HTMLElement[], currentItem: HTMLElement): void {
36
+ let newMenuItem: HTMLElement, index: number;
37
+
38
+ if (itemNodes.length > 0){
39
+ if (currentItem === itemNodes[0]) {
40
+ newMenuItem = itemNodes[itemNodes.length-1];
41
+ } else {
42
+ index = itemNodes.indexOf(currentItem);
43
+ newMenuItem = itemNodes[index - 1];
44
+ }
45
+ setFocusToItem(itemNodes, newMenuItem);
46
+ }
47
+ }
48
+
49
+ // set focus to the next item in item list
50
+ export function setFocusToNextMenuitem (itemNodes: HTMLElement[], currentItem: HTMLElement): void {
51
+ let newMenuItem: HTMLElement, index: number;
52
+
53
+ if (itemNodes.length > 0){
54
+ if (currentItem === itemNodes[itemNodes.length-1]) {
55
+ newMenuItem = itemNodes[0];
56
+ } else {
57
+ index = itemNodes.indexOf(currentItem);
58
+ newMenuItem = itemNodes[index + 1];
59
+ }
60
+ setFocusToItem(itemNodes, newMenuItem);
61
+ }
62
+ }
63
+
64
+ export function findIndexByCharacter(itemList: HTMLElement[], curItem: HTMLElement, firstCharList: string[], char: string): number {
65
+ let start: number, index: number;
66
+
67
+ if (!itemList || !firstCharList || !char || char.length > 1) {
68
+ return -1;
69
+ }
70
+
71
+ char = char.toLowerCase();
72
+
73
+ // Get start index for search based on position of currentItem
74
+ start = itemList.indexOf(curItem) + 1;
75
+ if (start >= itemList.length) {
76
+ start = 0;
77
+ }
78
+
79
+ // Check remaining menu items in the menu
80
+ index = firstCharList.indexOf(char, start);
81
+
82
+ // If not found in remaining menu items, check from beginning
83
+ if (index === -1) {
84
+ index = firstCharList.indexOf(char, 0);
85
+ }
86
+
87
+ return index >= 0 ? index : -1;
88
+ }
89
+
90
+ export function getAncestorNodeByRole(curElement: Element, role: string): Element {
91
+ if (!curElement) {
92
+ return null;
93
+ }
94
+ while (curElement.parentElement && get(curElement.parentElement, 'attributes.role.value', '') !== role){
95
+ curElement = curElement.parentElement;
96
+ }
97
+ return curElement.parentElement;
98
+ }
99
+
100
+ // According to the Id, find the corresponding data-popupid element
101
+ export function getMenuButton(focusableEle: NodeListOf<HTMLElement>, Id: string): HTMLElement{
102
+ for (let i = 0; i < focusableEle.length; i++){
103
+ const curAriDescribedby = focusableEle[i].attributes['data-popupid'];
104
+ if (curAriDescribedby && curAriDescribedby.value === Id){
105
+ return focusableEle[i];
106
+ }
107
+ }
108
+ return null;
4
109
  }