@streamscloud/embeddable 14.0.3 → 14.1.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 (29) hide show
  1. package/dist/media-center/media-center/discover/discover-view-handler.svelte.js +1 -1
  2. package/dist/media-center/media-center/handlers/media-center-settings-handler.svelte.d.ts +12 -5
  3. package/dist/media-center/media-center/handlers/media-center-settings-handler.svelte.js +17 -5
  4. package/dist/media-center/media-center/header/media-center-header-mobile.svelte +2 -2
  5. package/dist/media-center/media-center/header/media-center-header.svelte +2 -2
  6. package/dist/media-center/media-center/media-center-view.svelte +7 -3
  7. package/dist/media-center/media-center/menu/menu.svelte +1 -1
  8. package/dist/posts/post-viewer/attachments-horizontal.svelte +15 -6
  9. package/dist/posts/post-viewer/cmp.post-viewer.svelte +4 -1
  10. package/dist/posts/post-viewer/cmp.post-viewer.svelte.d.ts +2 -0
  11. package/dist/posts/post-viewer/heading.svelte +6 -4
  12. package/dist/posts/post-viewer/media/post-media.svelte +5 -1
  13. package/dist/posts/post-viewer/post-texts.svelte +4 -2
  14. package/dist/streams/layout/cmp.layout.svelte +4 -1
  15. package/dist/streams/layout/cmp.layout.svelte.d.ts +1 -0
  16. package/dist/streams/layout/element-views/cmp.stock-stream-element.svelte +3 -2
  17. package/dist/streams/layout/element-views/price-element-view.svelte +5 -5
  18. package/dist/streams/layout/styles-transformer.d.ts +2 -1
  19. package/dist/streams/layout/styles-transformer.js +15 -9
  20. package/dist/streams/layout/styles.d.ts +1 -0
  21. package/dist/streams/stream-page-viewer/cmp.stream-page-viewer.svelte +2 -2
  22. package/dist/streams/stream-page-viewer/cmp.stream-page-viewer.svelte.d.ts +2 -0
  23. package/dist/streams/streams-player/streams-player-view.svelte +166 -46
  24. package/dist/streams/streams-player/types.d.ts +1 -0
  25. package/dist/ui/player/progress/cmp.chunks-progress.svelte +65 -0
  26. package/dist/ui/player/progress/cmp.chunks-progress.svelte.d.ts +9 -0
  27. package/dist/ui/player/progress/index.d.ts +1 -0
  28. package/dist/ui/player/progress/index.js +1 -0
  29. package/package.json +1 -1
@@ -58,7 +58,7 @@ export class DiscoverViewHandler {
58
58
  }
59
59
  }
60
60
  finally {
61
- this._shortVideosLoadingDeferred.resolve();
61
+ this._shortVideosLoadingDeferred?.resolve();
62
62
  this._shortVideosLoadingDeferred = null;
63
63
  }
64
64
  };
@@ -12,11 +12,7 @@ export declare class MediaCenterSettingsHandler {
12
12
  readonly dataProvider: IMediaCenterDataProvider;
13
13
  settings: MediaCenterSettings;
14
14
  });
15
- get playerSettings(): {
16
- locale?: Locale;
17
- showStreamsCloudWatermark?: boolean;
18
- playerColors?: Record<ThemeValue, PlayerColors>;
19
- };
15
+ get playerSettings(): ContentPlayerSettingsLocal;
20
16
  get actualMediaCenterColors(): PlayerColors;
21
17
  get locale(): Locale;
22
18
  get backgroundWrapperProps(): {
@@ -25,8 +21,19 @@ export declare class MediaCenterSettingsHandler {
25
21
  backgroundColor?: string | null;
26
22
  };
27
23
  get backgroundImageLoadedHandler(): ((url: string | null) => void) | undefined;
24
+ setMinOverlayOffsetTop: (value: number) => void;
28
25
  updateBackgroundImageUrl: (url: string | null | "not-applicable") => void;
29
26
  }
30
27
  export type MediaCenterSettingsWithColors = MediaCenterSettings & {
31
28
  playerColors?: PlayerColors;
32
29
  };
