@streamscloud/embeddable 5.0.0 → 5.1.1

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.
@@ -3,7 +3,7 @@ export const mapToAdCardModel = (payload) => {
3
3
  return {
4
4
  id: payload.id,
5
5
  type: payload.type,
6
- image: getMediaItemImageUrl(payload.media[0]),
6
+ image: getMediaItemImageUrl(payload.media?.[0]),
7
7
  title: payload.title,
8
8
  description: payload.description,
9
9
  price: payload.price,
@@ -1,2 +1,2 @@
1
1
  import { type MediaItemModel } from './types';
2
- export declare const getMediaItemImageUrl: (media: MediaItemModel) => string;
2
+ export declare const getMediaItemImageUrl: (media?: MediaItemModel | null) => string;
@@ -1,3 +1,8 @@
1
1
  import { MediaType } from '../enums';
2
2
  import {} from './types';
3
- export const getMediaItemImageUrl = (media) => (media.type === MediaType.Image ? media.url : media.thumbnailUrl);
3
+ export const getMediaItemImageUrl = (media) => {
4
+ if (!media) {
5
+ return '';
6
+ }
7
+ return media.type === MediaType.Image ? media.url : media.thumbnailUrl;
8
+ };
@@ -1,8 +1,8 @@
1
1
  import { getMediaItemImageUrl } from '../../core/media';
2
2
  import { shouldUseSalePrice } from '../price-helper';
3
3
  export const mapToProductCard = (payload, referenceDate) => {
4
- const effectiveSalePrice = payload.priceAndAvailability.productSalePrices?.find((x) => shouldUseSalePrice({
5
- price: payload.priceAndAvailability.price,
4
+ const effectiveSalePrice = payload.priceAndAvailability?.productSalePrices?.find((x) => shouldUseSalePrice({
5
+ price: payload.priceAndAvailability?.price,
6
6
  salePrice: x.salePrice,
7
7
  effectiveDateFrom: x.salePriceEffectiveDateFrom,
8
8
  effectiveDateTo: x.salePriceEffectiveDateTo,
@@ -14,7 +14,7 @@ export const mapToProductCard = (payload, referenceDate) => {
14
14
  shortDescription: payload.shortDescription,
15
15
  link: payload.link,
16
16
  brandName: payload.brand?.name || null,
17
- image: getMediaItemImageUrl(payload.media[0]),
17
+ image: getMediaItemImageUrl(payload.media?.[0]),
18
18
  currency: payload.priceAndAvailability.currency,
19
19
  price: payload.priceAndAvailability.price,
20
20
  salePrice: effectiveSalePrice?.salePrice ?? null
@@ -1,4 +1,4 @@
1
- import type { ShortVideoSocialInteractionsHanlder } from './types';
1
+ import type { ShortVideoSocialInteractionsHandler } from './types';
2
2
  type Props = {
3
3
  model: {
4
4
  id: string;
@@ -9,7 +9,7 @@ type Props = {
9
9
  isImage: boolean;
10
10
  };
11
11
  };
12
- socialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
12
+ socialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
13
13
  on?: {
14
14
  attachmentsClicked?: () => void;
15
15
  };
@@ -1,9 +1,9 @@
1
1
  import type { Locale } from '../../core/locale';
2
2
  import { type IShortVideoViewerLocalization } from './short-video-viewer-localization';
3
- import type { ShortVideoSocialInteractionsHanlder, ShortVideoViewerModel } from './types';
3
+ import type { ShortVideoSocialInteractionsHandler, ShortVideoViewerModel } from './types';
4
4
  type Props = {
5
5
  model: ShortVideoViewerModel;
6
- socialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
6
+ socialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
7
7
  availableSideSpace?: number;
8
8
  showAttachments?: boolean;
9
9
  showControls?: boolean;
@@ -2,7 +2,7 @@ export { default as ShortVideoViewer } from './cmp.short-video-viewer.svelte';
2
2
  export { default as ShortVideoControls } from './cmp.short-video-controls.svelte';
3
3
  export { default as ShortVideoViewerAttachments } from './cmp.attachments.svelte';
4
4
  export { default as ShortVideoViewerAttachmentsInline } from './cmp.attachments-inline.svelte';
5
- export type { ShortVideoViewerModel, ShortVideoSocialInteractionsHanlder, ShortVideoAdCardModel, ShortVideoProductCardModel } from './types';
5
+ export type { ShortVideoViewerModel, ShortVideoSocialInteractionsHandler, ShortVideoAdCardModel, ShortVideoProductCardModel } from './types';
6
6
  export type { IShortVideoViewerLocalization } from './short-video-viewer-localization';
7
7
  export type { IShortVideoAttachmentsLocalization } from './short-video-attachments-localization';
8
8
  export { mapToShortVideoViewerModel } from './mapper';
@@ -21,7 +21,7 @@ export type ShortVideoViewerHeadingModel = {
21
21
  displayDate: string;
22
22
  viewsCount: number;
23
23
  };
24
- export type ShortVideoSocialInteractionsHanlder = {
24
+ export type ShortVideoSocialInteractionsHandler = {
25
25
  isLiked: (shortVideoId: string) => Promise<{
26
26
  readonly isLiked: boolean;
27
27
  }>;
@@ -1,10 +1,10 @@
1
1
  import { type Locale } from '../../core/locale';
2
- import type { ShortVideoSocialInteractionsHanlder } from '../short-video-viewer';
2
+ import type { ShortVideoSocialInteractionsHandler } from '../short-video-viewer';
3
3
  import { type IShortVideosPlayerLocalization } from './short-videos-player-localization';
4
4
  import type { PlayerInput } from './types';
5
5
  type Props = {
6
6
  input: PlayerInput;
7
- socialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
7
+ socialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
8
8
  localization?: IShortVideosPlayerLocalization | Locale;
9
9
  showStreamsCloudWatermark?: boolean;
10
10
  disableBackground?: boolean;
@@ -1,4 +1,4 @@
1
- import { type ShortVideoSocialInteractionsHanlder, type ShortVideoViewerModel } from '../short-video-viewer';
1
+ import { type ShortVideoSocialInteractionsHandler, type ShortVideoViewerModel } from '../short-video-viewer';
2
2
  import type { IPlayerBuffer } from '../../ui/player';
3
3
  import { ShortVideosPlayerLocalization } from './short-videos-player-localization';
4
4
  import type { ShortVideosPlayerUiManager } from './ui-manager.svelte';
@@ -6,7 +6,7 @@ type Props = {
6
6
  buffer: IPlayerBuffer<ShortVideoViewerModel>;
7
7
  uiManager: ShortVideosPlayerUiManager;
8
8
  localization: ShortVideosPlayerLocalization;
9
- socialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
9
+ socialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
10
10
  on: {
11
11
  closePlayer?: () => void;
12
12
  productClick?: (productId: string) => void;
@@ -1,10 +1,10 @@
1
1
  import type { Locale } from '../../core/locale';
2
- import { type ShortVideoSocialInteractionsHanlder } from '../short-video-viewer';
2
+ import { type ShortVideoSocialInteractionsHandler } from '../short-video-viewer';
3
3
  import { type IShortVideosPlayerLocalization } from './short-videos-player-localization';
4
4
  import type { PlayerInput } from './types';
5
5
  type Props = {
6
6
  input: PlayerInput;
7
- socialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
7
+ socialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
8
8
  localization?: IShortVideosPlayerLocalization | Locale;
9
9
  showStreamsCloudWatermark?: boolean;
10
10
  disableBackground?: boolean;
@@ -1,4 +1,4 @@
1
- import type { ShortVideoViewerModel, ShortVideoSocialInteractionsHanlder } from '../short-video-viewer';
1
+ import type { ShortVideoViewerModel, ShortVideoSocialInteractionsHandler } from '../short-video-viewer';
2
2
  import type { PlayerItemsProvider } from '../../ui/player';
3
3
  import type { IShortVideosPlayerLocalization } from './short-videos-player-localization';
4
4
  export type PlayerInput = ProviderPlayerInput | IdsPlayerInput;
@@ -24,7 +24,7 @@ type IdsInit = PlayerSettings & {
24
24
  initiator?: string;
25
25
  };
26
26
  type PlayerSettings = {
27
- socialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
27
+ socialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
28
28
  disableBackground?: boolean;
29
29
  localization?: IShortVideosPlayerLocalization | 'en' | 'no';
30
30
  showStreamsCloudWatermark?: boolean;
@@ -1,12 +1,12 @@
1
1
  import type { Locale } from '../../core/locale';
2
- import { type ShortVideoSocialInteractionsHanlder } from '../../short-videos/short-video-viewer';
2
+ import { type ShortVideoSocialInteractionsHandler } from '../../short-videos/short-video-viewer';
3
3
  import { type IStreamPlayerLocalization } from './stream-player-localization';
4
4
  type Props = {
5
5
  streamId: string;
6
6
  localization?: IStreamPlayerLocalization | Locale;
7
7
  graphqlOrigin?: string;
8
8
  showStreamsCloudWatermark?: boolean;
9
- shortVideoSocialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
9
+ shortVideoSocialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
10
10
  initiator?: string;
11
11
  on?: {
12
12
  closePlayer?: () => void;
@@ -1,11 +1,11 @@
1
- import { type ShortVideoSocialInteractionsHanlder } from '../../short-videos/short-video-viewer';
1
+ import { type ShortVideoSocialInteractionsHandler } from '../../short-videos/short-video-viewer';
2
2
  import type { StreamPlayerBuffer } from './stream-player-buffer.svelte';
3
3
  import { StreamPlayerLocalization } from './stream-player-localization';
4
4
  import type { StreamPlayerUiManager } from './ui-manager.svelte';
5
5
  type Props = {
6
6
  buffer: StreamPlayerBuffer;
7
7
  uiManager: StreamPlayerUiManager;
8
- shortVideoSocialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
8
+ shortVideoSocialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
9
9
  localization: StreamPlayerLocalization;
10
10
  on: {
11
11
  closePlayer: () => void;
@@ -1,4 +1,4 @@
1
- import type { ShortVideoSocialInteractionsHanlder } from '../../short-videos/short-video-viewer';
1
+ import type { ShortVideoSocialInteractionsHandler } from '../../short-videos/short-video-viewer';
2
2
  import type { IStreamPlayerLocalization } from './stream-player-localization';
3
3
  export type { IStreamPlayerLocalization };
4
4
  /**
@@ -29,7 +29,7 @@ export declare const openStreamPlayer: (init: {
29
29
  graphqlOrigin?: string;
30
30
  localization?: IStreamPlayerLocalization | "en" | "no";
31
31
  showStreamsCloudWatermark?: boolean;
32
- shortVideoSocialInteractionsHandler?: ShortVideoSocialInteractionsHanlder;
32
+ shortVideoSocialInteractionsHandler?: ShortVideoSocialInteractionsHandler;
33
33
  initiator?: string;
34
34
  on?: {
35
35
  streamActivated?: (data: {
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">import { isScrollingPrevented } from './prevent-slider-scroll';
2
2
  import { onDestroy, onMount, untrack } from 'svelte';
3
+ const MOUSE_DETECTION_THRESHOLD_MS = 100;
3
4
  let { buffer, on, children } = $props();
4
5
  let slidesRef;
5
6
  let sliderHeight = $state(0);
@@ -131,7 +132,7 @@ onMount(() => {
131
132
  const lastEvent = waveDetector.events[waveDetector.events.length - 1];
132
133
  const timeSinceLastEvent = lastEvent ? now - lastEvent.time : 1000;
133
134
  // If enough time has passed since the last event - it's a mouse
134
- if (timeSinceLastEvent > 100) {
135
+ if (timeSinceLastEvent > MOUSE_DETECTION_THRESHOLD_MS) {
135
136
  triggerAnimation(direction);
136
137
  waveDetector = { events: [], direction: 0, peakReached: false, lastPeak: 0, waveStartTime: 0 };
137
138
  return;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">let { value, listenParentClicks = false, on } = $props();
2
2
  let seekBarRef;
3
+ let scrubberRef;
3
4
  let progressRef;
4
5
  let isDragging = $state(false);
5
6
  const cssValue = $derived(`${100 * (value <= 1 ? value : 1)}%`);
@@ -36,6 +37,7 @@ const onMouseUp = () => {
36
37
  isDragging = false;
37
38
  window.removeEventListener('mousemove', onMouseMove);
38
39
  window.removeEventListener('mouseup', onMouseUp);
40
+ scrubberRef === null || scrubberRef === void 0 ? void 0 : scrubberRef.blur();
39
41
  (_a = on === null || on === void 0 ? void 0 : on.dragEnd) === null || _a === void 0 ? void 0 : _a.call(on);
40
42
  };
41
43
  const onMouseDown = (e) => {
@@ -45,6 +47,7 @@ const onMouseDown = (e) => {
45
47
  handleSeek(e);
46
48
  window.addEventListener('mousemove', onMouseMove);
47
49
  window.addEventListener('mouseup', onMouseUp);
50
+ scrubberRef === null || scrubberRef === void 0 ? void 0 : scrubberRef.blur();
48
51
  };
49
52
  const handleScrubberKeyDown = (event) => {
50
53
  const step = 0.05;
@@ -67,15 +70,18 @@ const handleScrubberKeyDown = (event) => {
67
70
  }
68
71
  };
69
72
  $effect(() => {
73
+ let parent = null;
70
74
  if (listenParentClicks && seekBarRef) {
71
- const parent = seekBarRef.parentElement;
75
+ parent = seekBarRef.parentElement;
72
76
  if (parent) {
73
77
  parent.addEventListener('click', handleParentClick);
74
- return () => {
75
- parent.removeEventListener('click', handleParentClick);
76
- };
77
78
  }
78
79
  }
80
+ return () => {
81
+ if (parent) {
82
+ parent.removeEventListener('click', handleParentClick);
83
+ }
84
+ };
79
85
  });
80
86
  $effect(() => {
81
87
  return () => {
@@ -90,6 +96,8 @@ $effect(() => {
90
96
  <span class="seek-bar__value" style="width: {cssValue}"> &nbsp; </span>
91
97
  <div
92
98
  class="seek-bar__scrubber"
99
+ bind:this={scrubberRef}
100
+ class:is-dragging={isDragging}
93
101
  style="left: {cssValue}"
94
102
  role="slider"
95
103
  tabindex="0"
@@ -116,9 +124,15 @@ $effect(() => {
116
124
  .seek-bar {
117
125
  cursor: pointer;
118
126
  position: relative;
127
+ padding: 0.3125rem 0;
119
128
  --_seek-bar--container-color: var(--seek-bar--container-color, #b0b0b0);
120
- --_seek-bar--value-color: var(--seek-bar--value-color, #fff);
121
- --_seek-bar--scrubber-color: var(--seek-bar--scrubber-color, #fff);
129
+ --_seek-bar--value-color: var(--seek-bar--value-color, #ffffff);
130
+ --_seek-bar--scrubber-color: var(--seek-bar--scrubber-color, #ffffff);
131
+ --_seek-bar--scrubber-border-color: var(--seek-bar--scrubber-border-color, #b0b0b0);
132
+ --_seek-bar--scrubber-opacity: 0;
133
+ }
134
+ .seek-bar:hover {
135
+ --_seek-bar--scrubber-opacity: 1;
122
136
  }
123
137
  .seek-bar__container {
124
138
  width: 100%;
@@ -139,9 +153,17 @@ $effect(() => {
139
153
  width: 0.75rem;
140
154
  height: 0.75rem;
141
155
  background: var(--_seek-bar--scrubber-color);
142
- border-color: var(--_seek-bar--container-color);
156
+ border-color: var(--_seek-bar--scrubber-border-color);
143
157
  border-width: 0.0625rem;
144
158
  border-radius: 50%;
145
159
  transform: translate(-50%, -50%);
146
160
  z-index: 1;
161
+ opacity: var(--_seek-bar--scrubber-opacity);
162
+ transition: opacity 0.2s ease-in-out;
163
+ }
164
+ .seek-bar__scrubber.is-dragging, .seek-bar__scrubber:focus {
165
+ --_seek-bar--scrubber-opacity: 1;
166
+ }
167
+ .seek-bar__scrubber:focus-visible {
168
+ outline: none;
147
169
  }</style>
@@ -10,7 +10,6 @@
10
10
  import { randomNanoid } from '../../core/utils/string-generator';
11
11
  import { Icon, IconColor } from '../icon';
12
12
  import { MediaVolumeManager, PlaybackManager } from '../media-playback';
13
- import { Progress } from '../progress';
14
13
  import { SeekBar } from '../seek-bar';
15
14
  import { ScrubberPosition } from './types';
16
15
  import IconPause from '@fluentui/svg-icons/icons/pause_20_regular.svg?raw';
@@ -286,17 +285,15 @@ const handleSeek = (percent) => {
286
285
  class:video__progress-container--bottom={scrubberPosition === ScrubberPosition.Bottom}
287
286
  onmouseenter={() => setShowControlsOnHover(true)}
288
287
  onmouseleave={() => setShowControlsOnHover(false)}
289
- onclick={(e) => e.stopPropagation()}
290
- onkeydown={() => ({})}
291
288
  role="none">
292
- {#if !showControlsOnHover && isVideoPaused}
293
- <div class="video__progress">
294
- <Progress value={percentageCompleted} />
295
- </div>
296
- {/if}
297
- {#if showControlsOnHover}
298
- <div class="video__progress" transition:fade={{ duration: isVideoPaused ? 0 : 300 }}>
299
- <SeekBar value={percentageCompleted} listenParentClicks={true} on={{ seek: handleSeek }} />
289
+ {#if showControlsOnHover || (!showControlsOnHover && isVideoPaused)}
290
+ <div
291
+ class="video__seek-bar"
292
+ transition:fade={{ duration: isVideoPaused ? 0 : 300 }}
293
+ onclick={(e) => e.stopPropagation()}
294
+ onkeydown={() => ({})}
295
+ role="none">
296
+ <SeekBar value={percentageCompleted} on={{ seek: handleSeek }} />
300
297
  </div>
301
298
  {/if}
302
299
  </div>
@@ -321,9 +318,6 @@ const handleSeek = (percent) => {
321
318
  --_video--border-radius: var(--video--border-radius, 0);
322
319
  --_video--media-fit: var(--video--media-fit, contain);
323
320
  --_video--poster--media-fit: var(--video--poster--media-fit, cover);
324
- --_video--progress--background-color: var(--video--progress--background-color, transparent);
325
- --_video--progress--back-color: var(--video--progress--back-color);
326
- --_video--progress--front-color: var(--video--progress--front-color);
327
321
  height: 100%;
328
322
  min-height: 100%;
329
323
  max-height: 100%;
@@ -390,20 +384,16 @@ const handleSeek = (percent) => {
390
384
  left: 0;
391
385
  right: 0;
392
386
  z-index: 1;
393
- min-height: 1.5625rem;
394
- padding: 0 0.25rem;
387
+ height: 2.5rem;
388
+ padding: 0.625rem 0.25rem;
395
389
  }
396
390
  .video__progress-container--top {
397
- top: 0.5625rem;
391
+ top: -0.625rem;
398
392
  bottom: auto;
393
+ padding-bottom: 0.9375rem;
399
394
  }
400
395
  .video__progress-container--bottom {
401
396
  top: auto;
402
- bottom: 0;
403
- }
404
- .video__progress {
405
- --progress--height: 0.3125em;
406
- --progress--back-color: var(--_video--progress--back-color);
407
- --progress--front-color: var(--_video--progress--front-color);
408
- padding: 0.625rem 0;
397
+ bottom: -0.625rem;
398
+ padding-top: 0.9375rem;
409
399
  }</style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/embeddable",
3
- "version": "5.0.0",
3
+ "version": "5.1.1",
4
4
  "author": "StreamsCloud",
5
5
  "repository": "https://github.com/StreamsCloud/streamscloud-frontend-packages.git",
6
6
  "type": "module",