fansunited-sports-ui 0.0.1
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/LICENSE +22 -0
- package/README.md +269 -0
- package/dist/BADGE.md +50 -0
- package/dist/BADGE_GROUP.md +43 -0
- package/dist/BETTING_ODDS.md +245 -0
- package/dist/CONTENT_CARD.md +1586 -0
- package/dist/COUNTDOWN.md +85 -0
- package/dist/CTA_BUTTON.md +138 -0
- package/dist/CTA_GROUP.md +55 -0
- package/dist/ENTITY_DISPLAY.md +198 -0
- package/dist/FORM_INDICATOR.md +344 -0
- package/dist/MATCHUP_HEADER.md +192 -0
- package/dist/MATCH_SCORE.md +788 -0
- package/dist/PROFILE_HEADER.md +113 -0
- package/dist/README.md +269 -0
- package/dist/STATS_GRID.md +298 -0
- package/dist/STATS_HEAD_TO_HEAD.md +430 -0
- package/dist/STATS_TABLE.md +348 -0
- package/dist/TOOLTIP.md +129 -0
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2367 -0
- package/dist/index.es.js +4460 -0
- package/dist/index.es.js.map +1 -0
- package/package.json +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Fans United
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# fansunited-sports-ui
|
|
2
|
+
|
|
3
|
+
A comprehensive React component library for building sports websites and applications. Built with **Tailwind CSS v4** for seamless theming integration.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/fansunited-sports-ui)
|
|
6
|
+
[](https://github.com/nicklatkovich/fansunited-sports-ui/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🏟️ **Sports-focused** — Purpose-built for media sites, football clubs, federations, and betting affiliates
|
|
11
|
+
- 🎨 **Tailwind CSS v4** — Uses native Tailwind classes to automatically match your design system
|
|
12
|
+
- 📦 **Three-layer architecture** — Primitives, Components, and Blocks for flexible composition
|
|
13
|
+
- 🎯 **Type-Safe** — Full TypeScript support with comprehensive type definitions
|
|
14
|
+
- 🔧 **Highly Customizable** — Flexible styling system with standardized customization options
|
|
15
|
+
- 📱 **Responsive** — Mobile-first design that works on all screen sizes
|
|
16
|
+
- 🎭 **Theme Support** — Works with Tailwind's theme system and custom color palettes
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Install the package via npm:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install fansunited-sports-ui
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Peer Dependencies
|
|
29
|
+
|
|
30
|
+
This library requires the following peer dependencies:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
35
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
36
|
+
"tailwindcss": "^4.0.0",
|
|
37
|
+
"@tailwindcss/postcss": "^4.0.0"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Install them if you haven't already:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install react react-dom tailwindcss @tailwindcss/postcss
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Setup
|
|
50
|
+
|
|
51
|
+
### 1. Configure Tailwind CSS
|
|
52
|
+
|
|
53
|
+
Update your Tailwind CSS configuration to scan the component library files. This ensures that all Tailwind classes used by the components are included in your build.
|
|
54
|
+
|
|
55
|
+
**For Tailwind CSS v4 with `@import` in CSS:**
|
|
56
|
+
|
|
57
|
+
In your main CSS file (e.g., `src/index.css` or `app/globals.css`):
|
|
58
|
+
|
|
59
|
+
```css
|
|
60
|
+
@import "tailwindcss";
|
|
61
|
+
|
|
62
|
+
/* Your custom theme and styles */
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Configure content paths in `tailwind.config.js` (or `tailwind.config.ts`):**
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
/** @type {import('tailwindcss').Config} */
|
|
69
|
+
export default {
|
|
70
|
+
content: [
|
|
71
|
+
"./index.html",
|
|
72
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
73
|
+
// Add this line to scan the component library
|
|
74
|
+
"./node_modules/fansunited-sports-ui/dist/**/*.{js,mjs,cjs}",
|
|
75
|
+
],
|
|
76
|
+
theme: {
|
|
77
|
+
extend: {},
|
|
78
|
+
},
|
|
79
|
+
plugins: [],
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Important:** The component library uses Tailwind's native color classes (e.g., `bg-primary-600`, `from-primary-600`) instead of hardcoded colors. This means the components will automatically match your application's design system and theme.
|
|
84
|
+
|
|
85
|
+
### 2. Configure PostCSS
|
|
86
|
+
|
|
87
|
+
Ensure your `postcss.config.js` includes the Tailwind CSS PostCSS plugin:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
export default {
|
|
91
|
+
plugins: {
|
|
92
|
+
"@tailwindcss/postcss": {},
|
|
93
|
+
autoprefixer: {},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Define Your Theme (Optional)
|
|
99
|
+
|
|
100
|
+
The components use Tailwind's color system. You can customize the primary and secondary colors in your CSS using the `@theme` directive:
|
|
101
|
+
|
|
102
|
+
```css
|
|
103
|
+
@import "tailwindcss";
|
|
104
|
+
|
|
105
|
+
@theme {
|
|
106
|
+
/* Primary color palette */
|
|
107
|
+
--color-primary-50: oklch(0.97 0.01 250);
|
|
108
|
+
--color-primary-100: oklch(0.93 0.03 250);
|
|
109
|
+
--color-primary-200: oklch(0.85 0.06 250);
|
|
110
|
+
--color-primary-300: oklch(0.75 0.1 250);
|
|
111
|
+
--color-primary-400: oklch(0.65 0.14 250);
|
|
112
|
+
--color-primary-500: oklch(0.57 0.17 250);
|
|
113
|
+
--color-primary-600: oklch(0.5 0.17 250);
|
|
114
|
+
--color-primary-700: oklch(0.43 0.15 250);
|
|
115
|
+
--color-primary-800: oklch(0.35 0.12 250);
|
|
116
|
+
--color-primary-900: oklch(0.27 0.09 250);
|
|
117
|
+
--color-primary-950: oklch(0.18 0.06 250);
|
|
118
|
+
|
|
119
|
+
/* Secondary color palette */
|
|
120
|
+
--color-secondary-50: oklch(0.97 0.02 50);
|
|
121
|
+
--color-secondary-100: oklch(0.94 0.04 50);
|
|
122
|
+
--color-secondary-200: oklch(0.88 0.08 50);
|
|
123
|
+
--color-secondary-300: oklch(0.8 0.12 50);
|
|
124
|
+
--color-secondary-400: oklch(0.72 0.15 50);
|
|
125
|
+
--color-secondary-500: oklch(0.68 0.18 50);
|
|
126
|
+
--color-secondary-600: oklch(0.62 0.18 50);
|
|
127
|
+
--color-secondary-700: oklch(0.54 0.16 50);
|
|
128
|
+
--color-secondary-800: oklch(0.45 0.13 50);
|
|
129
|
+
--color-secondary-900: oklch(0.36 0.1 50);
|
|
130
|
+
--color-secondary-950: oklch(0.25 0.07 50);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Quick Start
|
|
137
|
+
|
|
138
|
+
### Basic Usage
|
|
139
|
+
|
|
140
|
+
Import and use the components in your React application:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { MatchScore, ContentCard } from "fansunited-sports-ui";
|
|
144
|
+
|
|
145
|
+
function App() {
|
|
146
|
+
return (
|
|
147
|
+
<MatchScore
|
|
148
|
+
match={{
|
|
149
|
+
id: "1",
|
|
150
|
+
competitorOne: { id: "1", name: "Arsenal", score: { main: 2 } },
|
|
151
|
+
competitorTwo: { id: "2", name: "Chelsea", score: { main: 1 } },
|
|
152
|
+
status: "final",
|
|
153
|
+
}}
|
|
154
|
+
variant="scoreCard"
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Architecture
|
|
163
|
+
|
|
164
|
+
The library is organized into three layers:
|
|
165
|
+
|
|
166
|
+
### Layer 1: Primitives
|
|
167
|
+
|
|
168
|
+
Pure UI building blocks with no domain knowledge:
|
|
169
|
+
|
|
170
|
+
| Component | Description |
|
|
171
|
+
| ------------------------ | ---------------------------------- |
|
|
172
|
+
| `Badge` / `BadgeGroup` | Status labels and tags |
|
|
173
|
+
| `CtaButton` / `CtaGroup` | Call-to-action buttons |
|
|
174
|
+
| `EntityDisplay` | Generic entity with image and name |
|
|
175
|
+
| `Countdown` | Timer display |
|
|
176
|
+
| `Pagination` | Page navigation |
|
|
177
|
+
| `Tooltip` | Hover information |
|
|
178
|
+
|
|
179
|
+
### Layer 2: Components
|
|
180
|
+
|
|
181
|
+
Domain-aware components with single responsibility:
|
|
182
|
+
|
|
183
|
+
| Component | Description |
|
|
184
|
+
| ----------------- | ------------------------------------------------------------------------------------------ |
|
|
185
|
+
| `MatchScore` | Match display with variants: `inline`, `scoreCard`, `scoreLine`, `embedded` |
|
|
186
|
+
| `ContentCard` | Flexible cards with variants: `hero`, `standard`, `standardHorizontal`, `promo`, `minimal` |
|
|
187
|
+
| `BettingOdds` | Odds display with operator branding |
|
|
188
|
+
| `StatsTable` | Tabular statistics (standings, leaderboards) |
|
|
189
|
+
| `StatsGrid` | Grid of stat items |
|
|
190
|
+
| `StatsHeadToHead` | Comparison bars |
|
|
191
|
+
| `FormIndicator` | Win/draw/loss form display |
|
|
192
|
+
| `ProfileHeader` | Entity profile headers |
|
|
193
|
+
| `MatchupHeader` | Two-entity matchup display |
|
|
194
|
+
| `SectionHeader` | Section titles with optional actions |
|
|
195
|
+
|
|
196
|
+
### Layer 3: Blocks
|
|
197
|
+
|
|
198
|
+
Pre-assembled composites ready for use:
|
|
199
|
+
|
|
200
|
+
| Block | Description |
|
|
201
|
+
| ------------------------ | --------------------------------- |
|
|
202
|
+
| `MatchListBlock` | List of matches with grouping |
|
|
203
|
+
| `ContentListBlock` | Grid/list of content cards |
|
|
204
|
+
| `FeaturedContentBlock` | Hero + supporting content layouts |
|
|
205
|
+
| `HeadToHeadBlock` | Complete H2H comparison |
|
|
206
|
+
| `CompetitorProfileBlock` | Full competitor profile |
|
|
207
|
+
| `SquadBlock` | Team squad display |
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Styling
|
|
212
|
+
|
|
213
|
+
Every component accepts a `styles` prop for customization:
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
<MatchScore
|
|
217
|
+
match={match}
|
|
218
|
+
styles={{
|
|
219
|
+
borderRadius: "lg",
|
|
220
|
+
shadow: "md",
|
|
221
|
+
padding: "lg",
|
|
222
|
+
typography: {
|
|
223
|
+
teamName: "text-lg font-bold",
|
|
224
|
+
score: "text-3xl font-black",
|
|
225
|
+
},
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Common Style Options
|
|
231
|
+
|
|
232
|
+
| Option | Values |
|
|
233
|
+
| ------------------------ | ------------------------------------------------------------------ |
|
|
234
|
+
| `borderRadius` | `none` \| `sm` \| `md` \| `lg` \| `xl` \| `2xl` \| `3xl` \| `full` |
|
|
235
|
+
| `shadow` / `hoverShadow` | `none` \| `sm` \| `md` \| `lg` \| `xl` \| `2xl` |
|
|
236
|
+
| `padding` | `none` \| `sm` \| `md` \| `lg` \| `xl` |
|
|
237
|
+
| `typography` | Tailwind classes for text elements |
|
|
238
|
+
| `background` | Custom background classes |
|
|
239
|
+
| `container` | Additional container classes |
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## TypeScript Support
|
|
244
|
+
|
|
245
|
+
All components are fully typed. Import types as needed:
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import type {
|
|
249
|
+
MatchScoreContent,
|
|
250
|
+
MatchScoreProps,
|
|
251
|
+
CardContent,
|
|
252
|
+
CardStyles,
|
|
253
|
+
Competitor,
|
|
254
|
+
MatchSimple,
|
|
255
|
+
} from "fansunited-sports-ui";
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Browser Support
|
|
261
|
+
|
|
262
|
+
- Chrome, Firefox, Safari, Edge (latest 2 versions)
|
|
263
|
+
- React 18.x or 19.x
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT © [Fans United](https://fansunited.com)
|
package/dist/BADGE.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Badge
|
|
2
|
+
|
|
3
|
+
A visual badge component for displaying labels, tags, or status indicators.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Badge } from "@fansunited/tailwind-ui-components";
|
|
9
|
+
|
|
10
|
+
// Primary badge
|
|
11
|
+
<Badge badge={{ text: "NEW", variant: "primary", position: "top-right" }} />
|
|
12
|
+
|
|
13
|
+
// Success badge
|
|
14
|
+
<Badge badge={{ text: "Live", variant: "success", position: "top-left" }} />
|
|
15
|
+
|
|
16
|
+
// Custom styled badge
|
|
17
|
+
<Badge badge={{ text: "VIP", variant: "custom", position: "top-right", styles: { container: "bg-gold text-black" } }} />
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Description |
|
|
23
|
+
| ------- | ------------- | -------------------------- |
|
|
24
|
+
| `badge` | `BadgeConfig` | Badge configuration object |
|
|
25
|
+
|
|
26
|
+
### BadgeConfig
|
|
27
|
+
|
|
28
|
+
| Property | Type | Default | Description |
|
|
29
|
+
| ---------- | --------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------- |
|
|
30
|
+
| `text` | `string` | - | Badge text content |
|
|
31
|
+
| `variant` | `"default" \| "primary" \| "secondary" \| "success" \| "warning" \| "danger" \| "info" \| "custom"` | `"default"` | Visual variant |
|
|
32
|
+
| `position` | `"top-left" \| "top-right" \| "bottom-left" \| "bottom-right"` | - | Position when used in card context |
|
|
33
|
+
| `styles` | `BadgeStyles` | - | Style customization options |
|
|
34
|
+
|
|
35
|
+
### BadgeStyles
|
|
36
|
+
|
|
37
|
+
| Property | Type | Description |
|
|
38
|
+
| ----------- | -------- | ----------------------------------------------------------- |
|
|
39
|
+
| `container` | `string` | Tailwind classes for the badge (used with "custom" variant) |
|
|
40
|
+
|
|
41
|
+
## Variants
|
|
42
|
+
|
|
43
|
+
- **default**: Neutral gray background
|
|
44
|
+
- **primary**: Primary color background
|
|
45
|
+
- **secondary**: Secondary color background
|
|
46
|
+
- **success**: Green background
|
|
47
|
+
- **warning**: Yellow background
|
|
48
|
+
- **danger**: Red background
|
|
49
|
+
- **info**: Cyan background
|
|
50
|
+
- **custom**: No default styles - use `styles.container` for full customization
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# BadgeGroup
|
|
2
|
+
|
|
3
|
+
A component for rendering multiple badges at specific positions within a container.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { BadgeGroup } from "@fansunited/tailwind-ui-components";
|
|
9
|
+
|
|
10
|
+
// Render all badges
|
|
11
|
+
<BadgeGroup badges={content.badges} />
|
|
12
|
+
|
|
13
|
+
// Only render top badges
|
|
14
|
+
<BadgeGroup badges={content.badges} positions={["top-left", "top-right"]} />
|
|
15
|
+
|
|
16
|
+
// With custom container styling
|
|
17
|
+
<BadgeGroup badges={content.badges} styles={{ container: "p-4" }} />
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
| ----------- | --------------------------------------------------------------------- | ------------- | --------------------------- |
|
|
24
|
+
| `badges` | `BadgeConfig[]` | - | Array of badge configs |
|
|
25
|
+
| `positions` | `Array<"top-left" \| "top-right" \| "bottom-left" \| "bottom-right">` | All positions | Filter badges by position |
|
|
26
|
+
| `styles` | `BadgeGroupStyles` | - | Style customization options |
|
|
27
|
+
|
|
28
|
+
### BadgeGroupStyles
|
|
29
|
+
|
|
30
|
+
| Property | Type | Description |
|
|
31
|
+
| ----------- | -------- | -------------------------------------------------- |
|
|
32
|
+
| `container` | `string` | Tailwind classes for each position group container |
|
|
33
|
+
|
|
34
|
+
## Positioning
|
|
35
|
+
|
|
36
|
+
Badges are automatically positioned using absolute positioning:
|
|
37
|
+
|
|
38
|
+
- **top-left**: Top left corner with padding
|
|
39
|
+
- **top-right**: Top right corner with padding
|
|
40
|
+
- **bottom-left**: Bottom left corner with padding
|
|
41
|
+
- **bottom-right**: Bottom right corner with padding
|
|
42
|
+
|
|
43
|
+
Multiple badges at the same position are displayed in a flex row with gap.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# BettingOdds Component
|
|
2
|
+
|
|
3
|
+
A flexible React component for displaying betting odds with operator branding, change indicators, and multiple display modes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Responsive Layouts**: Automatically switches between horizontal and grid layouts based on the number of odds
|
|
8
|
+
- **Operator Branding**: Display betting operator logos with custom colors
|
|
9
|
+
- **Change Indicators**: Visual arrows showing odds movement (up/down)
|
|
10
|
+
- **Deep Linking**: Support for clickable odds and operator logos
|
|
11
|
+
- **Flash Mode**: Alternating display between operator logo and odds
|
|
12
|
+
- **Customizable Styling**: Configurable padding, typography, and display modes
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { BettingOdds } from "@fansunited/tailwind-ui-components";
|
|
18
|
+
import type { BettingOddsProps } from "@fansunited/tailwind-ui-components";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
### Simple 1X2 Odds
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
<BettingOdds
|
|
27
|
+
odds={[
|
|
28
|
+
{ label: "1", value: 2.8, change: "up" },
|
|
29
|
+
{ label: "X", value: 3.1, change: "none" },
|
|
30
|
+
{ label: "2", value: 2.5, change: "down" },
|
|
31
|
+
]}
|
|
32
|
+
operator={{
|
|
33
|
+
name: "Bet365",
|
|
34
|
+
logo: { url: "https://example.com/bet365-logo.png", alt: "Bet365" },
|
|
35
|
+
url: "https://www.bet365.com",
|
|
36
|
+
backgroundColor: "#037b5b",
|
|
37
|
+
textColor: "#ffffff",
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Moneyline (2 Odds)
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<BettingOdds
|
|
46
|
+
odds={[
|
|
47
|
+
{ label: "Home Win", value: 1.85, url: "https://example.com/home" },
|
|
48
|
+
{ label: "Away Win", value: 2.05, url: "https://example.com/away" },
|
|
49
|
+
]}
|
|
50
|
+
operator={{
|
|
51
|
+
name: "William Hill",
|
|
52
|
+
url: "https://www.williamhill.com",
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Layout Behavior
|
|
58
|
+
|
|
59
|
+
The component automatically adjusts its layout based on the number of odds:
|
|
60
|
+
|
|
61
|
+
- **1-3 odds**: Horizontal layout with operator logo (max 110px width) on the left
|
|
62
|
+
- **4+ odds**: Vertical layout with full-width operator logo on top and odds in a 3-column grid
|
|
63
|
+
|
|
64
|
+
## Props
|
|
65
|
+
|
|
66
|
+
### `BettingOddsProps`
|
|
67
|
+
|
|
68
|
+
| Prop | Type | Required | Default | Description |
|
|
69
|
+
| ---------- | ------------------- | -------- | ------- | -------------------------------------------- |
|
|
70
|
+
| `odds` | `OddsValue[]` | Yes | - | Array of odds values to display |
|
|
71
|
+
| `operator` | `BettingOperator` | No | - | Betting operator providing the odds |
|
|
72
|
+
| `label` | `string` | No | - | Overall label for odds section (e.g., "1X2") |
|
|
73
|
+
| `styles` | `BettingOddsStyles` | No | `{}` | Style configuration |
|
|
74
|
+
|
|
75
|
+
### `OddsValue`
|
|
76
|
+
|
|
77
|
+
| Property | Type | Required | Description |
|
|
78
|
+
| ------------- | -------------------------- | -------- | --------------------------------------------------- |
|
|
79
|
+
| `label` | `string` | Yes | Label for the odd (e.g., "1", "X", "2", "Over 2.5") |
|
|
80
|
+
| `value` | `number \| string` | Yes | Odds value (e.g., 2.8, "1") |
|
|
81
|
+
| `url` | `string` | No | Deep link URL for this specific odd |
|
|
82
|
+
| `change` | `"up" \| "down" \| "none"` | No | Change indicator (default: "none") |
|
|
83
|
+
| `onClick` | `(odd: OddsValue) => void` | No | Click handler (suppresses default navigation) |
|
|
84
|
+
| `eventId` | `string` | No | Optional event ID for platform integration |
|
|
85
|
+
| `selectionId` | `string` | No | Optional selection ID for platform integration |
|
|
86
|
+
|
|
87
|
+
### `BettingOperator`
|
|
88
|
+
|
|
89
|
+
| Property | Type | Required | Description |
|
|
90
|
+
| ----------------- | ------------------------------------- | -------- | --------------------------------------------- |
|
|
91
|
+
| `name` | `string` | Yes | Operator name (e.g., "Bet365") |
|
|
92
|
+
| `url` | `string` | Yes | Deep link URL to the betting page |
|
|
93
|
+
| `logo` | `ImageWithVariations` | No | Operator logo |
|
|
94
|
+
| `backgroundColor` | `string` | No | Custom background color (e.g., "#1e3a8a") |
|
|
95
|
+
| `textColor` | `string` | No | Custom text color (e.g., "#ffffff") |
|
|
96
|
+
| `onClick` | `(operator: BettingOperator) => void` | No | Click handler (suppresses default navigation) |
|
|
97
|
+
|
|
98
|
+
### `BettingOddsStyles`
|
|
99
|
+
|
|
100
|
+
| Property | Type | Default | Description |
|
|
101
|
+
| --------------- | -------------------------------- | ----------- | ----------------------------------- |
|
|
102
|
+
| `padding` | `"none" \| "sm" \| "md" \| "lg"` | `"md"` | Padding size |
|
|
103
|
+
| `typography` | `"sm" \| "md" \| "lg"` | `"md"` | Typography size |
|
|
104
|
+
| `mode` | `"default" \| "flash"` | `"default"` | Display mode |
|
|
105
|
+
| `flashInterval` | `number` | `3000` | Flash mode interval in milliseconds |
|
|
106
|
+
|
|
107
|
+
## Advanced Examples
|
|
108
|
+
|
|
109
|
+
### Flash Mode
|
|
110
|
+
|
|
111
|
+
Flash mode alternates between showing the operator logo and the odds at a configurable interval:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<BettingOdds
|
|
115
|
+
odds={[
|
|
116
|
+
{ label: "1", value: 2.8 },
|
|
117
|
+
{ label: "X", value: 3.1 },
|
|
118
|
+
{ label: "2", value: 2.5 },
|
|
119
|
+
]}
|
|
120
|
+
operator={{
|
|
121
|
+
name: "Bet365",
|
|
122
|
+
logo: { url: "https://example.com/bet365-logo.png", alt: "Bet365" },
|
|
123
|
+
url: "https://www.bet365.com",
|
|
124
|
+
backgroundColor: "#037b5b",
|
|
125
|
+
}}
|
|
126
|
+
styles={{
|
|
127
|
+
mode: "flash",
|
|
128
|
+
flashInterval: 3000, // Switch every 3 seconds
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Multiple Odds (Grid Layout)
|
|
134
|
+
|
|
135
|
+
When displaying 4 or more odds, the component automatically uses a grid layout with 3 odds per row:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<BettingOdds
|
|
139
|
+
odds={[
|
|
140
|
+
{ label: "1-0", value: 8.5, change: "up" },
|
|
141
|
+
{ label: "2-0", value: 12.0 },
|
|
142
|
+
{ label: "2-1", value: 9.0, change: "down" },
|
|
143
|
+
{ label: "0-0", value: 11.0 },
|
|
144
|
+
{ label: "1-1", value: 6.5, change: "up" },
|
|
145
|
+
]}
|
|
146
|
+
operator={{
|
|
147
|
+
name: "Bet365",
|
|
148
|
+
url: "https://www.bet365.com",
|
|
149
|
+
}}
|
|
150
|
+
label="Correct Score"
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Custom Styling
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<BettingOdds
|
|
158
|
+
odds={[
|
|
159
|
+
{ label: "Over 2.5", value: 1.85 },
|
|
160
|
+
{ label: "Under 2.5", value: 2.05 },
|
|
161
|
+
]}
|
|
162
|
+
operator={{
|
|
163
|
+
name: "Betfair",
|
|
164
|
+
url: "https://www.betfair.com",
|
|
165
|
+
}}
|
|
166
|
+
styles={{
|
|
167
|
+
padding: "lg",
|
|
168
|
+
typography: "lg",
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Custom Click Handlers
|
|
174
|
+
|
|
175
|
+
You can provide custom click handlers to intercept clicks on odds or the operator logo. When provided, these handlers suppress the default navigation behavior:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
<BettingOdds
|
|
179
|
+
odds={[
|
|
180
|
+
{
|
|
181
|
+
label: "1",
|
|
182
|
+
value: 2.8,
|
|
183
|
+
url: "https://www.bet365.com/home-win",
|
|
184
|
+
onClick: (odd) => {
|
|
185
|
+
// Custom tracking or navigation logic
|
|
186
|
+
console.log("Clicked odd:", odd.label, odd.value);
|
|
187
|
+
// Navigate programmatically or trigger analytics
|
|
188
|
+
trackBetClick(odd);
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
label: "X",
|
|
193
|
+
value: 3.1,
|
|
194
|
+
url: "https://www.bet365.com/draw",
|
|
195
|
+
onClick: (odd) => {
|
|
196
|
+
trackBetClick(odd);
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
label: "2",
|
|
201
|
+
value: 2.5,
|
|
202
|
+
url: "https://www.bet365.com/away-win",
|
|
203
|
+
onClick: (odd) => {
|
|
204
|
+
trackBetClick(odd);
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
]}
|
|
208
|
+
operator={{
|
|
209
|
+
name: "Bet365",
|
|
210
|
+
url: "https://www.bet365.com",
|
|
211
|
+
onClick: (operator) => {
|
|
212
|
+
// Custom operator click handling
|
|
213
|
+
console.log("Clicked operator:", operator.name);
|
|
214
|
+
trackOperatorClick(operator);
|
|
215
|
+
},
|
|
216
|
+
}}
|
|
217
|
+
/>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
This is useful for:
|
|
221
|
+
|
|
222
|
+
- Analytics tracking before navigation
|
|
223
|
+
- Custom navigation logic (e.g., opening modals, in-app browsers)
|
|
224
|
+
- Integration with state management
|
|
225
|
+
- Platform-specific deep linking
|
|
226
|
+
|
|
227
|
+
## Styling
|
|
228
|
+
|
|
229
|
+
The component uses Tailwind CSS utility classes and respects your application's theme configuration. Key design tokens used:
|
|
230
|
+
|
|
231
|
+
- `bg-muted` / `bg-muted/50` - Background colors
|
|
232
|
+
- `text-foreground` / `text-muted-foreground` - Text colors
|
|
233
|
+
- `text-green-500` / `text-red-500` - Change indicators
|
|
234
|
+
- Custom colors via `backgroundColor` and `textColor` props
|
|
235
|
+
|
|
236
|
+
## Accessibility
|
|
237
|
+
|
|
238
|
+
- All clickable odds and operator logos are properly linked with `<a>` tags
|
|
239
|
+
- External links include `target="_blank"` and `rel="noopener noreferrer"`
|
|
240
|
+
- Click events include `stopPropagation()` to prevent unwanted bubbling
|
|
241
|
+
- Operator logos include proper `alt` text
|
|
242
|
+
|
|
243
|
+
## Examples
|
|
244
|
+
|
|
245
|
+
For complete working examples, see `src/examples/BettingOddsExamples.tsx` in the repository.
|