项目文件夹

文件
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

115 行
3.4 KiB
TypeScript

import React from 'react';
import { IcDelete, IcExpand } from '../res/icons';
import { useAlipayGestures } from '../hooks/useAlipayGestures';
interface AmountKeyboardProps {
onInput: (key: string) => void;
onDelete: () => void;
confirmLabel: string;
confirmEnabled?: boolean;
onConfirm: () => void;
actionPrefix?: string;
open: boolean;
onToggle: (open: boolean) => void;
}
/**
* 支付宝风格金额输入键盘 — 模拟真实支付宝 App 内部自绘键盘。
* 4×4 网格:左 3 列数字区 + 右 1 列(删除 + 确认按钮)。
* 带 data-hide-on-keyboard,系统键盘弹出时自动隐藏。
* onMouseDown preventDefault 阻止点击按钮时抢夺金额输入框的焦点。
*/
export const AmountKeyboard: React.FC<AmountKeyboardProps> = ({
onInput,
onDelete,
confirmLabel,
confirmEnabled = true,
onConfirm,
actionPrefix = 'amountKeyboard',
open,
onToggle,
}) => {
const { bindTap } = useAlipayGestures();
const preventFocusSteal = (e: React.MouseEvent) => { e.preventDefault(); };
const numBtn = (digit: string, extraClassName = '') => (
<button
key={digit}
onMouseDown={preventFocusSteal}
{...bindTap<HTMLButtonElement>(
{ kind: 'action', id: `${actionPrefix}.keypad.press` },
{ params: { digit }, onTrigger: () => onInput(digit) },
)}
className={`bg-white text-[30px] font-normal text-black active:bg-[#F7F7F7] border-r border-b border-[#E8E8E8] ${extraClassName}`}
>
{digit}
</button>
);
if (!open) return null;
return (
<div
className="bg-white flex-shrink-0"
data-hide-on-keyboard
>
<button
onMouseDown={preventFocusSteal}
{...bindTap<HTMLButtonElement>(
{ kind: 'action', id: `${actionPrefix}.keypad.toggle` },
{ onTrigger: () => onToggle(false) },
)}
className="h-9 w-full border-y border-[#E8E8E8] bg-white flex items-center justify-center active:bg-[#F7F7F7]"
>
<IcExpand size={16} className="text-[#9CA3AF]" />
</button>
<div className="grid grid-cols-4 grid-rows-[repeat(4,64px)] border-l border-t border-[#E8E8E8]">
{numBtn('1')}
{numBtn('2')}
{numBtn('3')}
<button
onMouseDown={preventFocusSteal}
{...bindTap<HTMLButtonElement>(
{ kind: 'action', id: `${actionPrefix}.keypad.delete` },
{ onTrigger: onDelete },
)}
className="bg-white border-r border-b border-[#E8E8E8] flex items-center justify-center active:bg-[#F7F7F7]"
>
<IcDelete size={22} className="text-[#444444]" />
</button>
{numBtn('4')}
{numBtn('5')}
{numBtn('6')}
<button
onMouseDown={preventFocusSteal}
{...(confirmEnabled
? bindTap<HTMLButtonElement>(
{ kind: 'action', id: `${actionPrefix}.confirm` },
{ onTrigger: onConfirm },
)
: {})}
className={`row-span-3 border-r border-b border-[#E8E8E8] text-[28px] font-normal transition-colors ${
confirmEnabled
? 'bg-[#1677FF] text-white active:bg-[#0E6AE6]'
: 'bg-[#D3E9FF] text-white/80'
}`}
>
{confirmLabel}
</button>
{numBtn('7')}
{numBtn('8')}
{numBtn('9')}
{numBtn('0', 'col-span-2')}
{numBtn('.')}
</div>
</div>
);
};