diff --git a/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx b/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx index f62c4be0d..64c24649c 100644 --- a/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx +++ b/packages/shared/src/components/PlatformProfileRechargeModal/index.tsx @@ -36,6 +36,32 @@ import { resolveMembershipProductId } from './membershipProduct'; const WECHAT_NATIVE_PAY_QR_IMAGE_SIZE = 180; +/** + * roving tabindex / radiogroup 的方向键索引解析:`Home` / `End` 跳首尾,左右键循环, + * 其余按键返回 `null`(调用方据此忽略事件)。`currentIndex` 允许为 `-1`(未选中)。 + */ +function resolveRovingIndex( + key: string, + currentIndex: number, + length: number, +): number | null { + if (length <= 0) { + return null; + } + switch (key) { + case 'Home': + return 0; + case 'End': + return length - 1; + case 'ArrowLeft': + return (currentIndex - 1 + length) % length; + case 'ArrowRight': + return (currentIndex + 1) % length; + default: + return null; + } +} + type PlatformProfileRechargeTab = 'subscription' | 'recharge'; export type PlatformProfileRechargeNativePaymentState = { @@ -511,20 +537,16 @@ function PlatformRechargeTabNav({ aria-label="会员与泥点" className="platform-subpanel flex gap-1 rounded-full p-1" onKeyDown={(event) => { - if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) { + const currentIndex = tabs.findIndex((tab) => tab.id === activeTab); + const nextIndex = resolveRovingIndex( + event.key, + currentIndex, + tabs.length, + ); + if (nextIndex === null) { return; } event.preventDefault(); - const currentIndex = tabs.findIndex((tab) => tab.id === activeTab); - const nextIndex = - event.key === 'Home' - ? 0 - : event.key === 'End' - ? tabs.length - 1 - : (currentIndex + - (event.key === 'ArrowRight' ? 1 : -1) + - tabs.length) % - tabs.length; const nextTab = tabs[nextIndex]; if (nextTab) { onChange(nextTab.id); @@ -851,22 +873,18 @@ function PlatformPointProductGrid({ aria-label="泥点充值档位" className="grid grid-cols-2 gap-2 sm:grid-cols-3" onKeyDown={(event) => { - if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) { - return; - } - event.preventDefault(); const currentIndex = products.findIndex( (product) => product.productId === selectedProductId, ); - const nextIndex = - event.key === 'Home' - ? 0 - : event.key === 'End' - ? products.length - 1 - : (currentIndex + - (event.key === 'ArrowRight' ? 1 : -1) + - products.length) % - products.length; + const nextIndex = resolveRovingIndex( + event.key, + currentIndex, + products.length, + ); + if (nextIndex === null) { + return; + } + event.preventDefault(); const nextProduct = products[nextIndex]; if (nextProduct) { onSelect(nextProduct);