Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10,379 changes: 5,311 additions & 5,068 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/locales/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface BaseLang {
edit: string
reset: string
close: string
back: string
video: {
errorTip: string
clickRetry: string
Expand Down Expand Up @@ -142,4 +143,8 @@ export interface BaseLang {
errorCanvasTips: string
}
mask: string
arithmetic: {
plus: string
minus: string
}
}
5 changes: 5 additions & 0 deletions src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const enUS: BaseLang = {
edit: 'Edit',
reset: 'Reset',
close: 'Close',
back: 'Back',
video: {
errorTip: 'Error Tip',
clickRetry: 'Click Retry',
Expand Down Expand Up @@ -141,5 +142,9 @@ const enUS: BaseLang = {
errorCanvasTips: 'Canvas is not supported in the current environment',
},
mask: 'Mask',
arithmetic: {
plus: 'Plus',
minus: 'Minus',
},
}
export default enUS
5 changes: 5 additions & 0 deletions src/locales/id-ID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const idID: BaseLang = {
edit: 'Sunting',
reset: 'Mengatur Ulang',
close: 'Tutup',
back: 'Kembali',
video: {
errorTip: 'Terjadi Kesalahan',
clickRetry: 'Coba Lagi',
Expand Down Expand Up @@ -142,5 +143,9 @@ const idID: BaseLang = {
errorCanvasTips: 'Canvas is not supported in the current environment',
},
mask: 'Masker',
arithmetic: {
plus: 'Selain itu',
minus: 'minus',
},
}
export default idID
5 changes: 5 additions & 0 deletions src/locales/tr-TR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const trTR: BaseLang = {
edit: 'düzenlemek',
reset: 'sıfırlama',
close: 'Kapat',
back: 'Geri dön',
video: {
errorTip: 'Video yüklenemedi',
clickRetry: 'Yeniden Tıklayın',
Expand Down Expand Up @@ -150,5 +151,9 @@ const trTR: BaseLang = {
errorCanvasTips: 'Geçerli ortam Canvası desteklemiyor',
},
mask: 'Maske',
arithmetic: {
plus: 'Ayrıca',
minus: 'eksi',
},
}
export default trTR
5 changes: 5 additions & 0 deletions src/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const zhCN: BaseLang = {
edit: '编辑',
reset: '重置',
close: '关闭',
back: '返回',
video: {
errorTip: '视频加载失败',
clickRetry: '点击重试',
Expand Down Expand Up @@ -142,5 +143,9 @@ const zhCN: BaseLang = {
errorCanvasTips: '当前环境不支持Canvas',
},
mask: '蒙层',
arithmetic: {
plus: '加',
minus: '减',
},
}
export default zhCN
5 changes: 5 additions & 0 deletions src/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const zhCN: BaseLang = {
edit: '編輯',
reset: '重置',
close: '關閉',
back: '返回',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

繁体中文 arithmetic.minus 使用了简体字,建议改为「減」

整份 zh-TW 文案基本采用繁体字,但 arithmetic.minus 当前为 '减'(简体),与同文件如「暫無數據」「視頻」等用字风格不一致。建议改为繁体写法 '減',保证整体一致性:

-    minus: '减',
+    minus: '減',

Also applies to: 146-149

🤖 Prompt for AI Agents
In src/locales/zh-TW.ts around line 14 (and also lines 146-149), the Traditional
Chinese locale uses the simplified character '减' for arithmetic.minus; update
those entries to use the Traditional character '減' instead and scan the file for
any other occurrences of the simplified form to replace so the locale stays
consistent.

video: {
errorTip: '視頻加載失敗',
clickRetry: '點擊重試',
Expand Down Expand Up @@ -142,5 +143,9 @@ const zhCN: BaseLang = {
errorCanvasTips: '當前環境不支持Canvas',
},
mask: '蒙層',
arithmetic: {
plus: '加',
minus: '减',
},
}
export default zhCN
5 changes: 5 additions & 0 deletions src/locales/zh-UG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const zhUG: BaseLang = {
edit: 'يحرر',
reset: 'إعادة ضبط',
close: 'تاقاش',
back: 'ئاۋازقا',
video: {
errorTip: 'فىلىمنى قويۇش مەغلۇپ بولدى',
clickRetry: 'قايتا سىناش',
Expand Down Expand Up @@ -140,5 +141,9 @@ const zhUG: BaseLang = {
errorCanvasTips: 'Canvas نى قوللىمايدۇ',
},
mask: 'mask',
arithmetic: {
plus: 'ئەڭ‌',
minus: 'ئەممە',
},
}
export default zhUG
3 changes: 3 additions & 0 deletions src/packages/button/button.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
...rest
} = { ...defaultProps, ...props }

const role = 'button'
const getStyle = useMemo(() => {
const style: CSSProperties = {}
if (color) {
Expand Down Expand Up @@ -161,6 +162,8 @@
className={buttonClassNames}
style={{ ...getStyle, ...style }}
onClick={(e) => handleClick(e as any)}
ariaRole={role}
ariaDisabled={disabled}

Check failure on line 166 in src/packages/button/button.taro.tsx

View workflow job for this annotation

GitHub Actions / build

Type '{ children: Element; ref: ForwardedRef<HTMLButtonElement>; className: string; style: { [x: `--${string}`]: any; accentColor?: AccentColor | undefined; ... 855 more ...; glyphOrientationVertical?: GlyphOrientationVertical | undefined; }; onClick: (e: ITouchEvent) => void; ariaRole: string; ariaDisabled: boolean | und...' is not assignable to type 'IntrinsicAttributes & ViewProps'.

Check failure on line 166 in src/packages/button/button.taro.tsx

View workflow job for this annotation

GitHub Actions / build

Type '{ children: Element; ref: ForwardedRef<HTMLButtonElement>; className: string; style: { [x: `--${string}`]: any; accentColor?: AccentColor | undefined; ... 855 more ...; glyphOrientationVertical?: GlyphOrientationVertical | undefined; }; onClick: (e: ITouchEvent) => void; ariaRole: string; ariaDisabled: boolean | und...' is not assignable to type 'IntrinsicAttributes & ViewProps'.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

加一个 button.tsx 下的多语言

>
<View className="nut-button-wrap">
{loading && <Loading className="nut-icon-loading" />}
Expand Down
33 changes: 33 additions & 0 deletions src/packages/countdown/countdown.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const defaultProps = {
autoStart: true,
time: 0,
destroy: false,
ariaLabel: '倒计时',
} as TaroCountDownProps

const InternalCountDown: ForwardRefRenderFunction<
Expand All @@ -48,6 +49,7 @@ const InternalCountDown: ForwardRefRenderFunction<
onRestart,
onUpdate,
children,
ariaLabel,
...rest
} = { ...defaultProps, ...props }
const classPrefix = 'nut-countdown'
Expand All @@ -64,6 +66,11 @@ const InternalCountDown: ForwardRefRenderFunction<
diffTime: 0, // 设置了 startTime 时,与 date.now() 的差异
})

const [role, setRole] = useState('')
// ARIA alert提示内容
const [alertContent, setAlertContent] = useState('')
const alertTimerRef = useRef<ReturnType<typeof setTimeout>>()

// 时间戳转换 或 获取当前时间的时间戳
const getTimeStamp = (timeStr?: string | number) => {
if (!timeStr) return Date.now()
Expand Down Expand Up @@ -102,6 +109,12 @@ const InternalCountDown: ForwardRefRenderFunction<
stateRef.current.counting = false
pause()
onEnd && onEnd()
setRole('alert')
setAlertContent(`${ariaLabel}倒计时结束`)
alertTimerRef.current = setTimeout(() => {
setRole('')
setAlertContent('')
}, 3000)
}

if (remainTime > 0) {
Expand Down Expand Up @@ -257,6 +270,9 @@ const InternalCountDown: ForwardRefRenderFunction<

const componentWillUnmount = () => {
destroy && cancelAnimationFrame(stateRef.current.timer)
if (alertTimerRef.current) {
clearTimeout(alertTimerRef.current)
}
}

const getUnit = (unit: string) => {
Expand Down Expand Up @@ -327,9 +343,26 @@ const InternalCountDown: ForwardRefRenderFunction<
<View
className={`${classPrefix} ${className}`}
style={{ ...style }}
ariaLabel={ariaLabel}
{...rest}
>
{renderTaroTime()}
<View
role={role}
style={{
position: 'absolute',
width: 1,
height: 1,
padding: 0,
margin: -1,
overflow: 'hidden',
clip: 'rect(0 0 0 0)',
whiteSpace: 'nowrap',
border: 0,
}}
>
{alertContent}
</View>
</View>
)}
</>
Expand Down
19 changes: 18 additions & 1 deletion src/packages/countdown/countdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const defaultProps = {
autoStart: true,
time: 0,
destroy: false,
ariaLabel: '倒计时',
} as WebCountDownProps

const InternalCountDown: ForwardRefRenderFunction<
Expand All @@ -45,6 +46,7 @@ const InternalCountDown: ForwardRefRenderFunction<
onRestart,
onUpdate,
children,
ariaLabel,
...rest
} = { ...defaultProps, ...props }
const classPrefix = 'nut-countdown'
Expand All @@ -61,6 +63,11 @@ const InternalCountDown: ForwardRefRenderFunction<
diffTime: 0, // 设置了 startTime 时,与 date.now() 的差异
})

const [role, setRole] = useState('')
// ARIA alert提示内容
const [alertContent, setAlertContent] = useState('')
const alertTimerRef = useRef<number>()

// 时间戳转换 或 获取当前时间的时间戳
const getTimeStamp = (timeStr?: string | number) => {
if (!timeStr) return Date.now()
Expand Down Expand Up @@ -97,6 +104,12 @@ const InternalCountDown: ForwardRefRenderFunction<
stateRef.current.counting = false
pause()
onEnd && onEnd()
setRole('alert')
setAlertContent(`${ariaLabel}倒计时结束`)
alertTimerRef.current = window.setTimeout(() => {
setRole('')
setAlertContent('')
}, 3000)
}

if (remainTime > 0) {
Expand Down Expand Up @@ -270,6 +283,9 @@ const InternalCountDown: ForwardRefRenderFunction<

const componentWillUnmount = () => {
destroy && cancelAnimationFrame(stateRef.current.timer)
if (alertTimerRef.current) {
clearTimeout(alertTimerRef.current)
}
}

const renderTime = (() => {
Expand All @@ -282,9 +298,10 @@ const InternalCountDown: ForwardRefRenderFunction<
<div
className={`${classPrefix} ${className}`}
style={{ ...style }}
aria-label={ariaLabel}
{...rest}
dangerouslySetInnerHTML={{
__html: `${renderTime}`,
__html: `${renderTime}<span style="display:none" role=${role}>${alertContent}</span>`,
}}
Comment on lines 302 to 305
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

存在 XSS 安全风险,建议重构

使用 dangerouslySetInnerHTML 注入包含用户提供的 ariaLabel 的内容存在跨站脚本攻击(XSS)风险。虽然 ariaLabel 有默认值,但如果开发者传入了包含恶意脚本的字符串,这些脚本会被执行。

建议采用更安全的实现方式:

方案一:使用独立的 React 元素(推荐)

-<div
-  className={`${classPrefix} ${className}`}
-  style={{ ...style }}
-  aria-label={ariaLabel}
-  {...rest}
-  dangerouslySetInnerHTML={{
-    __html: `${renderTime}<span style="display:none" role=${role}>${alertContent}</span>`,
-  }}
-/>
+<div
+  className={`${classPrefix} ${className}`}
+  style={{ ...style }}
+  aria-label={ariaLabel}
+  {...rest}
+>
+  <span dangerouslySetInnerHTML={{ __html: renderTime }} />
+  <span style={{ display: 'none' }} role={role}>
+    {alertContent}
+  </span>
+</div>

方案二:如果必须使用 HTML,进行内容转义
使用 DOMPurify 或类似库对 ariaLabel 进行清理:

import DOMPurify from 'dompurify'
const sanitizedLabel = DOMPurify.sanitize(ariaLabel)

基于静态分析工具的提示。

🧰 Tools
🪛 ast-grep (0.39.9)

[warning] 302-302: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🤖 Prompt for AI Agents
In src/packages/countdown/countdown.tsx around lines 302 to 305, the code uses
dangerouslySetInnerHTML to inject renderTime plus an ariaLabel-derived string,
which creates an XSS risk; replace this with safe React elements: render the
rendered time as normal children and add a separate span element (visually
hidden if needed) with the appropriate role and the ariaLabel/alertContent as
plain text child so React escapes it automatically; if you absolutely must
inject HTML instead, sanitize ariaLabel first with a library like DOMPurify and
use the sanitized result in dangerouslySetInnerHTML.

/>
)}
Expand Down
6 changes: 6 additions & 0 deletions src/packages/countdown/demo.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Demo6 from './demos/taro/demo6'
import Demo7 from './demos/taro/demo7'
import Demo8 from './demos/taro/demo8'
import Demo9 from './demos/taro/demo9'
import Demo10 from './demos/taro/demo10'

const CountDownDemo = () => {
const [translated] = useTranslate({
Expand All @@ -25,6 +26,7 @@ const CountDownDemo = () => {
controlTime: '控制开始和暂停的倒计时',
customStyle: '自定义展示样式',
handleControl: '手动控制',
supportAria: '支持ARIA',
},
'zh-TW': {
basic: '基础用法',
Expand All @@ -36,6 +38,7 @@ const CountDownDemo = () => {
controlTime: '控製開始和暫停的倒計時',
customStyle: '自定義展示樣式',
handleControl: '手動控製',
supportAria: '支持ARIA',
},
'en-US': {
basic: 'Basic Usage',
Expand All @@ -47,6 +50,7 @@ const CountDownDemo = () => {
controlTime: 'Manual Control',
customStyle: 'Custom Style',
handleControl: 'Handle Control',
supportAria: 'support ARIA',
},
})

Expand Down Expand Up @@ -76,6 +80,8 @@ const CountDownDemo = () => {
<Demo8 />
<View className="h2">{translated.handleControl}</View>
<Demo9 />
<View className="h2">{translated.supportAria}</View>
<Demo10 />
</ScrollView>
</>
)
Expand Down
8 changes: 7 additions & 1 deletion src/packages/countdown/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Demo6 from './demos/h5/demo6'
import Demo7 from './demos/h5/demo7'
import Demo8 from './demos/h5/demo8'
import Demo9 from './demos/h5/demo9'
import Demo10 from './demos/h5/demo10'

const CountDownDemo = () => {
const [translated] = useTranslate({
Expand All @@ -22,9 +23,10 @@ const CountDownDemo = () => {
controlTime: '控制开始和暂停的倒计时',
customStyle: '自定义展示样式',
handleControl: '手动控制',
supportAria: '支持ARIA',
},
'zh-TW': {
basic: '基础用法',
basic: '基礎用法',
remainingTime: '剩余時間用法',
format: '自定義格式',
millisecond: '毫秒級渲染',
Expand All @@ -33,6 +35,7 @@ const CountDownDemo = () => {
controlTime: '控製開始和暫停的倒計時',
customStyle: '自定義展示樣式',
handleControl: '手動控製',
supportAria: '支持ARIA',
},
'en-US': {
basic: 'Basic Usage',
Expand All @@ -44,6 +47,7 @@ const CountDownDemo = () => {
controlTime: 'Manual Control',
customStyle: 'Custom Style',
handleControl: 'Handle Control',
supportAria: 'support ARIA',
},
})

Expand All @@ -68,6 +72,8 @@ const CountDownDemo = () => {
<Demo8 />
<h2>{translated.handleControl}</h2>
<Demo9 />
<h2>{translated.supportAria}</h2>
<Demo10 />
</div>
</>
)
Expand Down
Loading
Loading