@rainbow-oh/yee-c 0.8.0 → 0.8.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 (101) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +40 -25
  3. package/dist/cjs/Card/card.js +7 -3
  4. package/dist/cjs/Carousel/carousel-item.js +7 -2
  5. package/dist/cjs/Carousel/carousel.js +31 -4
  6. package/dist/cjs/Cascader/cascader.js +1 -1
  7. package/dist/cjs/Cascader/column.js +1 -1
  8. package/dist/cjs/Cascader/node.js +16 -4
  9. package/dist/cjs/Cascader/search-list.js +13 -4
  10. package/dist/cjs/Checkbox/checkbox-group.js +1 -1
  11. package/dist/cjs/Checkbox/checkbox.js +6 -3
  12. package/dist/cjs/Collapse/panel.js +10 -1
  13. package/dist/cjs/Dialog/dialog.js +22 -7
  14. package/dist/cjs/Drawer/drawer.js +14 -5
  15. package/dist/cjs/Menu/menu-item.js +14 -5
  16. package/dist/cjs/Menu/menu.js +10 -2
  17. package/dist/cjs/Pagination/items.js +10 -5
  18. package/dist/cjs/Pagination/pagination.js +9 -3
  19. package/dist/cjs/Radio/radio-group.js +6 -5
  20. package/dist/cjs/Radio/radio.js +39 -29
  21. package/dist/cjs/Rate/rate.js +6 -1
  22. package/dist/cjs/Rate/star.js +7 -3
  23. package/dist/cjs/Steps/step.js +7 -5
  24. package/dist/cjs/Steps/steps.js +1 -1
  25. package/dist/cjs/Tabs/tab-content.js +3 -1
  26. package/dist/cjs/Tabs/tab-header/index.js +3 -1
  27. package/dist/cjs/Tabs/tab-header/tab-header-title.js +3 -1
  28. package/dist/cjs/hooks/useDelayState.js +3 -2
  29. package/dist/cjs/hooks/useFocusManage.d.ts +1 -1
  30. package/dist/cjs/hooks/useFocusManage.js +27 -22
  31. package/dist/cjs/style/themes.css +1 -1
  32. package/dist/cjs/style/variables.css +1 -1
  33. package/dist/cjs/utils/focusStack.d.ts +6 -2
  34. package/dist/cjs/utils/focusStack.js +40 -11
  35. package/dist/cjs/version.d.ts +1 -1
  36. package/dist/cjs/version.js +1 -1
  37. package/dist/esm/Card/card.js +7 -3
  38. package/dist/esm/Carousel/carousel-item.js +7 -2
  39. package/dist/esm/Carousel/carousel.js +31 -4
  40. package/dist/esm/Cascader/cascader.js +3 -1
  41. package/dist/esm/Cascader/column.js +2 -1
  42. package/dist/esm/Cascader/node.js +18 -4
  43. package/dist/esm/Cascader/search-list.js +18 -4
  44. package/dist/esm/Checkbox/checkbox-group.js +3 -1
  45. package/dist/esm/Checkbox/checkbox.js +6 -3
  46. package/dist/esm/Collapse/panel.js +11 -2
  47. package/dist/esm/Dialog/dialog.js +26 -9
  48. package/dist/esm/Drawer/drawer.js +16 -7
  49. package/dist/esm/Menu/menu-item.js +16 -5
  50. package/dist/esm/Menu/menu.js +11 -5
  51. package/dist/esm/Pagination/items.js +14 -5
  52. package/dist/esm/Pagination/pagination.js +16 -4
  53. package/dist/esm/Radio/radio-group.js +5 -4
  54. package/dist/esm/Radio/radio.js +5 -4
  55. package/dist/esm/Rate/rate.js +6 -1
  56. package/dist/esm/Rate/star.js +12 -4
  57. package/dist/esm/Steps/step.js +12 -6
  58. package/dist/esm/Steps/steps.js +3 -1
  59. package/dist/esm/Tabs/tab-content.js +3 -1
  60. package/dist/esm/Tabs/tab-header/index.js +3 -1
  61. package/dist/esm/Tabs/tab-header/tab-header-title.js +3 -1
  62. package/dist/esm/hooks/useDelayState.js +5 -2
  63. package/dist/esm/hooks/useFocusManage.d.ts +1 -1
  64. package/dist/esm/hooks/useFocusManage.js +31 -13
  65. package/dist/esm/style/themes.css +1 -1
  66. package/dist/esm/style/variables.css +1 -1
  67. package/dist/esm/utils/focusStack.d.ts +6 -2
  68. package/dist/esm/utils/focusStack.js +37 -14
  69. package/dist/esm/version.d.ts +1 -1
  70. package/dist/esm/version.js +1 -1
  71. package/dist/umd/yee-c.min.js +1 -1
  72. package/package.json +10 -6
  73. package/src/Card/card.tsx +5 -1
  74. package/src/Carousel/carousel-item.tsx +7 -1
  75. package/src/Carousel/carousel.tsx +30 -3
  76. package/src/Cascader/cascader.tsx +1 -1
  77. package/src/Cascader/column.tsx +1 -1
  78. package/src/Cascader/node.tsx +14 -2
  79. package/src/Cascader/search-list.tsx +11 -2
  80. package/src/Checkbox/checkbox-group.tsx +1 -1
  81. package/src/Checkbox/checkbox.tsx +3 -0
  82. package/src/Collapse/panel.tsx +11 -1
  83. package/src/Dialog/dialog.tsx +31 -6
  84. package/src/Drawer/drawer.tsx +12 -3
  85. package/src/Menu/menu-item.tsx +12 -3
  86. package/src/Menu/menu.tsx +11 -5
  87. package/src/Pagination/items.tsx +9 -4
  88. package/src/Pagination/pagination.tsx +10 -4
  89. package/src/Radio/radio-group.tsx +2 -1
  90. package/src/Radio/radio.tsx +7 -2
  91. package/src/Rate/rate.tsx +5 -0
  92. package/src/Rate/star.tsx +8 -4
  93. package/src/Steps/step.tsx +15 -5
  94. package/src/Steps/steps.tsx +1 -1
  95. package/src/Tabs/tab-content.tsx +2 -0
  96. package/src/Tabs/tab-header/index.tsx +2 -0
  97. package/src/Tabs/tab-header/tab-header-title.tsx +3 -1
  98. package/src/hooks/useDelayState.ts +4 -2
  99. package/src/hooks/useFocusManage.ts +33 -17
  100. package/src/utils/focusStack.ts +55 -12
  101. package/src/version.ts +1 -1
