@24i/bigscreen-sdk 1.0.12-alpha.2254 → 1.0.12

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 (37) hide show
  1. package/package.json +6 -2
  2. package/packages/analytics/README.md +1 -58
  3. package/packages/analytics/src/A.ts +1 -51
  4. package/packages/analytics/src/Analytics.ts +1 -114
  5. package/packages/analytics/src/analyticsOnScroll.ts +1 -11
  6. package/packages/analytics/src/clients/ConsoleAnalytics/ConsoleAnalytics.ts +1 -12
  7. package/packages/analytics/src/constants.ts +1 -56
  8. package/packages/analytics/src/interface.ts +1 -82
  9. package/packages/driver-webos/src/DeviceWebos.ts +8 -0
  10. package/packages/epg/README.md +3 -1
  11. package/packages/epg/src/v2/Cell.tsx +16 -2
  12. package/packages/epg/src/v2/DataManager.ts +4 -1
  13. package/packages/epg/src/v2/Epg.tsx +3 -1
  14. package/packages/epg/src/v2/Row.tsx +3 -1
  15. package/packages/epg/src/v2/basic/CellsManager.tsx +7 -2
  16. package/packages/epg/src/v2/basic/RowsManager.tsx +3 -1
  17. package/packages/epg/src/v2/interface.ts +13 -0
  18. package/packages/events-manager/src/EventsManager.ts +1 -56
  19. package/packages/list/src/Base/Base.tsx +6 -1
  20. package/packages/list/src/Base/interface.ts +2 -2
  21. package/packages/list/src/EdgeOffsetList/EdgeOffsetList.tsx +2 -2
  22. package/packages/logger/src/loggers/SentryLogger/Sentry.ts +1 -0
  23. package/packages/logger/src/loggers/SentryLogger/SentryLogger.ts +2 -2
  24. package/packages/perf-utils/README.md +1 -120
  25. package/packages/perf-utils/src/array/filter.ts +1 -54
  26. package/packages/perf-utils/src/array/find.ts +1 -43
  27. package/packages/perf-utils/src/array/findIndex.ts +1 -43
  28. package/packages/perf-utils/src/array/forEach.ts +1 -48
  29. package/packages/perf-utils/src/array/includes.ts +1 -41
  30. package/packages/perf-utils/src/array/includesNaN.ts +1 -32
  31. package/packages/perf-utils/src/array/index.ts +1 -7
  32. package/packages/perf-utils/src/array/map.ts +1 -52
  33. package/packages/player-ui/src/PlayerUI.tsx +15 -9
  34. package/packages/player-ui/src/Seekbar.tsx +4 -2
  35. package/packages/player-ui/src/index.ts +1 -0
  36. package/packages/player-ui/src/mocks.ts +3 -3
  37. package/packages/player-ui/src/types.ts +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@24i/bigscreen-sdk",
3
- "version": "1.0.12-alpha.2254",
3
+ "version": "1.0.12",
4
4
  "description": "SmartApps BIGscreen SDK monorepo",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -37,9 +37,13 @@
37
37
  },
38
38
  "homepage": "https://github.com/24i/smartapps-bigscreen-sdk#readme",
