@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.
- package/LICENSE +1 -1
- package/README.md +40 -25
- package/dist/cjs/Card/card.js +7 -3
- package/dist/cjs/Carousel/carousel-item.js +7 -2
- package/dist/cjs/Carousel/carousel.js +31 -4
- package/dist/cjs/Cascader/cascader.js +1 -1
- package/dist/cjs/Cascader/column.js +1 -1
- package/dist/cjs/Cascader/node.js +16 -4
- package/dist/cjs/Cascader/search-list.js +13 -4
- package/dist/cjs/Checkbox/checkbox-group.js +1 -1
- package/dist/cjs/Checkbox/checkbox.js +6 -3
- package/dist/cjs/Collapse/panel.js +10 -1
- package/dist/cjs/Dialog/dialog.js +22 -7
- package/dist/cjs/Drawer/drawer.js +14 -5
- package/dist/cjs/Menu/menu-item.js +14 -5
- package/dist/cjs/Menu/menu.js +10 -2
- package/dist/cjs/Pagination/items.js +10 -5
- package/dist/cjs/Pagination/pagination.js +9 -3
- package/dist/cjs/Radio/radio-group.js +6 -5
- package/dist/cjs/Radio/radio.js +39 -29
- package/dist/cjs/Rate/rate.js +6 -1
- package/dist/cjs/Rate/star.js +7 -3
- package/dist/cjs/Steps/step.js +7 -5
- package/dist/cjs/Steps/steps.js +1 -1
- package/dist/cjs/Tabs/tab-content.js +3 -1
- package/dist/cjs/Tabs/tab-header/index.js +3 -1
- package/dist/cjs/Tabs/tab-header/tab-header-title.js +3 -1
- package/dist/cjs/hooks/useDelayState.js +3 -2
- package/dist/cjs/hooks/useFocusManage.d.ts +1 -1
- package/dist/cjs/hooks/useFocusManage.js +27 -22
- package/dist/cjs/style/themes.css +1 -1
- package/dist/cjs/style/variables.css +1 -1
- package/dist/cjs/utils/focusStack.d.ts +6 -2
- package/dist/cjs/utils/focusStack.js +40 -11
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Card/card.js +7 -3
- package/dist/esm/Carousel/carousel-item.js +7 -2
- package/dist/esm/Carousel/carousel.js +31 -4
- package/dist/esm/Cascader/cascader.js +3 -1
- package/dist/esm/Cascader/column.js +2 -1
- package/dist/esm/Cascader/node.js +18 -4
- package/dist/esm/Cascader/search-list.js +18 -4
- package/dist/esm/Checkbox/checkbox-group.js +3 -1
- package/dist/esm/Checkbox/checkbox.js +6 -3
- package/dist/esm/Collapse/panel.js +11 -2
- package/dist/esm/Dialog/dialog.js +26 -9
- package/dist/esm/Drawer/drawer.js +16 -7
- package/dist/esm/Menu/menu-item.js +16 -5
- package/dist/esm/Menu/menu.js +11 -5
- package/dist/esm/Pagination/items.js +14 -5
- package/dist/esm/Pagination/pagination.js +16 -4
- package/dist/esm/Radio/radio-group.js +5 -4
- package/dist/esm/Radio/radio.js +5 -4
- package/dist/esm/Rate/rate.js +6 -1
- package/dist/esm/Rate/star.js +12 -4
- package/dist/esm/Steps/step.js +12 -6
- package/dist/esm/Steps/steps.js +3 -1
- package/dist/esm/Tabs/tab-content.js +3 -1
- package/dist/esm/Tabs/tab-header/index.js +3 -1
- package/dist/esm/Tabs/tab-header/tab-header-title.js +3 -1
- package/dist/esm/hooks/useDelayState.js +5 -2
- package/dist/esm/hooks/useFocusManage.d.ts +1 -1
- package/dist/esm/hooks/useFocusManage.js +31 -13
- package/dist/esm/style/themes.css +1 -1
- package/dist/esm/style/variables.css +1 -1
- package/dist/esm/utils/focusStack.d.ts +6 -2
- package/dist/esm/utils/focusStack.js +37 -14
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +10 -6
- package/src/Card/card.tsx +5 -1
- package/src/Carousel/carousel-item.tsx +7 -1
- package/src/Carousel/carousel.tsx +30 -3
- package/src/Cascader/cascader.tsx +1 -1
- package/src/Cascader/column.tsx +1 -1
- package/src/Cascader/node.tsx +14 -2
- package/src/Cascader/search-list.tsx +11 -2
- package/src/Checkbox/checkbox-group.tsx +1 -1
- package/src/Checkbox/checkbox.tsx +3 -0
- package/src/Collapse/panel.tsx +11 -1
- package/src/Dialog/dialog.tsx +31 -6
- package/src/Drawer/drawer.tsx +12 -3
- package/src/Menu/menu-item.tsx +12 -3
- package/src/Menu/menu.tsx +11 -5
- package/src/Pagination/items.tsx +9 -4
- package/src/Pagination/pagination.tsx +10 -4
- package/src/Radio/radio-group.tsx +2 -1
- package/src/Radio/radio.tsx +7 -2
- package/src/Rate/rate.tsx +5 -0
- package/src/Rate/star.tsx +8 -4
- package/src/Steps/step.tsx +15 -5
- package/src/Steps/steps.tsx +1 -1
- package/src/Tabs/tab-content.tsx +2 -0
- package/src/Tabs/tab-header/index.tsx +2 -0
- package/src/Tabs/tab-header/tab-header-title.tsx +3 -1
- package/src/hooks/useDelayState.ts +4 -2
- package/src/hooks/useFocusManage.ts +33 -17
- package/src/utils/focusStack.ts +55 -12
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rainbow-oh/yee-c",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "yee basic components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
"design-system",
|
|
11
11
|
"yee"
|
|
12
12
|
],
|
|
13
|
-
"homepage": "https://github.com/
|
|
13
|
+
"homepage": "https://github.com/insureMO/oh-yee/tree/main/packages/yee-c#readme",
|
|
14
14
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/
|
|
15
|
+
"url": "https://github.com/insureMO/oh-yee/issues"
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/insureMO/oh-yee.git",
|
|
20
20
|
"directory": "packages/yee-c"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@commitlint/cli": "^17.1.2",
|
|
96
96
|
"@commitlint/config-conventional": "^17.1.0",
|
|
97
|
+
"@testing-library/react": "16.3.2",
|
|
97
98
|
"@types/react": "^19.2.7",
|
|
98
99
|
"@types/react-dom": "^19.2.3",
|
|
99
100
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
@@ -102,6 +103,7 @@
|
|
|
102
103
|
"eslint": "^8.23.0",
|
|
103
104
|
"father": "^4.1.0",
|
|
104
105
|
"husky": "^8.0.1",
|
|
106
|
+
"jsdom": "25.0.1",
|
|
105
107
|
"lint-staged": "^13.0.3",
|
|
106
108
|
"prettier": "^3.6.2",
|
|
107
109
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
@@ -109,7 +111,8 @@
|
|
|
109
111
|
"react": "^19.2.0",
|
|
110
112
|
"react-dom": "^19.2.0",
|
|
111
113
|
"stylelint": "^14.16.1",
|
|
112
|
-
"stylelint-order": "^6.0.4"
|
|
114
|
+
"stylelint-order": "^6.0.4",
|
|
115
|
+
"vitest": "3.2.6"
|
|
113
116
|
},
|
|
114
117
|
"peerDependencies": {
|
|
115
118
|
"react": ">=18.0.0",
|
|
@@ -120,7 +123,7 @@
|
|
|
120
123
|
"registry": "https://registry.npmjs.org/"
|
|
121
124
|
},
|
|
122
125
|
"authors": [
|
|
123
|
-
"
|
|
126
|
+
"insureMO"
|
|
124
127
|
],
|
|
125
128
|
"scripts": {
|
|
126
129
|
"build": "node scripts/generate-version.js && rm -rf dist && father build",
|
|
@@ -138,6 +141,7 @@
|
|
|
138
141
|
"lint:css": "stylelint \"{src,test}/**/*.{css,less}\" --ignore-pattern \"src/style/color.*.css\"",
|
|
139
142
|
"lint:es": "eslint \"{src,test}/**/*.{js,jsx,ts,tsx}\"",
|
|
140
143
|
"start": "npm run dev",
|
|
144
|
+
"test": "vitest run",
|
|
141
145
|
"watch": "father dev"
|
|
142
146
|
}
|
|
143
147
|
}
|
package/src/Card/card.tsx
CHANGED
|
@@ -73,12 +73,12 @@ const Card: FC<CardProps> = (baseprops) => {
|
|
|
73
73
|
)}
|
|
74
74
|
style={styles?.expandIcon}
|
|
75
75
|
aria-expanded={mergedExpanded}
|
|
76
|
+
aria-label={mergedExpanded ? 'Collapse content' : 'Expand content'}
|
|
76
77
|
icon={
|
|
77
78
|
icon ? (
|
|
78
79
|
icon
|
|
79
80
|
) : (
|
|
80
81
|
<ChevronUp
|
|
81
|
-
aria-label="expanded"
|
|
82
82
|
strokeWidth={1.5}
|
|
83
83
|
size={16}
|
|
84
84
|
style={{
|
|
@@ -134,6 +134,7 @@ const Card: FC<CardProps> = (baseprops) => {
|
|
|
134
134
|
onKeyDown={handleKeyDown}
|
|
135
135
|
tabIndex={headerClickable ? 0 : undefined}
|
|
136
136
|
role={headerClickable ? 'button' : undefined}
|
|
137
|
+
aria-expanded={headerClickable ? mergedExpanded : undefined}
|
|
137
138
|
>
|
|
138
139
|
{iconPosition === 'left' ? renderExpandIcon() : null}
|
|
139
140
|
{title ? (
|
|
@@ -154,6 +155,9 @@ const Card: FC<CardProps> = (baseprops) => {
|
|
|
154
155
|
animate={mergedExpanded ? { height: 'auto' } : { height: 0 }}
|
|
155
156
|
exit={{ height: 0, display: 'none' }}
|
|
156
157
|
transition={{ duration: animationDuration }}
|
|
158
|
+
role="region"
|
|
159
|
+
aria-labelledby={title ? headerId : undefined}
|
|
160
|
+
aria-hidden={!mergedExpanded}
|
|
157
161
|
>
|
|
158
162
|
{children}
|
|
159
163
|
</motion.div>
|
|
@@ -7,17 +7,23 @@ const CarouselItem = (props: CarouselItemProps) => {
|
|
|
7
7
|
const { style, className, children, index } = props;
|
|
8
8
|
const { prefixCls, current } = React.useContext(CarouselCtx);
|
|
9
9
|
|
|
10
|
+
const isActive = index === current - 1;
|
|
11
|
+
|
|
10
12
|
return (
|
|
11
13
|
<div
|
|
12
14
|
className={clsx(
|
|
13
15
|
`${prefixCls}-item`,
|
|
14
16
|
{
|
|
15
|
-
[`${prefixCls}-item-active`]:
|
|
17
|
+
[`${prefixCls}-item-active`]: isActive,
|
|
16
18
|
},
|
|
17
19
|
className,
|
|
18
20
|
)}
|
|
19
21
|
data-index={index}
|
|
20
22
|
style={style}
|
|
23
|
+
role="tabpanel"
|
|
24
|
+
aria-roledescription="slide"
|
|
25
|
+
aria-label={`Slide ${(index ?? 0) + 1}`}
|
|
26
|
+
aria-hidden={!isActive}
|
|
21
27
|
>
|
|
22
28
|
{children}
|
|
23
29
|
</div>
|
|
@@ -173,6 +173,9 @@ const Carousel = React.forwardRef(
|
|
|
173
173
|
{ [`${prefixCls}-arrows-visible`]: showArrow },
|
|
174
174
|
className,
|
|
175
175
|
),
|
|
176
|
+
role: 'region' as const,
|
|
177
|
+
'aria-roledescription': 'carousel',
|
|
178
|
+
'aria-live': autoplay ? ('off' as const) : ('polite' as const),
|
|
176
179
|
onMouseEnter: handleMouseEnter,
|
|
177
180
|
onMouseLeave: handleMouseLeave,
|
|
178
181
|
};
|
|
@@ -198,6 +201,15 @@ const Carousel = React.forwardRef(
|
|
|
198
201
|
)}
|
|
199
202
|
style={styles?.prev}
|
|
200
203
|
onClick={prev}
|
|
204
|
+
role="button"
|
|
205
|
+
aria-label="Previous slide"
|
|
206
|
+
tabIndex={0}
|
|
207
|
+
onKeyDown={(e) => {
|
|
208
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
prev();
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
201
213
|
>
|
|
202
214
|
{direction === 'vertical' ? <ChevronUp /> : <ChevronLeft />}
|
|
203
215
|
</div>
|
|
@@ -211,6 +223,15 @@ const Carousel = React.forwardRef(
|
|
|
211
223
|
)}
|
|
212
224
|
style={styles?.next}
|
|
213
225
|
onClick={next}
|
|
226
|
+
role="button"
|
|
227
|
+
aria-label="Next slide"
|
|
228
|
+
tabIndex={0}
|
|
229
|
+
onKeyDown={(e) => {
|
|
230
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
231
|
+
e.preventDefault();
|
|
232
|
+
next();
|
|
233
|
+
}
|
|
234
|
+
}}
|
|
214
235
|
>
|
|
215
236
|
{direction === 'vertical' ? <ChevronDown /> : <ChevronRight />}
|
|
216
237
|
</div>
|
|
@@ -228,17 +249,23 @@ const Carousel = React.forwardRef(
|
|
|
228
249
|
classNames?.dot,
|
|
229
250
|
)}
|
|
230
251
|
style={styles?.dot}
|
|
252
|
+
role="tablist"
|
|
253
|
+
aria-label="Slide controls"
|
|
231
254
|
>
|
|
232
255
|
{new Array(React.Children.count(children))
|
|
233
256
|
.fill(0)
|
|
234
257
|
.map((i, index: number) => {
|
|
258
|
+
const isActive =
|
|
259
|
+
current === index + 1 ||
|
|
260
|
+
(current === 0 && index === count - 1);
|
|
235
261
|
return (
|
|
236
262
|
<li
|
|
237
263
|
className={clsx({
|
|
238
|
-
active:
|
|
239
|
-
current === index + 1 ||
|
|
240
|
-
(current === 0 && index === count),
|
|
264
|
+
active: isActive,
|
|
241
265
|
})}
|
|
266
|
+
role="tab"
|
|
267
|
+
aria-selected={isActive}
|
|
268
|
+
aria-label={`Slide ${index + 1}`}
|
|
242
269
|
tabIndex={0}
|
|
243
270
|
onClick={() => handleIndicator(index + 1, 'click')}
|
|
244
271
|
onKeyDown={(e) => {
|
|
@@ -446,7 +446,7 @@ const Cascader = (baseprops: CascaderProps) => {
|
|
|
446
446
|
|
|
447
447
|
const renderPopup = () => {
|
|
448
448
|
return (
|
|
449
|
-
<div className={clsx(prefixCls)}>
|
|
449
|
+
<div className={clsx(prefixCls)} role="group" aria-label="Cascader options">
|
|
450
450
|
{isFilter ? (
|
|
451
451
|
<SearchList
|
|
452
452
|
prefixCls={prefixCls}
|
package/src/Cascader/column.tsx
CHANGED
|
@@ -11,7 +11,7 @@ export interface ColumnProps {
|
|
|
11
11
|
const Column = (props: ColumnProps) => {
|
|
12
12
|
const { prefixCls, data, index } = props;
|
|
13
13
|
return (
|
|
14
|
-
<ul className={`${prefixCls}-menu`} role="
|
|
14
|
+
<ul className={`${prefixCls}-menu`} role="listbox" aria-label={`Level ${index + 1} options`}>
|
|
15
15
|
{Array.isArray(data) &&
|
|
16
16
|
data.map((item, idx: number) => {
|
|
17
17
|
return (
|
package/src/Cascader/node.tsx
CHANGED
|
@@ -107,12 +107,24 @@ const Node = (props: NodeProps) => {
|
|
|
107
107
|
className={clsName}
|
|
108
108
|
onMouseEnter={handleMouseEnter}
|
|
109
109
|
onClick={(e: React.MouseEvent<HTMLElement>) => handleClick(false, e)}
|
|
110
|
+
role="option"
|
|
111
|
+
aria-disabled={disabled || undefined}
|
|
112
|
+
aria-selected={checked || expandedPath[level]?.key === uid}
|
|
113
|
+
aria-expanded={isParent ? expandedPath[level]?.key === uid : undefined}
|
|
114
|
+
tabIndex={disabled ? -1 : 0}
|
|
115
|
+
onKeyDown={(e) => {
|
|
116
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
handleClick(false, e as unknown as React.MouseEvent<HTMLElement>);
|
|
119
|
+
}
|
|
120
|
+
}}
|
|
110
121
|
>
|
|
111
122
|
{multiple && (
|
|
112
123
|
<Checkbox
|
|
113
124
|
checked={isParent ? checkedAll : checked}
|
|
114
125
|
onChange={isParent ? () => handleClick(true) : undefined}
|
|
115
126
|
indeterminate={indeterminate}
|
|
127
|
+
aria-label={`Select ${typeof label === 'string' ? label : String(label)}`}
|
|
116
128
|
/>
|
|
117
129
|
)}
|
|
118
130
|
<span
|
|
@@ -125,9 +137,9 @@ const Node = (props: NodeProps) => {
|
|
|
125
137
|
</span>
|
|
126
138
|
|
|
127
139
|
{load === 1 ? (
|
|
128
|
-
<Spin size="small" />
|
|
140
|
+
<Spin size="small" aria-label="Loading" />
|
|
129
141
|
) : isLeaf === false ? (
|
|
130
|
-
<ChevronRight size={20} />
|
|
142
|
+
<ChevronRight size={20} aria-hidden="true" />
|
|
131
143
|
) : null}
|
|
132
144
|
</li>
|
|
133
145
|
);
|
|
@@ -32,7 +32,7 @@ const SearchList = (props: SearchListProps) => {
|
|
|
32
32
|
const { prefixCls, items, onClick } = props;
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<ul className={`${prefixCls}-filter-menu`}>
|
|
35
|
+
<ul className={`${prefixCls}-filter-menu`} role="listbox" aria-label="Search results">
|
|
36
36
|
{items.length ? (
|
|
37
37
|
items.map((item) => {
|
|
38
38
|
const title = item.data.$source?.title || item.data.labelPath.join(' / ');
|
|
@@ -43,13 +43,22 @@ const SearchList = (props: SearchListProps) => {
|
|
|
43
43
|
title={typeof title === 'string' ? title : String(title)}
|
|
44
44
|
onClick={() => onClick(item.data)}
|
|
45
45
|
key={item.data.uid}
|
|
46
|
+
role="option"
|
|
47
|
+
aria-disabled={item.data.$source?.disabled || undefined}
|
|
48
|
+
tabIndex={0}
|
|
49
|
+
onKeyDown={(e) => {
|
|
50
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
onClick(item.data);
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
46
55
|
>
|
|
47
56
|
{renderHighlightText(item.matches)}
|
|
48
57
|
</li>
|
|
49
58
|
);
|
|
50
59
|
})
|
|
51
60
|
) : (
|
|
52
|
-
<
|
|
61
|
+
<li role="option" aria-disabled="true">{'No result'}</li>
|
|
53
62
|
)}
|
|
54
63
|
</ul>
|
|
55
64
|
);
|
|
@@ -63,7 +63,7 @@ const CheckboxGroup = (baseprops: CheckboxGroupProps) => {
|
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
return (
|
|
66
|
-
<div className={cls} style={style}>
|
|
66
|
+
<div className={cls} style={style} role="group" aria-disabled={disabled || undefined}>
|
|
67
67
|
<CheckboxGroupCtx.Provider
|
|
68
68
|
value={{ name, buttonStyle, onChange: handleChange }}
|
|
69
69
|
>
|
|
@@ -51,6 +51,7 @@ const Checkbox = (baseprops: CheckboxProps) => {
|
|
|
51
51
|
className,
|
|
52
52
|
)}
|
|
53
53
|
style={style}
|
|
54
|
+
aria-disabled={disabled || undefined}
|
|
54
55
|
>
|
|
55
56
|
<span
|
|
56
57
|
className={clsx(`${prefixCls}`, {
|
|
@@ -68,10 +69,12 @@ const Checkbox = (baseprops: CheckboxProps) => {
|
|
|
68
69
|
disabled={disabled}
|
|
69
70
|
checked={mergedChecked}
|
|
70
71
|
onChange={handleChange}
|
|
72
|
+
aria-checked={indeterminate ? 'mixed' : mergedChecked}
|
|
71
73
|
/>
|
|
72
74
|
<span
|
|
73
75
|
className={clsx(`${prefixCls}-inner`, classNames?.inner)}
|
|
74
76
|
style={styles?.inner}
|
|
77
|
+
aria-hidden="true"
|
|
75
78
|
></span>
|
|
76
79
|
</span>
|
|
77
80
|
{children ? (
|
package/src/Collapse/panel.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import { ChevronRight } from 'lucide-react';
|
|
3
3
|
import { AnimatePresence, motion } from 'motion/react';
|
|
4
|
-
import React, { useContext } from 'react';
|
|
4
|
+
import React, { useContext, useId } from 'react';
|
|
5
5
|
import Button from '../Button';
|
|
6
6
|
import { CollapseContext } from './collapse';
|
|
7
7
|
import type { PanelProps } from './interface';
|
|
@@ -12,6 +12,10 @@ const Panel: React.FC<PanelProps & { id: string | number }> = (props) => {
|
|
|
12
12
|
|
|
13
13
|
const { prefixCls, activeKey, onExpand } = useContext(CollapseContext);
|
|
14
14
|
|
|
15
|
+
const panelId = useId();
|
|
16
|
+
const headerId = `${panelId}-header`;
|
|
17
|
+
const contentId = `${panelId}-content`;
|
|
18
|
+
|
|
15
19
|
const expanded = activeKey.includes(id);
|
|
16
20
|
|
|
17
21
|
const itemClassName = clsx(
|
|
@@ -28,7 +32,9 @@ const Panel: React.FC<PanelProps & { id: string | number }> = (props) => {
|
|
|
28
32
|
className={clsx(`${prefixCls}-header`, classNames?.header)}
|
|
29
33
|
role="button"
|
|
30
34
|
style={styles?.header}
|
|
35
|
+
id={headerId}
|
|
31
36
|
aria-expanded={expanded}
|
|
37
|
+
aria-controls={contentId}
|
|
32
38
|
aria-disabled="false"
|
|
33
39
|
tabIndex={0}
|
|
34
40
|
onClick={() => onExpand(id)}
|
|
@@ -44,6 +50,7 @@ const Panel: React.FC<PanelProps & { id: string | number }> = (props) => {
|
|
|
44
50
|
style={styles?.expandIcon}
|
|
45
51
|
animate={{ rotate: expanded ? 90 : 0 }}
|
|
46
52
|
transition={{ duration: 0.15 }}
|
|
53
|
+
aria-hidden="true"
|
|
47
54
|
>
|
|
48
55
|
<Button icon={<ChevronRight size={16} />} type="text" />
|
|
49
56
|
</motion.div>
|
|
@@ -69,6 +76,9 @@ const Panel: React.FC<PanelProps & { id: string | number }> = (props) => {
|
|
|
69
76
|
<motion.div
|
|
70
77
|
className={clsx(`${prefixCls}-content`, classNames?.content)}
|
|
71
78
|
style={styles?.content}
|
|
79
|
+
id={contentId}
|
|
80
|
+
role="region"
|
|
81
|
+
aria-labelledby={headerId}
|
|
72
82
|
initial={{ height: 0, opacity: 0 }}
|
|
73
83
|
animate={{ height: 'auto', opacity: 1 }}
|
|
74
84
|
exit={{ height: 0, opacity: 0 }}
|
package/src/Dialog/dialog.tsx
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import { X } from 'lucide-react';
|
|
3
3
|
import { motion } from 'motion/react';
|
|
4
|
-
import React, {
|
|
4
|
+
import React, {
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useId,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react';
|
|
5
12
|
import Button from '../Button';
|
|
6
13
|
import { GlobalContext } from '../Config-Provider';
|
|
7
14
|
import useEsc from '../hooks/useEsc';
|
|
@@ -55,7 +62,17 @@ const Dialog = (baseprops: DialogProps) => {
|
|
|
55
62
|
} = props;
|
|
56
63
|
|
|
57
64
|
const [delayOpen, setDelayOpen] = useState(open);
|
|
65
|
+
const [contentElement, setContentElement] = useState<HTMLDivElement | null>(
|
|
66
|
+
null,
|
|
67
|
+
);
|
|
58
68
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
69
|
+
const setContentRef = useCallback((element: HTMLDivElement | null) => {
|
|
70
|
+
contentRef.current = element;
|
|
71
|
+
setContentElement(element);
|
|
72
|
+
}, []);
|
|
73
|
+
const dialogId = useId();
|
|
74
|
+
const titleId = `${dialogId}-title`;
|
|
75
|
+
const bodyId = `${dialogId}-body`;
|
|
59
76
|
|
|
60
77
|
// When open changes and openResetLocation is true, reset position
|
|
61
78
|
useEffect(() => {
|
|
@@ -71,7 +88,7 @@ const Dialog = (baseprops: DialogProps) => {
|
|
|
71
88
|
});
|
|
72
89
|
|
|
73
90
|
// lock focus in the dialog
|
|
74
|
-
useLockFocus(
|
|
91
|
+
useLockFocus(contentElement, delayOpen);
|
|
75
92
|
|
|
76
93
|
// enable esc to close dialog
|
|
77
94
|
useEsc({
|
|
@@ -81,8 +98,8 @@ const Dialog = (baseprops: DialogProps) => {
|
|
|
81
98
|
},
|
|
82
99
|
});
|
|
83
100
|
|
|
84
|
-
// manage focus
|
|
85
|
-
useFocusManage(
|
|
101
|
+
// manage focus once per open cycle
|
|
102
|
+
useFocusManage(contentElement, open);
|
|
86
103
|
|
|
87
104
|
const cls = clsx(prefixCls, {
|
|
88
105
|
[`${prefixCls}-open`]: delayOpen,
|
|
@@ -143,6 +160,7 @@ const Dialog = (baseprops: DialogProps) => {
|
|
|
143
160
|
<div
|
|
144
161
|
className={clsx(`${prefixCls}-mask`, classNames?.mask)}
|
|
145
162
|
style={styles?.mask}
|
|
163
|
+
aria-hidden="true"
|
|
146
164
|
></div>
|
|
147
165
|
)}
|
|
148
166
|
<div
|
|
@@ -154,7 +172,11 @@ const Dialog = (baseprops: DialogProps) => {
|
|
|
154
172
|
style={{ width: fullscreen ? '100%' : width, ...styles?.content }}
|
|
155
173
|
tabIndex={0}
|
|
156
174
|
onClick={(e) => e.stopPropagation()}
|
|
157
|
-
ref={
|
|
175
|
+
ref={setContentRef}
|
|
176
|
+
role="dialog"
|
|
177
|
+
aria-modal={showMask ? true : undefined}
|
|
178
|
+
aria-labelledby={title ? titleId : undefined}
|
|
179
|
+
aria-describedby={bodyId}
|
|
158
180
|
>
|
|
159
181
|
{closable ? (
|
|
160
182
|
<Button
|
|
@@ -175,12 +197,15 @@ const Dialog = (baseprops: DialogProps) => {
|
|
|
175
197
|
className={clsx(`${prefixCls}-header`, classNames?.header)}
|
|
176
198
|
style={styles?.header}
|
|
177
199
|
>
|
|
178
|
-
<div className={`${prefixCls}-title`}
|
|
200
|
+
<div className={`${prefixCls}-title`} id={titleId}>
|
|
201
|
+
{title}
|
|
202
|
+
</div>
|
|
179
203
|
</div>
|
|
180
204
|
) : null}
|
|
181
205
|
<div
|
|
182
206
|
className={clsx(`${prefixCls}-body`, classNames?.body)}
|
|
183
207
|
style={styles?.body}
|
|
208
|
+
id={bodyId}
|
|
184
209
|
>
|
|
185
210
|
{children}
|
|
186
211
|
</div>
|
package/src/Drawer/drawer.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import { X } from 'lucide-react';
|
|
3
3
|
import { motion } from 'motion/react';
|
|
4
|
-
import React, { useContext, useRef, useState } from 'react';
|
|
4
|
+
import React, { useContext, useId, useRef, useState } from 'react';
|
|
5
5
|
import Button from '../Button';
|
|
6
6
|
import { GlobalContext } from '../Config-Provider';
|
|
7
7
|
import Portal from '../Portal';
|
|
@@ -81,6 +81,9 @@ const Drawer = (baseprops: DrawerProps) => {
|
|
|
81
81
|
|
|
82
82
|
const [delayOpen, setDelayOpen] = useState(open);
|
|
83
83
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
84
|
+
const drawerId = useId();
|
|
85
|
+
const titleId = `${drawerId}-title`;
|
|
86
|
+
const bodyId = `${drawerId}-body`;
|
|
84
87
|
|
|
85
88
|
useLockFocus(contentRef.current, delayOpen);
|
|
86
89
|
|
|
@@ -149,7 +152,7 @@ const Drawer = (baseprops: DrawerProps) => {
|
|
|
149
152
|
const renderNode = () => {
|
|
150
153
|
const { initial, animate, exit } = getAnimate(placement, open);
|
|
151
154
|
return (
|
|
152
|
-
<div {...rest} className={cls} style={style}
|
|
155
|
+
<div {...rest} className={cls} style={style}>
|
|
153
156
|
{showMask && delayOpen && (
|
|
154
157
|
<motion.div
|
|
155
158
|
className={clsx(`${prefixCls}-mask`, classNames?.mask)}
|
|
@@ -159,6 +162,7 @@ const Drawer = (baseprops: DrawerProps) => {
|
|
|
159
162
|
animate={open ? { opacity: 1 } : { opacity: 0 }}
|
|
160
163
|
exit={{ opacity: 0 }}
|
|
161
164
|
transition={{ duration: ANIMATION_DURATION }}
|
|
165
|
+
aria-hidden="true"
|
|
162
166
|
></motion.div>
|
|
163
167
|
)}
|
|
164
168
|
<motion.div
|
|
@@ -191,6 +195,10 @@ const Drawer = (baseprops: DrawerProps) => {
|
|
|
191
195
|
tabIndex={0}
|
|
192
196
|
style={styles?.content}
|
|
193
197
|
ref={contentRef}
|
|
198
|
+
role="dialog"
|
|
199
|
+
aria-modal={showMask ? true : undefined}
|
|
200
|
+
aria-labelledby={title ? titleId : undefined}
|
|
201
|
+
aria-describedby={bodyId}
|
|
194
202
|
>
|
|
195
203
|
{renderClose()}
|
|
196
204
|
{title ? (
|
|
@@ -198,12 +206,13 @@ const Drawer = (baseprops: DrawerProps) => {
|
|
|
198
206
|
className={clsx(`${prefixCls}-header`, classNames?.header)}
|
|
199
207
|
style={styles?.header}
|
|
200
208
|
>
|
|
201
|
-
<div className={clsx(`${prefixCls}-title`, classNames?.title)} style={styles?.title}>{title}</div>
|
|
209
|
+
<div className={clsx(`${prefixCls}-title`, classNames?.title)} style={styles?.title} id={titleId}>{title}</div>
|
|
202
210
|
</div>
|
|
203
211
|
) : null}
|
|
204
212
|
<div
|
|
205
213
|
className={clsx(`${prefixCls}-body`, classNames?.body)}
|
|
206
214
|
style={styles?.body}
|
|
215
|
+
id={bodyId}
|
|
207
216
|
>
|
|
208
217
|
{children}
|
|
209
218
|
</div>
|
package/src/Menu/menu-item.tsx
CHANGED
|
@@ -69,6 +69,10 @@ const MenuItem = (props: MenuItemProps) => {
|
|
|
69
69
|
style={{ paddingLeft, ...styles?.item }}
|
|
70
70
|
onClick={handleClick}
|
|
71
71
|
title={isCollapsed && typeof label === 'string' ? label : undefined}
|
|
72
|
+
role="menuitem"
|
|
73
|
+
aria-disabled={disabled || undefined}
|
|
74
|
+
aria-current={selectedKeys?.includes(key) ? 'page' : undefined}
|
|
75
|
+
tabIndex={disabled ? -1 : 0}
|
|
72
76
|
>
|
|
73
77
|
{iconNode}
|
|
74
78
|
{!isCollapsed && (
|
|
@@ -149,7 +153,7 @@ const MenuItem = (props: MenuItemProps) => {
|
|
|
149
153
|
if (isCollapsed) return null;
|
|
150
154
|
if (mode === 'vertical' || mode === 'horizontal') {
|
|
151
155
|
return (
|
|
152
|
-
<span className={`${prefixCls}-item-switch`}>
|
|
156
|
+
<span className={`${prefixCls}-item-switch`} aria-hidden="true">
|
|
153
157
|
<ChevronRight strokeWidth={2} size={18} />
|
|
154
158
|
</span>
|
|
155
159
|
);
|
|
@@ -159,6 +163,7 @@ const MenuItem = (props: MenuItemProps) => {
|
|
|
159
163
|
className={`${prefixCls}-item-switch`}
|
|
160
164
|
animate={{ rotateX: opened ? 180 : 0 }}
|
|
161
165
|
transition={{ duration: 0.15 }}
|
|
166
|
+
aria-hidden="true"
|
|
162
167
|
>
|
|
163
168
|
<ChevronDown strokeWidth={2} size={18} />
|
|
164
169
|
</motion.span>
|
|
@@ -167,7 +172,10 @@ const MenuItem = (props: MenuItemProps) => {
|
|
|
167
172
|
|
|
168
173
|
const triggerNode = (
|
|
169
174
|
<div
|
|
170
|
-
role="
|
|
175
|
+
role="menuitem"
|
|
176
|
+
aria-haspopup="true"
|
|
177
|
+
aria-expanded={opened}
|
|
178
|
+
aria-disabled={disabled || undefined}
|
|
171
179
|
className={clsx(`${prefixCls}-submenu-title`, {
|
|
172
180
|
[`${prefixCls}-submenu-open`]: opened,
|
|
173
181
|
[`${prefixCls}-submenu-selected`]:
|
|
@@ -178,6 +186,7 @@ const MenuItem = (props: MenuItemProps) => {
|
|
|
178
186
|
})}
|
|
179
187
|
style={{ paddingLeft }}
|
|
180
188
|
onClick={handleExpand}
|
|
189
|
+
tabIndex={disabled ? -1 : 0}
|
|
181
190
|
title={isCollapsed && typeof label === 'string' ? label : undefined}
|
|
182
191
|
>
|
|
183
192
|
{iconNode}
|
|
@@ -203,7 +212,7 @@ const MenuItem = (props: MenuItemProps) => {
|
|
|
203
212
|
mode === 'horizontal' && !isCollapsed ? 'bottomLeft' : 'rightTop';
|
|
204
213
|
|
|
205
214
|
const childNode = (
|
|
206
|
-
<li className={cls}>
|
|
215
|
+
<li className={cls} role="none">
|
|
207
216
|
{usePopup ? (
|
|
208
217
|
<Trigger
|
|
209
218
|
open={openKeys.includes(key)}
|
package/src/Menu/menu.tsx
CHANGED
|
@@ -40,9 +40,15 @@ const MenuGroup = ({
|
|
|
40
40
|
prefixCls: string;
|
|
41
41
|
renderItems: (items: MenuItemType[]) => React.ReactNode[];
|
|
42
42
|
}) => (
|
|
43
|
-
<li className={`${prefixCls}-item-group`}>
|
|
44
|
-
<div className={`${prefixCls}-item-group-title`}
|
|
45
|
-
|
|
43
|
+
<li className={`${prefixCls}-item-group`} role="none">
|
|
44
|
+
<div className={`${prefixCls}-item-group-title`} role="presentation" id={`${prefixCls}-group-${String(item.label)}`}>
|
|
45
|
+
{item.label}
|
|
46
|
+
</div>
|
|
47
|
+
<ul
|
|
48
|
+
className={`${prefixCls}-item-group-list`}
|
|
49
|
+
role="group"
|
|
50
|
+
aria-labelledby={`${prefixCls}-group-${String(item.label)}`}
|
|
51
|
+
>
|
|
46
52
|
{renderItems(item.children)}
|
|
47
53
|
</ul>
|
|
48
54
|
</li>
|
|
@@ -112,11 +118,11 @@ const Menu = (props: InnerMenuProps) => {
|
|
|
112
118
|
const rootStyle = root ? { ...style } : {};
|
|
113
119
|
|
|
114
120
|
return (
|
|
115
|
-
<ul {...dataAttrs} className={cls} style={rootStyle} role=
|
|
121
|
+
<ul {...dataAttrs} className={cls} style={rootStyle} role={root && mode === 'horizontal' ? 'menubar' : 'menu'}>
|
|
116
122
|
<MenuCtx.Provider value={{ level, mode, classNames, styles }}>
|
|
117
123
|
{getChildFromItems(items)}
|
|
118
124
|
{level === 0 && footer && (
|
|
119
|
-
<li className={`${prefixCls}-footer`}>{footer}</li>
|
|
125
|
+
<li className={`${prefixCls}-footer`} role="none">{footer}</li>
|
|
120
126
|
)}
|
|
121
127
|
</MenuCtx.Provider>
|
|
122
128
|
</ul>
|
package/src/Pagination/items.tsx
CHANGED
|
@@ -81,18 +81,19 @@ const Items: React.FC<PaginationItemsProps> = (props) => {
|
|
|
81
81
|
}
|
|
82
82
|
}}
|
|
83
83
|
role="button"
|
|
84
|
+
aria-label="Jump back 7 pages"
|
|
84
85
|
key="turnleft"
|
|
85
86
|
>
|
|
86
87
|
{components?.jumpPrev ?? (
|
|
87
88
|
<>
|
|
88
|
-
<span className={`${prefixCls}-jump-prev-ellipsis`}>
|
|
89
|
+
<span className={`${prefixCls}-jump-prev-ellipsis`} aria-hidden="true">
|
|
89
90
|
<Ellipsis
|
|
90
91
|
className="ellipsis"
|
|
91
92
|
size={18}
|
|
92
93
|
strokeWidth={1.5}
|
|
93
94
|
/>
|
|
94
95
|
</span>
|
|
95
|
-
<span className={`${prefixCls}-jump-prev-icon`}>
|
|
96
|
+
<span className={`${prefixCls}-jump-prev-icon`} aria-hidden="true">
|
|
96
97
|
<ChevronsLeft
|
|
97
98
|
className="turn"
|
|
98
99
|
size={18}
|
|
@@ -116,18 +117,19 @@ const Items: React.FC<PaginationItemsProps> = (props) => {
|
|
|
116
117
|
}
|
|
117
118
|
}}
|
|
118
119
|
role="button"
|
|
120
|
+
aria-label="Jump forward 7 pages"
|
|
119
121
|
key="turn-right"
|
|
120
122
|
>
|
|
121
123
|
{components?.jumpNext ?? (
|
|
122
124
|
<>
|
|
123
|
-
<span className={`${prefixCls}-jump-next-ellipsis`}>
|
|
125
|
+
<span className={`${prefixCls}-jump-next-ellipsis`} aria-hidden="true">
|
|
124
126
|
<Ellipsis
|
|
125
127
|
className="ellipsis"
|
|
126
128
|
size={18}
|
|
127
129
|
strokeWidth={1.5}
|
|
128
130
|
/>
|
|
129
131
|
</span>
|
|
130
|
-
<span className={`${prefixCls}-jump-next-icon`}>
|
|
132
|
+
<span className={`${prefixCls}-jump-next-icon`} aria-hidden="true">
|
|
131
133
|
<ChevronsRight
|
|
132
134
|
className="turn"
|
|
133
135
|
size={18}
|
|
@@ -159,6 +161,9 @@ const Items: React.FC<PaginationItemsProps> = (props) => {
|
|
|
159
161
|
onChange(number as number);
|
|
160
162
|
}
|
|
161
163
|
}}
|
|
164
|
+
role="button"
|
|
165
|
+
aria-label={`Page ${number}`}
|
|
166
|
+
aria-current={current === number ? 'page' : undefined}
|
|
162
167
|
>
|
|
163
168
|
{number}
|
|
164
169
|
</li>
|