@@ -179,9 +179,12 @@ const Pagination = React.forwardRef(
179
179
  prevClick();
180
180
  }
181
181
  }}
182
+ role="button"
183
+ aria-label="Previous page"
184
+ aria-disabled={mergedCurrent === 1 || undefined}
182
185
  key="prev"
183
186
  >
184
- {components?.prev ?? <ChevronLeft size={20} strokeWidth={1.5} />}
187
+ {components?.prev ?? <ChevronLeft size={20} strokeWidth={1.5} aria-hidden="true" />}
185
188
  </li>
186
189
  );
187
190
  };
@@ -204,9 +207,12 @@ const Pagination = React.forwardRef(
204
207
  nextClick();
205
208
  }
206
209
  }}
210
+ role="button"
211
+ aria-label="Next page"
212
+ aria-disabled={mergedCurrent === pageCount || undefined}
207
213
  key="next"
208
214
  >
209
- {components?.next ?? <ChevronRight size={20} strokeWidth={1.5} />}
215
+ {components?.next ?? <ChevronRight size={20} strokeWidth={1.5} aria-hidden="true" />}
210
216
  </li>
211
217
  );
212
218
  };
@@ -249,13 +255,13 @@ const Pagination = React.forwardRef(
249
255
  }
250
256
 
251
257
  return (
252
- <ul {...rest} className={cls} style={style} ref={ref}>
258
+ <ul {...rest} className={cls} style={style} ref={ref} role="navigation" aria-label="Pagination" aria-disabled={disabled || undefined}>
253
259
  {renderTotal()}
254
260
  {renderPrevious()}
255
261
  {simple ? (
256
262
  <li className={`${prefixCls}-simple`}>
257
263
  {renderNumber(false)}
258
- <span className="split">/</span>
264
+ <span className="split" aria-hidden="true">/</span>
259
265
  <span className="total-page">{pageCount}</span>
260
266
  </li>
261
267
  ) : (
@@ -61,6 +61,8 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
61
61
  style={{ ...style, gap: gap }}
62
62
  ref={ref}
63
63
  role="radiogroup"
64
+ aria-disabled={disabled || undefined}
65
+ {...rest}
64
66
  >
65
67
  <GroupCtx.Provider
66
68
  value={{ name, buttonStyle, onChange: handleChange }}
@@ -68,7 +70,6 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
68
70
  {options?.map((option, index) => (
69
71
  <Radio
70
72
  disabled={disabled}
71
- {...rest}
72
73
  {...option}
73
74
  checked={option.value === mergedValue}
74
75
  key={option.value || index}
@@ -67,7 +67,12 @@ const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
67
67
  );
68
68
 
69
69
  return (
70
- <label className={cls} style={style} onClick={handleClick}>
70
+ <label
71
+ className={cls}
72
+ style={style}
73
+ onClick={handleClick}
74
+ aria-disabled={disabled || undefined}
75
+ >
71
76
  <span
72
77
  className={clsx(prefixCls, {
73
78
  [`${prefixCls}-disabled`]: disabled,
@@ -83,12 +88,12 @@ const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
83
88
  checked={mergedChecked}
84
89
  disabled={disabled}
85
90
  onChange={handleChange}
86
- aria-checked={mergedChecked}
87
91
  />
88
92
  {!buttonStyle && (
89
93
  <span
90
94
  className={clsx(`${prefixCls}-inner`, classNames?.inner)}
91
95
  style={styles?.inner}
96
+ aria-hidden="true"
92
97
  ></span>
93
98
  )}
94
99
  </span>
package/src/Rate/rate.tsx CHANGED
@@ -106,6 +106,11 @@ const Rate = React.forwardRef<HTMLUListElement, RateProps>((baseprops, ref) => {
106
106
  ref={componentRef}
107
107
  role="radiogroup"
108
108
  aria-label="Rating"
109
+ aria-disabled={disabled || undefined}
110
+ aria-valuemin={0}
111
+ aria-valuemax={count}
112
+ aria-valuenow={mergedValue}
113
+ aria-valuetext={`${mergedValue} out of ${count} stars`}
109
114
  >
110
115
  {Array(count)
111
116
  .fill(0)
package/src/Rate/star.tsx CHANGED
@@ -45,6 +45,7 @@ const Star: React.FC<StarProps> = (props) => {
45
45
  aria-checked={active}
46
46
  aria-posinset={index + 1}
47
47
  aria-setsize={count}
48
+ aria-label={`${index + 1} ${index + 1 === 1 ? 'star' : 'stars'}`}
48
49
  key={'star-' + index}
49
50
  >
50
51
  <div
@@ -57,6 +58,7 @@ const Star: React.FC<StarProps> = (props) => {
57
58
  }
58
59
  }}
59
60
  tabIndex={0}
61
+ aria-label={`${index + 0.5} stars`}
60
62
  key={'star-' + index + 'left'}
61
63
  >
62
64
  {character ? (
@@ -64,7 +66,7 @@ const Star: React.FC<StarProps> = (props) => {
64
66
  {renderCharacter()}
65
67
  </span>
66
68
  ) : (
67
- <StarHalf className={`${prefixCls}-star-half`} />
69
+ <StarHalf className={`${prefixCls}-star-half`} aria-hidden="true" />
68
70
  )}
69
71
  </div>
70
72
  <div
@@ -77,6 +79,7 @@ const Star: React.FC<StarProps> = (props) => {
77
79
  }
78
80
  }}
79
81
  tabIndex={0}
82
+ aria-label={`${index + 1} ${index + 1 === 1 ? 'star' : 'stars'}`}
80
83
  key={'star-' + index + 'right'}
81
84
  >
82
85
  {character ? (
@@ -84,7 +87,7 @@ const Star: React.FC<StarProps> = (props) => {
84
87
  {renderCharacter()}
85
88
  </span>
86
89
  ) : (
87
- <StarFull className={`${prefixCls}-star-full`} />
90
+ <StarFull className={`${prefixCls}-star-full`} aria-hidden="true" />
88
91
  )}
89
92
  </div>
90
93
  </li>
@@ -103,6 +106,7 @@ const Star: React.FC<StarProps> = (props) => {
103
106
  aria-checked={active}
104
107
  aria-posinset={index + 1}
105
108
  aria-setsize={count}
109
+ aria-label={`${index + 1} ${index + 1 === 1 ? 'star' : 'stars'}`}
106
110
  key={'star-' + index}
107
111
  >
108
112
  {character ? (
@@ -110,9 +114,9 @@ const Star: React.FC<StarProps> = (props) => {
110
114
  {renderCharacter()}
111
115
  </span>
112
116
  ) : active ? (
113
- <StarFull className={`${prefixCls}-star-active`} />
117
+ <StarFull className={`${prefixCls}-star-active`} aria-hidden="true" />
114
118
  ) : (
115
- <StarFull className={`${prefixCls}-star-inactive`} />
119
+ <StarFull className={`${prefixCls}-star-inactive`} aria-hidden="true" />
116
120
  )}
117
121
  </li>
118
122
  )}
@@ -43,6 +43,15 @@ const Step: React.FC<StepProps> = (props) => {
43
43
  status ??
44
44
  (index < current ? 'finish' : index === current ? 'process' : 'wait');
45
45
 
46
+ const statusLabel =
47
+ mergedStatus === 'finish'
48
+ ? 'completed'
49
+ : mergedStatus === 'process'
50
+ ? 'in progress'
51
+ : mergedStatus === 'error'
52
+ ? 'error'
53
+ : 'pending';
54
+
46
55
  const renderIcon = useMemo(() => {
47
56
  if (dot) return <span className={`${prefixCls}-item-dot`}></span>;
48
57
 
@@ -89,7 +98,7 @@ const Step: React.FC<StepProps> = (props) => {
89
98
  return (
90
99
  <>
91
100
  {type === 'ribbon' && (
92
- <div className={`${prefixCls}-item-ribbon-end ${ribbonStatus}`} />
101
+ <div className={`${prefixCls}-item-ribbon-end ${ribbonStatus}`} aria-hidden="true" />
93
102
  )}
94
103
  <div
95
104
  className={cls}
@@ -97,11 +106,12 @@ const Step: React.FC<StepProps> = (props) => {
97
106
  onClick={handleClick}
98
107
  onKeyDown={handleKeyDown}
99
108
  tabIndex={disabled ? -1 : onChange ? 0 : undefined}
100
- role={onChange ? 'button' : undefined}
109
+ role={onChange ? 'button' : 'listitem'}
101
110
  aria-disabled={disabled}
102
111
  aria-current={index === current ? 'step' : undefined}
112
+ aria-label={`Step ${index + 1}${typeof title === 'string' ? `: ${title}` : ''}, ${statusLabel}`}
103
113
  >
104
- <div className={`${prefixCls}-item-icon`}>{renderIcon}</div>
114
+ <div className={`${prefixCls}-item-icon`} aria-hidden="true">{renderIcon}</div>
105
115
  <div className={`${prefixCls}-item-content`}>
106
116
  <div className={`${prefixCls}-item-title`}>
107
117
  <span title={typeof title === 'string' ? title : undefined}>
@@ -117,12 +127,12 @@ const Step: React.FC<StepProps> = (props) => {
117
127
  </div>
118
128
  </div>
119
129
  {type === 'navigation' && (
120
- <div className={`${prefixCls}-item-navi-arrow`}>
130
+ <div className={`${prefixCls}-item-navi-arrow`} aria-hidden="true">
121
131
  <ChevronRight />
122
132
  </div>
123
133
  )}
124
134
  {type === 'ribbon' && (
125
- <div className={`${prefixCls}-item-ribbon-head ${ribbonStatus}`} />
135
+ <div className={`${prefixCls}-item-ribbon-head ${ribbonStatus}`} aria-hidden="true" />
126
136
  )}
127
137
  </>
128
138
  );
@@ -71,7 +71,7 @@ const Steps = forwardRef<HTMLDivElement, StepsProps>((baseprops, ref) => {
71
71
  };
72
72
 
73
73
  return (
74
- <div {...rest} className={cls} style={style} ref={ref}>
74
+ <div {...rest} className={cls} style={style} ref={ref} role="list" aria-label="Steps">
75
75
  <StepsContext.Provider value={contextValue}>
76
76
  {renderSteps()}
77
77
  </StepsContext.Provider>
@@ -67,9 +67,11 @@ const TabContent: React.FC<TabContentProps> = ({
67
67
  className={`${prefixCls}-tab-content`}
68
68
  style={{ display: activeKey === key ? 'block' : 'none' }}
69
69
  key={key}
70
+ id={`tabpanel-${key}`}
70
71
  role="tabpanel"
71
72
  aria-labelledby={`tab-${key}`}
72
73
  aria-hidden={activeKey !== key}
74
+ tabIndex={activeKey === key ? 0 : -1}
73
75
  >
74
76
  {children}
75
77
  </div>
@@ -170,6 +170,8 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
170
170
  className={clsx(`${prefixCls}-nav-list`, classNames?.list)}
171
171
  style={styles?.list}
172
172
  ref={navList}
173
+ role="tablist"
174
+ aria-orientation={direction}
173
175
  >
174
176
  {items?.map((item: TabItemType, index: number) => (
175
177
  <TabHeaderTitle
@@ -166,10 +166,12 @@ const TabHeaderTitle: React.FC<TabsItemProps> = (props) => {
166
166
  {...rest}
167
167
  ref={tabRef}
168
168
  role="tab"
169
+ id={`tab-${$key}`}
169
170
  aria-selected={activeKey === $key}
170
171
  aria-disabled={disabled}
172
+ aria-controls={`tabpanel-${$key}`}
171
173
  className={cls}
172
- tabIndex={disabled ? -1 : 0}
174
+ tabIndex={disabled ? -1 : activeKey === $key ? 0 : -1}
173
175
  onClick={handleTabClick}
174
176
  onKeyDown={handleKeyDown}
175
177
  >
@@ -4,10 +4,12 @@ export default function useDelayState<T>(state: T, delay = 0): T {
4
4
  const [delayState, setDelayState] = useState<T>();
5
5
 
6
6
  useEffect(() => {
7
- setTimeout(() => {
7
+ const timer = window.setTimeout(() => {
8
8
  setDelayState(state);
9
9
  }, delay * 1000);
10
- }, [state]);
10
+
11
+ return () => window.clearTimeout(timer);
12
+ }, [delay, state]);
11
13
 
12
14
  return delayState as T;
13
15
  }
@@ -1,20 +1,36 @@
1
- import { useEffect } from 'react';
2
- import { popFocus, pushFocus } from '../utils/focusStack';
3
- import useDelayState from './useDelayState';
4
-
5
- export default function useFocusManage(ele: HTMLElement, open: boolean) {
6
- const delayOpen = useDelayState(open);
7
-
1
+ import { useEffect, useRef } from 'react';
2
+ import { type FocusStackEntry, popFocus, pushFocus } from '../utils/focusStack';
3
+ export default function useFocusManage(
4
+ element: HTMLElement | null,
5
+ open: boolean,
6
+ ) {
7
+ const entryRef = useRef<FocusStackEntry | null>(null);
8
+ const previousFocusRef = useRef<HTMLElement | null>(null);
9
+ const openCycleRef = useRef(false);
10
+ const releaseEntry = () => {
11
+ if (entryRef.current) {
12
+ popFocus(entryRef.current);
13
+ entryRef.current = null;
14
+ }
15
+ };
16
+ const endOpenCycle = () => {
17
+ releaseEntry();
18
+ previousFocusRef.current = null;
19
+ openCycleRef.current = false;
20
+ };
21
+ useEffect(() => {
22
+ if (open && !openCycleRef.current) {
23
+ openCycleRef.current = true;
24
+ previousFocusRef.current = document.activeElement as HTMLElement | null;
25
+ } else if (!open && openCycleRef.current) {
26
+ endOpenCycle();
27
+ }
28
+ }, [open]);
8
29
  useEffect(() => {
9
- if (!ele) return;
10
- if (delayOpen) {
11
- pushFocus(ele);
12
- } else {
13
- popFocus();
30
+ if (!open || !openCycleRef.current || !element || entryRef.current) {
31
+ return;
14
32
  }
15
-
16
- return () => {
17
- popFocus();
18
- };
19
- }, [ele, delayOpen]);
33
+ entryRef.current = pushFocus(element, previousFocusRef.current);
34
+ }, [element, open]);
35
+ useEffect(() => releaseEntry, []);
20
36
  }
@@ -1,22 +1,65 @@
1
- // focusStack.ts
2
1
  type Focusable = HTMLElement | null;
3
2
 
4
- const stack: Focusable[] = [];
3
+ export interface FocusStackEntry {
4
+ container: HTMLElement;
5
+ previous: Focusable;
6
+ }
7
+
8
+ const focusableSelector = [
9
+ 'a[href]',
10
+ 'button:not([disabled])',
11
+ 'textarea:not([disabled])',
12
+ 'input:not([disabled])',
13
+ 'select:not([disabled])',
14
+ 'details',
15
+ '[tabindex]:not([tabindex="-1"])',
16
+ ].join(',');
17
+
18
+ const stack: FocusStackEntry[] = [];
5
19
  let raf = 0;
6
20
 
7
- const schedule = (el: Focusable) => {
21
+ const schedule = (callback: () => void) => {
8
22
  cancelAnimationFrame(raf);
9
- raf = requestAnimationFrame(() => el?.focus());
23
+ raf = requestAnimationFrame(callback);
10
24
  };
11
25
 
12
- export const pushFocus = (el: Focusable) => {
13
- const prev = document.activeElement as HTMLElement;
14
- if (prev && prev !== el) stack.push(prev);
15
- schedule(el);
26
+ const focusInitialElement = (entry: FocusStackEntry) => {
27
+ if (stack[stack.length - 1] !== entry || !entry.container.isConnected) {
28
+ return;
29
+ }
30
+
31
+ const activeElement = document.activeElement;
32
+ if (activeElement && entry.container.contains(activeElement)) {
33
+ return;
34
+ }
35
+
36
+ const firstFocusable =
37
+ entry.container.querySelector<HTMLElement>(focusableSelector);
38
+ (firstFocusable ?? entry.container).focus();
16
39
  };
17
40
 
18
- export const popFocus = () => {
19
- const el = stack.pop();
20
- if (!el) return;
21
- schedule(el);
41
+ export const pushFocus = (
42
+ container: HTMLElement,
43
+ previous: Focusable = document.activeElement as HTMLElement | null,
44
+ ): FocusStackEntry => {
45
+ const entry = { container, previous };
46
+ stack.push(entry);
47
+ schedule(() => focusInitialElement(entry));
48
+ return entry;
49
+ };
50
+
51
+ export const popFocus = (entry: FocusStackEntry) => {
52
+ const index = stack.lastIndexOf(entry);
53
+ if (index === -1) return;
54
+
55
+ const isTopEntry = index === stack.length - 1;
56
+ stack.splice(index, 1);
57
+
58
+ if (!isTopEntry) return;
59
+
60
+ schedule(() => {
61
+ if (entry.previous?.isConnected) {
62
+ entry.previous.focus();
63
+ }
64
+ });
22
65
  };
package/src/version.ts CHANGED
@@ -4,7 +4,7 @@ declare global {
4
4
  }
5
5
  }
6
6
 
7
- export const version = "0.8.0";
7
+ export const version = "0.8.1";
8
8
 
9
9
  if (typeof window !== 'undefined') {
10
10
  window.__yee_c_version__ = version;