@douyinfe/semi-foundation 2.14.0 → 2.15.0-beta.0

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 (81) 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/checkboxFoundation.ts +1 -0
  5. package/datePicker/foundation.ts +1 -0
  6. package/dropdown/dropdown.scss +4 -0
  7. package/dropdown/foundation.ts +38 -1
  8. package/dropdown/menuFoundation.ts +77 -0
  9. package/lib/cjs/cascader/cascader.css +15 -0
  10. package/lib/cjs/cascader/cascader.scss +20 -0
  11. package/lib/cjs/cascader/foundation.d.ts +6 -0
  12. package/lib/cjs/cascader/foundation.js +23 -0
  13. package/lib/cjs/cascader/variables.scss +2 -0
  14. package/lib/cjs/checkbox/checkboxFoundation.d.ts +1 -0
  15. package/lib/cjs/datePicker/foundation.d.ts +1 -0
  16. package/lib/cjs/dropdown/dropdown.css +4 -0
  17. package/lib/cjs/dropdown/dropdown.scss +4 -0
  18. package/lib/cjs/dropdown/foundation.d.ts +4 -0
  19. package/lib/cjs/dropdown/foundation.js +48 -0
  20. package/lib/cjs/dropdown/menuFoundation.d.ts +9 -0
  21. package/lib/cjs/dropdown/menuFoundation.js +119 -0
  22. package/lib/cjs/list/list.css +1 -1
  23. package/lib/cjs/list/list.scss +1 -1
  24. package/lib/cjs/list/variables.scss +2 -1
  25. package/lib/cjs/modal/modalFoundation.d.ts +1 -0
  26. package/lib/cjs/rating/foundation.d.ts +13 -0
  27. package/lib/cjs/rating/foundation.js +123 -35
  28. package/lib/cjs/rating/rating.css +14 -5
  29. package/lib/cjs/rating/rating.scss +21 -8
  30. package/lib/cjs/rating/variables.scss +4 -0
  31. package/lib/cjs/tabs/foundation.js +28 -6
  32. package/lib/cjs/tooltip/foundation.js +39 -9
  33. package/lib/cjs/tree/foundation.d.ts +1 -0
  34. package/lib/cjs/treeSelect/foundation.js +9 -1
  35. package/lib/cjs/utils/FocusHandle.d.ts +1 -0
  36. package/lib/cjs/utils/FocusHandle.js +6 -1
  37. package/lib/cjs/utils/a11y.d.ts +9 -0
  38. package/lib/cjs/utils/a11y.js +123 -0
  39. package/lib/es/cascader/cascader.css +15 -0
  40. package/lib/es/cascader/cascader.scss +20 -0
  41. package/lib/es/cascader/foundation.d.ts +6 -0
  42. package/lib/es/cascader/foundation.js +23 -0
  43. package/lib/es/cascader/variables.scss +2 -0
  44. package/lib/es/checkbox/checkboxFoundation.d.ts +1 -0
  45. package/lib/es/datePicker/foundation.d.ts +1 -0
  46. package/lib/es/dropdown/dropdown.css +4 -0
  47. package/lib/es/dropdown/dropdown.scss +4 -0
  48. package/lib/es/dropdown/foundation.d.ts +4 -0
  49. package/lib/es/dropdown/foundation.js +45 -0
  50. package/lib/es/dropdown/menuFoundation.d.ts +9 -0
  51. package/lib/es/dropdown/menuFoundation.js +99 -0
  52. package/lib/es/list/list.css +1 -1
  53. package/lib/es/list/list.scss +1 -1
  54. package/lib/es/list/variables.scss +2 -1
  55. package/lib/es/modal/modalFoundation.d.ts +1 -0
  56. package/lib/es/rating/foundation.d.ts +13 -0
  57. package/lib/es/rating/foundation.js +116 -32
  58. package/lib/es/rating/rating.css +14 -5
  59. package/lib/es/rating/rating.scss +21 -8
  60. package/lib/es/rating/variables.scss +4 -0
  61. package/lib/es/tabs/foundation.js +30 -6
  62. package/lib/es/tooltip/foundation.js +38 -9
  63. package/lib/es/tree/foundation.d.ts +1 -0
  64. package/lib/es/treeSelect/foundation.js +9 -1
  65. package/lib/es/utils/FocusHandle.d.ts +1 -0
  66. package/lib/es/utils/FocusHandle.js +6 -1
  67. package/lib/es/utils/a11y.d.ts +9 -0
  68. package/lib/es/utils/a11y.js +101 -0
  69. package/list/list.scss +1 -1
  70. package/list/variables.scss +2 -1
  71. package/modal/modalFoundation.ts +1 -0
  72. package/package.json +2 -2
  73. package/rating/foundation.ts +90 -31
  74. package/rating/rating.scss +21 -8
  75. package/rating/variables.scss +4 -0
  76. package/tabs/foundation.ts +9 -6
  77. package/tooltip/foundation.ts +16 -8
  78. package/tree/foundation.ts +1 -0
  79. package/treeSelect/foundation.ts +5 -1
  80. package/utils/FocusHandle.ts +3 -1
  81. package/utils/a11y.ts +105 -0
