@douyinfe/semi-foundation 2.7.1 → 2.8.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 (34) hide show
  1. package/datePicker/_utils/getDefaultPickerDate.ts +54 -0
  2. package/datePicker/datePicker.scss +2 -2
  3. package/form/form.scss +12 -4
  4. package/inputNumber/foundation.ts +1 -1
  5. package/lib/cjs/datePicker/_utils/getDefaultPickerDate.d.ts +15 -0
  6. package/lib/cjs/datePicker/_utils/getDefaultPickerDate.js +73 -0
  7. package/lib/cjs/datePicker/datePicker.css +14 -14
  8. package/lib/cjs/datePicker/datePicker.scss +2 -2
  9. package/lib/cjs/form/form.css +11 -2
  10. package/lib/cjs/form/form.scss +12 -4
  11. package/lib/cjs/inputNumber/foundation.js +1 -1
  12. package/lib/cjs/tooltip/foundation.d.ts +27 -1
  13. package/lib/cjs/tooltip/foundation.js +159 -3
  14. package/lib/cjs/utils/isEscPress.d.ts +4 -0
  15. package/lib/cjs/utils/isEscPress.js +22 -0
  16. package/lib/cjs/utils/keyCode.d.ts +1 -0
  17. package/lib/cjs/utils/keyCode.js +3 -1
  18. package/lib/es/datePicker/_utils/getDefaultPickerDate.d.ts +15 -0
  19. package/lib/es/datePicker/_utils/getDefaultPickerDate.js +57 -0
  20. package/lib/es/datePicker/datePicker.css +14 -14
  21. package/lib/es/datePicker/datePicker.scss +2 -2
  22. package/lib/es/form/form.css +11 -2
  23. package/lib/es/form/form.scss +12 -4
  24. package/lib/es/inputNumber/foundation.js +1 -1
  25. package/lib/es/tooltip/foundation.d.ts +27 -1
  26. package/lib/es/tooltip/foundation.js +159 -3
  27. package/lib/es/utils/isEscPress.d.ts +4 -0
  28. package/lib/es/utils/isEscPress.js +8 -0
  29. package/lib/es/utils/keyCode.d.ts +1 -0
  30. package/lib/es/utils/keyCode.js +1 -0
  31. package/package.json +3 -3
  32. package/tooltip/foundation.ts +131 -3
  33. package/utils/isEscPress.ts +8 -0
  34. package/utils/keyCode.ts +1 -0
@@ -46,6 +46,7 @@ export interface TooltipAdapter<P = Record<string, any>, S = Record<string, any>
46
46
  click: string;
47
47
  focus: string;
48
48
  blur: string;
49
+ keydown: string;
49
50
  };
50
51
  registerTriggerEvent(...args: any[]): void;
51
52
  getTriggerBounding(...args: any[]): DOMRect;
@@ -61,6 +62,12 @@ export interface TooltipAdapter<P = Record<string, any>, S = Record<string, any>
61
62
  updateContainerPosition(): void;
62
63
  updatePlacementAttr(placement: Position): void;
63
64
  getContainerPosition(): string;
65
+ getFocusableElements(node: any): any[];
66
+ getActiveElement(): any;
67
+ getContainer(): any;
68
+ setInitialFocus(): void;
69
+ notifyEscKeydown(event: any): void;
70
+ getTriggerNode(): any;
64
71
  }
65
72
 
66
73
  export type Position = ArrayElement<typeof strings.POSITION_SET>;
@@ -154,7 +161,12 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
154
161
 
