@ankhorage/zora-tabletop 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +3 -3
- package/dist/cardLabel.d.ts.map +1 -1
- package/dist/cardLabel.js +10 -1
- package/dist/cardLabel.js.map +1 -1
- package/dist/cardSymbols.d.ts +4 -0
- package/dist/cardSymbols.d.ts.map +1 -0
- package/dist/cardSymbols.js +13 -0
- package/dist/cardSymbols.js.map +1 -0
- package/dist/components/playing-card/PlayingCard.d.ts +2 -2
- package/dist/components/playing-card/PlayingCard.d.ts.map +1 -1
- package/dist/components/playing-card/PlayingCard.js +5 -13
- package/dist/components/playing-card/PlayingCard.js.map +1 -1
- package/dist/components/tabletop-table/TabletopTable.d.ts.map +1 -1
- package/dist/components/tabletop-table/TabletopTable.js +59 -21
- package/dist/components/tabletop-table/TabletopTable.js.map +1 -1
- package/dist/tableLayout.d.ts +6 -0
- package/dist/tableLayout.d.ts.map +1 -0
- package/dist/tableLayout.js +6 -0
- package/dist/tableLayout.js.map +1 -0
- package/package.json +2 -2
- package/src/cardLabel.test.ts +11 -0
- package/src/cardLabel.ts +12 -1
- package/src/cardSymbols.test.ts +19 -0
- package/src/cardSymbols.ts +16 -0
- package/src/components/playing-card/PlayingCard.tsx +5 -16
- package/src/components/tabletop-table/TabletopTable.tsx +74 -31
- package/src/tableLayout.test.ts +15 -0
- package/src/tableLayout.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ankhorage/zora-tabletop
|
|
2
2
|
|
|
3
|
+
## 0.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 01b7403: Render playing-card suit glyphs, improve accessible card labels, and separate tabletop seat panels from token pills for more readable compact card-game trainer layouts.
|
|
8
|
+
|
|
9
|
+
## 0.0.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 51c846b: Update packages
|
|
14
|
+
|
|
3
15
|
## 0.0.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/zora-tabletop
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Reusable tabletop, playing-card, seat, token, and card-game presentation components for React Native and React Native Web.
|
|
9
9
|
|
|
@@ -210,8 +210,8 @@ PlayingCard({
|
|
|
210
210
|
Theme-aware face-up playing card primitive.
|
|
211
211
|
|
|
212
212
|
Use `PlayingCard` for visible card values in hands, shared table cards, piles,
|
|
213
|
-
or custom tabletop layouts. The component renders rank and suit
|
|
214
|
-
an accessible card label by default.
|
|
213
|
+
or custom tabletop layouts. The component renders rank and suit glyphs and
|
|
214
|
+
exposes an accessible card label by default.
|
|
215
215
|
|
|
216
216
|
#### Face-up card
|
|
217
217
|
|
package/dist/cardLabel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardLabel.d.ts","sourceRoot":"","sources":["../src/cardLabel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"cardLabel.d.ts","sourceRoot":"","sources":["../src/cardLabel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAShD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAElE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5D"}
|
package/dist/cardLabel.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
const rankLabels = {
|
|
2
|
+
A: 'ace',
|
|
3
|
+
J: 'jack',
|
|
4
|
+
K: 'king',
|
|
5
|
+
Q: 'queen',
|
|
6
|
+
};
|
|
1
7
|
export function getPlayingCardLabel(card) {
|
|
2
|
-
return `${card.rank} ${card.suit}`;
|
|
8
|
+
return `${getPlayingCardRankLabel(card.rank)} of ${card.suit}`;
|
|
3
9
|
}
|
|
4
10
|
export function createHiddenCardLabel(index) {
|
|
5
11
|
if (index === undefined)
|
|
6
12
|
return 'Hidden card';
|
|
7
13
|
return `Hidden card ${index + 1}`;
|
|
8
14
|
}
|
|
15
|
+
function getPlayingCardRankLabel(rank) {
|
|
16
|
+
return rankLabels[rank] ?? rank;
|
|
17
|
+
}
|
|
9
18
|
//# sourceMappingURL=cardLabel.js.map
|
package/dist/cardLabel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardLabel.js","sourceRoot":"","sources":["../src/cardLabel.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"cardLabel.js","sourceRoot":"","sources":["../src/cardLabel.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAqC;IACnD,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,OAAO;CACX,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,OAAO,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC;IAE9C,OAAO,eAAe,KAAK,GAAG,CAAC,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,uBAAuB,CAAC,IAAY;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAClC,CAAC","sourcesContent":["import type { PlayingCardValue } from './types';\n\nconst rankLabels: Readonly<Record<string, string>> = {\n A: 'ace',\n J: 'jack',\n K: 'king',\n Q: 'queen',\n};\n\nexport function getPlayingCardLabel(card: PlayingCardValue): string {\n return `${getPlayingCardRankLabel(card.rank)} of ${card.suit}`;\n}\n\nexport function createHiddenCardLabel(index?: number): string {\n if (index === undefined) return 'Hidden card';\n\n return `Hidden card ${index + 1}`;\n}\n\nfunction getPlayingCardRankLabel(rank: string): string {\n return rankLabels[rank] ?? rank;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cardSymbols.d.ts","sourceRoot":"","sources":["../src/cardSymbols.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAS/C,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAEnE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const suitMarks = {
|
|
2
|
+
clubs: '♣',
|
|
3
|
+
diamonds: '♦',
|
|
4
|
+
hearts: '♥',
|
|
5
|
+
spades: '♠',
|
|
6
|
+
};
|
|
7
|
+
export function getPlayingCardSuitMark(suit) {
|
|
8
|
+
return suitMarks[suit];
|
|
9
|
+
}
|
|
10
|
+
export function isRedPlayingCardSuit(suit) {
|
|
11
|
+
return suit === 'diamonds' || suit === 'hearts';
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=cardSymbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cardSymbols.js","sourceRoot":"","sources":["../src/cardSymbols.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAA8C;IAC3D,KAAK,EAAE,GAAG;IACV,QAAQ,EAAE,GAAG;IACb,MAAM,EAAE,GAAG;IACX,MAAM,EAAE,GAAG;CACZ,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,IAAqB;IAC1D,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAqB;IACxD,OAAO,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,CAAC;AAClD,CAAC","sourcesContent":["import type { PlayingCardSuit } from './types';\n\nconst suitMarks: Readonly<Record<PlayingCardSuit, string>> = {\n clubs: '♣',\n diamonds: '♦',\n hearts: '♥',\n spades: '♠',\n};\n\nexport function getPlayingCardSuitMark(suit: PlayingCardSuit): string {\n return suitMarks[suit];\n}\n\nexport function isRedPlayingCardSuit(suit: PlayingCardSuit): boolean {\n return suit === 'diamonds' || suit === 'hearts';\n}\n"]}
|
|
@@ -4,8 +4,8 @@ import type { PlayingCardProps } from './types';
|
|
|
4
4
|
* Theme-aware face-up playing card primitive.
|
|
5
5
|
*
|
|
6
6
|
* Use `PlayingCard` for visible card values in hands, shared table cards, piles,
|
|
7
|
-
* or custom tabletop layouts. The component renders rank and suit
|
|
8
|
-
* an accessible card label by default.
|
|
7
|
+
* or custom tabletop layouts. The component renders rank and suit glyphs and
|
|
8
|
+
* exposes an accessible card label by default.
|
|
9
9
|
*
|
|
10
10
|
* @readme
|
|
11
11
|
* @example Face-up card
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayingCard.d.ts","sourceRoot":"","sources":["../../../src/components/playing-card/PlayingCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"PlayingCard.d.ts","sourceRoot":"","sources":["../../../src/components/playing-card/PlayingCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAe,EACf,QAAgB,EAChB,KAAa,EACb,kBAAkB,EAClB,WAAW,EACX,MAAM,GACP,EAAE,gBAAgB,qBAsDlB"}
|
|
@@ -3,22 +3,14 @@ import React from 'react';
|
|
|
3
3
|
import { StyleSheet, Text, View } from 'react-native';
|
|
4
4
|
import { getPlayingCardLabel } from '../../cardLabel';
|
|
5
5
|
import { getTabletopCardDimensions } from '../../cardSizing';
|
|
6
|
+
import { getPlayingCardSuitMark, isRedPlayingCardSuit } from '../../cardSymbols';
|
|
6
7
|
import { createTabletopColorScheme } from '../../colors';
|
|
7
|
-
const suitMarks = {
|
|
8
|
-
clubs: 'C',
|
|
9
|
-
diamonds: 'D',
|
|
10
|
-
hearts: 'H',
|
|
11
|
-
spades: 'S',
|
|
12
|
-
};
|
|
13
|
-
function isRedSuit(suit) {
|
|
14
|
-
return suit === 'diamonds' || suit === 'hearts';
|
|
15
|
-
}
|
|
16
8
|
/***
|
|
17
9
|
* Theme-aware face-up playing card primitive.
|
|
18
10
|
*
|
|
19
11
|
* Use `PlayingCard` for visible card values in hands, shared table cards, piles,
|
|
20
|
-
* or custom tabletop layouts. The component renders rank and suit
|
|
21
|
-
* an accessible card label by default.
|
|
12
|
+
* or custom tabletop layouts. The component renders rank and suit glyphs and
|
|
13
|
+
* exposes an accessible card label by default.
|
|
22
14
|
*
|
|
23
15
|
* @readme
|
|
24
16
|
* @example Face-up card
|
|
@@ -30,7 +22,7 @@ export function PlayingCard({ card, size = 'medium', selected = false, muted = f
|
|
|
30
22
|
const { theme } = useZoraTheme();
|
|
31
23
|
const colors = React.useMemo(() => createTabletopColorScheme(theme, colorScheme), [colorScheme, theme]);
|
|
32
24
|
const dimensions = getTabletopCardDimensions(size);
|
|
33
|
-
const suitColor =
|
|
25
|
+
const suitColor = isRedPlayingCardSuit(card.suit) ? colors.redSuitText : colors.cardText;
|
|
34
26
|
return (<View accessibilityLabel={accessibilityLabel ?? getPlayingCardLabel(card)} accessibilityRole="image" style={[
|
|
35
27
|
styles.card,
|
|
36
28
|
{
|
|
@@ -60,7 +52,7 @@ export function PlayingCard({ card, size = 'medium', selected = false, muted = f
|
|
|
60
52
|
lineHeight: dimensions.suitFontSize + 3,
|
|
61
53
|
},
|
|
62
54
|
]}>
|
|
63
|
-
{
|
|
55
|
+
{getPlayingCardSuitMark(card.suit)}
|
|
64
56
|
</Text>
|
|
65
57
|
</View>);
|
|
66
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayingCard.js","sourceRoot":"","sources":["../../../src/components/playing-card/PlayingCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"PlayingCard.js","sourceRoot":"","sources":["../../../src/components/playing-card/PlayingCard.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useZoraTheme } from '@ankhorage/zora';\nimport React from 'react';\nimport { StyleSheet, Text, View } from 'react-native';\n\nimport { getPlayingCardLabel } from '../../cardLabel';\nimport { getTabletopCardDimensions } from '../../cardSizing';\nimport { getPlayingCardSuitMark, isRedPlayingCardSuit } from '../../cardSymbols';\nimport { createTabletopColorScheme } from '../../colors';\nimport type { PlayingCardProps } from './types';\n\n/***\n * Theme-aware face-up playing card primitive.\n *\n * Use `PlayingCard` for visible card values in hands, shared table cards, piles,\n * or custom tabletop layouts. The component renders rank and suit glyphs and\n * exposes an accessible card label by default.\n *\n * @readme\n * @example Face-up card\n * ```tsx\n * <PlayingCard card={{ rank: 'A', suit: 'spades' }} selected />\n * ```\n */\nexport function PlayingCard({\n card,\n size = 'medium',\n selected = false,\n muted = false,\n accessibilityLabel,\n colorScheme,\n testID,\n}: PlayingCardProps) {\n const { theme } = useZoraTheme();\n const colors = React.useMemo(\n () => createTabletopColorScheme(theme, colorScheme),\n [colorScheme, theme],\n );\n const dimensions = getTabletopCardDimensions(size);\n const suitColor = isRedPlayingCardSuit(card.suit) ? colors.redSuitText : colors.cardText;\n\n return (\n <View\n accessibilityLabel={accessibilityLabel ?? getPlayingCardLabel(card)}\n accessibilityRole=\"image\"\n style={[\n styles.card,\n {\n backgroundColor: colors.cardSurface,\n borderColor: selected ? colors.seatSelectedBorder : colors.cardBorder,\n borderRadius: dimensions.radius,\n height: dimensions.height,\n opacity: muted ? 0.48 : 1,\n width: dimensions.width,\n },\n ]}\n testID={testID}\n >\n <Text\n selectable={false}\n style={[\n styles.rank,\n {\n color: suitColor,\n fontSize: dimensions.rankFontSize,\n lineHeight: dimensions.rankFontSize + 4,\n },\n ]}\n >\n {card.rank}\n </Text>\n <Text\n selectable={false}\n style={[\n styles.suit,\n {\n color: suitColor,\n fontSize: dimensions.suitFontSize,\n lineHeight: dimensions.suitFontSize + 3,\n },\n ]}\n >\n {getPlayingCardSuitMark(card.suit)}\n </Text>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n card: {\n alignItems: 'center',\n borderWidth: 1,\n justifyContent: 'center',\n },\n rank: {\n fontWeight: '800',\n },\n suit: {\n fontWeight: '700',\n marginTop: -2,\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabletopTable.d.ts","sourceRoot":"","sources":["../../../src/components/tabletop-table/TabletopTable.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"TabletopTable.d.ts","sourceRoot":"","sources":["../../../src/components/tabletop-table/TabletopTable.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAelD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,WAAgB,EAChB,WAAW,EACX,cAAc,EACd,KAAc,EACd,SAAS,EACT,QAAkB,EAClB,QAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,MAAM,GACP,EAAE,kBAAkB,qBA8HpB"}
|
|
@@ -2,6 +2,7 @@ import { useZoraTheme } from '@ankhorage/zora';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { StyleSheet, Text, View } from 'react-native';
|
|
4
4
|
import { createTabletopColorScheme } from '../../colors';
|
|
5
|
+
import { getTabletopSeatCardSize } from '../../tableLayout';
|
|
5
6
|
import { getTabletopSeatPosition } from '../../tablePositions';
|
|
6
7
|
import { CardHand } from '../card-hand';
|
|
7
8
|
function createSeatAccessibilityLabel(seat) {
|
|
@@ -72,22 +73,44 @@ export function TabletopTable({ seats, centerCards = [], centerLabel, centerSubl
|
|
|
72
73
|
|
|
73
74
|
{seats.map((seat, index) => {
|
|
74
75
|
const position = getTabletopSeatPosition(index, resolvedSeatCount);
|
|
76
|
+
const hasCards = seat.cards !== undefined || seat.faceDownCards !== undefined;
|
|
77
|
+
const seatCardSize = getTabletopSeatCardSize({
|
|
78
|
+
selected: seat.selected,
|
|
79
|
+
tableCardSize: cardSize,
|
|
80
|
+
});
|
|
75
81
|
return (<View accessibilityLabel={createSeatAccessibilityLabel(seat)} accessibilityRole="summary" key={seat.id} style={[
|
|
76
|
-
styles.
|
|
82
|
+
styles.seatSlot,
|
|
77
83
|
{
|
|
78
|
-
borderColor: seat.selected ? colors.seatSelectedBorder : colors.seatBorder,
|
|
79
|
-
backgroundColor: colors.seatSurface,
|
|
80
84
|
left: position.left,
|
|
81
85
|
opacity: seat.muted ? 0.48 : 1,
|
|
82
86
|
top: position.top,
|
|
83
87
|
},
|
|
84
88
|
]} testID={testID ? `${testID}-seat-${seat.id}` : undefined}>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
<View style={[
|
|
90
|
+
styles.seatPanel,
|
|
91
|
+
seat.selected ? styles.selectedSeatPanel : null,
|
|
92
|
+
{
|
|
93
|
+
borderColor: seat.selected ? colors.seatSelectedBorder : colors.seatBorder,
|
|
94
|
+
backgroundColor: colors.seatSurface,
|
|
95
|
+
},
|
|
96
|
+
]}>
|
|
97
|
+
{hasCards ? (<CardHand cards={seat.cards} colorScheme={colorScheme} faceDownCards={seat.faceDownCards} muted={seat.muted ?? seat.disabled} size={seatCardSize}/>) : null}
|
|
98
|
+
<View style={styles.seatTextGroup}>
|
|
99
|
+
<Text style={[styles.seatLabel, { color: colors.seatText }]}>{seat.label}</Text>
|
|
100
|
+
{seat.sublabel !== undefined ? (<Text style={[styles.seatSublabel, { color: colors.seatMutedText }]}>
|
|
101
|
+
{seat.sublabel}
|
|
102
|
+
</Text>) : null}
|
|
103
|
+
</View>
|
|
104
|
+
</View>
|
|
105
|
+
{seat.tokenLabel !== undefined ? (<View style={[
|
|
106
|
+
styles.token,
|
|
107
|
+
{
|
|
108
|
+
backgroundColor: colors.tokenSurface,
|
|
109
|
+
borderColor: seat.selected
|
|
110
|
+
? colors.seatSelectedBorder
|
|
111
|
+
: colors.tableInnerBorder,
|
|
112
|
+
},
|
|
113
|
+
]}>
|
|
91
114
|
<Text style={[styles.tokenText, { color: colors.tokenText }]}>
|
|
92
115
|
{seat.tokenLabel}
|
|
93
116
|
</Text>
|
|
@@ -142,25 +165,39 @@ const styles = StyleSheet.create({
|
|
|
142
165
|
roundedSurface: {
|
|
143
166
|
borderRadius: 28,
|
|
144
167
|
},
|
|
145
|
-
seat: {
|
|
146
|
-
alignItems: 'center',
|
|
147
|
-
borderRadius: 10,
|
|
148
|
-
borderWidth: 1,
|
|
149
|
-
gap: 3,
|
|
150
|
-
minWidth: 64,
|
|
151
|
-
paddingHorizontal: 6,
|
|
152
|
-
paddingVertical: 5,
|
|
153
|
-
position: 'absolute',
|
|
154
|
-
transform: [{ translateX: -32 }, { translateY: -28 }],
|
|
155
|
-
},
|
|
156
168
|
seatLabel: {
|
|
157
169
|
fontSize: 11,
|
|
158
170
|
fontWeight: '800',
|
|
159
171
|
},
|
|
172
|
+
seatPanel: {
|
|
173
|
+
alignItems: 'center',
|
|
174
|
+
borderRadius: 12,
|
|
175
|
+
borderWidth: 1,
|
|
176
|
+
gap: 4,
|
|
177
|
+
minWidth: 68,
|
|
178
|
+
paddingHorizontal: 7,
|
|
179
|
+
paddingVertical: 6,
|
|
180
|
+
},
|
|
181
|
+
seatSlot: {
|
|
182
|
+
alignItems: 'center',
|
|
183
|
+
gap: 4,
|
|
184
|
+
minWidth: 78,
|
|
185
|
+
position: 'absolute',
|
|
186
|
+
transform: [{ translateX: -39 }, { translateY: -34 }],
|
|
187
|
+
},
|
|
160
188
|
seatSublabel: {
|
|
161
189
|
fontSize: 10,
|
|
162
190
|
fontWeight: '700',
|
|
163
191
|
},
|
|
192
|
+
seatTextGroup: {
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
gap: 1,
|
|
195
|
+
},
|
|
196
|
+
selectedSeatPanel: {
|
|
197
|
+
borderWidth: 2,
|
|
198
|
+
paddingHorizontal: 9,
|
|
199
|
+
paddingVertical: 7,
|
|
200
|
+
},
|
|
164
201
|
surface: {
|
|
165
202
|
borderWidth: 8,
|
|
166
203
|
bottom: 28,
|
|
@@ -171,7 +208,8 @@ const styles = StyleSheet.create({
|
|
|
171
208
|
},
|
|
172
209
|
token: {
|
|
173
210
|
borderRadius: 999,
|
|
174
|
-
|
|
211
|
+
borderWidth: 1,
|
|
212
|
+
paddingHorizontal: 7,
|
|
175
213
|
paddingVertical: 2,
|
|
176
214
|
},
|
|
177
215
|
tokenText: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabletopTable.js","sourceRoot":"","sources":["../../../src/components/tabletop-table/TabletopTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useZoraTheme } from '@ankhorage/zora';\nimport React from 'react';\nimport { StyleSheet, Text, View } from 'react-native';\n\nimport { createTabletopColorScheme } from '../../colors';\nimport { getTabletopSeatPosition } from '../../tablePositions';\nimport type { TabletopSeatState } from '../../types';\nimport { CardHand } from '../card-hand';\nimport type { TabletopTableProps } from './types';\n\nfunction createSeatAccessibilityLabel(seat: TabletopSeatState): string | undefined {\n if (seat.accessibilityLabel !== undefined) return seat.accessibilityLabel;\n if (typeof seat.label !== 'string') return undefined;\n\n const parts = [seat.label];\n if (typeof seat.sublabel === 'string') parts.push(seat.sublabel);\n if (typeof seat.tokenLabel === 'string') parts.push(seat.tokenLabel);\n if (seat.selected) parts.push('selected');\n if (seat.muted) parts.push('muted');\n\n return parts.join(', ');\n}\n\n/***\n * Responsive tabletop surface for generic card-game and board-game scenes.\n *\n * Use `TabletopTable` to arrange seats around a themed table surface, display\n * shared center cards, and show neutral seat labels/tokens without embedding game\n * rules into the component.\n *\n * @readme\n * @example Basic table\n * ```tsx\n * <TabletopTable\n * seats={seats}\n * centerCards={[{ rank: 'A', suit: 'spades' }]}\n * centerLabel=\"Round 1\"\n * />\n * ```\n */\nexport function TabletopTable({\n seats,\n centerCards = [],\n centerLabel,\n centerSublabel,\n shape = 'oval',\n seatCount,\n cardSize = 'small',\n disabled = false,\n colorScheme,\n accessibilityLabel,\n testID,\n}: TabletopTableProps) {\n const { theme } = useZoraTheme();\n const colors = React.useMemo(\n () => createTabletopColorScheme(theme, colorScheme),\n [colorScheme, theme],\n );\n const resolvedSeatCount = seatCount ?? seats.length;\n\n return (\n <View\n accessibilityLabel={accessibilityLabel}\n style={[styles.root, { opacity: disabled ? 0.56 : 1 }]}\n testID={testID}\n >\n <View\n style={[\n styles.surface,\n shape === 'circle' ? styles.circleSurface : styles.ovalSurface,\n shape === 'rounded' ? styles.roundedSurface : null,\n {\n backgroundColor: colors.tableFelt,\n borderColor: colors.tableBorder,\n },\n ]}\n >\n <View\n pointerEvents=\"none\"\n style={[\n styles.innerSurface,\n shape === 'circle' ? styles.circleSurface : styles.ovalSurface,\n shape === 'rounded' ? styles.roundedSurface : null,\n { borderColor: colors.tableInnerBorder },\n ]}\n />\n <View style={styles.centerContent}>\n {centerCards.length > 0 ? (\n <CardHand cards={centerCards} colorScheme={colorScheme} size={cardSize} />\n ) : null}\n {centerLabel !== undefined ? (\n <View style={[styles.centerLabel, { backgroundColor: colors.seatSurface }]}>\n <Text style={[styles.centerLabelText, { color: colors.seatText }]}>\n {centerLabel}\n </Text>\n </View>\n ) : null}\n {centerSublabel !== undefined ? (\n <Text style={[styles.centerSublabel, { color: colors.tableMutedText }]}>\n {centerSublabel}\n </Text>\n ) : null}\n </View>\n </View>\n\n {seats.map((seat, index) => {\n const position = getTabletopSeatPosition(index, resolvedSeatCount);\n return (\n <View\n accessibilityLabel={createSeatAccessibilityLabel(seat)}\n accessibilityRole=\"summary\"\n key={seat.id}\n style={[\n styles.seat,\n {\n borderColor: seat.selected ? colors.seatSelectedBorder : colors.seatBorder,\n backgroundColor: colors.seatSurface,\n left: position.left,\n opacity: seat.muted ? 0.48 : 1,\n top: position.top,\n },\n ]}\n testID={testID ? `${testID}-seat-${seat.id}` : undefined}\n >\n {seat.cards !== undefined || seat.faceDownCards !== undefined ? (\n <CardHand\n cards={seat.cards}\n colorScheme={colorScheme}\n faceDownCards={seat.faceDownCards}\n muted={seat.muted ?? seat.disabled}\n size={cardSize}\n />\n ) : null}\n <Text style={[styles.seatLabel, { color: colors.seatText }]}>{seat.label}</Text>\n {seat.sublabel !== undefined ? (\n <Text style={[styles.seatSublabel, { color: colors.seatMutedText }]}>\n {seat.sublabel}\n </Text>\n ) : null}\n {seat.tokenLabel !== undefined ? (\n <View style={[styles.token, { backgroundColor: colors.tokenSurface }]}>\n <Text style={[styles.tokenText, { color: colors.tokenText }]}>\n {seat.tokenLabel}\n </Text>\n </View>\n ) : null}\n </View>\n );\n })}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n centerContent: {\n alignItems: 'center',\n gap: 6,\n left: 0,\n position: 'absolute',\n right: 0,\n top: '35%',\n },\n centerLabel: {\n borderRadius: 999,\n paddingHorizontal: 10,\n paddingVertical: 4,\n },\n centerLabelText: {\n fontSize: 12,\n fontWeight: '800',\n },\n centerSublabel: {\n fontSize: 11,\n fontWeight: '700',\n },\n circleSurface: {\n borderRadius: 999,\n },\n innerSurface: {\n borderWidth: 1,\n bottom: 10,\n left: 10,\n opacity: 0.44,\n position: 'absolute',\n right: 10,\n top: 10,\n },\n ovalSurface: {\n borderRadius: 999,\n },\n root: {\n aspectRatio: 1.6,\n maxWidth: 760,\n position: 'relative',\n width: '100%',\n },\n roundedSurface: {\n borderRadius: 28,\n },\n seat: {\n alignItems: 'center',\n borderRadius: 10,\n borderWidth: 1,\n gap: 3,\n minWidth: 64,\n paddingHorizontal: 6,\n paddingVertical: 5,\n position: 'absolute',\n transform: [{ translateX: -32 }, { translateY: -28 }],\n },\n seatLabel: {\n fontSize: 11,\n fontWeight: '800',\n },\n seatSublabel: {\n fontSize: 10,\n fontWeight: '700',\n },\n surface: {\n borderWidth: 8,\n bottom: 28,\n left: 24,\n position: 'absolute',\n right: 24,\n top: 24,\n },\n token: {\n borderRadius: 999,\n paddingHorizontal: 6,\n paddingVertical: 2,\n },\n tokenText: {\n fontSize: 10,\n fontWeight: '800',\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"TabletopTable.js","sourceRoot":"","sources":["../../../src/components/tabletop-table/TabletopTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useZoraTheme } from '@ankhorage/zora';\nimport React from 'react';\nimport { StyleSheet, Text, View } from 'react-native';\n\nimport { createTabletopColorScheme } from '../../colors';\nimport { getTabletopSeatCardSize } from '../../tableLayout';\nimport { getTabletopSeatPosition } from '../../tablePositions';\nimport type { TabletopSeatState } from '../../types';\nimport { CardHand } from '../card-hand';\nimport type { TabletopTableProps } from './types';\n\nfunction createSeatAccessibilityLabel(seat: TabletopSeatState): string | undefined {\n if (seat.accessibilityLabel !== undefined) return seat.accessibilityLabel;\n if (typeof seat.label !== 'string') return undefined;\n\n const parts = [seat.label];\n if (typeof seat.sublabel === 'string') parts.push(seat.sublabel);\n if (typeof seat.tokenLabel === 'string') parts.push(seat.tokenLabel);\n if (seat.selected) parts.push('selected');\n if (seat.muted) parts.push('muted');\n\n return parts.join(', ');\n}\n\n/***\n * Responsive tabletop surface for generic card-game and board-game scenes.\n *\n * Use `TabletopTable` to arrange seats around a themed table surface, display\n * shared center cards, and show neutral seat labels/tokens without embedding game\n * rules into the component.\n *\n * @readme\n * @example Basic table\n * ```tsx\n * <TabletopTable\n * seats={seats}\n * centerCards={[{ rank: 'A', suit: 'spades' }]}\n * centerLabel=\"Round 1\"\n * />\n * ```\n */\nexport function TabletopTable({\n seats,\n centerCards = [],\n centerLabel,\n centerSublabel,\n shape = 'oval',\n seatCount,\n cardSize = 'small',\n disabled = false,\n colorScheme,\n accessibilityLabel,\n testID,\n}: TabletopTableProps) {\n const { theme } = useZoraTheme();\n const colors = React.useMemo(\n () => createTabletopColorScheme(theme, colorScheme),\n [colorScheme, theme],\n );\n const resolvedSeatCount = seatCount ?? seats.length;\n\n return (\n <View\n accessibilityLabel={accessibilityLabel}\n style={[styles.root, { opacity: disabled ? 0.56 : 1 }]}\n testID={testID}\n >\n <View\n style={[\n styles.surface,\n shape === 'circle' ? styles.circleSurface : styles.ovalSurface,\n shape === 'rounded' ? styles.roundedSurface : null,\n {\n backgroundColor: colors.tableFelt,\n borderColor: colors.tableBorder,\n },\n ]}\n >\n <View\n pointerEvents=\"none\"\n style={[\n styles.innerSurface,\n shape === 'circle' ? styles.circleSurface : styles.ovalSurface,\n shape === 'rounded' ? styles.roundedSurface : null,\n { borderColor: colors.tableInnerBorder },\n ]}\n />\n <View style={styles.centerContent}>\n {centerCards.length > 0 ? (\n <CardHand cards={centerCards} colorScheme={colorScheme} size={cardSize} />\n ) : null}\n {centerLabel !== undefined ? (\n <View style={[styles.centerLabel, { backgroundColor: colors.seatSurface }]}>\n <Text style={[styles.centerLabelText, { color: colors.seatText }]}>\n {centerLabel}\n </Text>\n </View>\n ) : null}\n {centerSublabel !== undefined ? (\n <Text style={[styles.centerSublabel, { color: colors.tableMutedText }]}>\n {centerSublabel}\n </Text>\n ) : null}\n </View>\n </View>\n\n {seats.map((seat, index) => {\n const position = getTabletopSeatPosition(index, resolvedSeatCount);\n const hasCards = seat.cards !== undefined || seat.faceDownCards !== undefined;\n const seatCardSize = getTabletopSeatCardSize({\n selected: seat.selected,\n tableCardSize: cardSize,\n });\n\n return (\n <View\n accessibilityLabel={createSeatAccessibilityLabel(seat)}\n accessibilityRole=\"summary\"\n key={seat.id}\n style={[\n styles.seatSlot,\n {\n left: position.left,\n opacity: seat.muted ? 0.48 : 1,\n top: position.top,\n },\n ]}\n testID={testID ? `${testID}-seat-${seat.id}` : undefined}\n >\n <View\n style={[\n styles.seatPanel,\n seat.selected ? styles.selectedSeatPanel : null,\n {\n borderColor: seat.selected ? colors.seatSelectedBorder : colors.seatBorder,\n backgroundColor: colors.seatSurface,\n },\n ]}\n >\n {hasCards ? (\n <CardHand\n cards={seat.cards}\n colorScheme={colorScheme}\n faceDownCards={seat.faceDownCards}\n muted={seat.muted ?? seat.disabled}\n size={seatCardSize}\n />\n ) : null}\n <View style={styles.seatTextGroup}>\n <Text style={[styles.seatLabel, { color: colors.seatText }]}>{seat.label}</Text>\n {seat.sublabel !== undefined ? (\n <Text style={[styles.seatSublabel, { color: colors.seatMutedText }]}>\n {seat.sublabel}\n </Text>\n ) : null}\n </View>\n </View>\n {seat.tokenLabel !== undefined ? (\n <View\n style={[\n styles.token,\n {\n backgroundColor: colors.tokenSurface,\n borderColor: seat.selected\n ? colors.seatSelectedBorder\n : colors.tableInnerBorder,\n },\n ]}\n >\n <Text style={[styles.tokenText, { color: colors.tokenText }]}>\n {seat.tokenLabel}\n </Text>\n </View>\n ) : null}\n </View>\n );\n })}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n centerContent: {\n alignItems: 'center',\n gap: 6,\n left: 0,\n position: 'absolute',\n right: 0,\n top: '35%',\n },\n centerLabel: {\n borderRadius: 999,\n paddingHorizontal: 10,\n paddingVertical: 4,\n },\n centerLabelText: {\n fontSize: 12,\n fontWeight: '800',\n },\n centerSublabel: {\n fontSize: 11,\n fontWeight: '700',\n },\n circleSurface: {\n borderRadius: 999,\n },\n innerSurface: {\n borderWidth: 1,\n bottom: 10,\n left: 10,\n opacity: 0.44,\n position: 'absolute',\n right: 10,\n top: 10,\n },\n ovalSurface: {\n borderRadius: 999,\n },\n root: {\n aspectRatio: 1.6,\n maxWidth: 760,\n position: 'relative',\n width: '100%',\n },\n roundedSurface: {\n borderRadius: 28,\n },\n seatLabel: {\n fontSize: 11,\n fontWeight: '800',\n },\n seatPanel: {\n alignItems: 'center',\n borderRadius: 12,\n borderWidth: 1,\n gap: 4,\n minWidth: 68,\n paddingHorizontal: 7,\n paddingVertical: 6,\n },\n seatSlot: {\n alignItems: 'center',\n gap: 4,\n minWidth: 78,\n position: 'absolute',\n transform: [{ translateX: -39 }, { translateY: -34 }],\n },\n seatSublabel: {\n fontSize: 10,\n fontWeight: '700',\n },\n seatTextGroup: {\n alignItems: 'center',\n gap: 1,\n },\n selectedSeatPanel: {\n borderWidth: 2,\n paddingHorizontal: 9,\n paddingVertical: 7,\n },\n surface: {\n borderWidth: 8,\n bottom: 28,\n left: 24,\n position: 'absolute',\n right: 24,\n top: 24,\n },\n token: {\n borderRadius: 999,\n borderWidth: 1,\n paddingHorizontal: 7,\n paddingVertical: 2,\n },\n tokenText: {\n fontSize: 10,\n fontWeight: '800',\n },\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tableLayout.d.ts","sourceRoot":"","sources":["../src/tableLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;CAC1C,GAAG,gBAAgB,CAInB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tableLayout.js","sourceRoot":"","sources":["../src/tableLayout.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,IAGvC;IACC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,aAAa,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC;IAErE,OAAO,IAAI,CAAC,aAAa,CAAC;AAC5B,CAAC","sourcesContent":["import type { TabletopCardSize } from './types';\n\nexport function getTabletopSeatCardSize(args: {\n readonly selected?: boolean;\n readonly tableCardSize: TabletopCardSize;\n}): TabletopCardSize {\n if (args.selected && args.tableCardSize === 'small') return 'medium';\n\n return args.tableCardSize;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora-tabletop",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"description": "Tabletop and card-game UI primitives for React Native and React Native Web, built on ZORA.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/zora-tabletop#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@ankhorage/devtools": "^1.0.6",
|
|
71
71
|
"@ankhorage/paradox": "^0.1.10",
|
|
72
|
-
"@ankhorage/zora": "^2.
|
|
72
|
+
"@ankhorage/zora": "^2.6.1",
|
|
73
73
|
"@changesets/cli": "^2.31.0",
|
|
74
74
|
"@types/bun": "^1.3.13",
|
|
75
75
|
"@types/node": "^25.6.0",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { getPlayingCardLabel } from './cardLabel';
|
|
4
|
+
|
|
5
|
+
describe('cardLabel', () => {
|
|
6
|
+
test('creates accessible rank and suit labels', () => {
|
|
7
|
+
expect(getPlayingCardLabel({ rank: 'A', suit: 'hearts' })).toBe('ace of hearts');
|
|
8
|
+
expect(getPlayingCardLabel({ rank: 'K', suit: 'clubs' })).toBe('king of clubs');
|
|
9
|
+
expect(getPlayingCardLabel({ rank: '10', suit: 'spades' })).toBe('10 of spades');
|
|
10
|
+
});
|
|
11
|
+
});
|
package/src/cardLabel.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { PlayingCardValue } from './types';
|
|
2
2
|
|
|
3
|
+
const rankLabels: Readonly<Record<string, string>> = {
|
|
4
|
+
A: 'ace',
|
|
5
|
+
J: 'jack',
|
|
6
|
+
K: 'king',
|
|
7
|
+
Q: 'queen',
|
|
8
|
+
};
|
|
9
|
+
|
|
3
10
|
export function getPlayingCardLabel(card: PlayingCardValue): string {
|
|
4
|
-
return `${card.rank} ${card.suit}`;
|
|
11
|
+
return `${getPlayingCardRankLabel(card.rank)} of ${card.suit}`;
|
|
5
12
|
}
|
|
6
13
|
|
|
7
14
|
export function createHiddenCardLabel(index?: number): string {
|
|
@@ -9,3 +16,7 @@ export function createHiddenCardLabel(index?: number): string {
|
|
|
9
16
|
|
|
10
17
|
return `Hidden card ${index + 1}`;
|
|
11
18
|
}
|
|
19
|
+
|
|
20
|
+
function getPlayingCardRankLabel(rank: string): string {
|
|
21
|
+
return rankLabels[rank] ?? rank;
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { getPlayingCardSuitMark, isRedPlayingCardSuit } from './cardSymbols';
|
|
4
|
+
|
|
5
|
+
describe('cardSymbols', () => {
|
|
6
|
+
test('maps card suits to visual glyphs', () => {
|
|
7
|
+
expect(getPlayingCardSuitMark('clubs')).toBe('♣');
|
|
8
|
+
expect(getPlayingCardSuitMark('diamonds')).toBe('♦');
|
|
9
|
+
expect(getPlayingCardSuitMark('hearts')).toBe('♥');
|
|
10
|
+
expect(getPlayingCardSuitMark('spades')).toBe('♠');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('identifies red suits', () => {
|
|
14
|
+
expect(isRedPlayingCardSuit('diamonds')).toBe(true);
|
|
15
|
+
expect(isRedPlayingCardSuit('hearts')).toBe(true);
|
|
16
|
+
expect(isRedPlayingCardSuit('clubs')).toBe(false);
|
|
17
|
+
expect(isRedPlayingCardSuit('spades')).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PlayingCardSuit } from './types';
|
|
2
|
+
|
|
3
|
+
const suitMarks: Readonly<Record<PlayingCardSuit, string>> = {
|
|
4
|
+
clubs: '♣',
|
|
5
|
+
diamonds: '♦',
|
|
6
|
+
hearts: '♥',
|
|
7
|
+
spades: '♠',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function getPlayingCardSuitMark(suit: PlayingCardSuit): string {
|
|
11
|
+
return suitMarks[suit];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isRedPlayingCardSuit(suit: PlayingCardSuit): boolean {
|
|
15
|
+
return suit === 'diamonds' || suit === 'hearts';
|
|
16
|
+
}
|
|
@@ -4,27 +4,16 @@ import { StyleSheet, Text, View } from 'react-native';
|
|
|
4
4
|
|
|
5
5
|
import { getPlayingCardLabel } from '../../cardLabel';
|
|
6
6
|
import { getTabletopCardDimensions } from '../../cardSizing';
|
|
7
|
+
import { getPlayingCardSuitMark, isRedPlayingCardSuit } from '../../cardSymbols';
|
|
7
8
|
import { createTabletopColorScheme } from '../../colors';
|
|
8
|
-
import type { PlayingCardSuit } from '../../types';
|
|
9
9
|
import type { PlayingCardProps } from './types';
|
|
10
10
|
|
|
11
|
-
const suitMarks: Readonly<Record<PlayingCardSuit, string>> = {
|
|
12
|
-
clubs: 'C',
|
|
13
|
-
diamonds: 'D',
|
|
14
|
-
hearts: 'H',
|
|
15
|
-
spades: 'S',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function isRedSuit(suit: PlayingCardSuit): boolean {
|
|
19
|
-
return suit === 'diamonds' || suit === 'hearts';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
11
|
/***
|
|
23
12
|
* Theme-aware face-up playing card primitive.
|
|
24
13
|
*
|
|
25
14
|
* Use `PlayingCard` for visible card values in hands, shared table cards, piles,
|
|
26
|
-
* or custom tabletop layouts. The component renders rank and suit
|
|
27
|
-
* an accessible card label by default.
|
|
15
|
+
* or custom tabletop layouts. The component renders rank and suit glyphs and
|
|
16
|
+
* exposes an accessible card label by default.
|
|
28
17
|
*
|
|
29
18
|
* @readme
|
|
30
19
|
* @example Face-up card
|
|
@@ -47,7 +36,7 @@ export function PlayingCard({
|
|
|
47
36
|
[colorScheme, theme],
|
|
48
37
|
);
|
|
49
38
|
const dimensions = getTabletopCardDimensions(size);
|
|
50
|
-
const suitColor =
|
|
39
|
+
const suitColor = isRedPlayingCardSuit(card.suit) ? colors.redSuitText : colors.cardText;
|
|
51
40
|
|
|
52
41
|
return (
|
|
53
42
|
<View
|
|
@@ -90,7 +79,7 @@ export function PlayingCard({
|
|
|
90
79
|
},
|
|
91
80
|
]}
|
|
92
81
|
>
|
|
93
|
-
{
|
|
82
|
+
{getPlayingCardSuitMark(card.suit)}
|
|
94
83
|
</Text>
|
|
95
84
|
</View>
|
|
96
85
|
);
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { StyleSheet, Text, View } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { createTabletopColorScheme } from '../../colors';
|
|
6
|
+
import { getTabletopSeatCardSize } from '../../tableLayout';
|
|
6
7
|
import { getTabletopSeatPosition } from '../../tablePositions';
|
|
7
8
|
import type { TabletopSeatState } from '../../types';
|
|
8
9
|
import { CardHand } from '../card-hand';
|
|
@@ -105,16 +106,20 @@ export function TabletopTable({
|
|
|
105
106
|
|
|
106
107
|
{seats.map((seat, index) => {
|
|
107
108
|
const position = getTabletopSeatPosition(index, resolvedSeatCount);
|
|
109
|
+
const hasCards = seat.cards !== undefined || seat.faceDownCards !== undefined;
|
|
110
|
+
const seatCardSize = getTabletopSeatCardSize({
|
|
111
|
+
selected: seat.selected,
|
|
112
|
+
tableCardSize: cardSize,
|
|
113
|
+
});
|
|
114
|
+
|
|
108
115
|
return (
|
|
109
116
|
<View
|
|
110
117
|
accessibilityLabel={createSeatAccessibilityLabel(seat)}
|
|
111
118
|
accessibilityRole="summary"
|
|
112
119
|
key={seat.id}
|
|
113
120
|
style={[
|
|
114
|
-
styles.
|
|
121
|
+
styles.seatSlot,
|
|
115
122
|
{
|
|
116
|
-
borderColor: seat.selected ? colors.seatSelectedBorder : colors.seatBorder,
|
|
117
|
-
backgroundColor: colors.seatSurface,
|
|
118
123
|
left: position.left,
|
|
119
124
|
opacity: seat.muted ? 0.48 : 1,
|
|
120
125
|
top: position.top,
|
|
@@ -122,23 +127,46 @@ export function TabletopTable({
|
|
|
122
127
|
]}
|
|
123
128
|
testID={testID ? `${testID}-seat-${seat.id}` : undefined}
|
|
124
129
|
>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
<View
|
|
131
|
+
style={[
|
|
132
|
+
styles.seatPanel,
|
|
133
|
+
seat.selected ? styles.selectedSeatPanel : null,
|
|
134
|
+
{
|
|
135
|
+
borderColor: seat.selected ? colors.seatSelectedBorder : colors.seatBorder,
|
|
136
|
+
backgroundColor: colors.seatSurface,
|
|
137
|
+
},
|
|
138
|
+
]}
|
|
139
|
+
>
|
|
140
|
+
{hasCards ? (
|
|
141
|
+
<CardHand
|
|
142
|
+
cards={seat.cards}
|
|
143
|
+
colorScheme={colorScheme}
|
|
144
|
+
faceDownCards={seat.faceDownCards}
|
|
145
|
+
muted={seat.muted ?? seat.disabled}
|
|
146
|
+
size={seatCardSize}
|
|
147
|
+
/>
|
|
148
|
+
) : null}
|
|
149
|
+
<View style={styles.seatTextGroup}>
|
|
150
|
+
<Text style={[styles.seatLabel, { color: colors.seatText }]}>{seat.label}</Text>
|
|
151
|
+
{seat.sublabel !== undefined ? (
|
|
152
|
+
<Text style={[styles.seatSublabel, { color: colors.seatMutedText }]}>
|
|
153
|
+
{seat.sublabel}
|
|
154
|
+
</Text>
|
|
155
|
+
) : null}
|
|
156
|
+
</View>
|
|
157
|
+
</View>
|
|
140
158
|
{seat.tokenLabel !== undefined ? (
|
|
141
|
-
<View
|
|
159
|
+
<View
|
|
160
|
+
style={[
|
|
161
|
+
styles.token,
|
|
162
|
+
{
|
|
163
|
+
backgroundColor: colors.tokenSurface,
|
|
164
|
+
borderColor: seat.selected
|
|
165
|
+
? colors.seatSelectedBorder
|
|
166
|
+
: colors.tableInnerBorder,
|
|
167
|
+
},
|
|
168
|
+
]}
|
|
169
|
+
>
|
|
142
170
|
<Text style={[styles.tokenText, { color: colors.tokenText }]}>
|
|
143
171
|
{seat.tokenLabel}
|
|
144
172
|
</Text>
|
|
@@ -197,25 +225,39 @@ const styles = StyleSheet.create({
|
|
|
197
225
|
roundedSurface: {
|
|
198
226
|
borderRadius: 28,
|
|
199
227
|
},
|
|
200
|
-
seat: {
|
|
201
|
-
alignItems: 'center',
|
|
202
|
-
borderRadius: 10,
|
|
203
|
-
borderWidth: 1,
|
|
204
|
-
gap: 3,
|
|
205
|
-
minWidth: 64,
|
|
206
|
-
paddingHorizontal: 6,
|
|
207
|
-
paddingVertical: 5,
|
|
208
|
-
position: 'absolute',
|
|
209
|
-
transform: [{ translateX: -32 }, { translateY: -28 }],
|
|
210
|
-
},
|
|
211
228
|
seatLabel: {
|
|
212
229
|
fontSize: 11,
|
|
213
230
|
fontWeight: '800',
|
|
214
231
|
},
|
|
232
|
+
seatPanel: {
|
|
233
|
+
alignItems: 'center',
|
|
234
|
+
borderRadius: 12,
|
|
235
|
+
borderWidth: 1,
|
|
236
|
+
gap: 4,
|
|
237
|
+
minWidth: 68,
|
|
238
|
+
paddingHorizontal: 7,
|
|
239
|
+
paddingVertical: 6,
|
|
240
|
+
},
|
|
241
|
+
seatSlot: {
|
|
242
|
+
alignItems: 'center',
|
|
243
|
+
gap: 4,
|
|
244
|
+
minWidth: 78,
|
|
245
|
+
position: 'absolute',
|
|
246
|
+
transform: [{ translateX: -39 }, { translateY: -34 }],
|
|
247
|
+
},
|
|
215
248
|
seatSublabel: {
|
|
216
249
|
fontSize: 10,
|
|
217
250
|
fontWeight: '700',
|
|
218
251
|
},
|
|
252
|
+
seatTextGroup: {
|
|
253
|
+
alignItems: 'center',
|
|
254
|
+
gap: 1,
|
|
255
|
+
},
|
|
256
|
+
selectedSeatPanel: {
|
|
257
|
+
borderWidth: 2,
|
|
258
|
+
paddingHorizontal: 9,
|
|
259
|
+
paddingVertical: 7,
|
|
260
|
+
},
|
|
219
261
|
surface: {
|
|
220
262
|
borderWidth: 8,
|
|
221
263
|
bottom: 28,
|
|
@@ -226,7 +268,8 @@ const styles = StyleSheet.create({
|
|
|
226
268
|
},
|
|
227
269
|
token: {
|
|
228
270
|
borderRadius: 999,
|
|
229
|
-
|
|
271
|
+
borderWidth: 1,
|
|
272
|
+
paddingHorizontal: 7,
|
|
230
273
|
paddingVertical: 2,
|
|
231
274
|
},
|
|
232
275
|
tokenText: {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { getTabletopSeatCardSize } from './tableLayout';
|
|
4
|
+
|
|
5
|
+
describe('tableLayout', () => {
|
|
6
|
+
test('promotes selected compact seat cards for readability', () => {
|
|
7
|
+
expect(getTabletopSeatCardSize({ selected: true, tableCardSize: 'small' })).toBe('medium');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('preserves non-selected and already readable card sizes', () => {
|
|
11
|
+
expect(getTabletopSeatCardSize({ selected: false, tableCardSize: 'small' })).toBe('small');
|
|
12
|
+
expect(getTabletopSeatCardSize({ selected: true, tableCardSize: 'medium' })).toBe('medium');
|
|
13
|
+
expect(getTabletopSeatCardSize({ selected: true, tableCardSize: 'large' })).toBe('large');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TabletopCardSize } from './types';
|
|
2
|
+
|
|
3
|
+
export function getTabletopSeatCardSize(args: {
|
|
4
|
+
readonly selected?: boolean;
|
|
5
|
+
readonly tableCardSize: TabletopCardSize;
|
|
6
|
+
}): TabletopCardSize {
|
|
7
|
+
if (args.selected && args.tableCardSize === 'small') return 'medium';
|
|
8
|
+
|
|
9
|
+
return args.tableCardSize;
|
|
10
|
+
}
|