@@ -175,6 +175,17 @@ $module: #{$prefix}-cascader;
175
175
  height: $height-cascader_selection_tagInput_input_large;
176
176
  }
177
177
  }
178
+
179
+ &-text {
180
+
181
+ &-inactive {
182
+ color: $color-cascader_selection_text_inactive;
183
+ }
184
+
185
+ &-hide {
186
+ display: none;
187
+ }
188
+ }
178
189
  }
179
190
 
180
191
  &-arrow,
@@ -294,8 +305,17 @@ $module: #{$prefix}-cascader;
294
305
  .#{$module}-selection {
295
306
  .#{$module}-search-wrapper {
296
307
  width: 100%;
308
+ height: $height-cascader_selection_wrapper;
309
+ display: flex;
310
+ align-items: center;
311
+ position: relative;
297
312
 
298
313
  .#{$prefix}-input-wrapper {
314
+ position: absolute;
315
+ top: 0;
316
+ left: 0;
317
+ border: none;
318
+ background-color: transparent;
299
319
  height: 100%;
300
320
  width: 100%;
301
321
  border: $color-cascader_input-border-default;
@@ -145,6 +145,7 @@ export interface BasicCascaderProps {
145
145
  disableStrictly?: boolean;
146
146
  leafOnly?: boolean;
147
147
  enableLeafClick?: boolean;
148
+ preventScroll?: boolean;
148
149
  onClear?: () => void;
149
150
  triggerRender?: (props: BasicTriggerRenderProps) => any;
150
151
  onListScroll?: (e: any, panel: BasicScrollPanelProps) => void;
@@ -183,6 +184,7 @@ export interface BasicCascaderInnerData {
183
184
  isFocus?: boolean;
184
185
  isInput?: boolean;
185
186
  disabledKeys?: Set<string>;
187
+ showInput?: boolean;
186
188
  }
187
189
 
188
190
  export interface CascaderAdapter extends DefaultAdapter<BasicCascaderProps, BasicCascaderInnerData> {
@@ -208,6 +210,8 @@ export interface CascaderAdapter extends DefaultAdapter<BasicCascaderProps, Basi
208
210
  notifyOnLoad: (newLoadedKeys: Set<string>, data: BasicCascaderData) => void;
209
211
  notifyListScroll: (e: any, panel: BasicScrollPanelProps) => void;
210
212
  notifyOnExceed: (data: BasicEntity[]) => void;
213
+ toggleInputShow: (show: boolean, cb: () => void) => void;
214
+ updateFocusState: (focus: boolean) => void,
211
215
  }
212
216
 
213
217
  // eslint-disable-next-line max-len
@@ -484,9 +488,11 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
484
488
 
485
489
  open() {
486
490
  const filterable = this._isFilterable();
491
+ const { multiple } = this.getProps();
487
492
  this._adapter.openMenu();
488
493
  if (filterable) {
489
494
  this._clearInput();
495
+ !multiple && this.toggle2SearchInput(true);
490
496
  }
491
497
  if (this._isControlledComponent()) {
492
498
  this.reCalcActiveKeys();
@@ -524,10 +530,25 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
524
530
  inputValue = this.renderDisplayText([...selectedKeys][0]);
525
531
  }
526
532
  this._adapter.updateStates({ inputValue });
533
+ !multiple && this.toggle2SearchInput(false);
534
+ !multiple && this._adapter.updateFocusState(false);
527
535
  }
528
536
  this._notifyBlur(e);
529
537
  }
530
538
 
