@ankhorage/zora-tabletop 0.0.0 → 0.0.2
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 +13 -0
- package/dist/colors.d.ts +4 -0
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +103 -8
- package/dist/colors.js.map +1 -1
- package/dist/components/tabletop-table/TabletopTable.js +6 -6
- package/dist/components/tabletop-table/TabletopTable.js.map +1 -1
- package/package.json +1 -1
- package/src/colors.test.ts +58 -0
- package/src/colors.ts +143 -8
- package/src/components/tabletop-table/TabletopTable.tsx +6 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @ankhorage/zora-tabletop
|
|
2
|
+
|
|
3
|
+
## 0.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3b3651b: Improve tabletop label readability by deriving contrast-aware text colors for table and seat surfaces.
|
|
8
|
+
|
|
9
|
+
## 0.0.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 58b3a2e: Add initial tabletop and card-game presentation primitives.
|
package/dist/colors.d.ts
CHANGED
|
@@ -7,11 +7,15 @@ export interface TabletopColorScheme {
|
|
|
7
7
|
readonly mutedText: string;
|
|
8
8
|
readonly redSuitText: string;
|
|
9
9
|
readonly seatBorder: string;
|
|
10
|
+
readonly seatMutedText: string;
|
|
10
11
|
readonly seatSelectedBorder: string;
|
|
11
12
|
readonly seatSurface: string;
|
|
13
|
+
readonly seatText: string;
|
|
12
14
|
readonly tableBorder: string;
|
|
13
15
|
readonly tableFelt: string;
|
|
14
16
|
readonly tableInnerBorder: string;
|
|
17
|
+
readonly tableMutedText: string;
|
|
18
|
+
readonly tableText: string;
|
|
15
19
|
readonly tokenSurface: string;
|
|
16
20
|
readonly tokenText: string;
|
|
17
21
|
}
|
package/dist/colors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE;QACT,MAAM,EAAE;YACN,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM,CAAC;gBACb,MAAM,EAAE,MAAM,CAAC;aAChB,CAAC;SACH,CAAC;QACF,OAAO,EAAE;YACP,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,OAAO,EAAE;YACP,MAAM,EAAE,MAAM,CAAC;YACf,OAAO,EAAE,MAAM,CAAC;YAChB,OAAO,EAAE,MAAM,CAAC;YAChB,YAAY,EAAE,MAAM,CAAC;SACtB,CAAC;QACF,OAAO,EAAE;YACP,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;
|
|
1
|
+
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE;QACT,MAAM,EAAE;YACN,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM,CAAC;gBACb,MAAM,EAAE,MAAM,CAAC;aAChB,CAAC;SACH,CAAC;QACF,OAAO,EAAE;YACP,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,OAAO,EAAE;YACP,MAAM,EAAE,MAAM,CAAC;YACf,OAAO,EAAE,MAAM,CAAC;YAChB,OAAO,EAAE,MAAM,CAAC;YAChB,YAAY,EAAE,MAAM,CAAC;SACtB,CAAC;QACF,OAAO,EAAE;YACP,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAYD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,uBAAuB,EAC9B,SAAS,GAAE,sBAA2B,GACrC,mBAAmB,CAgDrB"}
|
package/dist/colors.js
CHANGED
|
@@ -1,20 +1,115 @@
|
|
|
1
|
+
const DARK_TEXT_FALLBACK = '#111827';
|
|
2
|
+
const LIGHT_TEXT_FALLBACK = '#ffffff';
|
|
3
|
+
const MINIMUM_READABLE_CONTRAST = 4.5;
|
|
1
4
|
export function createTabletopColorScheme(theme, overrides = {}) {
|
|
5
|
+
const cardSurface = overrides.cardSurface ?? theme.semantics.neutral.surface;
|
|
6
|
+
const seatSurface = overrides.seatSurface ?? theme.semantics.neutral.surface;
|
|
7
|
+
const tableFelt = overrides.tableFelt ?? theme.semantics.action.primary.softBg;
|
|
8
|
+
const tokenSurface = overrides.tokenSurface ?? theme.semantics.warning.softBg;
|
|
9
|
+
const cardText = overrides.cardText ??
|
|
10
|
+
resolveReadableTextColor(cardSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
11
|
+
const seatText = overrides.seatText ??
|
|
12
|
+
resolveReadableTextColor(seatSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
13
|
+
const tableText = overrides.tableText ??
|
|
14
|
+
resolveReadableTextColor(tableFelt, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
15
|
+
const tokenText = overrides.tokenText ??
|
|
16
|
+
resolveReadableTextColor(tokenSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
2
17
|
return {
|
|
3
18
|
cardBack: overrides.cardBack ?? theme.semantics.warning.base,
|
|
4
19
|
cardBackBorder: overrides.cardBackBorder ?? theme.semantics.neutral.border,
|
|
5
20
|
cardBorder: overrides.cardBorder ?? theme.semantics.neutral.border,
|
|
6
|
-
cardSurface
|
|
7
|
-
cardText
|
|
8
|
-
mutedText: overrides.mutedText ??
|
|
9
|
-
|
|
21
|
+
cardSurface,
|
|
22
|
+
cardText,
|
|
23
|
+
mutedText: overrides.mutedText ??
|
|
24
|
+
resolveReadableTextColor(cardSurface, theme.semantics.content.muted, cardText),
|
|
25
|
+
redSuitText: overrides.redSuitText ??
|
|
26
|
+
resolveReadableTextColor(cardSurface, theme.semantics.warning.base, cardText),
|
|
10
27
|
seatBorder: overrides.seatBorder ?? theme.semantics.neutral.divider,
|
|
28
|
+
seatMutedText: overrides.seatMutedText ??
|
|
29
|
+
resolveReadableTextColor(seatSurface, theme.semantics.content.muted, seatText),
|
|
11
30
|
seatSelectedBorder: overrides.seatSelectedBorder ?? theme.semantics.action.primary.base,
|
|
12
|
-
seatSurface
|
|
31
|
+
seatSurface,
|
|
32
|
+
seatText,
|
|
13
33
|
tableBorder: overrides.tableBorder ?? theme.semantics.warning.base,
|
|
14
|
-
tableFelt
|
|
34
|
+
tableFelt,
|
|
15
35
|
tableInnerBorder: overrides.tableInnerBorder ?? theme.semantics.neutral.divider,
|
|
16
|
-
|
|
17
|
-
|
|
36
|
+
tableMutedText: overrides.tableMutedText ??
|
|
37
|
+
resolveReadableTextColor(tableFelt, theme.semantics.content.muted, tableText),
|
|
38
|
+
tableText,
|
|
39
|
+
tokenSurface,
|
|
40
|
+
tokenText,
|
|
18
41
|
};
|
|
19
42
|
}
|
|
43
|
+
function resolveReadableTextColor(background, preferred, fallback) {
|
|
44
|
+
const preferredContrast = getContrastRatio(background, preferred);
|
|
45
|
+
if (preferredContrast !== null && preferredContrast >= MINIMUM_READABLE_CONTRAST) {
|
|
46
|
+
return preferred;
|
|
47
|
+
}
|
|
48
|
+
const candidates = uniqueColors([preferred, fallback, LIGHT_TEXT_FALLBACK, DARK_TEXT_FALLBACK]);
|
|
49
|
+
const [readableCandidate] = candidates
|
|
50
|
+
.map((candidate) => ({ candidate, contrast: getContrastRatio(background, candidate) }))
|
|
51
|
+
.filter((entry) => entry.contrast !== null)
|
|
52
|
+
.sort((left, right) => right.contrast - left.contrast);
|
|
53
|
+
return readableCandidate?.candidate ?? preferred;
|
|
54
|
+
}
|
|
55
|
+
function uniqueColors(colors) {
|
|
56
|
+
return [...new Set(colors)];
|
|
57
|
+
}
|
|
58
|
+
function getContrastRatio(background, foreground) {
|
|
59
|
+
const backgroundColor = parseHexColor(background);
|
|
60
|
+
const foregroundColor = parseHexColor(foreground);
|
|
61
|
+
if (backgroundColor === null || foregroundColor === null)
|
|
62
|
+
return null;
|
|
63
|
+
const backgroundLuminance = getRelativeLuminance(backgroundColor);
|
|
64
|
+
const foregroundLuminance = getRelativeLuminance(foregroundColor);
|
|
65
|
+
const lighter = Math.max(backgroundLuminance, foregroundLuminance);
|
|
66
|
+
const darker = Math.min(backgroundLuminance, foregroundLuminance);
|
|
67
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
68
|
+
}
|
|
69
|
+
function parseHexColor(color) {
|
|
70
|
+
const value = color.trim();
|
|
71
|
+
const hex = value.startsWith('#') ? value.slice(1) : '';
|
|
72
|
+
if (!isSupportedHexLength(hex) || !isHexColorValue(hex))
|
|
73
|
+
return null;
|
|
74
|
+
if (hex.length === 3) {
|
|
75
|
+
const red = hex.slice(0, 1);
|
|
76
|
+
const green = hex.slice(1, 2);
|
|
77
|
+
const blue = hex.slice(2, 3);
|
|
78
|
+
return {
|
|
79
|
+
red: parseInt(red + red, 16),
|
|
80
|
+
green: parseInt(green + green, 16),
|
|
81
|
+
blue: parseInt(blue + blue, 16),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
red: parseInt(hex.slice(0, 2), 16),
|
|
86
|
+
green: parseInt(hex.slice(2, 4), 16),
|
|
87
|
+
blue: parseInt(hex.slice(4, 6), 16),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function isSupportedHexLength(hex) {
|
|
91
|
+
return hex.length === 3 || hex.length === 6 || hex.length === 8;
|
|
92
|
+
}
|
|
93
|
+
function isHexColorValue(hex) {
|
|
94
|
+
return [...hex].every((character) => isHexCharacter(character));
|
|
95
|
+
}
|
|
96
|
+
function isHexCharacter(character) {
|
|
97
|
+
const code = character.charCodeAt(0);
|
|
98
|
+
const isDigit = code >= 48 && code <= 57;
|
|
99
|
+
const isUpperHex = code >= 65 && code <= 70;
|
|
100
|
+
const isLowerHex = code >= 97 && code <= 102;
|
|
101
|
+
return isDigit || isUpperHex || isLowerHex;
|
|
102
|
+
}
|
|
103
|
+
function getRelativeLuminance(color) {
|
|
104
|
+
const red = normalizeColorChannel(color.red);
|
|
105
|
+
const green = normalizeColorChannel(color.green);
|
|
106
|
+
const blue = normalizeColorChannel(color.blue);
|
|
107
|
+
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
|
108
|
+
}
|
|
109
|
+
function normalizeColorChannel(value) {
|
|
110
|
+
const normalized = value / 255;
|
|
111
|
+
if (normalized <= 0.03928)
|
|
112
|
+
return normalized / 12.92;
|
|
113
|
+
return ((normalized + 0.055) / 1.055) ** 2.4;
|
|
114
|
+
}
|
|
20
115
|
//# sourceMappingURL=colors.js.map
|
package/dist/colors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AA6CA,MAAM,UAAU,yBAAyB,CACvC,KAA8B,EAC9B,YAAoC,EAAE;IAEtC,OAAO;QACL,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI;QAC5D,cAAc,EAAE,SAAS,CAAC,cAAc,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM;QAC1E,UAAU,EAAE,SAAS,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM;QAClE,WAAW,EAAE,SAAS,CAAC,WAAW,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;QACrE,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;QAC/D,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;QAC/D,WAAW,EAAE,SAAS,CAAC,WAAW,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI;QAClE,UAAU,EAAE,SAAS,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;QACnE,kBAAkB,EAAE,SAAS,CAAC,kBAAkB,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI;QACvF,WAAW,EAAE,SAAS,CAAC,WAAW,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;QACrE,WAAW,EAAE,SAAS,CAAC,WAAW,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI;QAClE,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM;QACvE,gBAAgB,EAAE,SAAS,CAAC,gBAAgB,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;QAC/E,YAAY,EAAE,SAAS,CAAC,YAAY,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM;QACtE,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;KAClE,CAAC;AACJ,CAAC","sourcesContent":["export interface TabletopColorScheme {\n readonly cardBack: string;\n readonly cardBackBorder: string;\n readonly cardBorder: string;\n readonly cardSurface: string;\n readonly cardText: string;\n readonly mutedText: string;\n readonly redSuitText: string;\n readonly seatBorder: string;\n readonly seatSelectedBorder: string;\n readonly seatSurface: string;\n readonly tableBorder: string;\n readonly tableFelt: string;\n readonly tableInnerBorder: string;\n readonly tokenSurface: string;\n readonly tokenText: string;\n}\n\nexport type TabletopColorOverrides = Partial<TabletopColorScheme>;\n\nexport interface TabletopColorThemeShape {\n semantics: {\n action: {\n primary: {\n base: string;\n softBg: string;\n };\n };\n content: {\n default: string;\n muted: string;\n };\n neutral: {\n border: string;\n divider: string;\n surface: string;\n surfaceHover: string;\n };\n warning: {\n base: string;\n softBg: string;\n };\n };\n}\n\nexport function createTabletopColorScheme(\n theme: TabletopColorThemeShape,\n overrides: TabletopColorOverrides = {},\n): TabletopColorScheme {\n return {\n cardBack: overrides.cardBack ?? theme.semantics.warning.base,\n cardBackBorder: overrides.cardBackBorder ?? theme.semantics.neutral.border,\n cardBorder: overrides.cardBorder ?? theme.semantics.neutral.border,\n cardSurface: overrides.cardSurface ?? theme.semantics.neutral.surface,\n cardText: overrides.cardText ?? theme.semantics.content.default,\n mutedText: overrides.mutedText ?? theme.semantics.content.muted,\n redSuitText: overrides.redSuitText ?? theme.semantics.warning.base,\n seatBorder: overrides.seatBorder ?? theme.semantics.neutral.divider,\n seatSelectedBorder: overrides.seatSelectedBorder ?? theme.semantics.action.primary.base,\n seatSurface: overrides.seatSurface ?? theme.semantics.neutral.surface,\n tableBorder: overrides.tableBorder ?? theme.semantics.warning.base,\n tableFelt: overrides.tableFelt ?? theme.semantics.action.primary.softBg,\n tableInnerBorder: overrides.tableInnerBorder ?? theme.semantics.neutral.divider,\n tokenSurface: overrides.tokenSurface ?? theme.semantics.warning.softBg,\n tokenText: overrides.tokenText ?? theme.semantics.content.default,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"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","sourcesContent":["export interface TabletopColorScheme {\n readonly cardBack: string;\n readonly cardBackBorder: string;\n readonly cardBorder: string;\n readonly cardSurface: string;\n readonly cardText: string;\n readonly mutedText: string;\n readonly redSuitText: string;\n readonly seatBorder: string;\n readonly seatMutedText: string;\n readonly seatSelectedBorder: string;\n readonly seatSurface: string;\n readonly seatText: string;\n readonly tableBorder: string;\n readonly tableFelt: string;\n readonly tableInnerBorder: string;\n readonly tableMutedText: string;\n readonly tableText: string;\n readonly tokenSurface: string;\n readonly tokenText: string;\n}\n\nexport type TabletopColorOverrides = Partial<TabletopColorScheme>;\n\nexport interface TabletopColorThemeShape {\n semantics: {\n action: {\n primary: {\n base: string;\n softBg: string;\n };\n };\n content: {\n default: string;\n muted: string;\n };\n neutral: {\n border: string;\n divider: string;\n surface: string;\n surfaceHover: string;\n };\n warning: {\n base: string;\n softBg: string;\n };\n };\n}\n\ninterface RgbColor {\n readonly red: number;\n readonly green: number;\n readonly blue: number;\n}\n\nconst DARK_TEXT_FALLBACK = '#111827';\nconst LIGHT_TEXT_FALLBACK = '#ffffff';\nconst MINIMUM_READABLE_CONTRAST = 4.5;\n\nexport function createTabletopColorScheme(\n theme: TabletopColorThemeShape,\n overrides: TabletopColorOverrides = {},\n): TabletopColorScheme {\n const cardSurface = overrides.cardSurface ?? theme.semantics.neutral.surface;\n const seatSurface = overrides.seatSurface ?? theme.semantics.neutral.surface;\n const tableFelt = overrides.tableFelt ?? theme.semantics.action.primary.softBg;\n const tokenSurface = overrides.tokenSurface ?? theme.semantics.warning.softBg;\n\n const cardText =\n overrides.cardText ??\n resolveReadableTextColor(cardSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);\n const seatText =\n overrides.seatText ??\n resolveReadableTextColor(seatSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);\n const tableText =\n overrides.tableText ??\n resolveReadableTextColor(tableFelt, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);\n const tokenText =\n overrides.tokenText ??\n resolveReadableTextColor(tokenSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);\n\n return {\n cardBack: overrides.cardBack ?? theme.semantics.warning.base,\n cardBackBorder: overrides.cardBackBorder ?? theme.semantics.neutral.border,\n cardBorder: overrides.cardBorder ?? theme.semantics.neutral.border,\n cardSurface,\n cardText,\n mutedText:\n overrides.mutedText ??\n resolveReadableTextColor(cardSurface, theme.semantics.content.muted, cardText),\n redSuitText:\n overrides.redSuitText ??\n resolveReadableTextColor(cardSurface, theme.semantics.warning.base, cardText),\n seatBorder: overrides.seatBorder ?? theme.semantics.neutral.divider,\n seatMutedText:\n overrides.seatMutedText ??\n resolveReadableTextColor(seatSurface, theme.semantics.content.muted, seatText),\n seatSelectedBorder: overrides.seatSelectedBorder ?? theme.semantics.action.primary.base,\n seatSurface,\n seatText,\n tableBorder: overrides.tableBorder ?? theme.semantics.warning.base,\n tableFelt,\n tableInnerBorder: overrides.tableInnerBorder ?? theme.semantics.neutral.divider,\n tableMutedText:\n overrides.tableMutedText ??\n resolveReadableTextColor(tableFelt, theme.semantics.content.muted, tableText),\n tableText,\n tokenSurface,\n tokenText,\n };\n}\n\nfunction resolveReadableTextColor(background: string, preferred: string, fallback: string): string {\n const preferredContrast = getContrastRatio(background, preferred);\n if (preferredContrast !== null && preferredContrast >= MINIMUM_READABLE_CONTRAST) {\n return preferred;\n }\n\n const candidates = uniqueColors([preferred, fallback, LIGHT_TEXT_FALLBACK, DARK_TEXT_FALLBACK]);\n const [readableCandidate] = candidates\n .map((candidate) => ({ candidate, contrast: getContrastRatio(background, candidate) }))\n .filter(\n (entry): entry is { readonly candidate: string; readonly contrast: number } =>\n entry.contrast !== null,\n )\n .sort((left, right) => right.contrast - left.contrast);\n\n return readableCandidate?.candidate ?? preferred;\n}\n\nfunction uniqueColors(colors: readonly string[]): string[] {\n return [...new Set(colors)];\n}\n\nfunction getContrastRatio(background: string, foreground: string): number | null {\n const backgroundColor = parseHexColor(background);\n const foregroundColor = parseHexColor(foreground);\n\n if (backgroundColor === null || foregroundColor === null) return null;\n\n const backgroundLuminance = getRelativeLuminance(backgroundColor);\n const foregroundLuminance = getRelativeLuminance(foregroundColor);\n const lighter = Math.max(backgroundLuminance, foregroundLuminance);\n const darker = Math.min(backgroundLuminance, foregroundLuminance);\n\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction parseHexColor(color: string): RgbColor | null {\n const value = color.trim();\n const hex = value.startsWith('#') ? value.slice(1) : '';\n if (!isSupportedHexLength(hex) || !isHexColorValue(hex)) return null;\n\n if (hex.length === 3) {\n const red = hex.slice(0, 1);\n const green = hex.slice(1, 2);\n const blue = hex.slice(2, 3);\n\n return {\n red: parseInt(red + red, 16),\n green: parseInt(green + green, 16),\n blue: parseInt(blue + blue, 16),\n };\n }\n\n return {\n red: parseInt(hex.slice(0, 2), 16),\n green: parseInt(hex.slice(2, 4), 16),\n blue: parseInt(hex.slice(4, 6), 16),\n };\n}\n\nfunction isSupportedHexLength(hex: string): boolean {\n return hex.length === 3 || hex.length === 6 || hex.length === 8;\n}\n\nfunction isHexColorValue(hex: string): boolean {\n return [...hex].every((character) => isHexCharacter(character));\n}\n\nfunction isHexCharacter(character: string): boolean {\n const code = character.charCodeAt(0);\n const isDigit = code >= 48 && code <= 57;\n const isUpperHex = code >= 65 && code <= 70;\n const isLowerHex = code >= 97 && code <= 102;\n\n return isDigit || isUpperHex || isLowerHex;\n}\n\nfunction getRelativeLuminance(color: RgbColor): number {\n const red = normalizeColorChannel(color.red);\n const green = normalizeColorChannel(color.green);\n const blue = normalizeColorChannel(color.blue);\n\n return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n}\n\nfunction normalizeColorChannel(value: number): number {\n const normalized = value / 255;\n if (normalized <= 0.03928) return normalized / 12.92;\n return ((normalized + 0.055) / 1.055) ** 2.4;\n}\n"]}
|
|
@@ -43,11 +43,11 @@ export function TabletopTable({ seats, centerCards = [], centerLabel, centerSubl
|
|
|
43
43
|
<View style={styles.centerContent}>
|
|
44
44
|
{centerCards.length > 0 ? (<CardHand cards={centerCards} colorScheme={colorScheme} size={cardSize}/>) : null}
|
|
45
45
|
{centerLabel !== undefined ? (<View style={[styles.centerLabel, { backgroundColor: colors.seatSurface }]}>
|
|
46
|
-
<Text style={[styles.centerLabelText, { color: colors.
|
|
46
|
+
<Text style={[styles.centerLabelText, { color: colors.seatText }]}>
|
|
47
47
|
{centerLabel}
|
|
48
48
|
</Text>
|
|
49
49
|
</View>) : null}
|
|
50
|
-
{centerSublabel !== undefined ? (<Text style={[styles.centerSublabel, { color: colors.
|
|
50
|
+
{centerSublabel !== undefined ? (<Text style={[styles.centerSublabel, { color: colors.tableMutedText }]}>
|
|
51
51
|
{centerSublabel}
|
|
52
52
|
</Text>) : null}
|
|
53
53
|
</View>
|
|
@@ -66,8 +66,8 @@ export function TabletopTable({ seats, centerCards = [], centerLabel, centerSubl
|
|
|
66
66
|
},
|
|
67
67
|
]} testID={testID ? `${testID}-seat-${seat.id}` : undefined}>
|
|
68
68
|
{seat.cards !== undefined || seat.faceDownCards !== undefined ? (<CardHand cards={seat.cards} colorScheme={colorScheme} faceDownCards={seat.faceDownCards} muted={seat.muted ?? seat.disabled} size={cardSize}/>) : null}
|
|
69
|
-
<Text style={[styles.seatLabel, { color: colors.
|
|
70
|
-
{seat.sublabel !== undefined ? (<Text style={[styles.seatSublabel, { color: colors.
|
|
69
|
+
<Text style={[styles.seatLabel, { color: colors.seatText }]}>{seat.label}</Text>
|
|
70
|
+
{seat.sublabel !== undefined ? (<Text style={[styles.seatSublabel, { color: colors.seatMutedText }]}>
|
|
71
71
|
{seat.sublabel}
|
|
72
72
|
</Text>) : null}
|
|
73
73
|
{seat.tokenLabel !== undefined ? (<View style={[styles.token, { backgroundColor: colors.tokenSurface }]}>
|
|
@@ -99,7 +99,7 @@ const styles = StyleSheet.create({
|
|
|
99
99
|
},
|
|
100
100
|
centerSublabel: {
|
|
101
101
|
fontSize: 11,
|
|
102
|
-
fontWeight: '
|
|
102
|
+
fontWeight: '700',
|
|
103
103
|
},
|
|
104
104
|
circleSurface: {
|
|
105
105
|
borderRadius: 999,
|
|
@@ -142,7 +142,7 @@ const styles = StyleSheet.create({
|
|
|
142
142
|
},
|
|
143
143
|
seatSublabel: {
|
|
144
144
|
fontSize: 10,
|
|
145
|
-
fontWeight: '
|
|
145
|
+
fontWeight: '700',
|
|
146
146
|
},
|
|
147
147
|
surface: {
|
|
148
148
|
borderWidth: 8,
|
|
@@ -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\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.cardText }]}>\n {centerLabel}\n </Text>\n </View>\n ) : null}\n {centerSublabel !== undefined ? (\n <Text style={[styles.centerSublabel, { color: colors.mutedText }]}>\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.cardText }]}>{seat.label}</Text>\n {seat.sublabel !== undefined ? (\n <Text style={[styles.seatSublabel, { color: colors.mutedText }]}>\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: '600',\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: '600',\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 { 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\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"]}
|
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.2",
|
|
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": {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { createTabletopColorScheme, type TabletopColorThemeShape } from './colors';
|
|
4
|
+
|
|
5
|
+
const baseTheme = {
|
|
6
|
+
semantics: {
|
|
7
|
+
action: {
|
|
8
|
+
primary: {
|
|
9
|
+
base: '#047857',
|
|
10
|
+
softBg: '#047857',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
content: {
|
|
14
|
+
default: '#047857',
|
|
15
|
+
muted: '#059669',
|
|
16
|
+
},
|
|
17
|
+
neutral: {
|
|
18
|
+
border: '#d1d5db',
|
|
19
|
+
divider: '#e5e7eb',
|
|
20
|
+
surface: '#ffffff',
|
|
21
|
+
surfaceHover: '#f9fafb',
|
|
22
|
+
},
|
|
23
|
+
warning: {
|
|
24
|
+
base: '#b45309',
|
|
25
|
+
softBg: '#fef3c7',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
} satisfies TabletopColorThemeShape;
|
|
29
|
+
|
|
30
|
+
describe('createTabletopColorScheme', () => {
|
|
31
|
+
test('derives readable table text when preferred theme text matches the table surface', () => {
|
|
32
|
+
const colors = createTabletopColorScheme(baseTheme);
|
|
33
|
+
|
|
34
|
+
expect(colors.tableFelt).toBe('#047857');
|
|
35
|
+
expect(colors.tableText).toBe('#ffffff');
|
|
36
|
+
expect(colors.tableMutedText).toBe('#ffffff');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('derives readable seat text for dark custom seat surfaces', () => {
|
|
40
|
+
const colors = createTabletopColorScheme(baseTheme, {
|
|
41
|
+
seatSurface: '#064e3b',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(colors.seatText).toBe('#ffffff');
|
|
45
|
+
expect(colors.seatMutedText).toBe('#ffffff');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('keeps explicit readable text overrides', () => {
|
|
49
|
+
const colors = createTabletopColorScheme(baseTheme, {
|
|
50
|
+
seatSurface: '#064e3b',
|
|
51
|
+
seatText: '#fef3c7',
|
|
52
|
+
tableText: '#fef3c7',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(colors.seatText).toBe('#fef3c7');
|
|
56
|
+
expect(colors.tableText).toBe('#fef3c7');
|
|
57
|
+
});
|
|
58
|
+
});
|
package/src/colors.ts
CHANGED
|
@@ -7,11 +7,15 @@ export interface TabletopColorScheme {
|
|
|
7
7
|
readonly mutedText: string;
|
|
8
8
|
readonly redSuitText: string;
|
|
9
9
|
readonly seatBorder: string;
|
|
10
|
+
readonly seatMutedText: string;
|
|
10
11
|
readonly seatSelectedBorder: string;
|
|
11
12
|
readonly seatSurface: string;
|
|
13
|
+
readonly seatText: string;
|
|
12
14
|
readonly tableBorder: string;
|
|
13
15
|
readonly tableFelt: string;
|
|
14
16
|
readonly tableInnerBorder: string;
|
|
17
|
+
readonly tableMutedText: string;
|
|
18
|
+
readonly tableText: string;
|
|
15
19
|
readonly tokenSurface: string;
|
|
16
20
|
readonly tokenText: string;
|
|
17
21
|
}
|
|
@@ -43,25 +47,156 @@ export interface TabletopColorThemeShape {
|
|
|
43
47
|
};
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
interface RgbColor {
|
|
51
|
+
readonly red: number;
|
|
52
|
+
readonly green: number;
|
|
53
|
+
readonly blue: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const DARK_TEXT_FALLBACK = '#111827';
|
|
57
|
+
const LIGHT_TEXT_FALLBACK = '#ffffff';
|
|
58
|
+
const MINIMUM_READABLE_CONTRAST = 4.5;
|
|
59
|
+
|
|
46
60
|
export function createTabletopColorScheme(
|
|
47
61
|
theme: TabletopColorThemeShape,
|
|
48
62
|
overrides: TabletopColorOverrides = {},
|
|
49
63
|
): TabletopColorScheme {
|
|
64
|
+
const cardSurface = overrides.cardSurface ?? theme.semantics.neutral.surface;
|
|
65
|
+
const seatSurface = overrides.seatSurface ?? theme.semantics.neutral.surface;
|
|
66
|
+
const tableFelt = overrides.tableFelt ?? theme.semantics.action.primary.softBg;
|
|
67
|
+
const tokenSurface = overrides.tokenSurface ?? theme.semantics.warning.softBg;
|
|
68
|
+
|
|
69
|
+
const cardText =
|
|
70
|
+
overrides.cardText ??
|
|
71
|
+
resolveReadableTextColor(cardSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
72
|
+
const seatText =
|
|
73
|
+
overrides.seatText ??
|
|
74
|
+
resolveReadableTextColor(seatSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
75
|
+
const tableText =
|
|
76
|
+
overrides.tableText ??
|
|
77
|
+
resolveReadableTextColor(tableFelt, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
78
|
+
const tokenText =
|
|
79
|
+
overrides.tokenText ??
|
|
80
|
+
resolveReadableTextColor(tokenSurface, theme.semantics.content.default, LIGHT_TEXT_FALLBACK);
|
|
81
|
+
|
|
50
82
|
return {
|
|
51
83
|
cardBack: overrides.cardBack ?? theme.semantics.warning.base,
|
|
52
84
|
cardBackBorder: overrides.cardBackBorder ?? theme.semantics.neutral.border,
|
|
53
85
|
cardBorder: overrides.cardBorder ?? theme.semantics.neutral.border,
|
|
54
|
-
cardSurface
|
|
55
|
-
cardText
|
|
56
|
-
mutedText:
|
|
57
|
-
|
|
86
|
+
cardSurface,
|
|
87
|
+
cardText,
|
|
88
|
+
mutedText:
|
|
89
|
+
overrides.mutedText ??
|
|
90
|
+
resolveReadableTextColor(cardSurface, theme.semantics.content.muted, cardText),
|
|
91
|
+
redSuitText:
|
|
92
|
+
overrides.redSuitText ??
|
|
93
|
+
resolveReadableTextColor(cardSurface, theme.semantics.warning.base, cardText),
|
|
58
94
|
seatBorder: overrides.seatBorder ?? theme.semantics.neutral.divider,
|
|
95
|
+
seatMutedText:
|
|
96
|
+
overrides.seatMutedText ??
|
|
97
|
+
resolveReadableTextColor(seatSurface, theme.semantics.content.muted, seatText),
|
|
59
98
|
seatSelectedBorder: overrides.seatSelectedBorder ?? theme.semantics.action.primary.base,
|
|
60
|
-
seatSurface
|
|
99
|
+
seatSurface,
|
|
100
|
+
seatText,
|
|
61
101
|
tableBorder: overrides.tableBorder ?? theme.semantics.warning.base,
|
|
62
|
-
tableFelt
|
|
102
|
+
tableFelt,
|
|
63
103
|
tableInnerBorder: overrides.tableInnerBorder ?? theme.semantics.neutral.divider,
|
|
64
|
-
|
|
65
|
-
|
|
104
|
+
tableMutedText:
|
|
105
|
+
overrides.tableMutedText ??
|
|
106
|
+
resolveReadableTextColor(tableFelt, theme.semantics.content.muted, tableText),
|
|
107
|
+
tableText,
|
|
108
|
+
tokenSurface,
|
|
109
|
+
tokenText,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resolveReadableTextColor(background: string, preferred: string, fallback: string): string {
|
|
114
|
+
const preferredContrast = getContrastRatio(background, preferred);
|
|
115
|
+
if (preferredContrast !== null && preferredContrast >= MINIMUM_READABLE_CONTRAST) {
|
|
116
|
+
return preferred;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const candidates = uniqueColors([preferred, fallback, LIGHT_TEXT_FALLBACK, DARK_TEXT_FALLBACK]);
|
|
120
|
+
const [readableCandidate] = candidates
|
|
121
|
+
.map((candidate) => ({ candidate, contrast: getContrastRatio(background, candidate) }))
|
|
122
|
+
.filter(
|
|
123
|
+
(entry): entry is { readonly candidate: string; readonly contrast: number } =>
|
|
124
|
+
entry.contrast !== null,
|
|
125
|
+
)
|
|
126
|
+
.sort((left, right) => right.contrast - left.contrast);
|
|
127
|
+
|
|
128
|
+
return readableCandidate?.candidate ?? preferred;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function uniqueColors(colors: readonly string[]): string[] {
|
|
132
|
+
return [...new Set(colors)];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getContrastRatio(background: string, foreground: string): number | null {
|
|
136
|
+
const backgroundColor = parseHexColor(background);
|
|
137
|
+
const foregroundColor = parseHexColor(foreground);
|
|
138
|
+
|
|
139
|
+
if (backgroundColor === null || foregroundColor === null) return null;
|
|
140
|
+
|
|
141
|
+
const backgroundLuminance = getRelativeLuminance(backgroundColor);
|
|
142
|
+
const foregroundLuminance = getRelativeLuminance(foregroundColor);
|
|
143
|
+
const lighter = Math.max(backgroundLuminance, foregroundLuminance);
|
|
144
|
+
const darker = Math.min(backgroundLuminance, foregroundLuminance);
|
|
145
|
+
|
|
146
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function parseHexColor(color: string): RgbColor | null {
|
|
150
|
+
const value = color.trim();
|
|
151
|
+
const hex = value.startsWith('#') ? value.slice(1) : '';
|
|
152
|
+
if (!isSupportedHexLength(hex) || !isHexColorValue(hex)) return null;
|
|
153
|
+
|
|
154
|
+
if (hex.length === 3) {
|
|
155
|
+
const red = hex.slice(0, 1);
|
|
156
|
+
const green = hex.slice(1, 2);
|
|
157
|
+
const blue = hex.slice(2, 3);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
red: parseInt(red + red, 16),
|
|
161
|
+
green: parseInt(green + green, 16),
|
|
162
|
+
blue: parseInt(blue + blue, 16),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
red: parseInt(hex.slice(0, 2), 16),
|
|
168
|
+
green: parseInt(hex.slice(2, 4), 16),
|
|
169
|
+
blue: parseInt(hex.slice(4, 6), 16),
|
|
66
170
|
};
|
|
67
171
|
}
|
|
172
|
+
|
|
173
|
+
function isSupportedHexLength(hex: string): boolean {
|
|
174
|
+
return hex.length === 3 || hex.length === 6 || hex.length === 8;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isHexColorValue(hex: string): boolean {
|
|
178
|
+
return [...hex].every((character) => isHexCharacter(character));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isHexCharacter(character: string): boolean {
|
|
182
|
+
const code = character.charCodeAt(0);
|
|
183
|
+
const isDigit = code >= 48 && code <= 57;
|
|
184
|
+
const isUpperHex = code >= 65 && code <= 70;
|
|
185
|
+
const isLowerHex = code >= 97 && code <= 102;
|
|
186
|
+
|
|
187
|
+
return isDigit || isUpperHex || isLowerHex;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getRelativeLuminance(color: RgbColor): number {
|
|
191
|
+
const red = normalizeColorChannel(color.red);
|
|
192
|
+
const green = normalizeColorChannel(color.green);
|
|
193
|
+
const blue = normalizeColorChannel(color.blue);
|
|
194
|
+
|
|
195
|
+
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function normalizeColorChannel(value: number): number {
|
|
199
|
+
const normalized = value / 255;
|
|
200
|
+
if (normalized <= 0.03928) return normalized / 12.92;
|
|
201
|
+
return ((normalized + 0.055) / 1.055) ** 2.4;
|
|
202
|
+
}
|
|
@@ -73,13 +73,13 @@ export function TabletopTable({
|
|
|
73
73
|
) : null}
|
|
74
74
|
{centerLabel !== undefined ? (
|
|
75
75
|
<View style={[styles.centerLabel, { backgroundColor: colors.seatSurface }]}>
|
|
76
|
-
<Text style={[styles.centerLabelText, { color: colors.
|
|
76
|
+
<Text style={[styles.centerLabelText, { color: colors.seatText }]}>
|
|
77
77
|
{centerLabel}
|
|
78
78
|
</Text>
|
|
79
79
|
</View>
|
|
80
80
|
) : null}
|
|
81
81
|
{centerSublabel !== undefined ? (
|
|
82
|
-
<Text style={[styles.centerSublabel, { color: colors.
|
|
82
|
+
<Text style={[styles.centerSublabel, { color: colors.tableMutedText }]}>
|
|
83
83
|
{centerSublabel}
|
|
84
84
|
</Text>
|
|
85
85
|
) : null}
|
|
@@ -114,9 +114,9 @@ export function TabletopTable({
|
|
|
114
114
|
size={cardSize}
|
|
115
115
|
/>
|
|
116
116
|
) : null}
|
|
117
|
-
<Text style={[styles.seatLabel, { color: colors.
|
|
117
|
+
<Text style={[styles.seatLabel, { color: colors.seatText }]}>{seat.label}</Text>
|
|
118
118
|
{seat.sublabel !== undefined ? (
|
|
119
|
-
<Text style={[styles.seatSublabel, { color: colors.
|
|
119
|
+
<Text style={[styles.seatSublabel, { color: colors.seatMutedText }]}>
|
|
120
120
|
{seat.sublabel}
|
|
121
121
|
</Text>
|
|
122
122
|
) : null}
|
|
@@ -154,7 +154,7 @@ const styles = StyleSheet.create({
|
|
|
154
154
|
},
|
|
155
155
|
centerSublabel: {
|
|
156
156
|
fontSize: 11,
|
|
157
|
-
fontWeight: '
|
|
157
|
+
fontWeight: '700',
|
|
158
158
|
},
|
|
159
159
|
circleSurface: {
|
|
160
160
|
borderRadius: 999,
|
|
@@ -197,7 +197,7 @@ const styles = StyleSheet.create({
|
|
|
197
197
|
},
|
|
198
198
|
seatSublabel: {
|
|
199
199
|
fontSize: 10,
|
|
200
|
-
fontWeight: '
|
|
200
|
+
fontWeight: '700',
|
|
201
201
|
},
|
|
202
202
|
surface: {
|
|
203
203
|
borderWidth: 8,
|