@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.
- package/cascader/cascader.scss +20 -0
- package/cascader/foundation.ts +21 -0
- package/cascader/variables.scss +2 -0
- package/checkbox/checkbox.scss +9 -9
- package/checkbox/checkboxFoundation.ts +1 -0
- package/datePicker/foundation.ts +32 -8
- package/datePicker/monthsGridFoundation.ts +1 -2
- package/dropdown/dropdown.scss +4 -0
- package/dropdown/foundation.ts +38 -1
- package/dropdown/menuFoundation.ts +77 -0
- package/lib/cjs/cascader/cascader.css +15 -0
- package/lib/cjs/cascader/cascader.scss +20 -0
- package/lib/cjs/cascader/foundation.d.ts +6 -0
- package/lib/cjs/cascader/foundation.js +23 -0
- package/lib/cjs/cascader/variables.scss +2 -0
- package/lib/cjs/checkbox/checkbox.css +5 -5
- package/lib/cjs/checkbox/checkbox.scss +9 -9
- package/lib/cjs/checkbox/checkboxFoundation.d.ts +1 -0
- package/lib/cjs/datePicker/foundation.d.ts +7 -0
- package/lib/cjs/datePicker/foundation.js +38 -7
- package/lib/cjs/datePicker/monthsGridFoundation.js +1 -4
- package/lib/cjs/dropdown/dropdown.css +4 -0
- package/lib/cjs/dropdown/dropdown.scss +4 -0
- package/lib/cjs/dropdown/foundation.d.ts +4 -0
- package/lib/cjs/dropdown/foundation.js +48 -0
- package/lib/cjs/dropdown/menuFoundation.d.ts +9 -0
- package/lib/cjs/dropdown/menuFoundation.js +119 -0
- package/lib/cjs/list/list.css +1 -1
- package/lib/cjs/list/list.scss +1 -1
- package/lib/cjs/list/variables.scss +2 -1
- package/lib/cjs/modal/modalFoundation.d.ts +1 -0
- package/lib/cjs/rating/foundation.d.ts +13 -0
- package/lib/cjs/rating/foundation.js +123 -35
- package/lib/cjs/rating/rating.css +14 -5
- package/lib/cjs/rating/rating.scss +21 -8
- package/lib/cjs/rating/variables.scss +4 -0
- package/lib/cjs/tabs/foundation.js +28 -6
- package/lib/cjs/tooltip/foundation.js +39 -9
- package/lib/cjs/tree/foundation.d.ts +1 -0
- package/lib/cjs/treeSelect/foundation.js +9 -1
- package/lib/cjs/utils/FocusHandle.d.ts +1 -0
- package/lib/cjs/utils/FocusHandle.js +6 -1
- package/lib/cjs/utils/a11y.d.ts +9 -0
- package/lib/cjs/utils/a11y.js +123 -0
- package/lib/es/cascader/cascader.css +15 -0
- package/lib/es/cascader/cascader.scss +20 -0
- package/lib/es/cascader/foundation.d.ts +6 -0
- package/lib/es/cascader/foundation.js +23 -0
- package/lib/es/cascader/variables.scss +2 -0
- package/lib/es/checkbox/checkbox.css +5 -5
- package/lib/es/checkbox/checkbox.scss +9 -9
- package/lib/es/checkbox/checkboxFoundation.d.ts +1 -0
- package/lib/es/datePicker/foundation.d.ts +7 -0
- package/lib/es/datePicker/foundation.js +37 -7
- package/lib/es/datePicker/monthsGridFoundation.js +1 -3
- package/lib/es/dropdown/dropdown.css +4 -0
- package/lib/es/dropdown/dropdown.scss +4 -0
- package/lib/es/dropdown/foundation.d.ts +4 -0
- package/lib/es/dropdown/foundation.js +45 -0
- package/lib/es/dropdown/menuFoundation.d.ts +9 -0
- package/lib/es/dropdown/menuFoundation.js +99 -0
- package/lib/es/list/list.css +1 -1
- package/lib/es/list/list.scss +1 -1
- package/lib/es/list/variables.scss +2 -1
- package/lib/es/modal/modalFoundation.d.ts +1 -0
- package/lib/es/rating/foundation.d.ts +13 -0
- package/lib/es/rating/foundation.js +116 -32
- package/lib/es/rating/rating.css +14 -5
- package/lib/es/rating/rating.scss +21 -8
- package/lib/es/rating/variables.scss +4 -0
- package/lib/es/tabs/foundation.js +30 -6
- package/lib/es/tooltip/foundation.js +38 -9
- package/lib/es/tree/foundation.d.ts +1 -0
- package/lib/es/treeSelect/foundation.js +9 -1
- package/lib/es/utils/FocusHandle.d.ts +1 -0
- package/lib/es/utils/FocusHandle.js +6 -1
- package/lib/es/utils/a11y.d.ts +9 -0
- package/lib/es/utils/a11y.js +101 -0
- package/list/list.scss +1 -1
- package/list/variables.scss +2 -1
- package/modal/modalFoundation.ts +1 -0
- package/package.json +2 -2
- package/rating/foundation.ts +90 -31
- package/rating/rating.scss +21 -8
- package/rating/variables.scss +4 -0
- package/tabs/foundation.ts +9 -6
- package/tooltip/foundation.ts +16 -8
- package/tree/foundation.ts +1 -0
- package/treeSelect/foundation.ts +5 -1
- package/utils/FocusHandle.ts +3 -1
- package/utils/a11y.ts +105 -0
package/tabs/foundation.ts
CHANGED
|
@@ -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
|
}
|
package/tooltip/foundation.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/tree/foundation.ts
CHANGED
|
@@ -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;
|
package/treeSelect/foundation.ts
CHANGED
|
@@ -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
|
}
|
package/utils/FocusHandle.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|