-
Notifications
You must be signed in to change notification settings - Fork 294
feat: 添加组件无障碍功能支持 #3383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat_v3.x
Are you sure you want to change the base?
feat: 添加组件无障碍功能支持 #3383
Changes from 1 commit
929799b
7ee836a
94c5073
5dfd20f
bd12ee6
e47d0d4
e7a4b1f
75057e6
7da9642
08e97b5
3da0ec6
e90b2b2
3cbfe75
88462db
8ce8c51
68c851b
32b4cea
4e6df5d
cf0553a
1509b39
710afd9
2be0323
2379441
e7f84cc
7054cee
93c518f
1b997a4
23dba7a
0354507
dadca5b
77ae33b
01a2bca
3d88959
075b551
2e7cbd2
61863f5
7d0f814
0cdc8f4
30d9fdf
76c0e2f
3ce6c79
9d0911f
7507379
858715d
c8b6c1f
cd4dd52
72a0e64
9954f35
e4f1ed9
66092f4
57ad0d6
884d3bc
b66439e
e9f4a44
01456f9
d32fd32
3af56f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -208,6 +208,9 @@ export const Checkbox: FunctionComponent< | |
| )} | ||
| {...rest} | ||
| onClick={handleClick} | ||
| role="checkbox" | ||
| tabIndex={0} | ||
| aria-checked={innerChecked && !innerIndeterminate} | ||
|
||
| > | ||
| {renderCheckboxItem()} | ||
| </div> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,7 @@ const InternalCountDown: ForwardRefRenderFunction< | |
| onRestart, | ||
| onUpdate, | ||
| children, | ||
| ariaRoledescription, | ||
| ...rest | ||
| } = { ...defaultProps, ...props } | ||
| const classPrefix = 'nut-countdown' | ||
|
|
@@ -64,6 +65,10 @@ const InternalCountDown: ForwardRefRenderFunction< | |
| diffTime: 0, // 设置了 startTime 时,与 date.now() 的差异 | ||
| }) | ||
|
|
||
| const [role, setRole] = useState('') | ||
| // ARIA alert提示内容 | ||
| const [alertContent, setAlertContent] = useState('') | ||
|
|
||
| // 时间戳转换 或 获取当前时间的时间戳 | ||
| const getTimeStamp = (timeStr?: string | number) => { | ||
| if (!timeStr) return Date.now() | ||
|
|
@@ -102,6 +107,12 @@ const InternalCountDown: ForwardRefRenderFunction< | |
| stateRef.current.counting = false | ||
| pause() | ||
| onEnd && onEnd() | ||
| setRole('alert') | ||
| setAlertContent('时间到') | ||
| setTimeout(() => { | ||
| setRole('') | ||
| setAlertContent('') | ||
| }, 3000) | ||
|
Comment on lines
112
to
117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 修复潜在的内存泄漏
基于学习经验(Based on learnings) 建议将定时器 ID 存储在 ref 中并在组件卸载时清除: + const alertTimerRef = useRef<number>()
+
// ... 在 tick 函数中
if (!remainTime) {
stateRef.current.counting = false
pause()
onEnd && onEnd()
setRole('alert')
setAlertContent('时间到')
- setTimeout(() => {
+ alertTimerRef.current = window.setTimeout(() => {
setRole('')
setAlertContent('')
}, 3000)
}并在 componentWillUnmount 中清除: const componentWillUnmount = () => {
destroy && cancelAnimationFrame(stateRef.current.timer)
+ if (alertTimerRef.current) {
+ clearTimeout(alertTimerRef.current)
+ }
}
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| if (remainTime > 0) { | ||
|
|
@@ -327,9 +338,14 @@ const InternalCountDown: ForwardRefRenderFunction< | |
| <View | ||
| className={`${classPrefix} ${className}`} | ||
| style={{ ...style }} | ||
| ariaLabel="倒计时" | ||
| ariaRoledescription={ariaRoledescription} | ||
| {...rest} | ||
| > | ||
| {renderTaroTime()} | ||
| <View role={role} style={{ display: 'none' }}> | ||
| {alertContent} | ||
| </View> | ||
| </View> | ||
| )} | ||
| </> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import React, { useRef } from 'react' | ||
| import { Cell, CountDown } from '@nutui/nutui-react' | ||
|
|
||
| const Demo1 = () => { | ||
| const stateRef = useRef({ | ||
| endTime: Date.now() + 10 * 1000, | ||
| }) | ||
yangyanAurora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const onEnd = () => { | ||
| console.log('countdown: ended.') | ||
| } | ||
| return ( | ||
| <> | ||
| <Cell> | ||
| <CountDown | ||
| endTime={stateRef.current.endTime} | ||
| type="primary" | ||
| onEnd={onEnd} | ||
| ariaRoledescription="双十一活动" | ||
| /> | ||
| </Cell> | ||
| <Cell> | ||
| <CountDown | ||
| endTime={stateRef.current.endTime} | ||
| onEnd={onEnd} | ||
| ariaRoledescription="双十一活动" | ||
| /> | ||
| </Cell> | ||
| <Cell> | ||
| <CountDown | ||
| endTime={stateRef.current.endTime} | ||
| type="text" | ||
| onEnd={onEnd} | ||
| ariaRoledescription="双十一活动" | ||
| /> | ||
| </Cell> | ||
| </> | ||
| ) | ||
| } | ||
| export default Demo1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import React, { useRef } from 'react' | ||
| import { Cell, CountDown } from '@nutui/nutui-react-taro' | ||
|
|
||
| const Demo1 = () => { | ||
| const stateRef = useRef({ | ||
| endTime: Date.now() + 60 * 1000, | ||
| }) | ||
| const onEnd = () => { | ||
| console.log('countdown: ended.') | ||
| } | ||
| return ( | ||
| <> | ||
| <Cell> | ||
| <CountDown | ||
| endTime={stateRef.current.endTime} | ||
| type="primary" | ||
| onEnd={onEnd} | ||
| ariaRoledescription="双十一活动" | ||
| /> | ||
| </Cell> | ||
| <Cell> | ||
| <CountDown | ||
| endTime={stateRef.current.endTime} | ||
| onEnd={onEnd} | ||
| ariaRoledescription="双十一活动" | ||
| /> | ||
| </Cell> | ||
| <Cell> | ||
| <CountDown | ||
| endTime={stateRef.current.endTime} | ||
| type="text" | ||
| onEnd={onEnd} | ||
| ariaRoledescription="双十一活动" | ||
| /> | ||
| </Cell> | ||
| </> | ||
| ) | ||
| } | ||
| export default Demo1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
硬编码的 ariaLabel 值缺乏实用性。
将
ariaLabel硬编码为 "button" 不会为屏幕阅读器用户提供有意义的信息。应该:建议应用以下修改:
在
ButtonProps接口中添加可选的ariaLabel属性:export interface ButtonProps extends BasicComponent, OmitMiniProgramButtonProps { color: string shape: ButtonShape type: ButtonType size: ButtonSize fill: ButtonFill block: boolean loading: boolean disabled: boolean icon: React.ReactNode rightIcon: React.ReactNode nativeType: 'submit' | 'reset' // | 'button' onClick: (e: MouseEvent<HTMLButtonElement>) => void + ariaLabel?: string }然后在组件中使用:
const { color, shape, fill, loading, disabled, type, size, block, icon, rightIcon, children, className, style, formType, nativeType, onClick, + ariaLabel, ...rest } = { ...defaultProps, ...props }在渲染时有条件地应用:
<TaroButton {...rest} ref={ref} formType={formType || nativeType} className={buttonClassNames} style={{ ...getStyle, ...style }} onClick={(e) => handleClick(e as any)} - ariaLabel="button" + {...(ariaLabel && { ariaLabel })} >🤖 Prompt for AI Agents