Frontend: prepinac vzhladu kariet (znaky vs suit-icon)
Toggle v hlavicke prepina znaky farieb medzi textovymi glyfmi a suit-icon obrazkami; volba perzistuje v localStorage. Gula ma v icon rezime hnedy popis, stredove ikonky srdca/gule/listu jemne zmensene aby sa neprekryvali s rohmi. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
97c71cf6e1
commit
04185a0d97
16 files changed
+145
-18
No files matched your search
@@ -0,0 +1,23 @@
|
||||
import { useCardSkin } from '../lib/cardSkin';
|
||||
|
||||
/** Small header button that flips the deck between the classic vector cards and
|
||||
* the illustrated "sedmové" art. Persists via the cardSkin store. */
|
||||
export default function CardSkinToggle({ className = '' }: { className?: string }) {
|
||||
const skin = useCardSkin((s) => s.skin);
|
||||
const toggle = useCardSkin((s) => s.toggle);
|
||||
const illustrated = skin === 'illustrated';
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggle}
|
||||
title="Prepnúť vzhľad kariet"
|
||||
aria-label="Prepnúť vzhľad kariet"
|
||||
aria-pressed={illustrated}
|
||||
className={`text-[13px] whitespace-nowrap transition-colors ${
|
||||
illustrated ? 'text-gold hover:text-gold-bright' : 'text-[#7a7058] hover:text-gold'
|
||||
} ${className}`}
|
||||
>
|
||||
{illustrated ? '🂠 Sedmové' : '🂠 Klasické'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Card } from '../types';
|
||||
import { useCardSkin, suitIcon } from '../lib/cardSkin';
|
||||
|
||||
const SUIT_SYMBOL: Record<string, string> = {
|
||||
HEARTS: '♥',
|
||||
@@ -19,6 +20,15 @@ const VALUE_LABEL: Record<string, string> = {
|
||||
LOWER: 'J', UPPER: 'Q', KING: 'K', ACE: 'A',
|
||||
};
|
||||
|
||||
// The bell, leaf and heart icons are a touch taller than the acorn, so in the
|
||||
// centre they sometimes bleed into the corner marks — nudge those three down.
|
||||
const CENTER_ICON_SCALE: Record<string, number> = {
|
||||
HEARTS: 0.85,
|
||||
LEAVES: 0.85,
|
||||
BELLS: 0.85,
|
||||
ACORNS: 1,
|
||||
};
|
||||
|
||||
// Per-size geometry. sm/md sit on the table, lg/xl are hand cards.
|
||||
const DIMS = {
|
||||
sm: { w: 38, h: 54, radius: 4, inset: 3, label: 9, suitSm: 7, suitLg: 18 },
|
||||
@@ -37,26 +47,45 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function CardView({ card, onClick, disabled = false, highlight = false, size = 'md' }: Props) {
|
||||
const skin = useCardSkin((s) => s.skin);
|
||||
const symbol = SUIT_SYMBOL[card.color];
|
||||
const color = SUIT_COLOR[card.color];
|
||||
const label = VALUE_LABEL[card.value];
|
||||
const d = DIMS[size];
|
||||
|
||||
const interactive = !disabled && !!onClick;
|
||||
// 'illustrated' skin keeps the classic card but swaps the ♥♠♣♦ glyphs for the
|
||||
// suit-icon artwork; 'classic' uses the plain text glyphs.
|
||||
const useIcons = skin === 'illustrated';
|
||||
// With the icon artwork the bell suit reads brown, so tint its value label to
|
||||
// match instead of the classic blue.
|
||||
const labelColor = useIcons && card.color === 'BELLS' ? '#8a5a1c' : color;
|
||||
|
||||
const corner = (rotated: boolean) => (
|
||||
<span
|
||||
className="absolute flex flex-col items-center leading-none"
|
||||
// Icons align to the number's left edge so a wide label (VIII) doesn't shove
|
||||
// the centred icon toward the middle; classic text stays centred as before.
|
||||
className={`absolute flex flex-col leading-none ${useIcons ? 'items-start' : 'items-center'}`}
|
||||
style={
|
||||
rotated
|
||||
? { bottom: d.inset, right: d.inset, transform: 'rotate(180deg)' }
|
||||
: { top: d.inset, left: d.inset }
|
||||
}
|
||||
>
|
||||
<span style={{ color, fontSize: d.label, fontFamily: 'Georgia,serif', fontWeight: 700, lineHeight: 1 }}>
|
||||
<span style={{ color: labelColor, fontSize: d.label, fontFamily: 'Georgia,serif', fontWeight: 700, lineHeight: 1 }}>
|
||||
{label}
|
||||
</span>
|
||||
<span style={{ color, fontSize: d.suitSm, lineHeight: 1.2 }}>{symbol}</span>
|
||||
{useIcons ? (
|
||||
<img
|
||||
src={suitIcon(card.color, 'small')}
|
||||
alt={symbol}
|
||||
draggable={false}
|
||||
style={{ height: d.suitSm * 1.25, width: 'auto', marginTop: 1 }}
|
||||
className="select-none"
|
||||
/>
|
||||
) : (
|
||||
<span style={{ color, fontSize: d.suitSm, lineHeight: 1.2 }}>{symbol}</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -76,7 +105,17 @@ export default function CardView({ card, onClick, disabled = false, highlight =
|
||||
>
|
||||
{corner(false)}
|
||||
<span className="absolute inset-0 flex items-center justify-center">
|
||||
<span style={{ color, fontSize: d.suitLg, lineHeight: 1 }}>{symbol}</span>
|
||||
{useIcons ? (
|
||||
<img
|
||||
src={suitIcon(card.color, 'large')}
|
||||
alt={symbol}
|
||||
draggable={false}
|
||||
style={{ height: d.suitLg * CENTER_ICON_SCALE[card.color], width: 'auto' }}
|
||||
className="select-none"
|
||||
/>
|
||||
) : (
|
||||
<span style={{ color, fontSize: d.suitLg, lineHeight: 1 }}>{symbol}</span>
|
||||
)}
|
||||
</span>
|
||||
{corner(true)}
|
||||
</button>
|
||||
|
||||
Reference in new issue
Block a user