539
+ toggle2SearchInput(isShow: boolean) {
540
+ if (isShow) {
541
+ this._adapter.toggleInputShow(isShow, () => this.focusInput());
542
+ } else {
543
+ this._adapter.toggleInputShow(isShow, () => undefined);
544
+ }
545
+ }
546
+
547
+ focusInput() {
548
+ this._adapter.focusInput();
549
+ this._adapter.updateFocusState(true);
550
+ }
551
+
531
552
  getMergedMotion = () => {
532
553
  const { motion } = this.getProps();
533
554
  const { isSearching } = this.getStates();
@@ -54,6 +54,7 @@ $spacing-cascader_clearBtn-marginRight: 12px; // 级联选择触发器清空按
54
54
 
55
55
  $color-cascader_selection_n-text-default: var(--semi-color-text-0); // 超出 maxTagCount 后,+n 的文字默认颜色
56
56
  $color-cascader_selection_n-text-disabled: var(--semi-color-disabled-text); // 超出 maxTagCount 后,+n 的文字disabled颜色
57
+ $color-cascader_selection_text_inactive: var(--semi-color-text-2); // 级联选择单选inpu输入框和text并存时,text颜色
57
58
  $color-cascader_selection-text-default: var(--semi-color-text-0); // 级联选择选中项文字颜色
58
59
  $color-cascader_placeholder-text-default: var(--semi-color-text-2); // 级联选择未选中项文字颜色
59
60
  $color-cascader-icon-default: var(--semi-color-text-2); // 级联选择图标颜色 - 默认
@@ -94,6 +95,7 @@ $height-cascader_option_list: 180px; // 级联选择菜单高度
94
95
  $height-cascader_selection_tagInput_input_small: 22px;
95
96
  $height-cascader_selection_tagInput_input_default: 30px;
96
97
  $height-cascader_selection_tagInput_input_large: 38px;
98
+ $height-cascader_selection_wrapper: 30px;
97
99
 
98
100
  $spacing-cascader_text-marginX: $spacing-base-tight; // 级联选择 prefix/suffix 文字水平内间距
99
101
  $spacing-cascader_icon-marginX: $spacing-tight; // 级联选择 prefix/suffix 图标水平内间距
@@ -170,6 +170,7 @@ export interface BaseCheckboxProps {
170
170
  extra?: any;
171
171
  addonId?: string;
172
172
  extraId?: string;
173
+ preventScroll?: boolean;
173
174
  }
174
175
 
175
176
  export default CheckboxFoundation;
@@ -162,6 +162,7 @@ export interface DatePickerFoundationProps extends ElementProps, RenderProps, Ev
162
162
  localeCode?: string;
163
163
  rangeSeparator?: string;
164
164
  insetInput?: boolean;
165
+ preventScroll?: boolean;
165
166
  }
166
167
 
