84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
/**
|
||
* Карточка с лицевой и обратной стороной (переключение без анимации переворота).
|
||
*/
|
||
|
||
'use client';
|
||
|
||
import React, { useMemo, useState } from 'react';
|
||
|
||
export interface FlipCardProps {
|
||
/** Контент лицевой стороны */
|
||
front: React.ReactNode;
|
||
/** Контент обратной стороны */
|
||
back: React.ReactNode;
|
||
/** Высота карточки */
|
||
height?: string | number;
|
||
/** Дополнительный класс */
|
||
className?: string;
|
||
/** Управляемый режим (если задан) */
|
||
flipped?: boolean;
|
||
/** Коллбек при смене состояния */
|
||
onFlippedChange?: (flipped: boolean) => void;
|
||
}
|
||
|
||
export const FlipCard: React.FC<FlipCardProps> = ({
|
||
front,
|
||
back,
|
||
height = 'auto',
|
||
className = '',
|
||
flipped,
|
||
onFlippedChange,
|
||
}) => {
|
||
const [internalFlipped, setInternalFlipped] = useState(false);
|
||
const isControlled = useMemo(() => flipped !== undefined, [flipped]);
|
||
const isFlipped = isControlled ? (flipped as boolean) : internalFlipped;
|
||
|
||
const setFlipped = (next: boolean) => {
|
||
if (!isControlled) setInternalFlipped(next);
|
||
onFlippedChange?.(next);
|
||
};
|
||
|
||
return (
|
||
<div
|
||
className={`flip-card ${className}`.trim()}
|
||
style={{
|
||
position: 'relative',
|
||
height: typeof height === 'number' ? `${height}px` : height,
|
||
width: '100%',
|
||
...(height === 'auto' && { minHeight: 340 }),
|
||
}}
|
||
>
|
||
<div
|
||
className="flip-card-front"
|
||
style={{
|
||
position: 'absolute',
|
||
top: 0,
|
||
left: 0,
|
||
width: '100%',
|
||
height: '100%',
|
||
opacity: isFlipped ? 0 : 1,
|
||
visibility: isFlipped ? 'hidden' : 'visible',
|
||
transition: 'opacity 0.2s ease',
|
||
}}
|
||
>
|
||
{front}
|
||
</div>
|
||
<div
|
||
className="flip-card-back"
|
||
style={{
|
||
position: 'absolute',
|
||
top: 0,
|
||
left: 0,
|
||
width: '100%',
|
||
height: '100%',
|
||
opacity: isFlipped ? 1 : 0,
|
||
visibility: isFlipped ? 'visible' : 'hidden',
|
||
transition: 'opacity 0.2s ease',
|
||
}}
|
||
>
|
||
{back}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|