155
162
  _generateEvent(types: ArrayElement<typeof strings.TRIGGER_SET>) {
156
163
  const eventNames = this._adapter.getEventName();
157
- const triggerEventSet = {};
164
+ const triggerEventSet = {
165
+ // bind esc keydown on trigger for a11y
166
+ [eventNames.keydown]: (event) => {
167
+ this._handleTriggerKeydown(event);
168
+ },
169
+ };
158
170
  let portalEventSet = {};
159
171
  switch (types) {
160
172
  case 'focus':
@@ -186,6 +198,13 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
186
198
  this.delayHide();
187
199
  // this.hide('trigger');
188
200
  };
201
+ // bind focus to hover trigger for a11y
202
+ triggerEventSet[eventNames.focus] = () => {
203
+ this.delayShow();
204
+ };
205
+ triggerEventSet[eventNames.blur] = () => {
206
+ this.delayHide();
207
+ };
189
208
 
190
209
  portalEventSet = { ...triggerEventSet };
191
210
  if (this.getProp('clickToHide')) {
@@ -205,7 +224,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
205
224
  break;
206
225
  case 'custom':
207
226
  // when trigger type is 'custom', no need to bind eventHandler
208
- // show/hide completely depond on props.visible which change by user
227
+ // show/hide completely depend on props.visible which change by user
209
228
  break;
210
229
  default:
211
230
  break;
@@ -292,7 +311,12 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
292
311
  _togglePortalVisible(isVisible: boolean) {
293
312
  const nowVisible = this.getState('visible');
294
313
  if (nowVisible !== isVisible) {
295
- this._adapter.togglePortalVisible(isVisible, () => this._adapter.notifyVisibleChange(isVisible));
314
+ this._adapter.togglePortalVisible(isVisible, () => {
315
+ if (isVisible) {
316
+ this._adapter.setInitialFocus();
317
+ }
318
+ this._adapter.notifyVisibleChange(isVisible);
319
+ });
296
320
  }
297
321
  }
298
322
 
@@ -797,4 +821,108 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
797
821
  _initContainerPosition() {
798
822
  this._adapter.updateContainerPosition();
799
823
  }
824
+
825
+ handleContainerKeydown = (event: any) => {
826
+ const { guardFocus, closeOnEsc } = this.getProps();
827
+ switch (event && event.key) {
828
+ case "Escape":
829
+ closeOnEsc && this._handleEscKeyDown(event);
830
+ break;
831
+ case "Tab":
832
+ if (guardFocus) {
833
+ const container = this._adapter.getContainer();
834
+ const focusableElements = this._adapter.getFocusableElements(container);
835
+ const focusableNum = focusableElements.length;
836
+
837
+ if (focusableNum) {
838
+ // Shift + Tab will move focus backward
839
+ if (event.shiftKey) {
840
+ this._handleContainerShiftTabKeyDown(focusableElements, event);
841
+ } else {
842
+ this._handleContainerTabKeyDown(focusableElements, event);
843
+ }
844
+ }
845
+ }
846
+ break;
847
+ default:
848
+ break;
849
+ }
850
+ }
851
+
852
+ _handleTriggerKeydown(event: any) {
853
+ const { closeOnEsc } = this.getProps();
854
+ const container = this._adapter.getContainer();
855
+ const focusableElements = this._adapter.getFocusableElements(container);
856
+ const focusableNum = focusableElements.length;
857
+
858
+ switch (event && event.key) {
859
+ case "Escape":
860
+ closeOnEsc && this._handleEscKeyDown(event);
861
+ break;
862
+ case "ArrowUp":
863
+ focusableNum && this._handleTriggerArrowUpKeydown(focusableElements, event);
864
+ break;
865
+ case "ArrowDown":
866
+ focusableNum && this._handleTriggerArrowDownKeydown(focusableElements, event);
867
+ break;
868
+ default:
869
+ break;
870
+ }
871
+ }
872
+
873
+ /**
874
+ * focus trigger
875
+ *
876
+ * when trigger is 'focus' or 'hover', onFocus is bind to show popup
877
+ * if we focus trigger, popup will show again
878
+ *
879
+ * 如果 trigger 是 focus 或者 hover,则它绑定了 onFocus,这里我们如果重新 focus 的话,popup 会再次打开
880
+ * 因此 returnFocusOnClose 只支持 click trigger
881
+ */
882
+ _focusTrigger() {
883
+ const { trigger, returnFocusOnClose } = this.getProps();
884
+ if (returnFocusOnClose && trigger === 'click') {
885
+ const triggerNode = this._adapter.getTriggerNode();
886
+ if (triggerNode && 'focus' in triggerNode) {
887
+ triggerNode.focus();
888
+ }
889
+ }
890
+ }
891
+
892
+ _handleEscKeyDown(event: any) {
893
+ const { trigger } = this.getProps();
894
+ if (trigger !== 'custom') {
895
+ this.hide();
896
+ this._focusTrigger();
897
+ }
898
+ this._adapter.notifyEscKeydown(event);
899
+ }
900
+
901
+ _handleContainerTabKeyDown(focusableElements: any[], event: any) {
902
+ const activeElement = this._adapter.getActiveElement();
903
+ const isLastCurrentFocus = focusableElements[focusableElements.length - 1] === activeElement;
904
+ if (isLastCurrentFocus) {
905
+ focusableElements[0].focus();
906
+ event.preventDefault(); // prevent browser default tab move behavior
907
+ }
908
+ }
909
+
910
+ _handleContainerShiftTabKeyDown(focusableElements: any[], event: any) {
911
+ const activeElement = this._adapter.getActiveElement();
912
+ const isFirstCurrentFocus = focusableElements[0] === activeElement;
913
+ if (isFirstCurrentFocus) {
914
+ focusableElements[focusableElements.length - 1].focus();
915
+ event.preventDefault(); // prevent browser default tab move behavior
916
+ }
917
+ }
918
+
919
+ _handleTriggerArrowDownKeydown(focusableElements: any[], event: any) {
920
+ focusableElements[0].focus();
921
+ event.preventDefault(); // prevent browser default scroll behavior
922
+ }
923
+
924
+ _handleTriggerArrowUpKeydown(focusableElements: any[], event: any) {
925
+ focusableElements[focusableElements.length - 1].focus();
926
+ event.preventDefault(); // prevent browser default scroll behavior
927
+ }
800
928
  }
@@ -0,0 +1,8 @@
1
+ import { get } from 'lodash';
2
+ import { ESC_KEY } from './keyCode';
3
+
4
+ function isEscPress<T extends { key: string }>(e: T) {
5
+ return get(e, 'key') === ESC_KEY ? true : false;
6
+ }
7
+
8
+ export default isEscPress;
package/utils/keyCode.ts CHANGED
@@ -428,5 +428,6 @@ const keyCode = {
428
428
 
429
429
  export const ENTER_KEY = 'Enter';
430
430
  export const TAB_KEY = 'Tab';
431
+ export const ESC_KEY = 'Escape';
431
432
 
432
433
  export default keyCode;