167
168
  export interface DatePickerFoundationState {
@@ -50,6 +50,10 @@ $module: #{$prefix}-dropdown;
50
50
  &:not(.#{$module}-item-active):active {
51
51
  background-color: $color-dropdown_item-bg-active;
52
52
  }
53
+ &:focus-visible {
54
+ background-color: $color-dropdown_item-bg-hover;
55
+ outline: 0;
56
+ }
53
57
  &-icon {
54
58
  display: inline-flex;
55
59
  align-items: center;
@@ -1,5 +1,5 @@
1
1
  import BaseFoundation, { DefaultAdapter } from '../base/foundation';
2
-
2
+ import { handlePrevent, setFocusToFirstItem, setFocusToLastItem } from '../utils/a11y';
3
3
 
4
4
  export interface DropdownAdapter extends Partial<DefaultAdapter> {
5
5
  setPopVisible(visible: boolean): void;
@@ -11,4 +11,41 @@ export default class DropdownFoundation extends BaseFoundation<DropdownAdapter>
11
11
  this._adapter.setPopVisible(visible);
12
12
  this._adapter.notifyVisibleChange(visible);
13
13
  }
14
+
15
+ getMenuItemNodes(target: any): HTMLElement[] {
16
+ const id = target.attributes['data-popupId'].value;
17
+ const menuWrapper = document.getElementById(id);
18
+ // if has dropdown item, the item must wrapped by li
19
+ return menuWrapper ? Array.from(menuWrapper.getElementsByTagName('li')).filter(item => item.ariaDisabled === "false") : null;
20
+ }
21
+
22
+ setFocusToFirstMenuItem(target: any): void {
23
+ const menuItemNodes = this.getMenuItemNodes(target);
24
+ menuItemNodes && setFocusToFirstItem(menuItemNodes);
25
+ }
26
+
27
+ setFocusToLastMenuItem(target: any): void {
28
+ const menuItemNodes = this.getMenuItemNodes(target);
29
+ menuItemNodes && setFocusToLastItem(menuItemNodes);
30
+ }
31
+
32
+ handleKeyDown(event: any): void {
33
+ switch (event.key) {
34
+ case ' ':
35
+ case 'Enter':
36
+ event.target.click();
37
+ handlePrevent(event);
38
+ break;
39
+ case 'ArrowDown':
40
+ this.setFocusToFirstMenuItem(event.target);
41
+ handlePrevent(event);
42
+ break;
43
+ case 'ArrowUp':
44
+ this.setFocusToLastMenuItem(event.target);
45
+ handlePrevent(event);
46
+ break;
47
+ default:
48
+ break;
49
+ }
50
+ }
14
51
  }
@@ -0,0 +1,77 @@
1
+
2
+ import BaseFoundation, { DefaultAdapter } from '../base/foundation';
3
+ import { handlePrevent, isPrintableCharacter, findIndexByCharacter, getAncestorNodeByRole, getMenuButton, setFocusToFirstItem, setFocusToItem, setFocusToNextMenuitem, setFocusToPreviousMenuItem } from '../utils/a11y';
4
+
5
+
6
+ export default class DropdownMenuFoundation extends BaseFoundation<Partial<DefaultAdapter>> {
7
+ menuItemNodes: HTMLElement[] = null;
8
+ firstChars: string[] = [];
9
+
10
+ // if trigger is click, auto focus to the first menu item
11
+ autoFocus(ulElement: any): void {
12
+ const trigger = this._adapter.getContext('trigger');
13
+ if (trigger === 'click'){
14
+ // find all non-disabled li under this menu and set focus to the first menu
15
+ this.menuItemNodes = [...ulElement.getElementsByTagName('li')].filter(item => item.ariaDisabled !== "true");
16
+ setFocusToFirstItem(this.menuItemNodes);
17
+ }
18
+ }
19
+
20
+ handleEscape(menu: Element): void {
21
+ const trigger = this._adapter.getContext('trigger');
22
+ if (trigger === 'custom'){
23
+ const menuButton = menu && getMenuButton(document.querySelectorAll(`[data-popupId]`), menu.id);
24
+ menuButton.focus();
25
+ }
26
+ }
27
+
28
+ setFocusByFirstCharacter(curItem: any, char: string): void {
29
+ const index = findIndexByCharacter(this.menuItemNodes, curItem, this.firstChars, char);
30
+
31
+ if (index >= 0) {
32
+ setFocusToItem(this.menuItemNodes, this.menuItemNodes[index]);
33
+ }
34
+ }
35
+
36
+ onMenuKeydown(event: any): void {
37
+ const menu = getAncestorNodeByRole(event.target, 'tooltip');
38
+
39
+ if (!this.menuItemNodes){
40
+ this.menuItemNodes = [...(event.target.parentNode).getElementsByTagName('li')].filter(item => item.ariaDisabled !== "true");
41
+ }
42
+
43
+ if (this.firstChars.length === 0){
44
+ this.menuItemNodes.forEach((item: Element) => {
45
+ this.firstChars.push(item.textContent.trim()[0].toLowerCase());
46
+ });
47
+ }
48
+
49
+ // get the currently focused menu item
50
+ const curItem = this.menuItemNodes.find(item => item.tabIndex === 0);
51
+
52
+ switch (event.key) {
53
+ case ' ':
54
+ case 'Enter':
55
+ event.target.click();
56
+ handlePrevent(event);
57
+ break;
58
+ case 'Escape':
59
+ this.handleEscape(menu);
60
+ break;
61
+ case 'ArrowUp':
62
+ setFocusToPreviousMenuItem(this.menuItemNodes, curItem);
63
+ handlePrevent(event);
64
+ break;
65
+ case 'ArrowDown':
66
+ setFocusToNextMenuitem(this.menuItemNodes, curItem);
67
+ handlePrevent(event);
68
+ break;
69
+ default:
70
+ if (isPrintableCharacter(event.key)) {
71
+ this.setFocusByFirstCharacter(curItem, event.key);
72
+ handlePrevent(event);
73
+ }
74
+ break;
75
+ }
76
+ }
77
+ }
@@ -149,6 +149,12 @@
149
149
  .semi-cascader-selection .semi-tagInput .semi-input-wrapper-large {
150
150
  height: 38px;
151
151
  }
152
+ .semi-cascader-selection-text-inactive {
153
+ color: var(--semi-color-text-2);
154
+ }
155
+ .semi-cascader-selection-text-hide {
156
+ display: none;
157
+ }
152
158
  .semi-cascader-arrow, .semi-cascader-clearbtn {
153
159
  display: inline-flex;
154
160
  align-items: center;
@@ -242,8 +248,17 @@
242
248
  }
243
249
  .semi-cascader-single.semi-cascader-filterable .semi-cascader-selection .semi-cascader-search-wrapper {
244
250
  width: 100%;
251
+ height: 30px;
252
+ display: flex;
253
+ align-items: center;
254
+ position: relative;
245
255
  }
246
256
  .semi-cascader-single.semi-cascader-filterable .semi-cascader-selection .semi-cascader-search-wrapper .semi-input-wrapper {
257
+ position: absolute;
258
+ top: 0;
259
+ left: 0;
260
+ border: none;
261
+ background-color: transparent;
247
262
  height: 100%;
248
263
  width: 100%;
249
264
  border: none;
@@ -175,6 +175,17 @@ $module: #{$prefix}-cascader;
175
175
  height: $height-cascader_selection_tagInput_input_large;
176
176
  }
177
177
  }