30
+ declare class ContentPlayerSettingsLocal {
31
+ private settings;
32
+ private dataProvider;
33
+ locale?: Locale;
34
+ showStreamsCloudWatermark?: boolean;
35
+ playerColors?: Record<ThemeValue, PlayerColors>;
36
+ overlayMinOffsetTop?: number;
37
+ constructor(settings: MediaCenterSettings, dataProvider: IMediaCenterDataProvider);
38
+ }
39
+ export {};
@@ -4,14 +4,11 @@ export class MediaCenterSettingsHandler {
4
4
  _backgroundImageUrl = $state(null);
5
5
  _mediaCenterSettings;
6
6
  _dataProvider;
7
- _contentPlayerSettings = $derived.by(() => ({
8
- locale: this._mediaCenterSettings.locale,
9
- playerColors: this._dataProvider.model?.playerColors,
10
- showStreamsCloudWatermark: this._mediaCenterSettings.showStreamsCloudWatermark
11
- }));
7
+ _contentPlayerSettings;
12
8
  constructor(init) {
13
9
  this._mediaCenterSettings = init.settings;
14
10
  this._dataProvider = init.dataProvider;
11
+ this._contentPlayerSettings = new ContentPlayerSettingsLocal(this._mediaCenterSettings, this._dataProvider);
15
12
  }
16
13
  get playerSettings() {
17
14
  return this._contentPlayerSettings;
@@ -32,7 +29,22 @@ export class MediaCenterSettingsHandler {
32
29
  get backgroundImageLoadedHandler() {
33
30
  return this._mediaCenterSettings.disableBackground ? undefined : (url) => this.updateBackgroundImageUrl(url);
34
31
  }
32
+ setMinOverlayOffsetTop = (value) => {
33
+ this._contentPlayerSettings.overlayMinOffsetTop = value;
34
+ };
35
35
  updateBackgroundImageUrl = (url) => {
36
36
  this._backgroundImageUrl = url;
37
37
  };
38
38
  }
39
+ class ContentPlayerSettingsLocal {
40
+ settings;
41
+ dataProvider;
42
+ locale = $derived.by(() => this.settings.locale);
43
+ showStreamsCloudWatermark = $derived.by(() => this.settings.showStreamsCloudWatermark);
44
+ playerColors = $derived.by(() => this.dataProvider.model?.playerColors);
45
+ overlayMinOffsetTop = $state(0);
46
+ constructor(settings, dataProvider) {
47
+ this.settings = settings;
48
+ this.dataProvider = dataProvider;
49
+ }
50
+ }
@@ -7,12 +7,12 @@ const localization = $derived(new MediaCenterHeaderLocalization(locale));
7
7
  const headerMounted = (node) => {
8
8
  const heightResizeObserver = new ResizeObserver(() => {
9
9
  const headerHeight = node.clientHeight;
10
- on.headerHeightChanged(headerHeight);
10
+ on === null || on === void 0 ? void 0 : on.headerHeightChanged(headerHeight);
11
11
  });
12
12
  heightResizeObserver.observe(node);
13
13
  return {
14
14
  destroy: () => {
15
- on.headerHeightChanged(0);
15
+ on === null || on === void 0 ? void 0 : on.headerHeightChanged(0);
16
16
  heightResizeObserver.disconnect();
17
17
  }
18
18
  };
@@ -20,13 +20,13 @@ const headerMounted = (node) => {
20
20
  headerRef = node;
21
21
  const heightResizeObserver = new ResizeObserver(() => {
22
22
  const headerHeight = node.clientHeight;
23
- on.headerHeightChanged(headerHeight);
23
+ on === null || on === void 0 ? void 0 : on.headerHeightChanged(headerHeight);
24
24
  calcHeaderGridProportions();
25
25
  });
26
26
  heightResizeObserver.observe(node);
27
27
  return {
28
28
  destroy: () => {
29
- on.headerHeightChanged(0);
29
+ on === null || on === void 0 ? void 0 : on.headerHeightChanged(0);
30
30
  heightResizeObserver.disconnect();
31
31
  }
32
32
  };
@@ -69,9 +69,13 @@ const activateSelectedStreamPlayer = (stream, categoryId) => {
69
69
  const activateSelectedStreamOfCategoryPlayer = (id, prefetchedStreams, categoryId) => {
70
70
  context.playStreamsFeed({ filter: { categoryId }, init: { prefetchedStreams, initialStreamId: id } });
71
71
  };
72
- const onHeaderHeightChanged = (height) => {
72
+ const onDesktopHeaderHeightChanged = (height) => {
73
73
  headerHeight = height;
74
74
  };
75
+ const onMobileHeaderHeightChanged = (height) => {
76
+ headerHeight = height;
77
+ context.settingsHandler.setMinOverlayOffsetTop(height);
78
+ };
75
79
  const onWidthAnchorMounted = (node) => {
76
80
  const resizeObserver = new ResizeObserver(() => {
77
81
  isMobileView = node.clientWidth <= 576;
@@ -142,9 +146,9 @@ const swipeToOpen = (node) => {
142
146
  <div class="media-center" use:onWidthAnchorMounted>
143
147
  <div class="media-center__header-and-content">
144
148
  {#if !isMobileView}
145
- <MediaCenterHeader context={context} dynamicActions={dynamicActions} on={{ headerHeightChanged: onHeaderHeightChanged }} />
149
+ <MediaCenterHeader context={context} dynamicActions={dynamicActions} on={{ headerHeightChanged: onDesktopHeaderHeightChanged }} />
146
150
  {:else}
147
- <MediaCenterHeaderMobile context={context} on={{ headerHeightChanged: onHeaderHeightChanged }} />
151
+ <MediaCenterHeaderMobile context={context} on={{ headerHeightChanged: onMobileHeaderHeightChanged }} />
148
152
  {/if}
149
153
 
150
154
  <div class="media-center__content-container" use:swipeToOpen>
@@ -291,7 +291,7 @@ const styles = $derived.by(() => {
291
291
  }
292
292
  .menu-item__text:hover, .menu-item__text--active {
293
293
  font-weight: 600;
294
- letter-spacing: -0.5px;
294
+ letter-spacing: -0.2px;
295
295
  }
296
296
  .menu-item__tag {
297
297
  height: min-content;
@@ -109,14 +109,13 @@ const variables = $derived.by(() => {
109
109
  <div
110
110
  class="attachments-horizontal__item"
111
111
  class:attachments-horizontal__item--single={attachmentsToShow.length === 1}
112
- data-theme={attachment.isAd ? 'default' : 'dark'}
113
112
  onclick={() => handleAttachmentClick(attachment)}
114
113
  onkeydown={() => {}}
115
114
  role="none"
116
115
  bind:this={attachmentElements[attachment.productId ? `product-${attachment.productId}` : `ad-${attachment.adId}`]}
117
116
  data-product-id={attachment.productId || undefined}
118
117
  data-ad-id={attachment.adId || undefined}>
119
- <div class="attachments-card" class:attachments-card--single={attachmentsToShow.length === 1}>
118
+ <div class="attachments-card" class:attachments-card--single={attachmentsToShow.length === 1} class:attachments-card--dark={!attachment.isAd}>
120
119
  <div class="attachments-card__thumb">
121
120
  <ImageRounded src={attachment.image} alt="" noBorders={true} />
122
121
  </div>
@@ -187,17 +186,27 @@ const variables = $derived.by(() => {
187
186
  }
188
187
 
189
188
  .attachments-card {
189
+ --_attachments-card--background-color: #ffffff;
190
+ --_attachments-card--border-color: #f2f2f2;
191
+ --_attachments-card--text--color: #000000;
192
+ --_attachments-card--text--secondary--color: #6b7280;
190
193
  display: grid;
191
194
  grid-template-columns: 2.375rem 1fr;
192
195
  grid-column-gap: 0.75rem;
193
196
  align-items: center;
194
197
  width: 15.625rem;
195
198
  padding: 0.375rem;
196
- background-color: rgb(from var(--sc-mc-color--bg-card) r g b/90%);
197
- color: var(--sc-mc-color--text-primary);
198
- border: 0.0625rem solid var(--sc-mc-color--border-card);
199
+ background-color: rgb(from var(--_attachments-card--background-color) r g b/90%);
200
+ color: var(--_attachments-card--text--color);
201
+ border: 0.0625rem solid var(--_attachments-card--border-color);
199
202
  border-radius: 0.25rem;
200
203
  }
204
+ .attachments-card--dark {
205
+ --_attachments-card--background-color: #000000;
206
+ --_attachments-card--border-color: #000000;
207
+ --_attachments-card--text--color: #ffffff;
208
+ --_attachments-card--text--secondary--color: #d1d5db;
209
+ }
201
210
  .attachments-card--single {
202
211
  width: 100%;
203
212
  }
@@ -222,7 +231,7 @@ const variables = $derived.by(() => {
222
231
  }
223
232
  .attachments-card__extra-info {
224
233
  font-size: 0.625rem;
225
- color: var(--sc-mc-color--text-secondary);
234
+ color: var(--_attachments-card--text--secondary--color);
226
235
  white-space: nowrap;
227
236
  overflow: hidden;
228
237
  text-overflow: ellipsis;
@@ -7,7 +7,7 @@ import { default as PostMedia } from './media/post-media.svelte';
7
7
  import { default as Texts } from './post-texts.svelte';
8
8
  import { PostViewerLocalization } from './post-viewer-localization';
9
9
  import { PostViewerUiManager } from './ui-manager.svelte';
10
- let { model, trackingParams: externalTrackingParams, controlsColors = null, enableAttachments = true, controlActions, enableControls = true, autoplay = 'on-appearance', locale = 'en', on } = $props();
10
+ let { model, trackingParams: externalTrackingParams, controlsColors = null, enableAttachments = true, controlActions, enableControls = true, autoplay = 'on-appearance', locale = 'en', on, overlay } = $props();
11
11
  const localization = $derived(new PostViewerLocalization(locale));
12
12
  const uiManager = new PostViewerUiManager();
13
13
  $effect(() => {
@@ -88,6 +88,9 @@ const variables = $derived.by(() => {
88
88
  scaleEffect={true} />
89
89
  </div>
90
90
  {/if}
91
+ {#if overlay}
92
+ {@render overlay()}
93
+ {/if}
91
94
  </div>
92
95
 
93
96
  <style>@keyframes fadeIn {
@@ -2,6 +2,7 @@ import type { Locale } from '../../core/locale';
2
2
  import type { TrackingParams } from '../../marketing-tracking';
3
3
  import { PostModel } from '../model';
4
4
  import { type PlayerButtonDef } from '../../ui/player/button';
5
+ import type { Snippet } from 'svelte';
5
6
  type Props = {
6
7
  model: PostModel;
7
8
  trackingParams: TrackingParams | null;
@@ -21,6 +22,7 @@ type Props = {
21
22
  adClick?: (adId: string) => void;
22
23
  adImpression?: (adId: string) => void;
23
24
  };
25
+ overlay?: Snippet;
24
26
  };
25
27
  declare const Cmp: import("svelte").Component<Props, {}, "">;
26
28
  type Cmp = ReturnType<typeof Cmp>;
@@ -41,6 +41,8 @@ const variables = $derived.by(() => {
41
41
  }
42
42
  }
43
43
  .post-viewer-heading {
44
+ --_post-viewer-heading-texts--color: #ffffff;
45
+ --_post-viewer-heading--text--shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
44
46
  display: flex;
45
47
  align-items: center;
46
48
  padding: var(--_post-viewer--heading--padding);
@@ -67,8 +69,8 @@ const variables = $derived.by(() => {
67
69
  font-size: 0.75rem;
68
70
  line-height: 0.9375rem;
69
71
  font-weight: 500;
70
- color: var(--sc-mc-color--text-white);
71
- text-shadow: var(--sc-mc-color--text-white-shadow);
72
+ color: var(--_post-viewer-heading-texts--color);
73
+ text-shadow: var(--_post-viewer-heading--text--shadow);
72
74
  text-overflow: ellipsis;
73
75
  width: 100%;
74
76
  white-space: nowrap;
@@ -79,6 +81,6 @@ const variables = $derived.by(() => {
79
81
  font-size: 0.625rem;
80
82
  line-height: 0.75rem;
81
83
  font-weight: 400;
82
- color: var(--sc-mc-color--text-white);
83
- text-shadow: var(--sc-mc-color--text-white-shadow);
84
+ color: var(--_post-viewer-heading-texts--color);
85
+ text-shadow: var(--_post-viewer-heading--text--shadow);
84
86
  }</style>
@@ -46,13 +46,17 @@ let { id, media, locale, autoplay = 'on-appearance', on } = $props();
46
46
  }
47
47
  }
48
48
  .post-media {
49
+ --_post-media--background-color: #ffffff;
49
50
  width: 100%;
50
51
  min-width: 100%;
51
52
  max-width: 100%;
52
53
  height: 100%;
53
54
  min-height: 100%;
54
55
  max-height: 100%;
55
- background-color: rgb(from var(--sc-mc-color--bg-panel) r g b/60%);
56
+ background-color: rgb(from var(--_post-media--background-color) r g b/60%);
57
+ }
58
+ :global([data-theme="dark"]) .post-media {
59
+ --_post-media--background-color: #000000;
56
60
  }
57
61
  .post-media--fit {
58
62
  --video--media-fit: contain;
@@ -45,8 +45,10 @@ const variables = $derived.by(() => {
45
45
  }
46
46
  }
47
47
  .post-viewer-texts {
48
- color: var(--sc-mc-color--text-white);
49
- text-shadow: var(--sc-mc-color--text-white-shadow);
48
+ --_post-viewer-texts--color: #ffffff;
49
+ --_post-viewer-texts--shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
50
+ color: var(--_post-viewer-texts--color);
51
+ text-shadow: var(--_post-viewer-texts--shadow);
50
52
  padding: var(--_post-viewer-texts--padding);
51
53
  display: flex;
52
54
  flex-direction: column;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">import { ProportionalContainer } from '../../ui/proportional-container';
2
2
  import { generateStreamLayoutStyles } from './styles-transformer';
3
- let { model, children, controls } = $props();
3
+ let { model, children, controls, overlay } = $props();
4
4
  </script>
5
5
 
6
6
  <ProportionalContainer ratio={9 / 16}>
@@ -9,6 +9,9 @@ let { model, children, controls } = $props();
9
9
  {#if controls}
10
10
  {@render controls()}
11
11
  {/if}
12
+ {#if overlay}
13
+ {@render overlay()}
14
+ {/if}
12
15
  </div>
13
16
  </ProportionalContainer>
14
17
 
@@ -6,6 +6,7 @@ type Props = {
6
6
  };
7
7
  children: Snippet;
8
8
  controls?: Snippet;
9
+ overlay?: Snippet;
9
10
  };
10
11
  declare const Cmp: import("svelte").Component<Props, {}, "">;
11
12
  type Cmp = ReturnType<typeof Cmp>;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">var _a;
2
2
  import { StockStreamElementLocalization } from './stock-stream-element-localization';
3
3
  import { StockStreamElementLevel } from '../enums';
4
- import { mapFlexJustifyContent, transformNumericValue } from '../styles-transformer';
4
+ import { mapFlexJustifyContent, transformNumericValue, transformTextColorValue } from '../styles-transformer';
5
5
  let { model, heightOverrideDdu, locale } = $props();
6
6
  const localization = $derived(new StockStreamElementLocalization(locale));
7
7
  const height = $derived(heightOverrideDdu || ((_a = model.styles) === null || _a === void 0 ? void 0 : _a.height) || 16);
@@ -19,7 +19,8 @@ const dotCustomStyles = $derived.by(() => {
19
19
  return values.join('');
20
20
  });
21
21
  const quantityCustomStyles = $derived.by(() => {
22
- const values = [`font-size: ${transformNumericValue(height * 0.75)};`];
22
+ var _a;
23
+ const values = [`font-size: ${transformNumericValue(height * 0.75)};`, `color: ${transformTextColorValue((_a = model.styles) === null || _a === void 0 ? void 0 : _a.color)};`];
23
24
  return values.join('');
24
25
  });
25
26
  </script>
@@ -12,7 +12,7 @@ import { toPriceRepresentation } from '../../../products/price-helper';
12
12
  import { LineClamp } from '../../../ui/line-clamp';
13
13
  import { PriceStreamElementLocalization } from './price-stream-element-localization';
14
14
  import { default as StockElementView } from './cmp.stock-stream-element.svelte';
15
- import { mapFlexJustifyContent, mapFontFamily, mapFontWeight, transformColorValue, transformFontSizeValue, transformNumericValue } from '../styles-transformer';
15
+ import { mapFlexJustifyContent, mapFontFamily, mapFontWeight, transformTextColorValue, transformBackgroundColorValue, transformFontSizeValue, transformNumericValue } from '../styles-transformer';
16
16
  let { maxElementHeight, model, data, localization, on } = $props();
17
17
  let priceElementRef = $state.raw(null);
18
18
  let priceContainerRef = $state.raw(null);
@@ -62,7 +62,7 @@ const priceCustomStyles = $derived.by(() => {
62
62
  `font-size: ${transformFontSizeValue(priceHeight / lineHeight)};`,
63
63
  `line-height: ${lineHeight};`,
64
64
  `font-weight: ${mapFontWeight((_a = model.styles) === null || _a === void 0 ? void 0 : _a.fontWeight)};`,
65
- `color: ${transformColorValue(data.salePrice ? (_b = model.styles) === null || _b === void 0 ? void 0 : _b.salePriceColor : (_c = model.styles) === null || _c === void 0 ? void 0 : _c.regularPriceColor)};`
65
+ `color: ${transformTextColorValue(data.salePrice ? (_b = model.styles) === null || _b === void 0 ? void 0 : _b.salePriceColor : (_c = model.styles) === null || _c === void 0 ? void 0 : _c.regularPriceColor)};`
66
66
  ];
67
67
  return values.join('');
68
68
  });
@@ -74,8 +74,8 @@ const saveValueCustomStyles = $derived.by(() => {
74
74
  `font-weight: 500;`,
75
75
  `padding: ${transformNumericValue(priceHeight / 8)} ${transformNumericValue(priceHeight / 4)};`,
76
76
  `border-radius: ${transformNumericValue(priceHeight / 8)};`,
77
- `background-color: ${transformColorValue((_a = model.styles) === null || _a === void 0 ? void 0 : _a.saveValueBackgroundColor)};`,
78
- `color: ${transformColorValue((_b = model.styles) === null || _b === void 0 ? void 0 : _b.saveValueColor)};`
77
+ `background-color: ${transformBackgroundColorValue((_a = model.styles) === null || _a === void 0 ? void 0 : _a.saveValueBackgroundColor)};`,
78
+ `color: ${transformTextColorValue((_b = model.styles) === null || _b === void 0 ? void 0 : _b.saveValueColor)};`
79
79
  ];
80
80
  return values.join('');
81
81
  });
@@ -84,7 +84,7 @@ const beforeValueCustomStyles = $derived.by(() => {
84
84
  const values = [
85
85
  `font-size: ${transformFontSizeValue(priceHeight / 5)};`,
86
86
  `font-weight: 500;`,
87
- `color: ${transformColorValue((_a = model.styles) === null || _a === void 0 ? void 0 : _a.beforeValueColor)};`
87
+ `color: ${transformTextColorValue((_a = model.styles) === null || _a === void 0 ? void 0 : _a.beforeValueColor)};`
88
88
  ];
89
89
  return values.join('');
90
90
  });
@@ -13,4 +13,5 @@ export declare const mapTextAlign: (value: StreamElementStyleHorizontalAlign | n
13
13
  export declare const mapFlexJustifyContent: (value: StreamElementStyleHorizontalAlign | null | undefined) => "center" | "flex-start" | "flex-end";
14
14
  export declare const transformFontSizeValue: (value: number | null | undefined) => string;
15
15
  export declare const transformNumericValue: (value: number | null | undefined, fallback?: string) => string;
16
- export declare const transformColorValue: (value: string | null | undefined) => string;
16
+ export declare const transformTextColorValue: (value: string | null | undefined) => string;
17
+ export declare const transformBackgroundColorValue: (value: string | null | undefined) => string;
@@ -4,7 +4,7 @@ export const generateStreamLayoutStyles = (styles) => {
4
4
  if (!styles) {
5
5
  return '';
6
6
  }
7
- const values = [`background-color: ${transformColorValue(styles.backgroundColor)}`, `font-family: ${mapFontFamily(styles.fontFamily)}`];
7
+ const values = [`background-color: ${transformBackgroundColorValue(styles.backgroundColor)}`, `font-family: ${mapFontFamily(styles.fontFamily)}`];
8
8
  return values.join(';');
9
9
  };
10
10
  export const generateContainerStyles = (styles) => {
@@ -20,10 +20,10 @@ export const generateContainerStyles = (styles) => {
20
20
  `padding-right: ${transformNumericValue(styles.paddingRight)}`,
21
21
  `padding-bottom: ${transformNumericValue(styles.paddingBottom)}`,
22
22
  `padding-left: ${transformNumericValue(styles.paddingLeft)}`,
23
- `background-color: ${transformColorValue(styles.backgroundColor)}`,
23
+ `background-color: ${transformBackgroundColorValue(styles.backgroundColor)}`,
24
24
  `border-width: ${styles.borderColor ? '1px' : '0'}`,
25
25
  `border-radius: ${transformNumericValue(styles.borderRadius)}`,
26
- `border-color: ${transformColorValue(styles.borderColor)}`,
26
+ `border-color: ${transformBackgroundColorValue(styles.borderColor)}`,
27
27
  `overflow: ${styles.borderRadius ? 'hidden' : 'visible'}`
28
28
  ];
29
29
  if (styles.direction) {
@@ -49,11 +49,11 @@ export const generateButtonStyles = (styles) => {
49
49
  `padding-right: ${transformNumericValue(styles.paddingRight)}`,
50
50
  `padding-bottom: ${transformNumericValue(styles.paddingBottom)}`,
51
51
  `padding-left: ${transformNumericValue(styles.paddingLeft)}`,
52
- `color: ${transformColorValue(styles.textColor)}`,
53
- `background-color: ${transformColorValue(styles.backgroundColor)}`,
52
+ `color: ${transformTextColorValue(styles.textColor)}`,
53
+ `background-color: ${transformBackgroundColorValue(styles.backgroundColor)}`,
54
54
  `border-width: ${styles.borderColor ? '1px' : '0'}`,
55
55
  `border-radius: ${transformNumericValue(styles.borderRadius)}`,
56
- `border-color: ${transformColorValue(styles.borderColor)}`,
56
+ `border-color: ${transformBackgroundColorValue(styles.borderColor)}`,
57
57
  `overflow: hidden`
58
58
  ];
59
59
  return values.join(';');
@@ -94,7 +94,7 @@ export const generateProductsSliderStyles = (styles) => {
94
94
  `padding-right: ${transformNumericValue(styles.paddingRight)}`,
95
95
  `padding-bottom: ${transformNumericValue(styles.paddingBottom)}`,
96
96
  `padding-left: ${transformNumericValue(styles.paddingLeft)}`,
97
- `background-color: ${transformColorValue(styles.backgroundColor)}`,
97
+ `background-color: ${transformBackgroundColorValue(styles.backgroundColor)}`,
98
98
  `overflow-x: auto`,
99
99
  `overflow-y: hidden`,
100
100
  `scrollbar-width: none`,
@@ -111,7 +111,7 @@ export const generateTextStyles = (styles) => {
111
111
  `font-weight: ${mapFontWeight(styles.fontWeight)}`,
112
112
  `line-height: ${transformNumericValue(styles.lineHeight, '1.2')}`,
113
113
  `text-align: ${mapTextAlign(styles.textAlign)}`,
114
- `color: ${transformColorValue(styles.color)}`
114
+ `color: ${transformTextColorValue(styles.color)}`
115
115
  ];
116
116
  return values.join(';');
117
117
  };
@@ -203,7 +203,13 @@ export const transformNumericValue = (value, fallback = '0') => {
203
203
  }
204
204
  return `${(value / baseContainerWidth) * 100}cqi`;
205
205
  };
206
- export const transformColorValue = (value) => {
206
+ export const transformTextColorValue = (value) => {
207
+ if (value === null || value === undefined) {
208
+ return '#000000';
209
+ }
210
+ return value;
211
+ };
212
+ export const transformBackgroundColorValue = (value) => {
207
213
  if (value === null || value === undefined) {
208
214
  return 'transparent';
209
215
  }
@@ -66,6 +66,7 @@ export type ProductsSliderStreamElementStyles = {
66
66
  export type StockStreamElementStyles = {
67
67
  height?: number | null;
68
68
  horizontalAlign?: StreamElementStyleHorizontalAlign | null;
69
+ color?: string | null;
69
70
  };
70
71
  export type TextStreamElementStyles = {
71
72
  fontSize?: number | null;
@@ -1,12 +1,12 @@
1
1
  <script lang="ts">import { StreamLayoutSlot, StreamPageLayout, StreamLayoutSlotContent } from '../layout';
2
2
  import { ResponsivePlayerButtonsGroup } from '../../ui/player/button';
3
3
  import { StreamPageViewerLocalization } from './stream-page-viewer-localization';
4
- let { page, trackingParams, overlayControls, locale, on } = $props();
4
+ let { page, trackingParams, overlayControls, locale, on, overlay } = $props();
5
5
  const localization = $derived(new StreamPageViewerLocalization(locale));
6
6
  </script>
7
7
 
8
8
  {#if page.type === 'general'}
9
- <StreamPageLayout model={page.layout}>
9
+ <StreamPageLayout model={page.layout} overlay={overlay}>
10
10
  {#each page.layout.slots as slot (slot)}
11
11
  <StreamLayoutSlot model={slot}>
12
12
  <StreamLayoutSlotContent model={slot} trackingParams={trackingParams} on={on} locale={localization.elementsLocale} />
@@ -2,6 +2,7 @@ import type { Locale } from '../../core/locale';
2
2
  import { type StreamTrackingParams } from '../layout';
3
3
  import { type PlayerButtonDef } from '../../ui/player/button';
4
4
  import type { StreamPageViewerModel } from './types';
5
+ import type { Snippet } from 'svelte';
5
6
  type Props = {
6
7
  page: StreamPageViewerModel;
7
8
  trackingParams: StreamTrackingParams;
@@ -19,6 +20,7 @@ type Props = {
19
20
  productImpression?: (productId: string) => void;
20
21
  progress?: (videoId: string, progress: number) => void;
21
22
  };
23
+ overlay?: Snippet;
22
24
  };
23
25
  declare const Cmp: import("svelte").Component<Props, {}, "">;
24
26
  type Cmp = ReturnType<typeof Cmp>;
@@ -17,6 +17,7 @@ import { mapToPostModel } from '../layout/models';
17
17
  import { StreamPageViewer } from '../stream-page-viewer';
18
18
  import { IconColor } from '../../ui/icon';
19
19
  import { Player, PlayerConfig, PlayerSettings } from '../../ui/player';
20
+ import { ChunksProgress } from '../../ui/player/progress';
20
21
  import { default as Overview } from './stream-overview.svelte';
21
22
  import { StreamPlayerLocalization } from './stream-player-localization';
22
23
  import { StreamsPlayerBuffer } from './streams-player-buffer.svelte';
@@ -235,6 +236,49 @@ const currentItemActions = $derived.by(() => {
235
236
  }
236
237
  return result;
237
238
  });
239
+ const handlePageViewMounted = (node) => {
240
+ const updatePosition = () => {
241
+ var _a;
242
+ const progressElement = node.querySelector('[id^="chunk-progress-"]');
243
+ if (!progressElement) {
244
+ return;
245
+ }
246
+ const overlayMinOffsetTop = (_a = playerSettings === null || playerSettings === void 0 ? void 0 : playerSettings.overlayMinOffsetTop) !== null && _a !== void 0 ? _a : 0;
247
+ if (!overlayMinOffsetTop) {
248
+ progressElement.style.top = '';
249
+ return;
250
+ }
251
+ const contentElement = node.firstElementChild;
252
+ if (!contentElement) {
253
+ progressElement.style.top = '';
254
+ return;
255
+ }
256
+ const nodeRect = node.getBoundingClientRect();
257
+ const contentRect = contentElement.getBoundingClientRect();
258
+ const contentOffsetTop = contentRect.top - nodeRect.top;
259
+ if (contentOffsetTop <= 0) {
260
+ progressElement.style.top = `${overlayMinOffsetTop}px`;
261
+ return;
262
+ }
263
+ const overlayOverlap = overlayMinOffsetTop - contentOffsetTop;
264
+ if (overlayOverlap > 0) {
265
+ progressElement.style.top = `${overlayOverlap}px`;
266
+ }
267
+ else {
268
+ progressElement.style.top = '';
269
+ }
270
+ };
271
+ updatePosition();
272
+ const resizeObserver = new ResizeObserver(() => {
273
+ updatePosition();
274
+ });
275
+ resizeObserver.observe(node);
276
+ return {
277
+ destroy() {
278
+ resizeObserver.disconnect();
279
+ }
280
+ };
281
+ };
238
282
  //#region Activity Tracking
239
283
  const resetInactivityTimer = () => {
240
284
  if (!isActive) {
@@ -300,59 +344,85 @@ const stopActivityTracking = () => {
300
344
  }} />
301
345
  {/if}
302
346
  {/snippet}
347
+ {#snippet overlay()}
348
+ {#if buffer?.activeChunk && buffer.activeChunk.chunkItems.length > 1}
349
+ <div class="stream-progress" id="chunk-progress-{buffer.activeChunk.model.id}">
350
+ {#snippet info()}
351
+ <div class="stream-progress-info">
352
+ <div class="stream-progress-info__title">
353
+ {buffer?.activeChunk.model.title}
354
+ </div>
355
+ {#if buffer?.activeChunk.model.subTitle}
356
+ <div class="stream-progress-info__sub-title">
357
+ {buffer.activeChunk.model.subTitle}
358
+ </div>
359
+ {/if}
360
+ </div>
361
+ {/snippet}
362
+ <ChunksProgress
363
+ allChunkItems={buffer.activeChunk.chunkItems.map((i) => i.model.id)}
364
+ activeChunkItem={buffer.activeChunk.activeChunkItem.model.id}
365
+ chunkInfo={info} />
366
+ </div>
367
+ {/if}
368
+ {/snippet}
303
369
  <Player
304
370
  config={contentPlayerConfig}
305
371
  itemActions={currentItemActions}
306
372
  attachmentsView={buffer?.current && itemAsPostModel(buffer.current)?.attachments ? attachmentsView : undefined}>
307
373
  {#snippet itemView({ item })}
308
- {#if item.type === 'general'}
309
- <StreamPageViewer
310
- page={item}
311
- trackingParams={streamTrackingParams}
312
- locale={localization.locale}
313
- overlayControls={{
314
- enabled: contentPlayerConfig.uiManager.showControlsOverlay,
315
- colors: {
316
- active: contentPlayerConfig.playerColors.button,
317
- inactive: contentPlayerConfig.playerColors.buttonInactive
318
- },
319
- actions: buffer?.activeChunk
320
- ? streamActionsGenerator.getGeneralStreamPageActions({ streamId: buffer.activeChunk.model.id, streamPageId: item.id })
321
- : []
322
- }}
323
- on={{
324
- progress: (videoId, progress) => onProgress(item.id, videoId, progress),
325
- productClick: (productId) => onStreamProductClick(productId),
326
- productImpression: (productId) => onStreamProductImpression(productId)
327
- }} />
328
- {:else}
329
- {@const postModel = itemAsPostModel(item)}
330
- {#if postModel}
331
- {@const handler = buffer?.activeChunk
332
- ? streamActionsGenerator.getPostActionsHandler({
333
- model: postModel,
334
- streamId: buffer.activeChunk.model.id,
335
- streamPageId: item.id
336
- })
337
- : null}
338
- <PostViewer
339
- model={postModel}
340
- controlsColors={{ active: contentPlayerConfig.playerColors.button, inactive: contentPlayerConfig.playerColors.buttonInactive }}
341
- trackingParams={trackingParams}
342
- enableAttachments={contentPlayerConfig.uiManager.showAttachmentsOverlay}
343
- enableControls={contentPlayerConfig.uiManager.showControlsOverlay}
344
- controlActions={handler?.actions ?? []}
345
- autoplay="on-appearance"
346
- locale={contentPlayerConfig.settings.locale}
374
+ <div class="page-view" use:handlePageViewMounted>
375
+ {#if item.type === 'general'}
376
+ <StreamPageViewer
377
+ page={item}
378
+ trackingParams={streamTrackingParams}
379
+ locale={localization.locale}
380
+ overlayControls={{
381
+ enabled: contentPlayerConfig.uiManager.showControlsOverlay,
382
+ colors: {
383
+ active: contentPlayerConfig.playerColors.button,
384
+ inactive: contentPlayerConfig.playerColors.buttonInactive
385
+ },
386
+ actions: buffer?.activeChunk
387
+ ? streamActionsGenerator.getGeneralStreamPageActions({ streamId: buffer.activeChunk.model.id, streamPageId: item.id })
388
+ : []
389
+ }}
347
390
  on={{
348
- progress: (progress) => onShortVideoProgress(item.id, postModel.id, progress),
349
- productClick: (productId) => onShortVideoProductClick(productId, postModel.id),
350
- productImpression: (productId) => onShortVideoProductImpression(productId, postModel.id),
351
- adClick: (adId) => onShortVideoAdClick(adId),
352
- adImpression: (adId) => onShortVideoAdImpression(adId)
353
- }} />
391
+ progress: (videoId, progress) => onProgress(item.id, videoId, progress),
392
+ productClick: (productId) => onStreamProductClick(productId),
393
+ productImpression: (productId) => onStreamProductImpression(productId)
394
+ }}
395
+ overlay={overlay} />
396
+ {:else}
397
+ {@const postModel = itemAsPostModel(item)}
398
+ {#if postModel}
399
+ {@const handler = buffer?.activeChunk
400
+ ? streamActionsGenerator.getPostActionsHandler({
401
+ model: postModel,
402
+ streamId: buffer.activeChunk.model.id,
403
+ streamPageId: item.id
404
+ })
405
+ : null}
406
+ <PostViewer
407
+ model={postModel}
408
+ controlsColors={{ active: contentPlayerConfig.playerColors.button, inactive: contentPlayerConfig.playerColors.buttonInactive }}
409
+ trackingParams={trackingParams}
410
+ enableAttachments={contentPlayerConfig.uiManager.showAttachmentsOverlay}
411
+ enableControls={contentPlayerConfig.uiManager.showControlsOverlay}
412
+ controlActions={handler?.actions ?? []}
413
+ autoplay="on-appearance"
414
+ locale={contentPlayerConfig.settings.locale}
415
+ on={{
416
+ progress: (progress) => onShortVideoProgress(item.id, postModel.id, progress),
417
+ productClick: (productId) => onShortVideoProductClick(productId, postModel.id),
418
+ productImpression: (productId) => onShortVideoProductImpression(productId, postModel.id),
419
+ adClick: (adId) => onShortVideoAdClick(adId),
420
+ adImpression: (adId) => onShortVideoAdImpression(adId)
421
+ }}
422
+ overlay={overlay} />
423
+ {/if}
354
424
  {/if}
355
- {/if}
425
+ </div>
356
426
  {/snippet}
357
427
  {#snippet overviewPanelContent()}
358
428
  {#if buffer}
@@ -365,3 +435,53 @@ const stopActivityTracking = () => {
365
435
  {/if}
366
436
  {/snippet}
367
437
  </Player>
438
+
439
+ <style>@keyframes fadeIn {
440
+ 0% {
441
+ opacity: 1;
442
+ }
443
+ 50% {
444
+ opacity: 0.4;
445
+ }
446
+ 100% {
447
+ opacity: 1;
448
+ }
449
+ }
450
+ .page-view {
451
+ width: 100%;
452
+ height: 100%;
453
+ position: relative;
454
+ display: flex;
455
+ justify-content: center;
456
+ align-items: center;
457
+ }
458
+
459
+ .stream-progress {
460
+ position: absolute;
461
+ top: 0.75rem;
462
+ left: 1rem;
463
+ right: 1rem;
464
+ }
465
+
466
+ .stream-progress-info {
467
+ color: var(--sc-mc-color--text-white);
468
+ text-shadow: var(--sc-mc-color--text-white-shadow);
469
+ display: flex;
470
+ flex-direction: column;
471
+ min-width: 0;
472
+ gap: 0.25rem;
473
+ }
474
+ .stream-progress-info__title {
475
+ font-size: 1.125rem;
476
+ text-overflow: ellipsis;
477
+ width: 100%;
478
+ white-space: nowrap;
479
+ overflow: hidden;
480
+ }
481
+ .stream-progress-info__sub-title {
482
+ font-size: 0.875rem;
483
+ text-overflow: ellipsis;
484
+ width: 100%;
485
+ white-space: nowrap;
486
+ overflow: hidden;
487
+ }</style>
@@ -48,6 +48,7 @@ export type StreamsPlayerSettings = {
48
48
  locale?: Locale;
49
49
  showStreamsCloudWatermark?: boolean;
50
50
  playerColors?: Record<ThemeValue, PlayerColors>;
51
+ overlayMinOffsetTop?: number;
51
52
  };
52
53
  export type StreamAmplificationParameters = {
53
54
  campaignId: string;
@@ -0,0 +1,65 @@
1
+ <script lang="ts">let { allChunkItems, activeChunkItem, chunkInfo } = $props();
2
+ let currentIndex = $derived(allChunkItems.indexOf(activeChunkItem));
3
+ export {};
4
+ </script>
5
+
6
+ <div class="chunks-progress">
7
+ <div
8
+ class="chunks"
9
+ class:chunks--few={allChunkItems.length <= 3}
10
+ class:chunks--common={allChunkItems.length > 3 && allChunkItems.length <= 6}
11
+ class:chunks--many={allChunkItems.length > 6}>
12
+ {#each allChunkItems as chunk, i (chunk)}
13
+ <div class="chunk" class:active={i <= currentIndex} aria-label={chunk}></div>
14
+ {/each}
15
+ </div>
16
+
17
+ {#if chunkInfo}
18
+ <div class="info">
19
+ {@render chunkInfo()}
20
+ </div>
21
+ {/if}
22
+ </div>
23
+
24
+ <style>@keyframes fadeIn {
25
+ 0% {
26
+ opacity: 1;
27
+ }
28
+ 50% {
29
+ opacity: 0.4;
30
+ }
31
+ 100% {
32
+ opacity: 1;
33
+ }
34
+ }
35
+ .chunks-progress {
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: 0.5rem;
39
+ }
40
+
41
+ .chunks {
42
+ display: flex;
43
+ }
44
+ .chunks--few {
45
+ gap: 0.75rem;
46
+ }
47
+ .chunks--common {
48
+ gap: 0.5625rem;
49
+ }
50
+ .chunks--many {
51
+ gap: 0.375rem;
52
+ }
53
+
54
+ .chunk {
55
+ flex: 1;
56
+ height: 0.125rem;
57
+ border-radius: 1px;
58
+ background-color: rgb(from #6b7280 r g b/40%);
59
+ transition: background-color 0.2s ease;
60
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
61
+ }
62
+
63
+ .chunk.active {
64
+ background-color: rgb(from #fafafa r g b/60%);
65
+ }</style>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Props = {
3
+ allChunkItems: string[];
4
+ activeChunkItem: string;
5
+ chunkInfo?: Snippet;
6
+ };
7
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
8
+ type Cmp = ReturnType<typeof Cmp>;
9
+ export default Cmp;
@@ -0,0 +1 @@
1
+ export { default as ChunksProgress } from './cmp.chunks-progress.svelte';
@@ -0,0 +1 @@
1
+ export { default as ChunksProgress } from './cmp.chunks-progress.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/embeddable",
3
- "version": "14.0.3",
3
+ "version": "14.1.0",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",