@maestro_io/maestro-web-sdk 5.1.1 → 5.1.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.
@@ -8,7 +8,14 @@ export type RequestLoginPanelEvent = {
8
8
  onLoginSuccess: () => void;
9
9
  };
10
10
  };
11
- export type PanelEvent = RequestLoginPanelEvent;
11
+ /**
12
+ * Generic panel event type that supports client-specific events
13
+ * without exposing specific event structures in the SDK interface
14
+ */
15
+ export type PanelEvent = {
16
+ type: string;
17
+ payload: unknown;
18
+ };
12
19
  /**
13
20
  * @description Interface for the client app to implement in order to handle Maestro TV SDK events.
14
21
  */
@@ -154,8 +154,10 @@ export class FantasyViewModel {
154
154
  await this.fetchData();
155
155
  }
156
156
  async fetchData() {
157
+ this.isDataLoading.value = true;
157
158
  const isAuthenticated = await this.delegate.userIsAuthenticated();
158
159
  if (!isAuthenticated) {
160
+ this.isDataLoading.value = false;
159
161
  this.append({
160
162
  id: 'not-logged-in',
161
163
  type: 'notLoggedIn',
@@ -211,8 +213,11 @@ export class FantasyViewModel {
211
213
  this.itemToLeagueKey.clear();
212
214
  this._fantasyLeagues = [];
213
215
  this._tournamentChallenges = [];
216
+ this._promoItem = null;
214
217
  this._tcProcessingCount = 0;
215
218
  this.isLoadingTc.value = false;
219
+ this.fantasyItems.value = [];
220
+ this.hasEverSuccessfullyLoaded.value = false;
216
221
  }
217
222
  /**
218
223
  * Track panel view event
@@ -334,6 +339,7 @@ export class FantasyViewModel {
334
339
  // Mark successful load
335
340
  this.hasEverSuccessfullyLoaded.value = true;
336
341
  this.errorState.value = 'none';
342
+ this.isDataLoading.value = false;
337
343
  // Initial rebuild even if empty, to ensure state is clean
338
344
  this.rebuildFantasyItems();
339
345
  }
@@ -342,6 +348,7 @@ export class FantasyViewModel {
342
348
  context: 'FantasyViewModel.processFantasy',
343
349
  });
344
350
  console.error('[FANTASY] Error processing fantasy data:', error);
351
+ this.isDataLoading.value = false;
345
352
  if (!this.hasEverSuccessfullyLoaded.value) {
346
353
  this.errorState.value = 'tabLoadFailure';
347
354
  }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @description Represents a single stream/camera listing available for multi-view
3
+ */
4
+ export interface Listing {
5
+ id: string;
6
+ title: string;
7
+ thumbnailUrl: string;
8
+ streamUrl: string;
9
+ homeTeam?: {
10
+ name: string;
11
+ abbreviation: string;
12
+ logoUrl: string;
13
+ score?: number;
14
+ };
15
+ awayTeam?: {
16
+ name: string;
17
+ abbreviation: string;
18
+ logoUrl: string;
19
+ score?: number;
20
+ };
21
+ status?: 'live' | 'upcoming' | 'ended';
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ /**
25
+ * @description Defines a multi-view layout configuration with positioned slots
26
+ */
27
+ export interface MultiViewLayout {
28
+ id: string;
29
+ name: string;
30
+ type: '50-50' | '60-40' | '70-30' | 'pip' | 'quad';
31
+ slots: Array<{
32
+ index: number;
33
+ x: number;
34
+ y: number;
35
+ width: number;
36
+ height: number;
37
+ }>;
38
+ }
39
+ /**
40
+ * @description Current multi-view state including listings and layout
41
+ */
42
+ export interface MultiViewConfig {
43
+ listings: Listing[];
44
+ layout: MultiViewLayout;
45
+ primaryAudioSlot?: number;
46
+ }
47
+ /**
48
+ * @description Represents a pre-configured curated multiview experience
49
+ */
50
+ export interface CuratedMultiview {
51
+ id: string;
52
+ title: string;
53
+ description?: string;
54
+ thumbnailUrl?: string;
55
+ listings: Listing[];
56
+ defaultLayout: MultiViewLayout;
57
+ metadata?: Record<string, unknown>;
58
+ }
59
+ /**
60
+ * @description Available layouts organized by stream count
61
+ */
62
+ export declare const LAYOUTS_BY_COUNT: Record<2 | 3 | 4, MultiViewLayout[]>;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @description Available layouts organized by stream count
3
+ */
4
+ export const LAYOUTS_BY_COUNT = {
5
+ 2: [
6
+ {
7
+ id: 'layout-2-50-50',
8
+ name: '50/50',
9
+ type: '50-50',
10
+ slots: [
11
+ { index: 0, x: 0, y: 0, width: 50, height: 100 },
12
+ { index: 1, x: 50, y: 0, width: 50, height: 100 },
13
+ ],
14
+ },
15
+ {
16
+ id: 'layout-2-60-40',
17
+ name: '60/40',
18
+ type: '60-40',
19
+ slots: [
20
+ { index: 0, x: 0, y: 0, width: 60, height: 100 },
21
+ { index: 1, x: 60, y: 0, width: 40, height: 100 },
22
+ ],
23
+ },
24
+ {
25
+ id: 'layout-2-70-30',
26
+ name: '70/30',
27
+ type: '70-30',
28
+ slots: [
29
+ { index: 0, x: 0, y: 0, width: 70, height: 100 },
30
+ { index: 1, x: 70, y: 0, width: 30, height: 100 },
31
+ ],
32
+ },
33
+ {
34
+ id: 'layout-2-pip',
35
+ name: 'PIP',
36
+ type: 'pip',
37
+ slots: [
38
+ { index: 0, x: 0, y: 0, width: 100, height: 100 },
39
+ { index: 1, x: 70, y: 70, width: 25, height: 25 },
40
+ ],
41
+ },
42
+ ],
43
+ 3: [
44
+ {
45
+ id: 'layout-3-main-side',
46
+ name: 'Main + Side',
47
+ type: '60-40',
48
+ slots: [
49
+ { index: 0, x: 0, y: 0, width: 60, height: 100 },
50
+ { index: 1, x: 60, y: 0, width: 40, height: 50 },
51
+ { index: 2, x: 60, y: 50, width: 40, height: 50 },
52
+ ],
53
+ },
54
+ {
55
+ id: 'layout-3-horizontal',
56
+ name: 'Horizontal',
57
+ type: '50-50',
58
+ slots: [
59
+ { index: 0, x: 0, y: 0, width: 100, height: 33.33 },
60
+ { index: 1, x: 0, y: 33.33, width: 100, height: 33.33 },
61
+ { index: 2, x: 0, y: 66.66, width: 100, height: 33.34 },
62
+ ],
63
+ },
64
+ ],
65
+ 4: [
66
+ {
67
+ id: 'layout-4-quad',
68
+ name: 'Quad',
69
+ type: 'quad',
70
+ slots: [
71
+ { index: 0, x: 0, y: 0, width: 50, height: 50 },
72
+ { index: 1, x: 50, y: 0, width: 50, height: 50 },
73
+ { index: 2, x: 0, y: 50, width: 50, height: 50 },
74
+ { index: 3, x: 50, y: 50, width: 50, height: 50 },
75
+ ],
76
+ },
77
+ {
78
+ id: 'layout-4-main-grid',
79
+ name: 'Main + Grid',
80
+ type: '60-40',
81
+ slots: [
82
+ { index: 0, x: 0, y: 0, width: 60, height: 100 },
83
+ { index: 1, x: 60, y: 0, width: 40, height: 33.33 },
84
+ { index: 2, x: 60, y: 33.33, width: 40, height: 33.33 },
85
+ { index: 3, x: 60, y: 66.66, width: 40, height: 33.34 },
86
+ ],
87
+ },
88
+ ],
89
+ };
@@ -0,0 +1,13 @@
1
+ import './LayoutSelector.style.css';
2
+ import { WithFocusableProps } from '@/external/spatial-navigation';
3
+ import { Direction } from '@/external/spatial-navigation/utils';
4
+ import { MultiViewLayout } from '../../../types';
5
+ export type LayoutSelectorProps = {
6
+ streamCount: 2 | 3 | 4;
7
+ selectedLayoutId?: string;
8
+ onLayoutSelect?: (layout: MultiViewLayout) => void;
9
+ onLayoutArrowPress?: (direction: Direction) => void;
10
+ className?: string;
11
+ };
12
+ declare const LayoutSelector: (props: LayoutSelectorProps & WithFocusableProps) => JSX.Element;
13
+ export default LayoutSelector;
@@ -0,0 +1,43 @@
1
+ import React, { useCallback } from 'react';
2
+ import './LayoutSelector.style.css';
3
+ import { withFocusable, } from '@/external/spatial-navigation';
4
+ import { FocusableItem, useScrollableContainer, } from '@/components/core/ScrollableContainer';
5
+ import SDK from '@/index';
6
+ import { LAYOUTS_BY_COUNT } from '../../../types';
7
+ const LayoutOptionRowComponent = (props) => {
8
+ const { layout, selected, focusable, focused } = props;
9
+ console.log('selected', selected);
10
+ return (React.createElement(FocusableItem, null,
11
+ React.createElement("div", { className: "layout-selector-option-focus focus-ring", "data-focusable": focusable, "data-focused": focused },
12
+ React.createElement("div", { className: "layout-selector-row" },
13
+ React.createElement("div", { className: "layout-selector-cell layout-selector-cell-preview" },
14
+ React.createElement("div", { className: "layout-selector-preview" }, layout.slots.map((slot) => (React.createElement("div", { key: `${layout.id}-slot-${slot.index}`, className: "layout-selector-slot", style: {
15
+ left: `${slot.x}%`,
16
+ top: `${slot.y}%`,
17
+ width: `${slot.width}%`,
18
+ height: `${slot.height}%`,
19
+ } }))))),
20
+ React.createElement("div", { className: "layout-selector-cell layout-selector-cell-label" },
21
+ React.createElement("div", { className: "layout-selector-label T30 FONT_BOLD" }, layout.name)),
22
+ React.createElement("div", { className: "layout-selector-cell layout-selector-cell-radio" },
23
+ React.createElement("div", { className: `layout-selector-radio ${selected ? 'selected' : ''}`, "aria-hidden": true }, selected ? (React.createElement("span", { className: "layout-selector-radio-check" }, "\u2713")) : null))))));
24
+ };
25
+ const LayoutOptionRow = withFocusable()(LayoutOptionRowComponent);
26
+ const LayoutSelector = (props) => {
27
+ const { scrollToFocusedElement } = useScrollableContainer();
28
+ const { streamCount, selectedLayoutId, onLayoutSelect } = props;
29
+ const layouts = LAYOUTS_BY_COUNT[streamCount] || [];
30
+ const onBecameFocused = useCallback((layout) => {
31
+ scrollToFocusedElement(layout);
32
+ }, [scrollToFocusedElement]);
33
+ const onArrowPress = useCallback((d, _props) => {
34
+ if (d === 'left') {
35
+ SDK.getMaestroEventViewModel().deliverFocus();
36
+ }
37
+ }, []);
38
+ return (React.createElement(React.Fragment, null, layouts.length === 0 ? (React.createElement("div", { className: "layout-selector-empty T10" },
39
+ "No layouts available for ",
40
+ streamCount,
41
+ " streams.")) : (React.createElement("div", { className: "layout-selector-options" }, layouts.map((layout) => (React.createElement(LayoutOptionRow, { key: layout.id, focusKey: `fox-multiview-layout-${layout.id}`, layout: layout, selected: layout.id === selectedLayoutId, onEnterPress: () => onLayoutSelect?.(layout), onArrowPress: onArrowPress, onBecameFocused: onBecameFocused })))))));
42
+ };
43
+ export default LayoutSelector;
@@ -0,0 +1,2 @@
1
+ import LayoutSelector from './LayoutSelector';
2
+ export default LayoutSelector;
@@ -0,0 +1,2 @@
1
+ import LayoutSelector from './LayoutSelector';
2
+ export default LayoutSelector;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maestro_io/maestro-web-sdk",
3
3
  "private": false,
4
- "version": "5.1.1",
4
+ "version": "5.1.2",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "main": "dist/maestro-web-sdk.umd.js",