178
+
179
+ &-text {
180
+
181
+ &-inactive {
182
+ color: $color-cascader_selection_text_inactive;
183
+ }
184
+
185
+ &-hide {
186
+ display: none;
187
+ }
188
+ }
178
189
  }
179
190
 
180
191
  &-arrow,
@@ -294,8 +305,17 @@ $module: #{$prefix}-cascader;
294
305
  .#{$module}-selection {
295
306
  .#{$module}-search-wrapper {
296
307
  width: 100%;
308
+ height: $height-cascader_selection_wrapper;
309
+ display: flex;
310
+ align-items: center;
311
+ position: relative;
297
312
 
298
313
  .#{$prefix}-input-wrapper {
314
+ position: absolute;
315
+ top: 0;
316
+ left: 0;
317
+ border: none;
318
+ background-color: transparent;
299
319
  height: 100%;
300
320
  width: 100%;
301
321
  border: $color-cascader_input-border-default;
@@ -99,6 +99,7 @@ export interface BasicCascaderProps {
99
99
  disableStrictly?: boolean;
100
100
  leafOnly?: boolean;
101
101
  enableLeafClick?: boolean;
102
+ preventScroll?: boolean;
102
103
  onClear?: () => void;
103
104
  triggerRender?: (props: BasicTriggerRenderProps) => any;
104
105
  onListScroll?: (e: any, panel: BasicScrollPanelProps) => void;
@@ -136,6 +137,7 @@ export interface BasicCascaderInnerData {
136
137
  isFocus?: boolean;
137
138
  isInput?: boolean;
138
139
  disabledKeys?: Set<string>;
140
+ showInput?: boolean;
139
141
  }
140
142
  export interface CascaderAdapter extends DefaultAdapter<BasicCascaderProps, BasicCascaderInnerData> {
141
143
  notifyClear?: () => void;
@@ -160,6 +162,8 @@ export interface CascaderAdapter extends DefaultAdapter<BasicCascaderProps, Basi
160
162
  notifyOnLoad: (newLoadedKeys: Set<string>, data: BasicCascaderData) => void;
161
163
  notifyListScroll: (e: any, panel: BasicScrollPanelProps) => void;
162
164
  notifyOnExceed: (data: BasicEntity[]) => void;
165
+ toggleInputShow: (show: boolean, cb: () => void) => void;
166
+ updateFocusState: (focus: boolean) => void;
163
167
  }
164
168
  export default class CascaderFoundation extends BaseFoundation<CascaderAdapter, BasicCascaderProps, BasicCascaderInnerData> {
165
169
  constructor(adapter: CascaderAdapter);
@@ -187,6 +191,8 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
187
191
  open(): void;
188
192
  reCalcActiveKeys(): void;
189
193
  close(e: any, key?: string): void;
194
+ toggle2SearchInput(isShow: boolean): void;
195
+ focusInput(): void;
190
196
  getMergedMotion: () => any;
191
197
  handleItemClick(e: any, item: BasicEntity | BasicData): void;
192
198
  handleItemHover(e: any, item: BasicEntity): void;
@@ -423,10 +423,16 @@ class CascaderFoundation extends _foundation.default {
423
423
  open() {
424
424
  const filterable = this._isFilterable();
425
425
 
426
+ const {
427
+ multiple
428
+ } = this.getProps();
429
+
426
430
  this._adapter.openMenu();
427
431
 
428
432
  if (filterable) {
429
433
  this._clearInput();
434
+
435
+ !multiple && this.toggle2SearchInput(true);
430
436
  }
431
437
 
432
438
  if (this._isControlledComponent()) {
@@ -486,11 +492,28 @@ class CascaderFoundation extends _foundation.default {
486
492
  this._adapter.updateStates({
487
493
  inputValue
488
494
  });
495
+
496
+ !multiple && this.toggle2SearchInput(false);
497
+ !multiple && this._adapter.updateFocusState(false);
489
498
  }
490
499
 
491
500
  this._notifyBlur(e);
492
501
  }
493
502
 
503
+ toggle2SearchInput(isShow) {
504
+ if (isShow) {
505
+ this._adapter.toggleInputShow(isShow, () => this.focusInput());
506
+ } else {
507
+ this._adapter.toggleInputShow(isShow, () => undefined);
508
+ }
509
+ }
510
+
511
+ focusInput() {
512
+ this._adapter.focusInput();
513
+
514
+ this._adapter.updateFocusState(true);
515
+ }
516
+
494
517
  handleItemClick(e, item) {
495
518
  const isDisabled = this._isDisabled();
496
519
 
@@ -54,6 +54,7 @@ $spacing-cascader_clearBtn-marginRight: 12px; // 级联选择触发器清空按
54
54
 
55
55
  $color-cascader_selection_n-text-default: var(--semi-color-text-0); // 超出 maxTagCount 后,+n 的文字默认颜色
56
56
  $color-cascader_selection_n-text-disabled: var(--semi-color-disabled-text); // 超出 maxTagCount 后,+n 的文字disabled颜色
57
+ $color-cascader_selection_text_inactive: var(--semi-color-text-2); // 级联选择单选inpu输入框和text并存时,text颜色
57
58
  $color-cascader_selection-text-default: var(--semi-color-text-0); // 级联选择选中项文字颜色
58
59
  $color-cascader_placeholder-text-default: var(--semi-color-text-2); // 级联选择未选中项文字颜色
59
60
  $color-cascader-icon-default: var(--semi-color-text-2); // 级联选择图标颜色 - 默认
@@ -94,6 +95,7 @@ $height-cascader_option_list: 180px; // 级联选择菜单高度
94
95
  $height-cascader_selection_tagInput_input_small: 22px;
95
96
  $height-cascader_selection_tagInput_input_default: 30px;
96
97
  $height-cascader_selection_tagInput_input_large: 38px;
98
+ $height-cascader_selection_wrapper: 30px;
97
99
 
98
100
  $spacing-cascader_text-marginX: $spacing-base-tight; // 级联选择 prefix/suffix 文字水平内间距
99
101
  $spacing-cascader_icon-marginX: $spacing-tight; // 级联选择 prefix/suffix 图标水平内间距
@@ -62,5 +62,6 @@ export interface BaseCheckboxProps {
62
62
  extra?: any;
63
63
  addonId?: string;
64
64
  extraId?: string;
65
+ preventScroll?: boolean;
65
66
  }
66
67
  export default CheckboxFoundation;
@@ -131,6 +131,7 @@ export interface DatePickerFoundationProps extends ElementProps, RenderProps, Ev
131
131
  localeCode?: string;
132
132
  rangeSeparator?: string;
133
133
  insetInput?: boolean;
134
+ preventScroll?: boolean;
134
135
  }
135
136
  export interface DatePickerFoundationState {
136
137
  panelShow: boolean;
@@ -53,6 +53,10 @@
53
53
  .semi-dropdown-item:not(.semi-dropdown-item-active):active {
54
54
  background-color: var(--semi-color-fill-1);
55
55
  }
56
+ .semi-dropdown-item:focus-visible {
57
+ background-color: var(--semi-color-fill-0);
58
+ outline: 0;
59
+ }
56
60
  .semi-dropdown-item-icon {
57
61
  display: inline-flex;
58
62
  align-items: center;
@@ -50,6 +50,10 @@ $module: #{$prefix}-dropdown;
50
50
  &:not(.#{$module}-item-active):active {
51
51
  background-color: $color-dropdown_item-bg-active;
52
52
  }
53
+ &:focus-visible {
54
+ background-color: $color-dropdown_item-bg-hover;
55
+ outline: 0;
56
+ }
53
57
  &-icon {
54
58
  display: inline-flex;
55
59
  align-items: center;
@@ -5,4 +5,8 @@ export interface DropdownAdapter extends Partial<DefaultAdapter> {
5
5
  }
6
6
  export default class DropdownFoundation extends BaseFoundation<DropdownAdapter> {
7
7
  handleVisibleChange(visible: boolean): void;
8
+ getMenuItemNodes(target: any): HTMLElement[];
9
+ setFocusToFirstMenuItem(target: any): void;
10
+ setFocusToLastMenuItem(target: any): void;
11
+ handleKeyDown(event: any): void;
8
12
  }
@@ -10,8 +10,14 @@ _Object$defineProperty(exports, "__esModule", {
10
10
 
11
11
  exports.default = void 0;
12
12
 
13
+ var _filter = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/filter"));
14
+
15
+ var _from = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/from"));
16
+
13
17
  var _foundation = _interopRequireDefault(require("../base/foundation"));
14
18
 
19
+ var _a11y = require("../utils/a11y");
20
+
15
21
  class DropdownFoundation extends _foundation.default {
16
22
  handleVisibleChange(visible) {
17
23
  this._adapter.setPopVisible(visible);
@@ -19,6 +25,48 @@ class DropdownFoundation extends _foundation.default {
19
25
  this._adapter.notifyVisibleChange(visible);
20
26
  }
21
27
 
28
+ getMenuItemNodes(target) {
29
+ var _context;
30
+
31
+ const id = target.attributes['data-popupId'].value;
32
+ const menuWrapper = document.getElementById(id); // if has dropdown item, the item must wrapped by li
33
+
34
+ return menuWrapper ? (0, _filter.default)(_context = (0, _from.default)(menuWrapper.getElementsByTagName('li'))).call(_context, item => item.ariaDisabled === "false") : null;
35
+ }
36
+
37
+ setFocusToFirstMenuItem(target) {
38
+ const menuItemNodes = this.getMenuItemNodes(target);
39
+ menuItemNodes && (0, _a11y.setFocusToFirstItem)(menuItemNodes);
40
+ }
41
+
42
+ setFocusToLastMenuItem(target) {
43
+ const menuItemNodes = this.getMenuItemNodes(target);
44
+ menuItemNodes && (0, _a11y.setFocusToLastItem)(menuItemNodes);
45
+ }
46
+
47
+ handleKeyDown(event) {
48
+ switch (event.key) {
49
+ case ' ':
50
+ case 'Enter':
51
+ event.target.click();
52
+ (0, _a11y.handlePrevent)(event);
53
+ break;
54
+
55
+ case 'ArrowDown':
56
+ this.setFocusToFirstMenuItem(event.target);
57
+ (0, _a11y.handlePrevent)(event);
58
+ break;
59
+
60
+ case 'ArrowUp':
61
+ this.setFocusToLastMenuItem(event.target);
62
+ (0, _a11y.handlePrevent)(event);
63
+ break;
64
+
65
+ default:
66
+ break;
67
+ }
68
+ }
69
+
22
70
  }
23
71
 
24
72
  exports.default = DropdownFoundation;
@@ -0,0 +1,9 @@
1
+ import BaseFoundation, { DefaultAdapter } from '../base/foundation';
2
+ export default class DropdownMenuFoundation extends BaseFoundation<Partial<DefaultAdapter>> {
3
+ menuItemNodes: HTMLElement[];
4
+ firstChars: string[];
5
+ autoFocus(ulElement: any): void;
6
+ handleEscape(menu: Element): void;
7
+ setFocusByFirstCharacter(curItem: any, char: string): void;
8
+ onMenuKeydown(event: any): void;
9
+ }
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+
3
+ var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
4
+
5
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
6
+
7
+ _Object$defineProperty(exports, "__esModule", {
8
+ value: true
9
+ });
10
+
11
+ exports.default = void 0;
12
+
13
+ var _filter = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/filter"));
14
+
15
+ var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
16
+
17
+ var _trim = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/trim"));
18
+
19
+ var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));
20
+
21
+ var _foundation = _interopRequireDefault(require("../base/foundation"));
22
+
23
+ var _a11y = require("../utils/a11y");
24
+
25
+ class DropdownMenuFoundation extends _foundation.default {
26
+ constructor() {
27
+ super(...arguments);
28
+ this.menuItemNodes = null;
29
+ this.firstChars = [];
30
+ } // if trigger is click, auto focus to the first menu item
31
+
32
+
33
+ autoFocus(ulElement) {
34
+ const trigger = this._adapter.getContext('trigger');
35
+
36
+ if (trigger === 'click') {
37
+ var _context;
38
+
39
+ // find all non-disabled li under this menu and set focus to the first menu
40
+ this.menuItemNodes = (0, _filter.default)(_context = [...ulElement.getElementsByTagName('li')]).call(_context, item => item.ariaDisabled !== "true");
41
+ (0, _a11y.setFocusToFirstItem)(this.menuItemNodes);
42
+ }
43
+ }
44
+
45
+ handleEscape(menu) {
46
+ const trigger = this._adapter.getContext('trigger');
47
+
48
+ if (trigger === 'custom') {
49
+ const menuButton = menu && (0, _a11y.getMenuButton)(document.querySelectorAll("[data-popupId]"), menu.id);
50
+ menuButton.focus();
51
+ }
52
+ }
53
+
54
+ setFocusByFirstCharacter(curItem, char) {
55
+ const index = (0, _a11y.findIndexByCharacter)(this.menuItemNodes, curItem, this.firstChars, char);
56
+
57
+ if (index >= 0) {
58
+ (0, _a11y.setFocusToItem)(this.menuItemNodes, this.menuItemNodes[index]);
59
+ }
60
+ }
61
+
62
+ onMenuKeydown(event) {
63
+ var _context5;
64
+
65
+ const menu = (0, _a11y.getAncestorNodeByRole)(event.target, 'tooltip');
66
+
67
+ if (!this.menuItemNodes) {
68
+ var _context2;
69
+
70
+ this.menuItemNodes = (0, _filter.default)(_context2 = [...event.target.parentNode.getElementsByTagName('li')]).call(_context2, item => item.ariaDisabled !== "true");
71
+ }
72
+
73
+ if (this.firstChars.length === 0) {
74
+ var _context3;
75
+
76
+ (0, _forEach.default)(_context3 = this.menuItemNodes).call(_context3, item => {
77
+ var _context4;
78
+
79
+ this.firstChars.push((0, _trim.default)(_context4 = item.textContent).call(_context4)[0].toLowerCase());
80
+ });
81
+ } // get the currently focused menu item
82
+
83
+
84
+ const curItem = (0, _find.default)(_context5 = this.menuItemNodes).call(_context5, item => item.tabIndex === 0);
85
+
86
+ switch (event.key) {
87
+ case ' ':
88
+ case 'Enter':
89
+ event.target.click();
90
+ (0, _a11y.handlePrevent)(event);
91
+ break;
92
+
93
+ case 'Escape':
94
+ this.handleEscape(menu);
95
+ break;
96
+
97
+ case 'ArrowUp':
98
+ (0, _a11y.setFocusToPreviousMenuItem)(this.menuItemNodes, curItem);
99
+ (0, _a11y.handlePrevent)(event);
100
+ break;
101
+
102
+ case 'ArrowDown':
103
+ (0, _a11y.setFocusToNextMenuitem)(this.menuItemNodes, curItem);
104
+ (0, _a11y.handlePrevent)(event);
105
+ break;
106
+
107
+ default:
108
+ if ((0, _a11y.isPrintableCharacter)(event.key)) {
109
+ this.setFocusByFirstCharacter(curItem, event.key);
110
+ (0, _a11y.handlePrevent)(event);
111
+ }
112
+
113
+ break;
114
+ }
115
+ }
116
+
117
+ }
118
+
119
+ exports.default = DropdownMenuFoundation;