react-native-football-formation 1.0.0

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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +447 -0
  3. package/dist/assets/index.d.ts +13 -0
  4. package/dist/assets/index.d.ts.map +1 -0
  5. package/dist/assets/index.js +15 -0
  6. package/dist/components/FormationField.d.ts +5 -0
  7. package/dist/components/FormationField.d.ts.map +1 -0
  8. package/dist/components/FormationField.js +147 -0
  9. package/dist/components/PlayerCard.d.ts +5 -0
  10. package/dist/components/PlayerCard.d.ts.map +1 -0
  11. package/dist/components/PlayerCard.js +365 -0
  12. package/dist/components/index.d.ts +3 -0
  13. package/dist/components/index.d.ts.map +1 -0
  14. package/dist/components/index.js +10 -0
  15. package/dist/index.d.ts +11 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +35 -0
  18. package/dist/theme/defaultTheme.d.ts +12 -0
  19. package/dist/theme/defaultTheme.d.ts.map +1 -0
  20. package/dist/theme/defaultTheme.js +70 -0
  21. package/dist/theme/index.d.ts +2 -0
  22. package/dist/theme/index.d.ts.map +1 -0
  23. package/dist/theme/index.js +17 -0
  24. package/dist/types/component.types.d.ts +48 -0
  25. package/dist/types/component.types.d.ts.map +1 -0
  26. package/dist/types/component.types.js +2 -0
  27. package/dist/types/formation.types.d.ts +59 -0
  28. package/dist/types/formation.types.d.ts.map +1 -0
  29. package/dist/types/formation.types.js +5 -0
  30. package/dist/types/index.d.ts +4 -0
  31. package/dist/types/index.d.ts.map +1 -0
  32. package/dist/types/index.js +19 -0
  33. package/dist/types/theme.types.d.ts +44 -0
  34. package/dist/types/theme.types.d.ts.map +1 -0
  35. package/dist/types/theme.types.js +5 -0
  36. package/dist/utils/formationCoordinates.d.ts +10 -0
  37. package/dist/utils/formationCoordinates.d.ts.map +1 -0
  38. package/dist/utils/formationCoordinates.js +311 -0
  39. package/dist/utils/index.d.ts +3 -0
  40. package/dist/utils/index.d.ts.map +1 -0
  41. package/dist/utils/index.js +18 -0
  42. package/dist/utils/transformLineup.d.ts +21 -0
  43. package/dist/utils/transformLineup.d.ts.map +1 -0
  44. package/dist/utils/transformLineup.js +126 -0
  45. package/package.json +60 -0
  46. package/src/assets/images/field.png +0 -0
  47. package/src/assets/images/football.png +0 -0
  48. package/src/assets/images/kicker.png +0 -0
  49. package/src/assets/images/ownGoals.png +0 -0
  50. package/src/assets/images/playerPlaceholder.png +0 -0
  51. package/src/assets/images/renewal.png +0 -0
  52. package/src/assets/index.ts +12 -0
  53. package/src/components/FormationField.tsx +182 -0
  54. package/src/components/PlayerCard.tsx +452 -0
  55. package/src/components/index.ts +2 -0
  56. package/src/index.ts +20 -0
  57. package/src/theme/defaultTheme.ts +74 -0
  58. package/src/theme/index.ts +1 -0
  59. package/src/types/component.types.ts +72 -0
  60. package/src/types/formation.types.ts +88 -0
  61. package/src/types/index.ts +3 -0
  62. package/src/types/theme.types.ts +48 -0
  63. package/src/utils/formationCoordinates.ts +335 -0
  64. package/src/utils/index.ts +2 -0
  65. package/src/utils/transformLineup.ts +158 -0
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default image assets for the Formation Field component
3
+ * Users can override these by passing custom ImageSourcePropType to the component
4
+ */
5
+ export const defaultAssets = {
6
+ field: require('./images/field.png'),
7
+ football: require('./images/football.png'),
8
+ kicker: require('./images/kicker.png'),
9
+ renewal: require('./images/renewal.png'),
10
+ ownGoals: require('./images/ownGoals.png'),
11
+ playerPlaceholder: require('./images/playerPlaceholder.png'),
12
+ };
@@ -0,0 +1,182 @@
1
+ import React, { useMemo } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ ImageBackground,
7
+ Dimensions,
8
+ I18nManager,
9
+ Platform,
10
+ } from 'react-native';
11
+ import { FormationFieldProps } from '../types';
12
+ import { transformLineupByFormationPlace } from '../utils';
13
+ import { mergeTheme } from '../theme';
14
+ import { defaultAssets } from '../assets';
15
+ import PlayerCard from './PlayerCard';
16
+
17
+ const { width: screenWidth } = Dimensions.get('window');
18
+
19
+ const FormationField: React.FC<FormationFieldProps> = ({
20
+ lineup,
21
+ width = screenWidth + 10,
22
+ height = 395,
23
+ theme: customTheme,
24
+ fieldImage = defaultAssets.field,
25
+ playerPlaceholder,
26
+ footballIcon,
27
+ kickerIcon,
28
+ renewalIcon,
29
+ ownGoalIcon,
30
+ logoImage,
31
+ renderPlayerCard,
32
+ renderFooter,
33
+ onPlayerPress,
34
+ showLogo = false,
35
+ showFormation = true,
36
+ showRating = false,
37
+ containerStyle,
38
+ playerCardStyle,
39
+ playerNameStyle,
40
+ getPlayerPhotoUrl,
41
+ }) => {
42
+ const theme = mergeTheme(customTheme);
43
+
44
+ const lineupTransformed = useMemo(() => {
45
+ if (!lineup) return [];
46
+
47
+ const formation = lineup?.formationUsed?.split('').join('-') || '4-3-3';
48
+ return transformLineupByFormationPlace(lineup, formation, getPlayerPhotoUrl);
49
+ }, [lineup, getPlayerPhotoUrl]);
50
+
51
+ const formationDisplay = lineup?.formationUsed?.split('').join('-') || '4-3-3';
52
+
53
+ return (
54
+ <ImageBackground
55
+ source={fieldImage}
56
+ style={[
57
+ styles.footballField,
58
+ {
59
+ width,
60
+ height,
61
+ },
62
+ containerStyle,
63
+ ]}
64
+ resizeMode="cover">
65
+ {/* Players */}
66
+ {!!lineupTransformed?.length &&
67
+ lineupTransformed?.map(player => {
68
+ if (renderPlayerCard) {
69
+ return (
70
+ <View key={player.playerId}>
71
+ {renderPlayerCard(player, width, height)}
72
+ </View>
73
+ );
74
+ }
75
+
76
+ return (
77
+ <PlayerCard
78
+ key={player.playerId}
79
+ player={player}
80
+ fieldWidth={width}
81
+ fieldHeight={height}
82
+ theme={theme}
83
+ onPress={onPlayerPress}
84
+ style={playerCardStyle}
85
+ nameStyle={playerNameStyle}
86
+ playerPlaceholder={playerPlaceholder}
87
+ footballIcon={footballIcon}
88
+ kickerIcon={kickerIcon}
89
+ renewalIcon={renewalIcon}
90
+ ownGoalIcon={ownGoalIcon}
91
+ />
92
+ );
93
+ })}
94
+
95
+ {/* Footer Section */}
96
+ {renderFooter ? (
97
+ <View style={styles.fieldBottomWrapper}>
98
+ {renderFooter(formationDisplay)}
99
+ </View>
100
+ ) : (
101
+ <View style={styles.fieldBottomWrapper}>
102
+ <View
103
+ style={[
104
+ styles.fieldBottomContainer,
105
+ {
106
+ flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
107
+ },
108
+ ]}>
109
+ {/* Formation Display */}
110
+ {showFormation && (
111
+ <View style={{ justifyContent: 'center' }}>
112
+ <View
113
+ style={[
114
+ styles.formationUsedContainer,
115
+ { backgroundColor: theme.colors.formationBadge },
116
+ ]}>
117
+ <Text
118
+ style={[
119
+ styles.formationText,
120
+ {
121
+ fontSize: theme.typography.formationSize,
122
+ fontFamily: theme.typography.fontFamilyBold,
123
+ color: theme.colors.white,
124
+ },
125
+ ]}>
126
+ {formationDisplay}
127
+ </Text>
128
+ </View>
129
+ </View>
130
+ )}
131
+
132
+ {/* Optional Logo */}
133
+ {showLogo && logoImage && (
134
+ <View style={styles.logoImageContainer}>
135
+ <ImageBackground source={logoImage} style={styles.logoImage} />
136
+ </View>
137
+ )}
138
+ </View>
139
+ </View>
140
+ )}
141
+ </ImageBackground>
142
+ );
143
+ };
144
+
145
+ const styles = StyleSheet.create({
146
+ footballField: {
147
+ position: 'relative',
148
+ marginTop: 20,
149
+ },
150
+ fieldBottomWrapper: {
151
+ height: 40,
152
+ position: 'absolute',
153
+ bottom: Platform.OS === 'ios' ? 4 : 5,
154
+ },
155
+ fieldBottomContainer: {
156
+ paddingHorizontal: 10,
157
+ width: '100%',
158
+ justifyContent: 'space-between',
159
+ alignItems: 'center',
160
+ },
161
+ formationUsedContainer: {
162
+ minWidth: 60,
163
+ minHeight: 20,
164
+ borderRadius: 99,
165
+ justifyContent: 'center',
166
+ alignItems: 'center',
167
+ paddingHorizontal: 12,
168
+ paddingVertical: 4,
169
+ },
170
+ formationText: {
171
+ includeFontPadding: false,
172
+ },
173
+ logoImageContainer: {
174
+ justifyContent: 'center',
175
+ },
176
+ logoImage: {
177
+ width: 64,
178
+ height: 60,
179
+ },
180
+ });
181
+
182
+ export default React.memo(FormationField);
@@ -0,0 +1,452 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ Image,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ I18nManager,
9
+ Platform,
10
+ } from 'react-native';
11
+ import { PlayerCardProps } from '../types';
12
+ import { defaultAssets } from '../assets';
13
+
14
+ const PlayerCard: React.FC<PlayerCardProps> = ({
15
+ player,
16
+ fieldWidth,
17
+ fieldHeight,
18
+ theme,
19
+ onPress,
20
+ style,
21
+ nameStyle,
22
+ playerPlaceholder = defaultAssets.playerPlaceholder,
23
+ footballIcon = defaultAssets.football,
24
+ kickerIcon = defaultAssets.kicker,
25
+ renewalIcon = defaultAssets.renewal,
26
+ ownGoalIcon = defaultAssets.ownGoals,
27
+ }) => {
28
+ const getFirst10Chars = (str: string) => {
29
+ const trimmed = [...str].slice(0, 10).join('');
30
+ return str?.length > 10 ? trimmed + '…' : trimmed;
31
+ };
32
+
33
+ const playerX = (Number(player?.x) / 100) * fieldWidth - theme.spacing.playerCardWidth / 2;
34
+ const playerY = (Number(player?.y) / 100) * fieldHeight - theme.spacing.playerCardHeight / 2;
35
+
36
+ const cardContent = (
37
+ <View
38
+ style={[
39
+ styles.playerCard,
40
+ {
41
+ left: playerX,
42
+ top: playerY,
43
+ width: theme.spacing.playerCardWidth,
44
+ height: theme.spacing.playerCardHeight,
45
+ },
46
+ style,
47
+ ]}>
48
+ <View
49
+ style={[
50
+ styles.playerImageContainer,
51
+ {
52
+ width: theme.spacing.playerImageSize,
53
+ height: theme.spacing.playerImageSize,
54
+ borderRadius: theme.borderRadius.playerImage,
55
+ borderColor: theme.colors.white,
56
+ },
57
+ ]}>
58
+ <Image
59
+ source={player?.photo ? { uri: player.photo } : playerPlaceholder}
60
+ style={[
61
+ styles.playerImage,
62
+ { borderRadius: theme.borderRadius.playerImage },
63
+ ]}
64
+ />
65
+
66
+ {/* Jersey Number */}
67
+ <View
68
+ style={[
69
+ styles.jerseyNumber,
70
+ I18nManager.isRTL ? { right: -5 } : { left: -5 },
71
+ ]}>
72
+ <View
73
+ style={[
74
+ styles.jerseyNumberWrapper,
75
+ {
76
+ width: theme.spacing.jerseyNumberSize,
77
+ height: theme.spacing.jerseyNumberSize,
78
+ borderRadius: theme.spacing.jerseyNumberSize,
79
+ backgroundColor: theme.colors.white,
80
+ borderColor: theme.colors.border,
81
+ },
82
+ ]}>
83
+ <Text
84
+ style={[
85
+ styles.jerseyText,
86
+ {
87
+ fontSize: theme.typography.jerseyNumberSize,
88
+ color: theme.colors.text,
89
+ fontFamily: theme.typography.fontFamily,
90
+ },
91
+ ]}>
92
+ {player?.shirtNumber}
93
+ </Text>
94
+ </View>
95
+ </View>
96
+
97
+ {/* Goal Assist Icon */}
98
+ {player?.isGoalAssist && (
99
+ <View
100
+ style={[
101
+ styles.assistIconWrapper,
102
+ I18nManager.isRTL ? { right: -8 } : { left: -8 },
103
+ ]}>
104
+ <Image
105
+ source={kickerIcon}
106
+ style={[
107
+ styles.assistIcon,
108
+ {
109
+ width: theme.spacing.iconSize,
110
+ height: theme.spacing.iconSize,
111
+ borderRadius: theme.spacing.iconSize,
112
+ },
113
+ ]}
114
+ />
115
+ </View>
116
+ )}
117
+
118
+ {/* Own Goals */}
119
+ {player?.isOwnGoal && (
120
+ <View
121
+ style={[
122
+ styles.ownGoalsWrapper,
123
+ I18nManager.isRTL ? { right: -4 } : { left: -4 },
124
+ ]}>
125
+ <View
126
+ style={[
127
+ styles.ownGoalsContainer,
128
+ {
129
+ height: theme.spacing.iconSize,
130
+ borderRadius: theme.spacing.iconSize,
131
+ backgroundColor: theme.colors.white,
132
+ borderColor: theme.colors.error,
133
+ },
134
+ Number(player?.ownGoals) > 1 && I18nManager.isRTL
135
+ ? { paddingLeft: 3 }
136
+ : { paddingRight: 3 },
137
+ ]}>
138
+ <View
139
+ style={{
140
+ flexDirection: I18nManager.isRTL ? 'row' : 'row-reverse',
141
+ }}>
142
+ {Number(player?.ownGoals) > 1 && (
143
+ <View
144
+ style={[
145
+ I18nManager.isRTL ? { paddingRight: 2 } : { paddingLeft: 2 },
146
+ ]}>
147
+ <Text
148
+ style={[
149
+ styles.goalsText,
150
+ {
151
+ fontSize: theme.typography.goalCountSize,
152
+ color: theme.colors.newError,
153
+ fontFamily: theme.typography.fontFamilyBold,
154
+ },
155
+ ]}>
156
+ {player?.ownGoals}
157
+ </Text>
158
+ </View>
159
+ )}
160
+ <Image
161
+ source={ownGoalIcon}
162
+ style={[
163
+ styles.assistIcon,
164
+ {
165
+ width: theme.spacing.iconSize,
166
+ height: theme.spacing.iconSize,
167
+ borderRadius: theme.spacing.iconSize,
168
+ },
169
+ ]}
170
+ />
171
+ </View>
172
+ </View>
173
+ </View>
174
+ )}
175
+
176
+ {/* Goals Scored */}
177
+ {player?.isScorer && (
178
+ <View
179
+ style={[
180
+ styles.footballIconWrapper,
181
+ I18nManager.isRTL
182
+ ? { left: Number(player?.goals) > 1 ? -10 : -2.8 }
183
+ : { right: Number(player?.goals) > 1 ? -10 : -2.8 },
184
+ ]}>
185
+ <View
186
+ style={[
187
+ styles.goalsContainer,
188
+ {
189
+ height: theme.spacing.iconSize,
190
+ borderRadius: theme.spacing.iconSize,
191
+ borderColor: theme.colors.success,
192
+ backgroundColor: theme.colors.white,
193
+ },
194
+ Number(player?.goals) > 1 && I18nManager.isRTL
195
+ ? { paddingRight: 3 }
196
+ : { paddingLeft: 3 },
197
+ ]}>
198
+ <View
199
+ style={{
200
+ flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
201
+ }}>
202
+ {Number(player?.goals) > 1 && (
203
+ <View
204
+ style={[
205
+ I18nManager.isRTL ? { paddingLeft: 2 } : { paddingRight: 2 },
206
+ ]}>
207
+ <Text
208
+ style={[
209
+ styles.goalsText,
210
+ {
211
+ fontSize: theme.typography.goalCountSize,
212
+ color: theme.colors.text,
213
+ fontFamily: theme.typography.fontFamilyBold,
214
+ },
215
+ ]}>
216
+ {player?.goals}
217
+ </Text>
218
+ </View>
219
+ )}
220
+ <Image
221
+ source={footballIcon}
222
+ style={[
223
+ styles.footballIcon,
224
+ {
225
+ width: theme.spacing.iconSize,
226
+ height: theme.spacing.iconSize,
227
+ borderRadius: theme.spacing.iconSize,
228
+ },
229
+ ]}
230
+ />
231
+ </View>
232
+ </View>
233
+ </View>
234
+ )}
235
+
236
+ {/* Yellow Card */}
237
+ {player?.isYellowCard && (
238
+ <View
239
+ style={[
240
+ styles.yellowCardWrapper,
241
+ I18nManager.isRTL ? { left: -8 } : { right: -8 },
242
+ ]}>
243
+ <View
244
+ style={[
245
+ styles.yellowCardContainer,
246
+ {
247
+ width: theme.spacing.iconSize,
248
+ height: theme.spacing.iconSize,
249
+ borderRadius: theme.spacing.iconSize,
250
+ backgroundColor: theme.colors.white,
251
+ borderColor: theme.colors.border,
252
+ },
253
+ ]}>
254
+ <View
255
+ style={[
256
+ styles.yellowCard,
257
+ {
258
+ borderRadius: theme.borderRadius.card,
259
+ backgroundColor: theme.colors.warning,
260
+ },
261
+ ]}
262
+ />
263
+ </View>
264
+ </View>
265
+ )}
266
+
267
+ {/* Red Card */}
268
+ {player?.isRedCard && (
269
+ <View
270
+ style={[
271
+ styles.redCardWrapper,
272
+ I18nManager.isRTL ? { left: -8 } : { right: -8 },
273
+ ]}>
274
+ <View
275
+ style={[
276
+ styles.redCardContainer,
277
+ {
278
+ width: theme.spacing.iconSize,
279
+ height: theme.spacing.iconSize,
280
+ borderRadius: theme.spacing.iconSize,
281
+ backgroundColor: theme.colors.white,
282
+ borderColor: theme.colors.border,
283
+ },
284
+ ]}>
285
+ <View
286
+ style={[
287
+ styles.redCard,
288
+ {
289
+ borderRadius: theme.borderRadius.card,
290
+ backgroundColor: theme.colors.error,
291
+ },
292
+ ]}
293
+ />
294
+ </View>
295
+ </View>
296
+ )}
297
+
298
+ {/* Substitute Icon */}
299
+ {player?.isSubstitute && (
300
+ <View
301
+ style={[
302
+ styles.playerSubstituteWrapper,
303
+ I18nManager.isRTL ? { left: -4 } : { right: -4 },
304
+ ]}>
305
+ <View
306
+ style={[
307
+ styles.playerSubstituteContainer,
308
+ {
309
+ width: theme.spacing.iconSize,
310
+ height: theme.spacing.iconSize,
311
+ backgroundColor: theme.colors.white,
312
+ borderRadius: theme.spacing.iconSize,
313
+ },
314
+ ]}>
315
+ <Image
316
+ source={renewalIcon}
317
+ style={styles.playerSubstituteIcon}
318
+ />
319
+ </View>
320
+ </View>
321
+ )}
322
+ </View>
323
+
324
+ <Text
325
+ numberOfLines={1}
326
+ style={[
327
+ styles.playerName,
328
+ {
329
+ fontSize: theme.typography.playerNameSize,
330
+ color: theme.colors.text,
331
+ fontFamily: theme.typography.fontFamily,
332
+ },
333
+ nameStyle,
334
+ ]}>
335
+ {getFirst10Chars(player?.matchName)}
336
+ </Text>
337
+ </View>
338
+ );
339
+
340
+ if (onPress) {
341
+ return (
342
+ <TouchableOpacity onPress={() => onPress(player)} activeOpacity={0.7}>
343
+ {cardContent}
344
+ </TouchableOpacity>
345
+ );
346
+ }
347
+
348
+ return cardContent;
349
+ };
350
+
351
+ const styles = StyleSheet.create({
352
+ playerCard: {
353
+ position: 'absolute',
354
+ borderRadius: 50,
355
+ alignItems: 'center',
356
+ justifyContent: 'center',
357
+ },
358
+ playerImageContainer: {
359
+ borderWidth: 1,
360
+ },
361
+ playerImage: {
362
+ width: '100%',
363
+ height: '100%',
364
+ resizeMode: 'cover',
365
+ },
366
+ jerseyNumber: {
367
+ position: 'absolute',
368
+ top: -4,
369
+ zIndex: 3,
370
+ },
371
+ jerseyNumberWrapper: {
372
+ borderWidth: 0.3,
373
+ alignItems: 'center',
374
+ justifyContent: 'center',
375
+ },
376
+ jerseyText: {
377
+ includeFontPadding: false,
378
+ },
379
+ assistIconWrapper: {
380
+ position: 'absolute',
381
+ top: 16,
382
+ zIndex: 3,
383
+ },
384
+ assistIcon: {},
385
+ ownGoalsWrapper: {
386
+ position: 'absolute',
387
+ bottom: -1,
388
+ zIndex: 3,
389
+ },
390
+ ownGoalsContainer: {
391
+ borderWidth: 0.3,
392
+ alignItems: 'center',
393
+ justifyContent: 'center',
394
+ },
395
+ footballIconWrapper: {
396
+ position: 'absolute',
397
+ top: -3,
398
+ },
399
+ goalsContainer: {
400
+ borderWidth: 0.3,
401
+ justifyContent: 'center',
402
+ alignItems: 'center',
403
+ },
404
+ goalsText: {
405
+ includeFontPadding: false,
406
+ lineHeight: 13.8,
407
+ },
408
+ footballIcon: {},
409
+ yellowCardWrapper: {
410
+ position: 'absolute',
411
+ top: 16,
412
+ },
413
+ yellowCardContainer: {
414
+ borderWidth: 0.3,
415
+ justifyContent: 'center',
416
+ alignItems: 'center',
417
+ },
418
+ yellowCard: {
419
+ width: 6,
420
+ height: 8,
421
+ },
422
+ redCardWrapper: {
423
+ position: 'absolute',
424
+ top: 16,
425
+ },
426
+ redCardContainer: {
427
+ borderWidth: 0.3,
428
+ justifyContent: 'center',
429
+ alignItems: 'center',
430
+ },
431
+ redCard: {
432
+ width: 6,
433
+ height: 8,
434
+ },
435
+ playerSubstituteWrapper: {
436
+ position: 'absolute',
437
+ bottom: -1,
438
+ },
439
+ playerSubstituteContainer: {
440
+ justifyContent: 'center',
441
+ alignItems: 'center',
442
+ },
443
+ playerSubstituteIcon: {
444
+ width: 7.6,
445
+ height: 7.6,
446
+ },
447
+ playerName: {
448
+ marginTop: 2,
449
+ },
450
+ });
451
+
452
+ export default React.memo(PlayerCard);
@@ -0,0 +1,2 @@
1
+ export { default as FormationField } from './FormationField';
2
+ export { default as PlayerCard } from './PlayerCard';
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * React Native Football Formation
3
+ * A highly customizable component for displaying football/soccer team formations
4
+ * @author Arbab Rafiq
5
+ */
6
+
7
+ // Main Components
8
+ export { FormationField, PlayerCard } from './components';
9
+
10
+ // Types
11
+ export * from './types';
12
+
13
+ // Utilities
14
+ export * from './utils';
15
+
16
+ // Theme
17
+ export * from './theme';
18
+
19
+ // Assets
20
+ export { defaultAssets } from './assets';