39
39
  "dependencies": {
40
+ "@24i/appstage-shared-analytics": "1.0.7",
41
+ "@24i/appstage-shared-events-manager": "1.0.5",
42
+ "@24i/appstage-shared-perf-utils": "1.0.5",
40
43
  "@24i/player-base": "6.10.0",
41
44
  "@24i/smartapps-datalayer": "3.0.6-alpha.978",
42
- "@sentry/browser": "^7.12.1",
45
+ "@sentry/browser": "7.35.0",
46
+ "@sentry/types": "7.35.0",
43
47
  "@types/gtag.js": "^0.0.12",
44
48
  "@types/prop-types": "^15.7.5",
45
49
  "@types/scheduler": "^0.16.2",
@@ -5,61 +5,4 @@ hide_title: true
5
5
  sidebar_label: Analytics
6
6
  ---
7
7
 
8
- # Analytics
9
- This package contains general implementation of analytics and implementation for these clients:
10
- - ConsoleAnalytics - debugging analytics that dump triggers to the console.
11
-
12
- ## analytics
13
- Single instance `analytics` is a general manager for analytics clients. It provides a unified
14
- interface for use by the app without it needing to know any specific analytics client implementation.
15
- It also adds some general app info to each trigger call (e.g. resolution, language, etc.)
16
-
17
- ## A
18
- `A` is a shorthand utility function to simplify sending analytics information. It allows setting
19
- a basic payload that is sent with each trigger call and shallowly merged with trigger payload.
20
- Trigger payload can override values inside this basic payload.
21
-
22
- ### Usage
23
- ```typescript
24
- import { A } from '@24i/bigscreen-sdk/analytics';
25
-
26
- class DetailScene {
27
- a = A.of({ sceneType: SceneType.DETAIL });
28
-
29
- activate(params: RouteParams) {
30
- this.a.addToPayload({
31
- sceneId: `/detail/${params.assetType}/${params.id}`,
32
- sceneName: `Detail - ${data.type} - [unknown]`,
33
- asset: undefined,
34
- });
35
- // ...
36
- }
37
-
38
- async loadAndRenderData() {
39
- this.a('buffering_start');
40
- const data = await dataProvider();
41
- this.a.addToPayload({
42
- sceneName: `Detail - ${data.type} - ${data.name}`,
43
- asset: data,
44
- });
45
- this.a('buffering_stop');
46
- // render
47
- this.a('scene_view');
48
- }
49
- }
50
- ```
51
- It is better to store shorthand `a` function inside the component instance. Some scenes can be
52
- instantiated multiple times by the router (e.g. Browse in WL). Therefore it cannot use a single
53
- instance from a local `./analytics'` import for example.
54
-
55
- ## Clients
56
- Analytics client is the integration to the specific analytics provider (e.g. Google Analytics).
57
- It needs to implement `AnalyticsClient` interface and register its instance to receive the events.
58
-
59
- Clients have to opt-in to events they wish to listen to. This can be done by listing all events or by
60
- using a regular expression.
61
-
62
- ```typescript
63
- analytics.addClient(new GoogleAnalytics(), ['scene_view', 'buffering_start', 'buffering_stop']);
64
- analytics.addClient(new ConsoleAnalytics(), /.*/);
65
- ```
8
+ https://github.com/24i/appstage-shared-packages/blob/main/packages/analytics/README.md
@@ -1,51 +1 @@
1
- import { analytics } from './Analytics';
2
- import { AnalyticsEventsMap, AnalyticsEventsNames, AnyPayload, AFunction } from './interface';
3
-
4
- interface PrivateAFunction extends AFunction {
5
- payload: AnyPayload,
6
- }
7
-
8
- export class A {
9
- /**
10
- * Creates an analytics 'a' function for easy triggering of events.
11
- * @param payload basic payload that is added to each payload of the returned
12
- * function call.
13
- * @returns a function for triggering analytics events
14
- * @example
15
- * ```typescript
16
- * // Creates a function and sets the basic payload.
17
- * const a = A.of({ sceneType: 'HOME', sceneId: 'home' });
18
- *
19
- * // Sends 'scene_view' event with the basic payload.
20
- * a('scene_view');
21
- *
22
- * // Sends 'buffering_stop' with shallow merge of the basic payload and { asset } object.
23
- * a('buffering_stop', { asset });
24
- * ```
25
- */
26
- static of(payload: AnyPayload) {
27
- const a: PrivateAFunction = (<
28
- EventName extends AnalyticsEventsNames,
29
- P extends Partial<Parameters<AnalyticsEventsMap[EventName]>[0]>,
30
- >(eventName: EventName, eventPayload: P) => {
31
- analytics.triggerEvent(eventName, {
32
- ...a.payload,
33
- ...eventPayload,
34
- } as unknown as Parameters<AnalyticsEventsMap[EventName]>[0]);
35
- }) as unknown as PrivateAFunction;
36
- a.payload = payload;
37
- a.addToPayload = (extendedPayload: AnyPayload) => {
38
- a.payload = {
39
- ...a.payload,
40
- ...extendedPayload,
41
- };
42
- };
43
- a.setPayload = (newPayload: AnyPayload) => {
44
- a.payload = newPayload;
45
- };
46
- a.clearPayload = () => {
47
- a.payload = {};
48
- };
49
- return a as AFunction;
50
- }
51
- }
1
+ export { A } from '@24i/appstage-shared-analytics';
@@ -1,114 +1 @@
1
- import { EventsManager } from '@24i/bigscreen-sdk/events-manager';
2
- import { i18n } from '@24i/bigscreen-sdk/i18n';
3
- import type {
4
- AnalyticsEventsMap,
5
- AnalyticsEventsNames,
6
- AnalyticsClient,
7
- GeneralAppData,
8
- AppInfo,
9
- UserInfo,
10
- } from './interface';
11
- import { EVENT_NAMES, ONLY_ONCE_EVENTS, UNREPEATABLE_EVENTS } from './constants';
12
-
13
- class Analytics {
14
- private events = new EventsManager<AnalyticsEventsMap>();
15
-
16
- private onlyOnceAlreadySent: string[] = [];
17
-
18
- private lastPayloadForEvent: Partial<Record<AnalyticsEventsNames, string>> = {};
19
-
20
- private clients: Record<string, {
21
- client: AnalyticsClient
22
- registeredEvents: Partial<Record<AnalyticsEventsNames, Function>>,
23
- }> = {};
24
-
25
- private generalAppData: Partial<GeneralAppData> = {
26
- language: i18n.getLanguage(),
27
- resolution: `${window.innerWidth}x${window.innerHeight}`,
28
- };
29
-
30
- private appInfo: Partial<AppInfo> = {};
31
-
32
- private userInfo: Partial<UserInfo> = {};
33
-
34
- constructor() {
35
- i18n.addEventListener('languagechange', this.onLanguageChange);
36
- }
37
-
38
- addClient(client: AnalyticsClient, events: AnalyticsEventsNames[] | RegExp) {
39
- if (this.clients[client.name]) {
40
- throw new Error(`${client.name} analytics client is already registered.`);
41
- }
42
- const eventsToRegister = Array.isArray(events)
43
- ? events
44
- : EVENT_NAMES.filter(events.test.bind(events));
45
- const registeredEvents: Partial<Record<AnalyticsEventsNames, Function>> = {};
46
- eventsToRegister.forEach((eventName) => {
47
- const listener = (payload: any) => {
48
- client.onEvent(eventName, payload);
49
- };
50
- this.events.addEventListener(eventName, listener);
51
- registeredEvents[eventName] = listener;
52
- });
53
- this.clients[client.name] = {
54
- client,
55
- registeredEvents,
56
- };
57
- }
58
-
59
- removeClient(client: AnalyticsClient) {
60
- if (this.clients[client.name]) {
61
- const { registeredEvents } = this.clients[client.name];
62
- Object.keys(registeredEvents).forEach((eventName) => {
63
- // @ts-ignore - specific payload typing
64
- this.events.removeEventListener(eventName, registeredEvents[eventName]!);
65
- });
66
- delete this.clients[client.name];
67
- }
68
- }
69
-
70
- triggerEvent<Event extends AnalyticsEventsNames>(
71
- event: Event,
72
- triggerPayload: Parameters<AnalyticsEventsMap[Event]>[0] = undefined,
73
- ) {
74
- const fullPayload = {
75
- ...this.generalAppData,
76
- ...this.appInfo,
77
- ...this.userInfo,
78
- ...triggerPayload,
79
- };
80
- if (ONLY_ONCE_EVENTS.includes(event)) {
81
- const serializedPayload = `${event} - ${JSON.stringify(fullPayload)}`;
82
- if (this.onlyOnceAlreadySent.includes(serializedPayload)) return;
83
- this.onlyOnceAlreadySent.push(serializedPayload);
84
- }
85
- if (UNREPEATABLE_EVENTS.includes(event)) {
86
- const serializedPayload = JSON.stringify(fullPayload);
87
- if (this.lastPayloadForEvent[event] === serializedPayload) return;
88
- this.lastPayloadForEvent[event] = serializedPayload;
89
- }
90
- this.tryFillUpMissingData();
91
- // @ts-ignore - payload typing
92
- this.events.triggerEvent(event, fullPayload);
93
- }
94
-
95
- setAppInfo(data: Partial<AppInfo>) {
96
- this.appInfo = data;
97
- }
98
-
99
- setUserInfo(data: Partial<UserInfo>) {
100
- this.userInfo = data;
101
- }
102
-
103
- private onLanguageChange = () => {
104
- this.generalAppData.language = i18n.getLanguage();
105
- };
106
-
107
- private tryFillUpMissingData() {
108
- if (!this.generalAppData.language) {
109
- this.generalAppData.language = i18n.getLanguage();
110
- }
111
- }
112
- }
113
-
114
- export const analytics = new Analytics();
1
+ export { analytics } from '@24i/appstage-shared-analytics';
@@ -1,11 +1 @@
1
- import { AFunction } from './interface';
2
-
3
- export const analyticsOnScroll = (a: AFunction, itemsTotal: number) => (_: any, index: number) => {
4
- const percent = (index + 1) / itemsTotal;
5
- /* eslint-disable no-magic-numbers */
6
- if (percent >= 0.9) a('scroll_90');
7
- else if (percent >= 0.75) a('scroll_75');
8
- else if (percent >= 0.50) a('scroll_50');
9
- else if (percent >= 0.25) a('scroll_25');
10
- /* eslint-enable no-magic-numbers */
11
- };
1
+ export { analyticsOnScroll } from '@24i/appstage-shared-analytics';
@@ -1,12 +1 @@
1
- import { AnalyticsClient } from '../../interface';
2
-
3
- /**
4
- * Simple debugging client that dumps all triggers to the console.
5
- */
6
- export class ConsoleAnalytics implements AnalyticsClient {
7
- name = 'ConsoleAnalytics';
8
-
9
- onEvent(eventName: string, payload: any) {
10
- console.info('CA: ', eventName, payload);
11
- }
12
- }
1
+ export { ConsoleAnalytics } from '@24i/appstage-shared-analytics/dist/clients/ConsoleAnalytics';
@@ -1,56 +1 @@
1
- import { AnalyticsEventsNames } from './interface';
2
-
3
- export const EVENT_NAMES = [
4
- // App
5
- 'app_close',
6
-
7
- // Data loading / Player buffering
8
- 'buffering_start',
9
- 'buffering_stop',
10
-
11
- // Heartbeat
12
- 'heartbeat_5',
13
-
14
- // Playback
15
- 'playback_10',
16
- 'playback_25',
17
- 'playback_50',
18
- 'playback_75',
19
- 'playback_90',
20
- 'playback_pause',
21
- 'playback_start',
22
- 'playback_stop',
23
-
24
- // Player
25
- 'player_close',
26
- 'player_open',
27
-
28
- // Scenes
29
- 'scene_view',
30
-
31
- // Scrolling in scene
32
- 'scroll_25',
33
- 'scroll_50',
34
- 'scroll_75',
35
- 'scroll_90',
36
- ] as const;
37
-
38
- export const ONLY_ONCE_EVENTS: AnalyticsEventsNames[] = [
39
- // Playback
40
- 'playback_10',
41
- 'playback_25',
42
- 'playback_50',
43
- 'playback_75',
44
- 'playback_90',
45
-
46
- // Scrolling in scene
47
- 'scroll_25',
48
- 'scroll_50',
49
- 'scroll_75',
50
- 'scroll_90',
51
- ];
52
-
53
- export const UNREPEATABLE_EVENTS: AnalyticsEventsNames[] = [
54
- // Scenes
55
- 'scene_view',
56
- ];
1
+ export * from '@24i/appstage-shared-analytics/dist/constants';
@@ -1,82 +1 @@
1
- export type SceneInfo = {
2
- sceneId: string,
3
- sceneType: string,
4
- sceneName: string,
5
- };
6
-
7
- export type AppInfo = {
8
- applicationId: string,
9
- serviceId: string,
10
- };
11
-
12
- export type UserInfo = {
13
- userId: string,
14
- };
15
-
16
- export type GeneralAppData = {
17
- language: string,
18
- resolution: string,
19
- };
20
-
21
- /**
22
- * List of all tracked events.
23
- */
24
- export type AnalyticsEventsMap = {
25
- // App
26
- 'app_close': (payload: void) => void,
27
-
28
- // Data loading / Player buffering
29
- 'buffering_start': (payload: SceneInfo) => void,
30
- 'buffering_stop': (payload: SceneInfo) => void,
31
-
32
- // Heartbeat
33
- 'heartbeat_5': (payload: void) => void,
34
-
35
- // Playback
36
- 'playback_10': (payload: void) => void,
37
- 'playback_25': (payload: void) => void,
38
- 'playback_50': (payload: void) => void,
39
- 'playback_75': (payload: void) => void,
40
- 'playback_90': (payload: void) => void,
41
- 'playback_pause': (payload: void) => void,
42
- 'playback_start': (payload: void) => void,
43
- 'playback_stop': (payload: void) => void,
44
-
45
- // Player
46
- 'player_close': (payload: void) => void,
47
- 'player_open': (payload: void) => void,
48
-
49
- // Scenes
50
- 'scene_view': (payload: SceneInfo) => void,
51
-
52
- // Scrolling in scene
53
- 'scroll_25': (payload: SceneInfo) => void,
54
- 'scroll_50': (payload: SceneInfo) => void,
55
- 'scroll_75': (payload: SceneInfo) => void,
56
- 'scroll_90': (payload: SceneInfo) => void,
57
- };
58
-
59
- export type AnalyticsEventsNames = keyof AnalyticsEventsMap;
60
-
61
- export interface AnalyticsClient {
62
- name: string,
63
- onEvent<Event extends keyof AnalyticsEventsMap>(
64
- eventName: Event,
65
- payload: Parameters<AnalyticsEventsMap[Event]>,
66
- ): void;
67
- }
68
-
69
- export type AnyPayload = Record<string, any>;
70
-
71
- export interface AFunction {
72
- <
73
- EventName extends AnalyticsEventsNames,
74
- P extends Partial<Parameters<AnalyticsEventsMap[EventName]>[0]>,
75
- >(eventName: EventName, payload?: P): void,
76
-
77
- addToPayload(payload: AnyPayload): void,
78
-
79
- setPayload(payload: AnyPayload): void,
80
-
81
- clearPayload(): void,
82
- }
1
+ export * from '@24i/appstage-shared-analytics/dist/interface';
@@ -95,7 +95,15 @@ export class DeviceWebos extends DeviceBase {
95
95
  }
96
96
 
97
97
  initMouseActiveListener() {
98
+ // checks if mouse is already being used since startup - PRDSAPPSBW-1005
99
+ const mouseActiveAtStartupHandler = () => {
100
+ document.removeEventListener('mousemove', mouseActiveAtStartupHandler);
101
+ this.isMouseActive = true;
102
+ this.triggerEvent('mouseactive', { isMouseActive: true });
103
+ };
104
+ document.addEventListener('mousemove', mouseActiveAtStartupHandler);
98
105
  document.addEventListener('cursorStateChange', (event) => {
106
+ document.removeEventListener('mousemove', mouseActiveAtStartupHandler);
99
107
  // @ts-ignore - the LG event has this structure.
100
108
  this.isMouseActive = event.detail.visibility;
101
109
  this.triggerEvent('mouseactive', {
@@ -18,6 +18,7 @@ This package contains a prepared Electronic Program Guide component.
18
18
  - `channelHeaderWidthPx` - the width of the channel headers (the part before programs in a row) in pixels
19
19
  - `channelData` - the array of channels of a type that extends `EpgChannel` from the data layer
20
20
  - `fetchPrograms` - the function to fetch more programs for a channel in a given time range
21
+ - `smallestAllowedCellPx` - optional - cells with width less than this are marked with `.too-small` class (defaults to `0`)
21
22
  - `timelineWidthMs` - optional - the width of the timeline (the part where the programs are) in milliseconds (defaults to `90 minutes`)
22
23
  - `scrollStepMs` - optional - the amount of movement step left/right in milliseconds (defaults to `0.5 hours`)
23
24
  - `idleTimerMs` - optional - the amount of ms without user input after which epg jumps to the current time (defaults to `scrollStepMs`)
@@ -80,10 +81,11 @@ then the initial time of EPG will be `13:30` (if not set otherwise)
80
81
  - `endTime` is always taken as excluded = not the part of the program
81
82
  - so the program that gets focused/marked as live / etc. is the one that is just starting
82
83
  - when the Program displayed by the cell is currently live, the cell has an `.live` class
83
- - the Cell has also these following classes based on its width and breakpoints defined by `programTextModeResolver`
84
+ - the Cell has also these following classes based on its width, breakpoints defined by `programTextModeResolver` and `smallestAllowedCellPx`
84
85
  - `.with-text` - this cell should display full or ellipsis truncated text
85
86
  - `.ellipsis-only` - this cell should display only ellipsis. You can use this class to center the text.
86
87
  - `.no-text` - this cell is so small that it should be empty
88
+ - `.too-small` - this cell is too small to render the usual way, as in its only several pixels wide, less than the needed margin/border etc.
87
89
  - the element of the currently focused row has an `.active` class
88
90
 
89
91
  ## Styles
@@ -17,6 +17,7 @@ const LIVE_CLASS = 'live';
17
17
  const NO_TEXT_CLASS = 'no-text';
18
18
  const ELLIPSIS_ONLY_CLASS = 'ellipsis-only';
19
19
  const WITH_TEXT_CLASS = 'with-text';
20
+ const TOO_SMALL_CLASS = 'too-small';
20
21
 
21
22
  const getClassByTextMode = (textMode: ProgramTextMode) => {
22
23
  switch (textMode) {
@@ -40,6 +41,8 @@ export class Cell<
40
41
 
41
42
  widthPx = this.props.widthPx;
42
43
 
44
+ smallestAllowedCellPx = this.props.smallestAllowedCellPx;
45
+
43
46
  xOffsetPx = this.props.xOffsetPx;
44
47
 
45
48
  textMode = this.props.programTextModeResolver(this.widthPx);
@@ -137,6 +140,14 @@ export class Cell<
137
140
  this.textMode = textMode;
138
141
  }
139
142
 
143
+ private setTooSmallClass() {
144
+ if (this.widthPx < this.smallestAllowedCellPx) {
145
+ addClass(this.div, TOO_SMALL_CLASS);
146
+ } else {
147
+ removeClass(this.div, TOO_SMALL_CLASS);
148
+ }
149
+ }
150
+
140
151
  focus(options?: FocusOptions) {
141
152
  if (!this.wasFocusedWithCurrentItem && document.activeElement === this.div.current) {
142
153
  this.onFocusChange();
@@ -169,6 +180,7 @@ export class Cell<
169
180
  this.item = item;
170
181
  this.setLiveClass();
171
182
  this.setTextModeClass(textMode);
183
+ this.setTooSmallClass();
172
184
  this.programComponent.current!.updateData(
173
185
  this.item!,
174
186
  textMode,
@@ -179,13 +191,15 @@ export class Cell<
179
191
 
180
192
  render() {
181
193
  const {
182
- isLoading, xOffsetPx, widthPx, item, programTextModeResolver, renderProgram,
194
+ isLoading, xOffsetPx, widthPx, smallestAllowedCellPx,
195
+ item, programTextModeResolver, renderProgram,
183
196
  } = this.props;
184
197
  const textMode = programTextModeResolver(widthPx);
198
+ const tooSmall = widthPx < smallestAllowedCellPx ? TOO_SMALL_CLASS : '';
185
199
  return (
186
200
  <Interactable
187
201
  ref={this.div}
188
- className={`epg-cell ${getClassByTextMode(textMode)}`}
202
+ className={`epg-cell ${getClassByTextMode(textMode)} ${tooSmall}`}
189
203
  style={{
190
204
  width: `${widthPx}px`,
191
205
  ...getXOffsetStyleObject(xOffsetPx),
@@ -1,6 +1,7 @@
1
1
  import { filter, find, findIndex, map } from '@24i/bigscreen-sdk/perf-utils/array';
2
2
  import { Asset } from '@24i/smartapps-datalayer/models';
3
3
  import { i18n } from '@24i/bigscreen-sdk/i18n';
4
+ import { MINUTE_IN_MS } from '@24i/bigscreen-sdk/utils/timeConstants';
4
5
  import { isStartVisible, isEndVisible, isOverFullWidth } from './utils';
5
6
  import {
6
7
  DataRange,
@@ -14,6 +15,7 @@ const START = 0;
14
15
  const END = 1;
15
16
 
16
17
  const TIMELINE_WIDTHS_TO_FETCH = 3;
18
+ const MINIMUM_BREAK_LENGTH = MINUTE_IN_MS;
17
19
 
18
20
  export class DataManager implements IDataManager {
19
21
  private readonly data: Asset[] = [];
@@ -355,7 +357,8 @@ export class DataManager implements IDataManager {
355
357
  for (let i = data.length - 1; i > 0; i--) {
356
358
  const current = data[i];
357
359
  const previous = data[i - 1];
358
- if (previous.program!.endTime < current.program!.startTime) {
360
+ const breakLength = current.program!.startTime - previous.program!.endTime;
361
+ if (breakLength > 0 && breakLength >= MINIMUM_BREAK_LENGTH) {
359
362
  data.splice(
360
363
  i,
361
364
  0,
@@ -83,6 +83,7 @@ export class Epg<
83
83
  timeProvider: Date.now,
84
84
  scrollStepMs: HALF_HOUR_IN_MS,
85
85
  timelineSectionWidthMs: HALF_HOUR_IN_MS,
86
+ smallestAllowedCellPx: 0,
86
87
  limitFromMs: -Infinity,
87
88
  limitToMs: Infinity,
88
89
  initialChannelIndex: 0,
@@ -382,7 +383,7 @@ export class Epg<
382
383
  const {
383
384
  datePickerProps, timelineWidthPx, channelHeaderWidthPx, scrollStepMs, rowHeightPx,
384
385
  timelineSectionWidthMs, timelineWidthMs, initialChannelIndex, plugins,
385
- navigatableRows, additionalRows, programTextModeResolver,
386
+ navigatableRows, additionalRows, programTextModeResolver, smallestAllowedCellPx,
386
387
  renderDay, renderTime, renderProgram, renderChannelHeader, renderNowLineHeader,
387
388
  } = this.props;
388
389
  return (
@@ -442,6 +443,7 @@ export class Epg<
442
443
  renderChannelHeader={renderChannelHeader}
443
444
  renderProgram={renderProgram}
444
445
  isFocusInRows={this.isFocusInRows}
446
+ smallestAllowedCellPx={smallestAllowedCellPx}
445
447
  />
446
448
  <EpgMouseNavigation
447
449
  // @ts-ignore - needed to convert fallback (false) to interface
@@ -109,7 +109,8 @@ export class Row<
109
109
 
110
110
  render() {
111
111
  const {
112
- timelineWidthMs, timelineWidthPx, timestamp, time, pixelInMs,
112
+ timelineWidthMs, timestamp, time,
113
+ pixelInMs, timelineWidthPx, smallestAllowedCellPx,
113
114
  programTextModeResolver, onMouseEnter, renderProgram,
114
115
  } = this.props;
115
116
  return (
@@ -126,6 +127,7 @@ export class Row<
126
127
  onItemPress={this.onItemPress}
127
128
  onMouseEnter={onMouseEnter}
128
129
  renderProgram={renderProgram}
130
+ smallestAllowedCellPx={smallestAllowedCellPx}
129
131
  />
130
132
  );
131
133
  }
@@ -180,7 +180,8 @@ export class CellsManager<
180
180
 
181
181
  private addCell(item: Asset) {
182
182
  const {
183
- time, programTextModeResolver, onMouseEnter, onItemPress, onFocusChange, renderProgram,
183
+ time, smallestAllowedCellPx,
184
+ programTextModeResolver, onMouseEnter, onItemPress, onFocusChange, renderProgram,
184
185
  } = this.props;
185
186
  const cell = createRef<ICell<T>>();
186
187
  this.mountBefore((
@@ -195,6 +196,7 @@ export class CellsManager<
195
196
  onFocusChange={onFocusChange}
196
197
  onMouseEnter={onMouseEnter}
197
198
  renderProgram={renderProgram}
199
+ smallestAllowedCellPx={smallestAllowedCellPx}
198
200
  />
199
201
  ), this.endLoadingCell.current!.div);
200
202
  this.cells.push(cell);
@@ -241,7 +243,7 @@ export class CellsManager<
241
243
 
242
244
  render() {
243
245
  const {
244
- timelineWidthPx, time, dataGetter, programTextModeResolver,
246
+ timelineWidthPx, smallestAllowedCellPx, time, dataGetter, programTextModeResolver,
245
247
  onMouseEnter, onItemPress, onFocusChange, renderProgram,
246
248
  } = this.props;
247
249
  const visibleData = dataGetter(this.getRange());
@@ -257,6 +259,7 @@ export class CellsManager<
257
259
  onFocusChange={onFocusChange}
258
260
  onMouseEnter={onMouseEnter}
259
261
  renderProgram={renderProgram}
262
+ smallestAllowedCellPx={smallestAllowedCellPx}
260
263
  isLoading
261
264
  />
262
265
  {map(visibleData, (item) => {
@@ -274,6 +277,7 @@ export class CellsManager<
274
277
  onFocusChange={onFocusChange}
275
278
  onMouseEnter={onMouseEnter}
276
279
  renderProgram={renderProgram}
280
+ smallestAllowedCellPx={smallestAllowedCellPx}
277
281
  />
278
282
  );
279
283
  })}
@@ -288,6 +292,7 @@ export class CellsManager<
288
292
  onMouseEnter={onMouseEnter}
289
293
  renderProgram={renderProgram}
290
294
  isLoading
295
+ smallestAllowedCellPx={smallestAllowedCellPx}
291
296
  />
292
297
  </div>
293
298
  );
@@ -465,7 +465,8 @@ export class RowsManager<
465
465
 
466
466
  render() {
467
467
  const {
468
- timelineWidthMs, timelineWidthPx, timestamp, time, scrollStepMs, pixelInMs,
468
+ timelineWidthMs, timestamp, time, scrollStepMs,
469
+ pixelInMs, timelineWidthPx, smallestAllowedCellPx,
469
470
  programTextModeResolver, onItemPress, onMouseEnter, renderProgram, renderChannelHeader,
470
471
  } = this.props;
471
472
  const visibleData = this.getVisibleChannels();
@@ -492,6 +493,7 @@ export class RowsManager<
492
493
  onFocusChange={this.onFocusChange}
493
494
  onMouseEnter={onMouseEnter}
494
495
  renderProgram={renderProgram}
496
+ smallestAllowedCellPx={smallestAllowedCellPx}
495
497
  />
496
498
  ))}
497
499
  </Scroller>