@maestro_io/maestro-web-sdk 2.1.2 → 2.1.3
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/dist/components/atoms/Rive/index.d.ts +1 -0
- package/dist/components/atoms/Rive/index.js +3 -2
- package/dist/components/molecules/ActionButton/ActionButton.d.ts +1 -0
- package/dist/components/molecules/PanelNavButton/PanelNavButton.d.ts +1 -0
- package/dist/components/organisms/PanelNavigation/PanelNavigation.js +5 -6
- package/dist/components/organisms/SegmentController/SegmentController.d.ts +2 -2
- package/dist/components/organisms/SegmentController/SegmentController.js +5 -6
- package/dist/external/spatial-navigation/index.d.ts +2 -3
- package/dist/external/spatial-navigation/index.js +1 -2
- package/dist/external/spatial-navigation/spatialNavigation.d.ts +2 -4
- package/dist/external/spatial-navigation/spatialNavigation.js +25 -36
- package/dist/external/spatial-navigation/withFocusable.d.ts +1 -0
- package/dist/external/spatial-navigation/withFocusable.js +17 -10
- package/dist/maestro-web-sdk.umd.js +3 -3
- package/dist/maestro-web-sdk.umd.js.map +1 -1
- package/dist/modules/bets/view/HotProps/HotPropsView.d.ts +7 -6
- package/dist/modules/bets/view/HotProps/HotPropsView.js +11 -11
- package/dist/modules/bets/view/SixPack/SixPackView.d.ts +2 -2
- package/dist/modules/bets/view/SixPack/SixPackView.js +4 -4
- package/dist/modules/bets/view/TabLoadFailureView.d.ts +1 -0
- package/dist/modules/bets/view/UserBets/BetsCard/BetsCard.d.ts +7 -6
- package/dist/modules/bets/view/UserBets/BetsCard/BetsCard.js +10 -10
- package/dist/modules/bets/view/components/Bet/Bet.d.ts +1 -0
- package/dist/modules/bets/view/components/PromoCodeBanner/PromoCodeBanner.d.ts +2 -2
- package/dist/modules/bets/view/components/PromoCodeBanner/PromoCodeBanner.js +4 -4
- package/dist/modules/bets/view-model/BetsViewModel.js +2 -2
- package/dist/modules/key-plays/view/KeyPlayCardView.d.ts +0 -4
- package/dist/modules/key-plays/view/KeyPlayCardView.js +4 -26
- package/dist/modules/key-plays/view/KeyPlaysView.js +1 -2
- package/dist/modules/stats/interfaces/IStats.d.ts +6 -3
- package/dist/modules/stats/mocks.d.ts +80 -0
- package/dist/modules/stats/mocks.js +140 -0
- package/dist/modules/stats/utils/renderTeamStatistic.d.ts +21 -0
- package/dist/modules/stats/utils/renderTeamStatistic.js +16 -0
- package/dist/modules/stats/view/ActiveAthletes/ActiveAthletes.d.ts +34 -0
- package/dist/modules/stats/view/ActiveAthletes/ActiveAthletes.js +78 -0
- package/dist/modules/stats/view/ActiveAthletes/CollapsedAthletes/CollapsedAthletes.d.ts +5 -0
- package/dist/modules/stats/view/ActiveAthletes/CollapsedAthletes/CollapsedAthletes.js +27 -0
- package/dist/modules/stats/view/ActiveAthletes/ExpandedAthletes/ExpandedAthletes.d.ts +10 -0
- package/dist/modules/stats/view/ActiveAthletes/ExpandedAthletes/ExpandedAthletes.js +15 -0
- package/dist/modules/stats/view/ActiveAthletes/index.d.ts +2 -0
- package/dist/modules/stats/view/ActiveAthletes/index.js +2 -0
- package/dist/modules/stats/view/GameLeaders/GameLeaders.d.ts +2 -23
- package/dist/modules/stats/view/GameLeaders/GameLeaders.js +1 -1
- package/dist/modules/stats/view/MatchPredictor/MatchPredictorView.d.ts +1 -0
- package/dist/modules/stats/view/RecentGames/RecentGames.d.ts +1 -0
- package/dist/modules/stats/view/StatsQRCode/StatsQRCodeView.d.ts +1 -0
- package/dist/modules/stats/view/StatsView.d.ts +1 -1
- package/dist/modules/stats/view/StatsView.js +31 -11
- package/dist/modules/stats/view/TeamStats/TeamStatsView.d.ts +54 -0
- package/dist/modules/stats/view/TeamStats/TeamStatsView.js +69 -0
- package/dist/modules/stats/view/TeamStats/index.d.ts +2 -0
- package/dist/modules/stats/view/TeamStats/index.js +1 -0
- package/dist/modules/stats/view/Teams/StatsProgressBar.js +7 -3
- package/dist/modules/stats/view/Teams/Teams.d.ts +1 -0
- package/dist/modules/stats/view/Teams/Teams.js +15 -8
- package/dist/modules/stats/view/WinProbability/WinProbability.d.ts +1 -0
- package/dist/modules/stats/view/components/AthletesStatistics/AthletesStatistics.d.ts +19 -3
- package/dist/modules/stats/view/components/AthletesStatistics/AthletesStatistics.js +7 -6
- package/dist/modules/stats/view/components/AthletesStatistics/StatisticTable.d.ts +2 -8
- package/dist/modules/stats/view/components/AthletesStatistics/StatisticTable.js +4 -3
- package/dist/modules/stats/view/index.d.ts +1 -0
- package/dist/modules/stats/view/index.js +1 -0
- package/dist/services/StatsService.d.ts +36 -0
- package/dist/services/StatsService.js +68 -0
- package/dist/view-models/MaestroEventViewModel.d.ts +7 -0
- package/dist/view-models/MaestroEventViewModel.js +18 -6
- package/package.json +1 -1
|
@@ -544,3 +544,143 @@ export const MOCK_STATS_API_PRE_GAME_RESPONSE = {
|
|
|
544
544
|
gameState: 'pre',
|
|
545
545
|
},
|
|
546
546
|
};
|
|
547
|
+
export const mockTeamWithStatistics = {
|
|
548
|
+
id: '12',
|
|
549
|
+
uid: 's:20~l:28~t:12',
|
|
550
|
+
abbreviation: 'KC',
|
|
551
|
+
displayName: 'Kansas City Chiefs',
|
|
552
|
+
color: 'e31837',
|
|
553
|
+
logo: 'https://a.espncdn.com/i/teamlogos/nfl/500/kc.png',
|
|
554
|
+
homeAway: 'home',
|
|
555
|
+
statistics: [
|
|
556
|
+
{
|
|
557
|
+
$key: 'totalYards',
|
|
558
|
+
value: 340,
|
|
559
|
+
displayValue: '340',
|
|
560
|
+
displayName: 'Total Yards',
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
$key: 'turnovers',
|
|
564
|
+
value: 0,
|
|
565
|
+
displayValue: '0',
|
|
566
|
+
displayName: 'Turnovers',
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
$key: 'firstDowns',
|
|
570
|
+
value: 21,
|
|
571
|
+
displayValue: '21',
|
|
572
|
+
displayName: '1st Downs',
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
$key: 'possession',
|
|
576
|
+
value: 24.22,
|
|
577
|
+
displayValue: '24:13',
|
|
578
|
+
displayName: 'Possession',
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
$key: 'thirdDownEfficiency',
|
|
582
|
+
value: 0.5,
|
|
583
|
+
displayValue: '4-8',
|
|
584
|
+
displayName: '3rd Down Efficiency',
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
$key: 'fourthDownEfficiency',
|
|
588
|
+
value: 0,
|
|
589
|
+
displayValue: '0-0',
|
|
590
|
+
displayName: '4th Down Efficiency',
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
$key: 'redZone',
|
|
594
|
+
value: 0.8,
|
|
595
|
+
displayValue: '4-5',
|
|
596
|
+
displayName: 'Red Zone',
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
$key: 'yardsPerPlay',
|
|
600
|
+
value: 5.8,
|
|
601
|
+
displayValue: '5.8',
|
|
602
|
+
displayName: 'Yards Per Play',
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
};
|
|
606
|
+
export const mockAwayTeamWithStatistics = {
|
|
607
|
+
id: '21',
|
|
608
|
+
uid: 's:20~l:28~t:21',
|
|
609
|
+
abbreviation: 'PHI',
|
|
610
|
+
displayName: 'Philadelphia Eagles',
|
|
611
|
+
color: '004C54',
|
|
612
|
+
logo: 'https://a.espncdn.com/i/teamlogos/nfl/500/phi.png',
|
|
613
|
+
homeAway: 'away',
|
|
614
|
+
statistics: [
|
|
615
|
+
{
|
|
616
|
+
$key: 'totalYards',
|
|
617
|
+
value: 417,
|
|
618
|
+
displayValue: '417',
|
|
619
|
+
displayName: 'Total Yards',
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
$key: 'turnovers',
|
|
623
|
+
value: 1,
|
|
624
|
+
displayValue: '1',
|
|
625
|
+
displayName: 'Turnovers',
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
$key: 'firstDowns',
|
|
629
|
+
value: 25,
|
|
630
|
+
displayValue: '25',
|
|
631
|
+
displayName: '1st Downs',
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
$key: 'possession',
|
|
635
|
+
value: 35.78,
|
|
636
|
+
displayValue: '35:47',
|
|
637
|
+
displayName: 'Possession',
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
$key: 'thirdDownEfficiency',
|
|
641
|
+
value: 0.727,
|
|
642
|
+
displayValue: '8-11',
|
|
643
|
+
displayName: '3rd Down Efficiency',
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
$key: 'fourthDownEfficiency',
|
|
647
|
+
value: 1.0,
|
|
648
|
+
displayValue: '2-2',
|
|
649
|
+
displayName: '4th Down Efficiency',
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
$key: 'redZone',
|
|
653
|
+
value: 0.6,
|
|
654
|
+
displayValue: '3-5',
|
|
655
|
+
displayName: 'Red Zone',
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
$key: 'yardsPerPlay',
|
|
659
|
+
value: 6.4,
|
|
660
|
+
displayValue: '6.4',
|
|
661
|
+
displayName: 'Yards Per Play',
|
|
662
|
+
},
|
|
663
|
+
],
|
|
664
|
+
};
|
|
665
|
+
export const mockStatsResponseWithTeamStats = {
|
|
666
|
+
entities: {
|
|
667
|
+
teams: {
|
|
668
|
+
'12': mockTeamWithStatistics,
|
|
669
|
+
'21': mockAwayTeamWithStatistics,
|
|
670
|
+
},
|
|
671
|
+
athletes: {},
|
|
672
|
+
},
|
|
673
|
+
leaders: [],
|
|
674
|
+
header: {
|
|
675
|
+
teams: [],
|
|
676
|
+
linescoresHeader: [],
|
|
677
|
+
},
|
|
678
|
+
predictor: null,
|
|
679
|
+
winProbability: null,
|
|
680
|
+
gameState: 'pre',
|
|
681
|
+
meta: {
|
|
682
|
+
gp_topic: 'test',
|
|
683
|
+
picker_topic: 'test',
|
|
684
|
+
gameState: 'pre',
|
|
685
|
+
},
|
|
686
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface TeamStatisticProps {
|
|
3
|
+
statisticName: string;
|
|
4
|
+
firstDisplayValue: string | number;
|
|
5
|
+
secondDisplayValue: string | number;
|
|
6
|
+
awayProgressValue: number;
|
|
7
|
+
homeProgressValue: number;
|
|
8
|
+
key?: string | number;
|
|
9
|
+
classNames?: {
|
|
10
|
+
container?: string;
|
|
11
|
+
header?: string;
|
|
12
|
+
firstValue?: string;
|
|
13
|
+
secondValue?: string;
|
|
14
|
+
statisticName?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Shared utility to render a team statistic with consistent structure
|
|
19
|
+
* This eliminates code duplication between Teams and TeamStatsView components
|
|
20
|
+
*/
|
|
21
|
+
export declare const renderTeamStatistic: (props: TeamStatisticProps) => React.JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import StatsProgressBar from '../view/Teams/StatsProgressBar';
|
|
3
|
+
/**
|
|
4
|
+
* Shared utility to render a team statistic with consistent structure
|
|
5
|
+
* This eliminates code duplication between Teams and TeamStatsView components
|
|
6
|
+
*/
|
|
7
|
+
export const renderTeamStatistic = (props) => {
|
|
8
|
+
const { statisticName, firstDisplayValue, secondDisplayValue, awayProgressValue, homeProgressValue, key, classNames = {}, } = props;
|
|
9
|
+
const { container = 'team-stat-item', header = 'stat-header', firstValue = 'stat-value away-value', secondValue = 'stat-value home-value', statisticName: statisticNameClass = 'stat-name', } = classNames;
|
|
10
|
+
return (React.createElement("div", { key: key, className: container },
|
|
11
|
+
React.createElement("div", { className: header },
|
|
12
|
+
React.createElement("span", { className: firstValue }, firstDisplayValue),
|
|
13
|
+
React.createElement("span", { className: statisticNameClass }, statisticName),
|
|
14
|
+
React.createElement("span", { className: secondValue }, secondDisplayValue)),
|
|
15
|
+
React.createElement(StatsProgressBar, { awayTeamValue: awayProgressValue, homeTeamValue: homeProgressValue })));
|
|
16
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './ActiveAthletes.styles.css';
|
|
3
|
+
import { WithFocusableProps } from '@/external/spatial-navigation';
|
|
4
|
+
import { Direction } from '@/external/spatial-navigation/utils';
|
|
5
|
+
import { AthletesStatisticsItem } from '../components/AthletesStatistics/AthletesStatistics';
|
|
6
|
+
export type ActiveAthletesItem = {
|
|
7
|
+
displayText: string;
|
|
8
|
+
awayTeam: {
|
|
9
|
+
id: string;
|
|
10
|
+
logo: string;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
homeTeam: {
|
|
14
|
+
id: string;
|
|
15
|
+
logo: string;
|
|
16
|
+
name: string;
|
|
17
|
+
};
|
|
18
|
+
athletes: AthletesStatisticsItem[];
|
|
19
|
+
};
|
|
20
|
+
export type ActiveAthletesProps = {
|
|
21
|
+
item: ActiveAthletesItem;
|
|
22
|
+
};
|
|
23
|
+
declare class ActiveAthletes extends React.Component<WithFocusableProps & ActiveAthletesProps, {
|
|
24
|
+
expanded: boolean;
|
|
25
|
+
}> {
|
|
26
|
+
constructor(props: WithFocusableProps & ActiveAthletesProps);
|
|
27
|
+
onEnterPress(): void;
|
|
28
|
+
onAthletesStatisticsArrowPress: (index: number) => (d: Direction) => boolean;
|
|
29
|
+
get cardFocusKey(): string;
|
|
30
|
+
onAthletesStatisticsBackPress: () => void;
|
|
31
|
+
renderContent(): React.JSX.Element;
|
|
32
|
+
render(): React.JSX.Element;
|
|
33
|
+
}
|
|
34
|
+
export default ActiveAthletes;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './ActiveAthletes.styles.css';
|
|
3
|
+
import Icon from '@/components/atoms/SvgIcon';
|
|
4
|
+
import { withFocusable, } from '@/external/spatial-navigation';
|
|
5
|
+
import CollapsedAthletes from './CollapsedAthletes/CollapsedAthletes';
|
|
6
|
+
import ExpandedAthletes from './ExpandedAthletes/ExpandedAthletes';
|
|
7
|
+
import SDK from '@/index';
|
|
8
|
+
class ActiveAthletes extends React.Component {
|
|
9
|
+
constructor(props) {
|
|
10
|
+
super(props);
|
|
11
|
+
Object.defineProperty(this, "onAthletesStatisticsArrowPress", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: (index) => (d) => {
|
|
16
|
+
if (d === 'up' && index === 0) {
|
|
17
|
+
SDK.getMaestroEventViewModel().navigation.setFocus(this.cardFocusKey);
|
|
18
|
+
this.setState({
|
|
19
|
+
expanded: false,
|
|
20
|
+
});
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "onAthletesStatisticsBackPress", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: () => {
|
|
31
|
+
this.setState({
|
|
32
|
+
expanded: false,
|
|
33
|
+
}, () => {
|
|
34
|
+
SDK.getMaestroEventViewModel().navigation.setFocus(this.cardFocusKey);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
this.state = {
|
|
39
|
+
expanded: false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
onEnterPress() {
|
|
43
|
+
const newValue = !this.state.expanded;
|
|
44
|
+
this.setState({
|
|
45
|
+
expanded: newValue,
|
|
46
|
+
}, () => {
|
|
47
|
+
if (newValue) {
|
|
48
|
+
const firstAthlete = this.props.item.athletes[0];
|
|
49
|
+
SDK.getMaestroEventViewModel().navigation.setFocus(`${firstAthlete.awayAthlete.id}-${firstAthlete.homeAthlete.id}-${firstAthlete.displayName}-statistics`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
get cardFocusKey() {
|
|
54
|
+
return this.props.item.displayText
|
|
55
|
+
.trim()
|
|
56
|
+
.toLowerCase()
|
|
57
|
+
.replace(/\s+/g, '-');
|
|
58
|
+
}
|
|
59
|
+
renderContent() {
|
|
60
|
+
if (this.state.expanded) {
|
|
61
|
+
return (React.createElement(ExpandedAthletes, { item: this.props.item, onArrowPress: this.onAthletesStatisticsArrowPress, onBackPress: this.onAthletesStatisticsBackPress }));
|
|
62
|
+
}
|
|
63
|
+
return React.createElement(CollapsedAthletes, { item: this.props.item });
|
|
64
|
+
}
|
|
65
|
+
render() {
|
|
66
|
+
return (React.createElement(FocusableWrapper, { displayText: this.props.item.displayText, onEnterPress: this.onEnterPress.bind(this), expanded: this.state.expanded, focusKey: this.cardFocusKey }, this.renderContent()));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const Wrapper = (props) => {
|
|
70
|
+
return (React.createElement("section", { className: "active-athletes focus-ring", "data-focused": props.focused },
|
|
71
|
+
React.createElement("div", { className: "header" },
|
|
72
|
+
React.createElement("h5", { className: "T05_ALT" }, props.displayText),
|
|
73
|
+
React.createElement("div", { className: "icon-container", "data-focused": props.focused },
|
|
74
|
+
React.createElement(Icon, { iconName: props.expanded ? 'collapse' : 'expand', height: 48, width: 48 }))),
|
|
75
|
+
props.children));
|
|
76
|
+
};
|
|
77
|
+
const FocusableWrapper = withFocusable()(Wrapper);
|
|
78
|
+
export default ActiveAthletes;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './CollapsedAthletes.styles.css';
|
|
3
|
+
import Image from '@/components/atoms/Image';
|
|
4
|
+
const CollapsedAthletes = (props) => {
|
|
5
|
+
const { awayTeam, homeTeam, athletes } = props.item;
|
|
6
|
+
return (React.createElement("div", { className: "minified-content" },
|
|
7
|
+
React.createElement("div", { className: "teams-header" }, [awayTeam, homeTeam].map((team) => {
|
|
8
|
+
return (React.createElement("div", { className: "team-header", key: team.id },
|
|
9
|
+
React.createElement(Image, { src: team.logo, crossOrigin: "anonymous", alt: team.name }),
|
|
10
|
+
React.createElement("span", { className: "T05_ALT" }, team.name)));
|
|
11
|
+
})),
|
|
12
|
+
athletes.map((item, index) => {
|
|
13
|
+
return (React.createElement("div", { className: "minified-athletes", key: item.awayAthlete.id + item.homeAthlete.id },
|
|
14
|
+
React.createElement("div", { className: "athletes-jersey-row" }, [item.awayAthlete, item.homeAthlete].map((athlete) => {
|
|
15
|
+
return (React.createElement("div", { className: "T05_ALT", key: athlete.id + index + '-jersey' },
|
|
16
|
+
React.createElement("span", null,
|
|
17
|
+
"#",
|
|
18
|
+
athlete.jersey),
|
|
19
|
+
" ",
|
|
20
|
+
athlete.shortName));
|
|
21
|
+
})),
|
|
22
|
+
React.createElement("div", { className: "athletes-statistics-row" }, [item.awayAthlete, item.homeAthlete].map((athlete) => {
|
|
23
|
+
return (React.createElement("span", { key: athlete.id + index + '-statistic', className: "T02" }, athlete.minifiedStatistics || ''));
|
|
24
|
+
}))));
|
|
25
|
+
})));
|
|
26
|
+
};
|
|
27
|
+
export default CollapsedAthletes;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './ExpandedAthletes.styles.css';
|
|
3
|
+
import { ActiveAthletesProps } from '../ActiveAthletes';
|
|
4
|
+
import { Direction } from '@/external/spatial-navigation/utils';
|
|
5
|
+
type Props = {
|
|
6
|
+
onArrowPress: (index: number) => (direction: Direction) => void;
|
|
7
|
+
onBackPress: () => void;
|
|
8
|
+
} & ActiveAthletesProps;
|
|
9
|
+
declare const ExpandedAthletes: (props: Props) => React.JSX.Element;
|
|
10
|
+
export default ExpandedAthletes;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Image from '@/components/atoms/Image';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import './ExpandedAthletes.styles.css';
|
|
4
|
+
import AthletesStatistics from '../../components/AthletesStatistics';
|
|
5
|
+
const ExpandedAthletes = (props) => {
|
|
6
|
+
const { awayTeam, homeTeam, athletes } = props.item;
|
|
7
|
+
const { onArrowPress, onBackPress } = props;
|
|
8
|
+
return (React.createElement("div", { className: "active-athletes-statistics" },
|
|
9
|
+
React.createElement("div", { className: "divider" }),
|
|
10
|
+
React.createElement("div", { className: "active-athletes-statistics-header" }, [awayTeam, homeTeam].map((team) => (React.createElement("div", { className: "team-header", key: team.id },
|
|
11
|
+
React.createElement(Image, { src: team.logo, crossOrigin: "anonymous", alt: team.name }),
|
|
12
|
+
React.createElement("span", { className: "T05_ALT" }, team.name))))),
|
|
13
|
+
athletes.map((item, index) => (React.createElement(AthletesStatistics, { key: item.awayAthlete.id + item.homeAthlete.id + item.displayName, focusKey: `${item.awayAthlete.id}-${item.homeAthlete.id}-${item.displayName}-statistics`, item: item, onArrowPress: onArrowPress(index), onBackPress: onBackPress })))));
|
|
14
|
+
};
|
|
15
|
+
export default ExpandedAthletes;
|
|
@@ -1,29 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import './GameLeaders.styles.css';
|
|
3
|
-
|
|
4
|
-
id: string;
|
|
5
|
-
displayName: string;
|
|
6
|
-
headshot: string;
|
|
7
|
-
position: string;
|
|
8
|
-
shortName: string;
|
|
9
|
-
jersey: string;
|
|
10
|
-
mainStatistic?: {
|
|
11
|
-
displayValue: string;
|
|
12
|
-
abbreviation: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
export type LeaderStatsItem = {
|
|
16
|
-
displayName: string;
|
|
17
|
-
awayAthlete: Athlete;
|
|
18
|
-
homeAthlete: Athlete;
|
|
19
|
-
statistics?: {
|
|
20
|
-
homeAthleteValue: string;
|
|
21
|
-
awayAthleteValue: string;
|
|
22
|
-
abbreviation: string;
|
|
23
|
-
}[];
|
|
24
|
-
};
|
|
3
|
+
import { AthletesStatisticsItem } from '../components/AthletesStatistics/AthletesStatistics';
|
|
25
4
|
export type GameLeadersStatsItem = {
|
|
26
|
-
items:
|
|
5
|
+
items: AthletesStatisticsItem[];
|
|
27
6
|
};
|
|
28
7
|
declare const GameLeaders: (props: GameLeadersStatsItem) => React.JSX.Element;
|
|
29
8
|
export default GameLeaders;
|
|
@@ -4,6 +4,6 @@ import AthletesStatistics from '../components/AthletesStatistics';
|
|
|
4
4
|
const GameLeaders = (props) => {
|
|
5
5
|
return (React.createElement("section", { className: "game-leaders" },
|
|
6
6
|
React.createElement("h5", { className: "T05_ALT" }, "Game Leaders"),
|
|
7
|
-
props.items.map((item, index) => (React.createElement(AthletesStatistics, { key: item.
|
|
7
|
+
props.items.map((item, index) => (React.createElement(AthletesStatistics, { key: `${index}-${item.awayAthlete.id}`, item: item, focusKey: String(index) })))));
|
|
8
8
|
};
|
|
9
9
|
export default GameLeaders;
|
|
@@ -10,6 +10,7 @@ export interface MatchPredictorProps {
|
|
|
10
10
|
declare const _default: {
|
|
11
11
|
new (props?: (MatchPredictorProps & WithFocusableProps) | undefined, context?: any): {
|
|
12
12
|
state: import("../../../../external/spatial-navigation/withFocusable").WithFocusableState;
|
|
13
|
+
navigation: import("../../../../external/spatial-navigation/spatialNavigation").default;
|
|
13
14
|
getChildContext(): {
|
|
14
15
|
parentFocusKey: string;
|
|
15
16
|
};
|
|
@@ -20,6 +20,7 @@ export type RecentGamesStatsItem = {
|
|
|
20
20
|
declare const _default: {
|
|
21
21
|
new (props?: (RecentGamesStatsItem & WithFocusableProps) | undefined, context?: any): {
|
|
22
22
|
state: import("../../../../external/spatial-navigation/withFocusable").WithFocusableState;
|
|
23
|
+
navigation: import("../../../../external/spatial-navigation/spatialNavigation").default;
|
|
23
24
|
getChildContext(): {
|
|
24
25
|
parentFocusKey: string;
|
|
25
26
|
};
|
|
@@ -9,6 +9,7 @@ export interface StatsQRCodeViewProps extends WithFocusableProps {
|
|
|
9
9
|
declare const _default: {
|
|
10
10
|
new (props?: (StatsQRCodeViewProps & WithFocusableProps) | undefined, context?: any): {
|
|
11
11
|
state: import("../../../../external/spatial-navigation/withFocusable").WithFocusableState;
|
|
12
|
+
navigation: import("../../../../external/spatial-navigation/spatialNavigation").default;
|
|
12
13
|
getChildContext(): {
|
|
13
14
|
parentFocusKey: string;
|
|
14
15
|
};
|
|
@@ -14,7 +14,7 @@ declare class StatsView extends Component<{}, StatsViewState> {
|
|
|
14
14
|
constructor(props: {});
|
|
15
15
|
componentDidMount(): void;
|
|
16
16
|
componentWillUnmount(): void;
|
|
17
|
-
renderStatsComponents: () => React.
|
|
17
|
+
renderStatsComponents: () => React.ReactElement[];
|
|
18
18
|
render(): React.JSX.Element;
|
|
19
19
|
}
|
|
20
20
|
export default StatsView;
|
|
@@ -4,6 +4,8 @@ import ScrollableContainer from '@/components/core/ScrollableContainer';
|
|
|
4
4
|
import MatchPredictorView from './MatchPredictor';
|
|
5
5
|
import SDK from '@/index';
|
|
6
6
|
import StatsQRCodeView from './StatsQRCode';
|
|
7
|
+
import TeamStatsView from './TeamStats';
|
|
8
|
+
const STICKY_HEADER_HEIGHT = 32; // Match KeyPlaysView
|
|
7
9
|
class StatsView extends Component {
|
|
8
10
|
constructor(props) {
|
|
9
11
|
super(props);
|
|
@@ -38,16 +40,30 @@ class StatsView extends Component {
|
|
|
38
40
|
value: () => {
|
|
39
41
|
const { statsData } = this.state;
|
|
40
42
|
if (!statsData) {
|
|
41
|
-
return
|
|
43
|
+
return [];
|
|
42
44
|
}
|
|
43
45
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
const teams = statsData.entities.teams;
|
|
47
|
+
const homeTeam = Object.values(teams).find((team) => team.homeAway === 'home');
|
|
48
|
+
const awayTeam = Object.values(teams).find((team) => team.homeAway === 'away');
|
|
49
|
+
const availableStatistics = homeTeam?.statistics || awayTeam?.statistics || [];
|
|
50
|
+
const components = [];
|
|
51
|
+
if (statsData.predictor) {
|
|
52
|
+
components.push(React.createElement(MatchPredictorView, { key: "match-predictor", predictor: statsData.predictor, teams: statsData.entities.teams }));
|
|
53
|
+
}
|
|
54
|
+
// Add TeamStatsView if available
|
|
55
|
+
if (homeTeam && awayTeam && availableStatistics.length > 0) {
|
|
56
|
+
components.push(React.createElement(TeamStatsView, { key: "team-stats", homeTeam: homeTeam, awayTeam: awayTeam, statistics: availableStatistics }));
|
|
57
|
+
}
|
|
58
|
+
// Add StatsQRCodeView if available
|
|
59
|
+
if (statsData.promotion) {
|
|
60
|
+
components.push(React.createElement(StatsQRCodeView, { key: "stats-qr-code", promotion: statsData.promotion }));
|
|
61
|
+
}
|
|
62
|
+
return components;
|
|
47
63
|
}
|
|
48
64
|
catch (error) {
|
|
49
65
|
console.error('Error rendering stats components:', error);
|
|
50
|
-
return
|
|
66
|
+
return [];
|
|
51
67
|
}
|
|
52
68
|
}
|
|
53
69
|
});
|
|
@@ -59,15 +75,15 @@ class StatsView extends Component {
|
|
|
59
75
|
};
|
|
60
76
|
}
|
|
61
77
|
componentDidMount() {
|
|
62
|
-
this.setState({
|
|
63
|
-
container: this.container,
|
|
64
|
-
});
|
|
65
78
|
this.statsDataUnsub = this.vm.statsData.subscribe((value) => {
|
|
66
79
|
this.setState({ statsData: value });
|
|
67
80
|
});
|
|
68
81
|
this.isDataLoadingUnsub = this.vm.isDataLoading.subscribe((value) => {
|
|
69
82
|
this.setState({ isDataLoading: value });
|
|
70
83
|
});
|
|
84
|
+
this.setState({
|
|
85
|
+
container: this.container,
|
|
86
|
+
});
|
|
71
87
|
// Initialize the view model
|
|
72
88
|
this.vm.init();
|
|
73
89
|
}
|
|
@@ -78,9 +94,13 @@ class StatsView extends Component {
|
|
|
78
94
|
this.vm.cleanup();
|
|
79
95
|
}
|
|
80
96
|
render() {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
97
|
+
const { container, statsData } = this.state;
|
|
98
|
+
const components = this.renderStatsComponents();
|
|
99
|
+
return (React.createElement(ScrollableContainer, { scrollableContainerRef: container, containerTopOffset: STICKY_HEADER_HEIGHT },
|
|
100
|
+
React.createElement("div", { ref: (el) => {
|
|
101
|
+
this.container = el;
|
|
102
|
+
}, className: "stats-view" },
|
|
103
|
+
!statsData ? (React.createElement("div", { className: "stats-loading T02" }, "Loading...")) : components.length > 0 ? (React.createElement("div", { className: "stats-content" }, components.map((component, index) => (React.createElement("div", { key: component.key || index, className: "stats-component" }, component))))) : (React.createElement("div", { className: "stats-loading T02" }, "No stats available")),
|
|
84
104
|
React.createElement("div", { className: "stats-bottom-padding" }))));
|
|
85
105
|
}
|
|
86
106
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// <reference types="prop-types" />
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import './TeamStatsView.styles.css';
|
|
4
|
+
import { WithFocusableProps } from '@/external/spatial-navigation';
|
|
5
|
+
import { Team, TeamStatistic } from '../../interfaces/IStats';
|
|
6
|
+
export interface TeamStatsViewProps {
|
|
7
|
+
homeTeam: Team;
|
|
8
|
+
awayTeam: Team;
|
|
9
|
+
statistics?: TeamStatistic[];
|
|
10
|
+
}
|
|
11
|
+
declare const _default: {
|
|
12
|
+
new (props?: (TeamStatsViewProps & WithFocusableProps) | undefined, context?: any): {
|
|
13
|
+
state: import("../../../../external/spatial-navigation/withFocusable").WithFocusableState;
|
|
14
|
+
navigation: import("../../../../external/spatial-navigation/spatialNavigation").default;
|
|
15
|
+
getChildContext(): {
|
|
16
|
+
parentFocusKey: string;
|
|
17
|
+
};
|
|
18
|
+
setFocus: (focusKey: string) => void;
|
|
19
|
+
navigateByDirection: (direction: import("../../../../external/spatial-navigation/utils").Direction, details?: Record<string, any>) => void;
|
|
20
|
+
stealFocus: () => void;
|
|
21
|
+
onUpdateFocus: (focused: boolean) => void;
|
|
22
|
+
onUpdateHasFocusedChild: (hasFocusedChild: boolean) => void;
|
|
23
|
+
onEnterPressHandler: (details: import("../../../../external/spatial-navigation/utils").Details) => void;
|
|
24
|
+
onEnterReleaseHandler: () => void;
|
|
25
|
+
onBackPressHandler: (details: import("../../../../external/spatial-navigation/utils").Details) => void;
|
|
26
|
+
onArrowPressHandler: (direction: import("../../../../external/spatial-navigation/utils").Direction, details: import("../../../../external/spatial-navigation/utils").Details) => boolean | void;
|
|
27
|
+
onBecameFocusedHandler: (layout: import("../../../../external/spatial-navigation/utils").Layout, details: import("../../../../external/spatial-navigation/utils").Details) => void;
|
|
28
|
+
onBecameBlurredHandler: (layout: import("../../../../external/spatial-navigation/utils").Layout, details: import("../../../../external/spatial-navigation/utils").Details) => void;
|
|
29
|
+
componentDidMount(): void;
|
|
30
|
+
componentDidUpdate(): void;
|
|
31
|
+
componentWillUnmount(): void;
|
|
32
|
+
render(): React.ReactElement<TeamStatsViewProps & WithFocusableProps>;
|
|
33
|
+
setState<K extends keyof import("../../../../external/spatial-navigation/withFocusable").WithFocusableState>(state: import("../../../../external/spatial-navigation/withFocusable").WithFocusableState | ((prevState: Readonly<import("../../../../external/spatial-navigation/withFocusable").WithFocusableState>, props: TeamStatsViewProps & WithFocusableProps) => import("../../../../external/spatial-navigation/withFocusable").WithFocusableState | Pick<import("../../../../external/spatial-navigation/withFocusable").WithFocusableState, K>) | Pick<import("../../../../external/spatial-navigation/withFocusable").WithFocusableState, K>, callback?: (() => any) | undefined): void;
|
|
34
|
+
forceUpdate(callBack?: (() => any) | undefined): void;
|
|
35
|
+
props: Readonly<{
|
|
36
|
+
children?: React.ReactNode;
|
|
37
|
+
}> & Readonly<TeamStatsViewProps & WithFocusableProps>;
|
|
38
|
+
context: any;
|
|
39
|
+
refs: {
|
|
40
|
+
[key: string]: React.ReactInstance;
|
|
41
|
+
};
|
|
42
|
+
componentWillMount?(): void;
|
|
43
|
+
componentWillReceiveProps?(nextProps: Readonly<TeamStatsViewProps & WithFocusableProps>, nextContext: any): void;
|
|
44
|
+
shouldComponentUpdate?(nextProps: Readonly<TeamStatsViewProps & WithFocusableProps>, nextState: Readonly<import("../../../../external/spatial-navigation/withFocusable").WithFocusableState>, nextContext: any): boolean;
|
|
45
|
+
componentWillUpdate?(nextProps: Readonly<TeamStatsViewProps & WithFocusableProps>, nextState: Readonly<import("../../../../external/spatial-navigation/withFocusable").WithFocusableState>, nextContext: any): void;
|
|
46
|
+
};
|
|
47
|
+
contextTypes: {
|
|
48
|
+
parentFocusKey: import("prop-types").Requireable<string>;
|
|
49
|
+
};
|
|
50
|
+
childContextTypes: {
|
|
51
|
+
parentFocusKey: import("prop-types").Requireable<string>;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export default _default;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './TeamStatsView.styles.css';
|
|
3
|
+
import Image from '@/components/atoms/Image';
|
|
4
|
+
import { withFocusable, } from '@/external/spatial-navigation';
|
|
5
|
+
import { renderTeamStatistic } from '../../utils/renderTeamStatistic';
|
|
6
|
+
const TeamStatsView = (props) => {
|
|
7
|
+
const { homeTeam, awayTeam, statistics } = props;
|
|
8
|
+
// If no statistics available, don't render the component
|
|
9
|
+
if (!statistics || statistics.length === 0) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
// Get statistics for both teams and match them by displayName
|
|
13
|
+
const homeStats = homeTeam.statistics || [];
|
|
14
|
+
const awayStats = awayTeam.statistics || [];
|
|
15
|
+
// Create a map of statistics by displayName for easier lookup
|
|
16
|
+
const createStatsMap = (stats) => {
|
|
17
|
+
return stats.reduce((map, stat) => {
|
|
18
|
+
map[stat.displayName] = stat;
|
|
19
|
+
return map;
|
|
20
|
+
}, {});
|
|
21
|
+
};
|
|
22
|
+
const homeStatsMap = createStatsMap(homeStats);
|
|
23
|
+
const awayStatsMap = createStatsMap(awayStats);
|
|
24
|
+
// Filter statistics to only include those that exist for both teams
|
|
25
|
+
const matchedStatistics = statistics.filter((stat) => homeStatsMap[stat.displayName] && awayStatsMap[stat.displayName]);
|
|
26
|
+
return (React.createElement("section", { className: "team-stats-view focus-ring", "data-focused": props.focused, style: {
|
|
27
|
+
'--home-team': `#${homeTeam.color}`,
|
|
28
|
+
'--away-team': `#${awayTeam.color}`,
|
|
29
|
+
} },
|
|
30
|
+
React.createElement("div", { className: "team-stats-header" },
|
|
31
|
+
React.createElement("div", { className: "team-stats-team away-team" },
|
|
32
|
+
React.createElement("div", { className: "team-stats-team-inner" },
|
|
33
|
+
awayTeam.logo && (React.createElement(Image, { src: awayTeam.logo, alt: awayTeam.displayName, crossOrigin: "anonymous", className: "team-logo" })),
|
|
34
|
+
React.createElement("div", { className: "team-swatch away-swatch" }),
|
|
35
|
+
React.createElement("span", { className: "team-abbreviation" }, awayTeam.abbreviation))),
|
|
36
|
+
React.createElement("h2", { className: "stats-title" }, "Team Stats"),
|
|
37
|
+
React.createElement("div", { className: "team-stats-team home-team" },
|
|
38
|
+
React.createElement("div", { className: "team-stats-team-inner" },
|
|
39
|
+
React.createElement("span", { className: "team-abbreviation" }, homeTeam.abbreviation),
|
|
40
|
+
React.createElement("div", { className: "team-swatch home-swatch" }),
|
|
41
|
+
homeTeam.logo && (React.createElement(Image, { src: homeTeam.logo, alt: homeTeam.displayName, crossOrigin: "anonymous", className: "team-logo" }))))),
|
|
42
|
+
React.createElement("div", { className: "team-stats-content" }, matchedStatistics.map((statistic) => {
|
|
43
|
+
const homeStat = homeStatsMap[statistic.displayName];
|
|
44
|
+
const awayStat = awayStatsMap[statistic.displayName];
|
|
45
|
+
// Parse values, handle different formats (numbers and strings)
|
|
46
|
+
const homeValue = homeStat
|
|
47
|
+
? parseFloat(homeStat.displayValue) || 0
|
|
48
|
+
: 0;
|
|
49
|
+
const awayValue = awayStat
|
|
50
|
+
? parseFloat(awayStat.displayValue) || 0
|
|
51
|
+
: 0;
|
|
52
|
+
return renderTeamStatistic({
|
|
53
|
+
statisticName: statistic.displayName,
|
|
54
|
+
firstDisplayValue: awayStat?.displayValue || '0',
|
|
55
|
+
secondDisplayValue: homeStat?.displayValue || '0',
|
|
56
|
+
awayProgressValue: awayValue,
|
|
57
|
+
homeProgressValue: homeValue,
|
|
58
|
+
key: statistic.displayName,
|
|
59
|
+
classNames: {
|
|
60
|
+
container: 'team-stat-item',
|
|
61
|
+
header: 'stat-header',
|
|
62
|
+
firstValue: 'stat-value away-value',
|
|
63
|
+
secondValue: 'stat-value home-value',
|
|
64
|
+
statisticName: 'stat-name',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}))));
|
|
68
|
+
};
|
|
69
|
+
export default withFocusable()(TeamStatsView);
|