Files
viberoom-web/src/features/exit-hold/ui/ExitHoldButton.tsx

126 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import type { KeyboardEvent } from 'react';
import { copy } from '@/shared/i18n';
import { cn } from '@/shared/lib/cn';
import { useHoldToConfirm } from '../model/useHoldToConfirm';
interface ExitHoldButtonProps {
variant: 'bar' | 'ring';
onConfirm: () => void;
className?: string;
}
const RING_RADIUS = 13;
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
export const ExitHoldButton = ({
variant,
onConfirm,
className,
}: ExitHoldButtonProps) => {
const { progress, isHolding, isCompleted, start, cancel } = useHoldToConfirm(onConfirm);
const ringOffset = RING_CIRCUMFERENCE * (1 - progress);
const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
start();
}
};
const handleKeyUp = (event: KeyboardEvent<HTMLButtonElement>) => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
cancel();
}
};
if (variant === 'ring') {
return (
<button
type="button"
aria-label={copy.space.exitHold.holdToExitAriaLabel}
onMouseDown={start}
onMouseUp={cancel}
onMouseLeave={cancel}
onTouchStart={start}
onTouchEnd={cancel}
onTouchCancel={cancel}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onClick={(event) => event.preventDefault()}
className={cn(
'relative inline-flex h-9 w-9 items-center justify-center rounded-full bg-white/8 text-white/74 transition hover:bg-white/14 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-200/80',
isHolding && 'bg-white/16',
className,
)}
>
<svg
aria-hidden
className="-rotate-90 absolute inset-0 h-9 w-9"
viewBox="0 0 32 32"
>
<circle
cx="16"
cy="16"
r={RING_RADIUS}
fill="none"
stroke="rgba(255,255,255,0.2)"
strokeWidth="2"
/>
<circle
cx="16"
cy="16"
r={RING_RADIUS}
fill="none"
stroke="rgba(186,230,253,0.92)"
strokeWidth="2"
strokeLinecap="round"
strokeDasharray={RING_CIRCUMFERENCE}
strokeDashoffset={ringOffset}
/>
</svg>
<span aria-hidden className="relative z-10 text-[12px]">
</span>
</button>
);
}
return (
<button
type="button"
aria-label={copy.space.exitHold.holdToExitAriaLabel}
onMouseDown={start}
onMouseUp={cancel}
onMouseLeave={cancel}
onTouchStart={start}
onTouchEnd={cancel}
onTouchCancel={cancel}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onClick={(event) => event.preventDefault()}
className={cn(
'relative overflow-hidden rounded-lg bg-white/8 px-2.5 py-1.5 text-xs text-white/82 transition hover:bg-white/14 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-200/80',
className,
)}
>
{isHolding || isCompleted ? (
<span
aria-hidden
className={cn(
'absolute inset-0 z-0 origin-left transform-gpu bg-sky-200/24 rounded-none',
isHolding && 'animate-[exit-hold-bar-fill_1000ms_linear_forwards]',
)}
style={isCompleted ? { transform: 'scaleX(1)' } : undefined}
/>
) : null}
<span className="relative z-10 inline-flex items-center gap-1">
<span aria-hidden></span>
<span>{copy.space.exitHold.exit}</span>
</span>
</button>
);
};