@fuzo/soccer-board 0.1.0-alpha.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/README.md ADDED
@@ -0,0 +1,335 @@
1
+ # ⚽ Soccer Board Angular
2
+
3
+ A beautiful, interactive soccer field component for Angular applications. Create tactical formations, manage player positions, and export your field as PNG images. Built with modern Angular features (signals, standalone components) and styled with Tailwind CSS.
4
+
5
+ ![Angular](https://img.shields.io/badge/Angular-21.0+-red.svg)
6
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
7
+ ![npm](https://img.shields.io/badge/npm-package-green.svg)
8
+
9
+ ## ✨ Features
10
+
11
+ - 🎨 **FIFA-compliant field** with accurate dimensions and markings
12
+ - 🔄 **Portrait & Landscape** orientations
13
+ - 👥 **Drag & Drop** players onto the field
14
+ - 🎯 **Tactical position detection** (GK, CB, RB, LB, CDM, CM, CAM, LW, RW, ST, etc.)
15
+ - 📱 **Responsive design** with `fit="contain"` mode
16
+ - 🎨 **Beautiful player cards** with photos, numbers, and positions
17
+ - 📤 **Export to PNG** with high-quality images
18
+ - 🎛️ **Fully configurable** layout and behavior
19
+ - ⚡ **100% Reactive** using Angular signals
20
+ - 🎨 **Tailwind CSS** styling (optional, works without it)
21
+
22
+ ## 📦 Installation
23
+
24
+ ```bash
25
+ npm install fuzo-soccer-board
26
+ ```
27
+
28
+ That's it! The library includes `html2canvas-pro` as a dependency for PNG export functionality (supports modern CSS color functions like `oklab`).
29
+
30
+ ## 🚀 Quick Start
31
+
32
+ ### 1. Import the Component
33
+
34
+ ```typescript
35
+ import { SoccerBoardComponent, SoccerBoardPlayer, SoccerBoardOrientation, SoccerBoardTeamSide } from 'fuzo-soccer-board';
36
+
37
+ @Component({
38
+ selector: 'app-my-component',
39
+ standalone: true,
40
+ imports: [SoccerBoardComponent],
41
+ template: `
42
+ <fuzo-soccer-board
43
+ [players]="players"
44
+ [orientation]="SoccerBoardOrientation.Landscape"
45
+ (playerPositioned)="onPlayerPositioned($event)"
46
+ (imageExported)="onImageExported($event)"
47
+ />
48
+ `,
49
+ })
50
+ export class MyComponent {
51
+ SoccerBoardOrientation = SoccerBoardOrientation;
52
+
53
+ players: SoccerBoardPlayer[] = [
54
+ {
55
+ id: '1',
56
+ name: 'Messi',
57
+ number: 10,
58
+ photo: '/path/to/photo.png',
59
+ preferredPosition: 'ST',
60
+ },
61
+ ];
62
+
63
+ onPlayerPositioned(event: any) {
64
+ console.log('Player positioned:', event);
65
+ }
66
+
67
+ onImageExported(event: { blob: Blob; dataUrl: string }) {
68
+ // Handle exported image
69
+ console.log('Image exported:', event.dataUrl);
70
+ }
71
+ }
72
+ ```
73
+
74
+ ### 2. Basic Usage
75
+
76
+ ```html
77
+ <fuzo-soccer-board
78
+ [players]="players"
79
+ [orientation]="SoccerBoardOrientation.Landscape"
80
+ [showPositions]="true"
81
+ [fit]="SoccerBoardFitMode.Contain"
82
+ />
83
+ ```
84
+
85
+ ## 📚 API Reference
86
+
87
+ ### Inputs
88
+
89
+ | Input | Type | Default | Description |
90
+ |-------|------|---------|-------------|
91
+ | `players` | `SoccerBoardPlayer[]` | `[]` | Array of players to display |
92
+ | `orientation` | `SoccerBoardOrientationType` | `SoccerBoardOrientation.Landscape` | Field orientation: `'portrait'` or `'landscape'` |
93
+ | `teamSide` | `SoccerBoardTeamSideType` | `null` | Show specific side: `SoccerBoardTeamSide.Home`, `SoccerBoardTeamSide.Away`, or `null` (both) |
94
+ | `showPositions` | `boolean` | `false` | Show tactical position zones |
95
+ | `fit` | `SoccerBoardFitModeType` | `null` | Responsive mode: `SoccerBoardFitMode.Contain` or `null` |
96
+ | `showPlayersSidebar` | `boolean` | `true` | Show sidebar with available players |
97
+ | `playersPosition` | `'left' \| 'right' \| 'top' \| 'bottom'` | `'left'` | Position of players sidebar |
98
+ | `playersColumns` | `number` | `2` | Number of columns in players grid |
99
+ | `maxPlayersPerSide` | `number` | `7` | Maximum players allowed per side (HOME/AWAY) |
100
+
101
+ ### Outputs
102
+
103
+ | Output | Event Data | Description |
104
+ |--------|-----------|-------------|
105
+ | `playerPositioned` | `{ playerId: string; fieldX: number; fieldY: number; side: SoccerBoardTeamSide; position: string }` | Emitted when a player is dropped on the field |
106
+ | `playerRemovedFromField` | `{ playerId: string }` | Emitted when a player is removed from the field |
107
+ | `imageExported` | `{ blob: Blob; dataUrl: string }` | Emitted when field is exported to PNG |
108
+
109
+ ### Public Methods
110
+
111
+ | Method | Parameters | Returns | Description |
112
+ |--------|-----------|---------|-------------|
113
+ | `exportToPNG()` | - | `Promise<void>` | Exports the field as PNG image (also triggers `imageExported` event) |
114
+
115
+ ### Player Model
116
+
117
+ ```typescript
118
+ interface SoccerBoardPlayer {
119
+ id: string; // Unique identifier
120
+ name: string; // Player name
121
+ number?: number; // Jersey number
122
+ photo?: string; // Photo URL
123
+ preferredPosition?: string; // Natural position (shown when off field)
124
+ fieldPosition?: string; // Position on field (set when placed)
125
+ fieldX?: number; // Normalized X coordinate (0-100)
126
+ fieldY?: number; // Normalized Y coordinate (0-100)
127
+ side?: SoccerBoardTeamSide; // Which half: SoccerBoardTeamSide.Home or SoccerBoardTeamSide.Away
128
+ }
129
+ ```
130
+
131
+ ### Enums & Types
132
+
133
+ ```typescript
134
+ enum SoccerBoardOrientation {
135
+ Portrait = 'portrait',
136
+ Landscape = 'landscape',
137
+ }
138
+
139
+ enum SoccerBoardTeamSide {
140
+ Home = 'home',
141
+ Away = 'away',
142
+ }
143
+
144
+ enum SoccerBoardFitMode {
145
+ Contain = 'contain',
146
+ }
147
+
148
+ enum SoccerBoardPlayerSize {
149
+ Small = 'small',
150
+ Medium = 'medium',
151
+ Large = 'large',
152
+ }
153
+ ```
154
+
155
+ ## 🎯 Examples
156
+
157
+ ### Basic Field
158
+
159
+ ```html
160
+ <fuzo-soccer-board [players]="players" />
161
+ ```
162
+
163
+ ### With Position Zones
164
+
165
+ ```html
166
+ <fuzo-soccer-board
167
+ [players]="players"
168
+ [showPositions]="true"
169
+ />
170
+ ```
171
+
172
+ ### Responsive Field
173
+
174
+ ```html
175
+ <fuzo-soccer-board
176
+ [players]="players"
177
+ [fit]="SoccerBoardFitMode.Contain"
178
+ />
179
+ ```
180
+
181
+ ### Custom Layout
182
+
183
+ ```html
184
+ <fuzo-soccer-board
185
+ [players]="players"
186
+ [showPlayersSidebar]="true"
187
+ [playersPosition]="'right'"
188
+ [playersColumns]="3"
189
+ />
190
+ ```
191
+ ```
192
+
193
+ ### Handle Player Positioning
194
+
195
+ ```typescript
196
+ onPlayerPositioned(event: {
197
+ playerId: string;
198
+ fieldX: number;
199
+ fieldY: number;
200
+ side: SoccerBoardTeamSide;
201
+ position: string;
202
+ }) {
203
+ // Update player in your data
204
+ const player = this.players.find(p => p.id === event.playerId);
205
+ if (player) {
206
+ player.fieldX = event.fieldX;
207
+ player.fieldY = event.fieldY;
208
+ player.side = event.side;
209
+ player.fieldPosition = event.position;
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### Export to PNG
215
+
216
+ ```typescript
217
+ // Method 1: Call the method directly
218
+ @ViewChild(SoccerBoardComponent) soccerBoard!: SoccerBoardComponent;
219
+
220
+ async exportField() {
221
+ await this.soccerBoard.exportToPNG();
222
+ }
223
+
224
+ // Method 2: Handle the event
225
+ onImageExported(event: { blob: Blob; dataUrl: string }) {
226
+ // Use blob for upload
227
+ const formData = new FormData();
228
+ formData.append('image', event.blob, 'soccer-field.png');
229
+
230
+ // Or use dataUrl for display/preview
231
+ this.imagePreview = event.dataUrl;
232
+
233
+ // Or send via email, etc.
234
+ this.sendEmailWithImage(event.dataUrl);
235
+ }
236
+ ```
237
+
238
+ ### Custom Styling
239
+
240
+ You can customize the field using CSS custom properties:
241
+
242
+ ```css
243
+ fuzo-soccer-board {
244
+ --soccer-board-width: 800px;
245
+ --soccer-board-max-width: 1200px;
246
+ --soccer-board-padding: 20px;
247
+ }
248
+ ```
249
+
250
+ ## 🎨 Styling
251
+
252
+ The component uses Tailwind CSS for player cards, but the field itself is styled with pure CSS/SCSS. If you're using Tailwind in your project, the player cards will automatically use your Tailwind configuration.
253
+
254
+ ### Without Tailwind
255
+
256
+ The component works without Tailwind CSS, but player cards will have minimal styling. You can add your own styles:
257
+
258
+ ```css
259
+ lib-soccer-board-player {
260
+ /* Your custom styles */
261
+ }
262
+ ```
263
+
264
+ ## 🔧 Advanced Usage
265
+
266
+ ### Programmatic Player Positioning
267
+
268
+ ```typescript
269
+ // Set player position programmatically
270
+ const player = this.players.find(p => p.id === '1');
271
+ if (player) {
272
+ player.fieldX = 50; // Center X
273
+ player.fieldY = 50; // Center Y
274
+ player.side = SoccerBoardTeamSide.Home;
275
+ player.fieldPosition = 'CM';
276
+ }
277
+ ```
278
+
279
+ ### Filter Players by Side
280
+
281
+ ```typescript
282
+ // Get only players on HOME side
283
+ const homePlayers = this.players.filter(
284
+ p => p.side === SoccerBoardTeamSide.Home && p.fieldX !== undefined
285
+ );
286
+ ```
287
+
288
+ ### Remove Player from Field
289
+
290
+ ```typescript
291
+ removePlayer(playerId: string) {
292
+ const player = this.players.find(p => p.id === playerId);
293
+ if (player) {
294
+ player.fieldX = undefined;
295
+ player.fieldY = undefined;
296
+ player.side = undefined;
297
+ player.fieldPosition = undefined;
298
+ }
299
+ }
300
+ ```
301
+
302
+ ## 🐛 Troubleshooting
303
+
304
+ ### Images not loading
305
+
306
+ Make sure player photos are accessible and CORS is configured correctly if loading from external sources.
307
+
308
+ ### Export not working
309
+
310
+ - Ensure `html2canvas-pro` is installed
311
+ - Check browser console for errors
312
+ - Make sure images are loaded before exporting
313
+
314
+ ### Field not responsive
315
+
316
+ - Use `[fit]="SoccerBoardFitMode.Contain"` for responsive behavior
317
+ - Ensure parent container has defined dimensions
318
+
319
+ ## 🤝 Contributing
320
+
321
+ Contributions are welcome! Please feel free to submit a Pull Request.
322
+
323
+ ## 📝 License
324
+
325
+ MIT License - see LICENSE file for details
326
+
327
+ ## 🙏 Acknowledgments
328
+
329
+ - Built with [Angular](https://angular.io/)
330
+ - Field dimensions based on FIFA regulations
331
+ - Export functionality powered by [html2canvas-pro](https://www.npmjs.com/package/html2canvas-pro)
332
+
333
+ ---
334
+
335
+ Made with ⚽ by the community