@streamscloud/embeddable 3.4.2 → 4.0.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 (98) hide show
  1. package/dist/ads/ad-card/cmp.ad-card.svelte +193 -0
  2. package/dist/{short-videos/short-video-viewer/cmp.ad.svelte.d.ts → ads/ad-card/cmp.ad-card.svelte.d.ts} +3 -2
  3. package/dist/ads/ad-card/index.d.ts +3 -0
  4. package/dist/ads/ad-card/index.js +2 -0
  5. package/dist/ads/ad-card/mapper.d.ts +3 -0
  6. package/dist/ads/ad-card/mapper.js +13 -0
  7. package/dist/ads/ad-card/operations.generated.d.ts +23 -0
  8. package/dist/ads/ad-card/operations.generated.js +47 -0
  9. package/dist/ads/ad-card/operations.graphql +20 -0
  10. package/dist/ads/ad-card/types.d.ts +17 -0
  11. package/dist/ads/ad-card/types.js +1 -0
  12. package/dist/core/locale.d.ts +3 -0
  13. package/dist/products/price-helper.d.ts +18 -3
  14. package/dist/products/price-helper.js +9 -6
  15. package/dist/products/product-card/cmp.product-card.svelte +218 -0
  16. package/dist/products/product-card/cmp.product-card.svelte.d.ts +15 -0
  17. package/dist/products/product-card/index.d.ts +4 -0
  18. package/dist/products/product-card/index.js +2 -0
  19. package/dist/products/product-card/mapper.d.ts +3 -0
  20. package/dist/products/product-card/mapper.js +22 -0
  21. package/dist/products/product-card/operations.generated.d.ts +26 -0
  22. package/dist/products/product-card/operations.generated.js +59 -0
  23. package/dist/products/product-card/operations.graphql +23 -0
  24. package/dist/products/product-card/product-card-localization.d.ts +8 -0
  25. package/dist/{short-videos/short-video-viewer/short-video-product-localization.js → products/product-card/product-card-localization.js} +1 -1
  26. package/dist/products/product-card/types.d.ts +12 -0
  27. package/dist/products/product-card/types.js +1 -0
  28. package/dist/short-videos/short-video-viewer/cmp.attachments.svelte +12 -40
  29. package/dist/short-videos/short-video-viewer/cmp.attachments.svelte.d.ts +1 -1
  30. package/dist/short-videos/short-video-viewer/cmp.short-video-controls.svelte +134 -0
  31. package/dist/short-videos/short-video-viewer/cmp.short-video-controls.svelte.d.ts +19 -0
  32. package/dist/short-videos/short-video-viewer/cmp.short-video-viewer.svelte +45 -20
  33. package/dist/short-videos/short-video-viewer/cmp.short-video-viewer.svelte.d.ts +1 -0
  34. package/dist/short-videos/short-video-viewer/index.d.ts +2 -6
  35. package/dist/short-videos/short-video-viewer/index.js +1 -4
  36. package/dist/short-videos/short-video-viewer/mapper.js +12 -42
  37. package/dist/short-videos/short-video-viewer/operations.generated.d.ts +5 -8
  38. package/dist/short-videos/short-video-viewer/operations.generated.js +98 -94
  39. package/dist/short-videos/short-video-viewer/operations.graphql +2 -42
  40. package/dist/short-videos/short-video-viewer/short-video-attachments-localization.d.ts +3 -7
  41. package/dist/short-videos/short-video-viewer/short-video-attachments-localization.js +0 -14
  42. package/dist/short-videos/short-video-viewer/short-video-viewer-localization.d.ts +3 -3
  43. package/dist/short-videos/short-video-viewer/types.d.ts +9 -28
  44. package/dist/short-videos/short-video-viewer/types.js +1 -1
  45. package/dist/short-videos/short-video-viewer/ui-manager.svelte.d.ts +5 -0
  46. package/dist/short-videos/short-video-viewer/ui-manager.svelte.js +14 -1
  47. package/dist/short-videos/short-videos-player/cmp.short-videos-player.svelte +23 -163
  48. package/dist/short-videos/short-videos-player/cmp.short-videos-player.svelte.d.ts +3 -2
  49. package/dist/short-videos/short-videos-player/controls.svelte +110 -127
  50. package/dist/short-videos/short-videos-player/controls.svelte.d.ts +2 -1
  51. package/dist/short-videos/short-videos-player/index.d.ts +2 -0
  52. package/dist/short-videos/short-videos-player/index.js +6 -3
  53. package/dist/short-videos/short-videos-player/operations.generated.d.ts +5 -8
  54. package/dist/short-videos/short-videos-player/operations.generated.js +77 -73
  55. package/dist/short-videos/short-videos-player/short-videos-player-localization.d.ts +1 -3
  56. package/dist/short-videos/short-videos-player/short-videos-player-localization.js +0 -2
  57. package/dist/short-videos/short-videos-player/short-videos-player-view.svelte +198 -0
  58. package/dist/short-videos/short-videos-player/short-videos-player-view.svelte.d.ts +14 -0
  59. package/dist/short-videos/short-videos-player/types.d.ts +2 -0
  60. package/dist/short-videos/short-videos-player/ui-manager.svelte.d.ts +3 -3
  61. package/dist/short-videos/short-videos-player/ui-manager.svelte.js +8 -8
  62. package/dist/streams/layout/element-views/price-element-view.svelte +5 -4
  63. package/dist/streams/layout/models/mapper.js +8 -17
  64. package/dist/streams/stream-player/cmp.stream-player.svelte +18 -5
  65. package/dist/streams/stream-player/cmp.stream-player.svelte.d.ts +1 -1
  66. package/dist/streams/stream-player/controls.svelte +66 -73
  67. package/dist/streams/stream-player/index.js +2 -2
  68. package/dist/streams/stream-player/stream-overview.svelte +12 -1
  69. package/dist/streams/stream-player/stream-overview.svelte.d.ts +1 -0
  70. package/dist/streams/stream-player/stream-player-localization.d.ts +1 -3
  71. package/dist/streams/stream-player/stream-player-localization.js +0 -2
  72. package/dist/streams/stream-player/ui-manager.svelte.d.ts +3 -2
  73. package/dist/streams/stream-player/ui-manager.svelte.js +9 -5
  74. package/dist/ui/button/resources/button-theme.svelte +1 -0
  75. package/dist/ui/shadow-dom/index.d.ts +2 -1
  76. package/dist/ui/shadow-dom/index.js +2 -1
  77. package/dist/ui/shadow-dom/{shadow-host.d.ts → modal-shadow-host.d.ts} +1 -1
  78. package/dist/ui/shadow-dom/modal-shadow-host.js +21 -0
  79. package/dist/ui/shadow-dom/shadow-root-service.d.ts +1 -0
  80. package/dist/ui/shadow-dom/shadow-root-service.js +23 -0
  81. package/dist/ui/video/cmp.video.svelte +2 -2
  82. package/dist/ui/video/cmp.video.svelte.d.ts +1 -0
  83. package/package.json +1 -1
  84. package/dist/short-videos/short-video-viewer/cmp.ad.svelte +0 -140
  85. package/dist/short-videos/short-video-viewer/cmp.product.svelte +0 -168
  86. package/dist/short-videos/short-video-viewer/cmp.product.svelte.d.ts +0 -14
  87. package/dist/short-videos/short-video-viewer/cmp.short-video-details.svelte +0 -125
  88. package/dist/short-videos/short-video-viewer/cmp.short-video-details.svelte.d.ts +0 -14
  89. package/dist/short-videos/short-video-viewer/cmp.social-interactions.svelte +0 -69
  90. package/dist/short-videos/short-video-viewer/cmp.social-interactions.svelte.d.ts +0 -10
  91. package/dist/short-videos/short-video-viewer/description.svelte +0 -53
  92. package/dist/short-videos/short-video-viewer/description.svelte.d.ts +0 -9
  93. package/dist/short-videos/short-video-viewer/short-video-details-localization.d.ts +0 -14
  94. package/dist/short-videos/short-video-viewer/short-video-details-localization.js +0 -17
  95. package/dist/short-videos/short-video-viewer/short-video-product-localization.d.ts +0 -8
  96. package/dist/short-videos/short-videos-player/action-button.svelte +0 -38
  97. package/dist/short-videos/short-videos-player/action-button.svelte.d.ts +0 -10
  98. package/dist/ui/shadow-dom/shadow-host.js +0 -32
@@ -0,0 +1,22 @@
1
+ import { getMediaItemImageUrl } from '../../core/media';
2
+ import { shouldUseSalePrice } from '../price-helper';
3
+ export const mapToProductCard = (payload, referenceDate) => {
4
+ const effectiveSalePrice = payload.priceAndAvailability.productSalePrices?.find((x) => shouldUseSalePrice({
5
+ price: payload.priceAndAvailability.price,
6
+ salePrice: x.salePrice,
7
+ effectiveDateFrom: x.salePriceEffectiveDateFrom,
8
+ effectiveDateTo: x.salePriceEffectiveDateTo,
9
+ referenceDate
10
+ }));
11
+ return {
12
+ id: payload.id,
13
+ title: payload.title,
14
+ shortDescription: payload.shortDescription,
15
+ link: payload.link,
16
+ brandName: payload.brand?.name || null,
17
+ image: getMediaItemImageUrl(payload.media[0]),
18
+ currency: payload.priceAndAvailability.currency,
19
+ price: payload.priceAndAvailability.price,
20
+ salePrice: effectiveSalePrice?.salePrice ?? null
21
+ };
22
+ };
@@ -0,0 +1,26 @@
1
+ import type * as SchemaTypes from '../../../gql/types';
2
+ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
3
+ export type ProductCardPayloadFragment = {
4
+ id: string;
5
+ title: string;
6
+ shortDescription: string | null;
7
+ link: string | null;
8
+ media: Array<{
9
+ url: string;
10
+ thumbnailUrl: string | null;
11
+ type: SchemaTypes.MediaType;
12
+ }>;
13
+ brand: {
14
+ name: string;
15
+ } | null;
16
+ priceAndAvailability: {
17
+ currency: SchemaTypes.Currency;
18
+ price: number;
19
+ productSalePrices: Array<{
20
+ salePrice: number;
21
+ salePriceEffectiveDateFrom: any | null;
22
+ salePriceEffectiveDateTo: any | null;
23
+ }> | null;
24
+ };
25
+ };
26
+ export declare const ProductCardPayloadFragmentDoc: DocumentNode<ProductCardPayloadFragment, unknown>;
@@ -0,0 +1,59 @@
1
+ export const ProductCardPayloadFragmentDoc = {
2
+ kind: 'Document',
3
+ definitions: [
4
+ {
5
+ kind: 'FragmentDefinition',
6
+ name: { kind: 'Name', value: 'ProductCardPayloadFragment' },
7
+ typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'Product' } },
8
+ selectionSet: {
9
+ kind: 'SelectionSet',
10
+ selections: [
11
+ { kind: 'Field', name: { kind: 'Name', value: 'id' } },
12
+ { kind: 'Field', name: { kind: 'Name', value: 'title' } },
13
+ { kind: 'Field', name: { kind: 'Name', value: 'shortDescription' } },
14
+ { kind: 'Field', name: { kind: 'Name', value: 'link' } },
15
+ {
16
+ kind: 'Field',
17
+ name: { kind: 'Name', value: 'media' },
18
+ selectionSet: {
19
+ kind: 'SelectionSet',
20
+ selections: [
21
+ { kind: 'Field', name: { kind: 'Name', value: 'url' } },
22
+ { kind: 'Field', name: { kind: 'Name', value: 'thumbnailUrl' } },
23
+ { kind: 'Field', name: { kind: 'Name', value: 'type' } }
24
+ ]
25
+ }
26
+ },
27
+ {
28
+ kind: 'Field',
29
+ name: { kind: 'Name', value: 'brand' },
30
+ selectionSet: { kind: 'SelectionSet', selections: [{ kind: 'Field', name: { kind: 'Name', value: 'name' } }] }
31
+ },
32
+ {
33
+ kind: 'Field',
34
+ name: { kind: 'Name', value: 'priceAndAvailability' },
35
+ selectionSet: {
36
+ kind: 'SelectionSet',
37
+ selections: [
38
+ { kind: 'Field', name: { kind: 'Name', value: 'currency' } },
39
+ { kind: 'Field', name: { kind: 'Name', value: 'price' } },
40
+ {
41
+ kind: 'Field',
42
+ name: { kind: 'Name', value: 'productSalePrices' },
43
+ selectionSet: {
44
+ kind: 'SelectionSet',
45
+ selections: [
46
+ { kind: 'Field', name: { kind: 'Name', value: 'salePrice' } },
47
+ { kind: 'Field', name: { kind: 'Name', value: 'salePriceEffectiveDateFrom' } },
48
+ { kind: 'Field', name: { kind: 'Name', value: 'salePriceEffectiveDateTo' } }
49
+ ]
50
+ }
51
+ }
52
+ ]
53
+ }
54
+ }
55
+ ]
56
+ }
57
+ }
58
+ ]
59
+ };
@@ -0,0 +1,23 @@
1
+ fragment ProductCardPayloadFragment on Product {
2
+ id
3
+ title
4
+ shortDescription
5
+ link
6
+ media {
7
+ url
8
+ thumbnailUrl
9
+ type
10
+ }
11
+ brand {
12
+ name
13
+ }
14
+ priceAndAvailability {
15
+ currency
16
+ price
17
+ productSalePrices {
18
+ salePrice
19
+ salePriceEffectiveDateFrom
20
+ salePriceEffectiveDateTo
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,8 @@
1
+ import { type Locale } from '../../core/locale';
2
+ export interface IProductCardLocalization {
3
+ beforeNowPrefix?: string | null;
4
+ }
5
+ export declare class ProductCardLocalization {
6
+ beforeNowPrefix: string | null;
7
+ constructor(init: IProductCardLocalization | Locale);
8
+ }
@@ -1,5 +1,5 @@
1
1
  import { isLocale } from '../../core/locale';
2
- export class ShortVideoProductLocalization {
2
+ export class ProductCardLocalization {
3
3
  beforeNowPrefix;
4
4
  constructor(init) {
5
5
  this.beforeNowPrefix = isLocale(init) ? loc.beforeNowPrefix[init] : init?.beforeNowPrefix || loc.beforeNowPrefix.en;
@@ -0,0 +1,12 @@
1
+ import type { Currency } from '../..';
2
+ export type ProductCardModel = {
3
+ id: string;
4
+ title: string;
5
+ shortDescription?: string | null;
6
+ link: string | null;
7
+ image: string | null;
8
+ brandName: string | null;
9
+ price: number;
10
+ currency: Currency;
11
+ salePrice: number | null;
12
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,27 +1,21 @@
1
- <script lang="ts">import { default as ShortVideoAdViewer } from './cmp.ad.svelte';
2
- import { default as ShortVideoProductViewer } from './cmp.product.svelte';
1
+ <script lang="ts">import { AdCard } from '../../ads/ad-card';
2
+ import { ProductCard } from '../../products/product-card';
3
3
  import { ShortVideoAttachmentsLocalization } from './short-video-attachments-localization';
4
4
  import {} from './types';
5
- let { shortVideo, localization: localizationInit, on } = $props();
5
+ let { shortVideo, localization: localizationInit = 'en', on } = $props();
6
6
  const localization = $derived(new ShortVideoAttachmentsLocalization(localizationInit));
7
7
  </script>
8
8
 
9
9
  {#if shortVideo.products.length || shortVideo.ad}
10
10
  <div class="short-video-attachments">
11
- {#if shortVideo.products.length}
12
- <div class="short-video-attachments__section-header">{localization.productsSection}</div>
13
- <div class="short-video-attachments__products">
14
- {#each shortVideo.products as product (product.id)}
15
- <ShortVideoProductViewer product={product} localization={localization.productLocalization} on={on} />
16
- {/each}
17
- </div>
11
+ {#if shortVideo.ad}
12
+ <AdCard ad={shortVideo.ad} />
18
13
  {/if}
19
14
 
20
- {#if shortVideo.ad}
21
- <div class="short-video-attachments__section-header">{localization.offersSection}</div>
22
- <div class="short-video-attachments__offer">
23
- <ShortVideoAdViewer ad={shortVideo.ad} />
24
- </div>
15
+ {#if shortVideo.products.length}
16
+ {#each shortVideo.products as product (product.id)}
17
+ <ProductCard product={product} localization={localization.productLocalization} on={on} />
18
+ {/each}
25
19
  {/if}
26
20
  </div>
27
21
  {/if}
@@ -38,31 +32,9 @@ const localization = $derived(new ShortVideoAttachmentsLocalization(localization
38
32
  }
39
33
  }
40
34
  .short-video-attachments {
41
- --_short-video-attachments--margin-vertical: var(--short-video-attachments--margin-vertical, 0);
42
- --_short-video-attachments--spacing-vertical: var(--short-video-attachments--spacing-vertical, 1.25rem);
43
- --_short-video-attachments--spacing-horizontal: var(--short-video-attachments--spacing-horizontal, 1rem);
35
+ --_short-video-attachments--gap: var(--short-video-attachments--gap, 2.75rem);
44
36
  display: flex;
45
37
  flex-direction: column;
46
- gap: var(--_short-video-attachments--spacing-vertical);
47
- padding: var(--_short-video-attachments--margin-vertical) 0;
48
- }
49
- .short-video-attachments__section-header {
50
- color: #d1d5db;
51
- font-size: 1rem;
52
- font-weight: 700;
53
- line-height: 1.2;
54
- display: flex;
55
- justify-content: space-between;
56
- margin-bottom: 0.5rem;
57
- padding: 0.5625rem var(--_short-video-attachments--spacing-horizontal) 0;
58
- }
59
- .short-video-attachments__products {
60
- padding: 0 var(--_short-video-attachments--spacing-horizontal);
61
- display: grid;
62
- grid-template-columns: repeat(2, minmax(0, 1fr));
63
- grid-column-gap: var(--_short-video-attachments--spacing-horizontal);
64
- grid-row-gap: var(--_short-video-attachments--spacing-vertical);
65
- }
66
- .short-video-attachments__offer {
67
- padding: 0 var(--_short-video-attachments--spacing-horizontal);
38
+ gap: var(--_short-video-attachments--gap);
39
+ padding: 0;
68
40
  }</style>
@@ -3,7 +3,7 @@ import { type IShortVideoAttachmentsLocalization } from './short-video-attachmen
3
3
  import { type ShortVideoViewerModel } from './types';
4
4
  type Props = {
5
5
  shortVideo: ShortVideoViewerModel;
6
- localization: IShortVideoAttachmentsLocalization | Locale;
6
+ localization?: IShortVideoAttachmentsLocalization | Locale;
7
7
  on?: {
8
8
  productClick?: (productId: string) => void;
9
9
  };
@@ -0,0 +1,134 @@
1
+ <script lang="ts">var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Icon } from '../../ui/icon';
11
+ import { MediaVolumeManager } from '../../ui/media-playback';
12
+ import IconHeartFilled from '@fluentui/svg-icons/icons/heart_20_filled.svg?raw';
13
+ import IconHeart from '@fluentui/svg-icons/icons/heart_28_regular.svg?raw';
14
+ import IconShare from '@fluentui/svg-icons/icons/share_28_regular.svg?raw';
15
+ import IconShoppingBag from '@fluentui/svg-icons/icons/shopping_bag_28_regular.svg?raw';
16
+ import IconSpeaker2 from '@fluentui/svg-icons/icons/speaker_2_28_regular.svg?raw';
17
+ import IconSpeakerMute from '@fluentui/svg-icons/icons/speaker_mute_28_regular.svg?raw';
18
+ let { model, socialInteractions, on } = $props();
19
+ const showSocialInteractions = $derived(!!socialInteractions && model.enableSocialInteractions);
20
+ let isLiked = $state(false);
21
+ const availableActionsCount = $derived.by(() => {
22
+ let actionsCount = 0;
23
+ if (model.products.length || model.ad) {
24
+ actionsCount++;
25
+ }
26
+ if (showSocialInteractions) {
27
+ actionsCount++;
28
+ }
29
+ if (!model.media.isImage) {
30
+ actionsCount++;
31
+ }
32
+ return actionsCount;
33
+ });
34
+ $effect(() => {
35
+ refreshIsLiked();
36
+ });
37
+ const onAttachmentsClicked = () => {
38
+ var _a;
39
+ (_a = on === null || on === void 0 ? void 0 : on.attachmentsClicked) === null || _a === void 0 ? void 0 : _a.call(on);
40
+ };
41
+ const onMuteClicked = () => {
42
+ MediaVolumeManager.isMuted = !MediaVolumeManager.isMuted;
43
+ };
44
+ const onLikeClicked = () => __awaiter(void 0, void 0, void 0, function* () {
45
+ if (!socialInteractions) {
46
+ return;
47
+ }
48
+ yield socialInteractions.onToggleLike(model.id);
49
+ yield refreshIsLiked();
50
+ });
51
+ const onShareClicked = () => __awaiter(void 0, void 0, void 0, function* () {
52
+ if (!socialInteractions) {
53
+ return;
54
+ }
55
+ yield socialInteractions.onShare(model.id);
56
+ });
57
+ const refreshIsLiked = () => __awaiter(void 0, void 0, void 0, function* () {
58
+ if (!socialInteractions) {
59
+ return;
60
+ }
61
+ console.warn('refereshIsLiked called');
62
+ isLiked = yield socialInteractions.isLiked(model.id);
63
+ });
64
+ </script>
65
+
66
+ {#if availableActionsCount}
67
+ <div class="short-video-controls" class:short-video-controls--single-action={availableActionsCount === 1}>
68
+ {#if model.products.length || model.ad}
69
+ <button type="button" class="short-video-controls__action" onclick={onAttachmentsClicked}>
70
+ <Icon src={IconShoppingBag} />
71
+ </button>
72
+ {/if}
73
+
74
+ {#if showSocialInteractions}
75
+ <button type="button" class="short-video-controls__action" onclick={onLikeClicked} aria-label="none" class:short-video-controls__action--liked={isLiked}>
76
+ <Icon src={isLiked ? IconHeartFilled : IconHeart} />
77
+ </button>
78
+
79
+ <button type="button" class="short-video-controls__action" onclick={onShareClicked}>
80
+ <Icon src={IconShare} />
81
+ </button>
82
+ {/if}
83
+
84
+ {#if !model.media.isImage}
85
+ <button type="button" class="short-video-controls__action" onclick={onMuteClicked}>
86
+ {#if MediaVolumeManager.isMuted}
87
+ <Icon src={IconSpeakerMute} />
88
+ {:else}
89
+ <Icon src={IconSpeaker2} />
90
+ {/if}
91
+ </button>
92
+ {/if}
93
+ </div>
94
+ {/if}
95
+
96
+ <style>@keyframes fadeIn {
97
+ 0% {
98
+ opacity: 1;
99
+ }
100
+ 50% {
101
+ opacity: 0.4;
102
+ }
103
+ 100% {
104
+ opacity: 1;
105
+ }
106
+ }
107
+ .short-video-controls {
108
+ --_short-video-controls--icon--size: var(--short-video-controls--icon--size, 1.75rem);
109
+ --_short-video-controls--padding-vertical: 1rem;
110
+ --_short-video-controls--padding-horizontal: 0.625rem;
111
+ cursor: pointer;
112
+ display: flex;
113
+ flex-direction: column;
114
+ justify-content: center;
115
+ gap: 1.125rem;
116
+ border-radius: 1.25rem;
117
+ border: 0.0625rem solid #1c1c1c;
118
+ background: rgba(0, 0, 0, 0.6);
119
+ padding: var(--_short-video-controls--padding-vertical) var(--_short-video-controls--padding-horizontal);
120
+ }
121
+ .short-video-controls--single-action {
122
+ padding: var(--_short-video-controls--padding-horizontal);
123
+ border-radius: calc((var(--_short-video-controls--padding-horizontal) * 2 + var(--_short-video-controls--icon--size)) / 2);
124
+ }
125
+ .short-video-controls__action {
126
+ --icon--size: var(--_short-video-controls--icon--size);
127
+ --icon--color: #ffffff;
128
+ display: flex;
129
+ justify-content: center;
130
+ align-items: center;
131
+ }
132
+ .short-video-controls__action--liked {
133
+ --icon--color: #e71d36;
134
+ }</style>
@@ -0,0 +1,19 @@
1
+ import type { ShortVideoSocialInteractions } from './types';
2
+ type Props = {
3
+ model: {
4
+ id: string;
5
+ enableSocialInteractions: boolean;
6
+ products: unknown[];
7
+ ad: unknown | null;
8
+ media: {
9
+ isImage: boolean;
10
+ };
11
+ };
12
+ socialInteractions?: ShortVideoSocialInteractions;
13
+ on?: {
14
+ attachmentsClicked?: () => void;
15
+ };
16
+ };
17
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
18
+ type Cmp = ReturnType<typeof Cmp>;
19
+ export default Cmp;
@@ -1,10 +1,12 @@
1
1
  <script lang="ts">import { Image } from '../../ui/image';
2
+ import { LineClamp } from '../../ui/line-clamp';
2
3
  import { Video } from '../../ui/video';
3
4
  import { default as AttachmentsInline } from './cmp.attachments-inline.svelte';
4
- import { default as ShortVideoDescription } from './description.svelte';
5
+ import { default as ShortVideoControls } from './cmp.short-video-controls.svelte';
6
+ import { default as ShortVideoHeading } from './cmp.short-video-heading.svelte';
5
7
  import { ShortVideoViewerLocalization } from './short-video-viewer-localization';
6
8
  import { ShortVideoViewerUiManager } from './ui-manager.svelte';
7
- let { model, availableSideSpace = 0, showAttachments = true, autoplay = 'on-appearance', localization: localizationInit, on } = $props();
9
+ let { model, availableSideSpace = 0, showAttachments = true, showControls = true, autoplay = 'on-appearance', localization: localizationInit, on } = $props();
8
10
  const localization = $derived(new ShortVideoViewerLocalization(localizationInit));
9
11
  let actionsPanelRef = $state.raw(null);
10
12
  const uiManager = new ShortVideoViewerUiManager();
@@ -26,13 +28,24 @@ $effect(() => {
26
28
  });
27
29
  $effect(() => {
28
30
  uiManager.updateCanShowAttachments(showAttachments);
31
+ uiManager.updateCanShowControls(showControls);
29
32
  });
30
33
  </script>
31
34
 
32
35
  <div class="short-video-viewer" style={uiManager.globalCssVariables}>
33
36
  <div class="short-video-viewer__media">
34
37
  {#if !model.media.isImage}
35
- <Video controls={false} autoplay={autoplay} src={model.media.url} loop={true} poster={model.media.thumbnailUrl} id={model.id} on={on} />
38
+ <Video
39
+ src={model.media.url}
40
+ poster={model.media.thumbnailUrl}
41
+ controls={false}
42
+ autoplay={autoplay}
43
+ loop={true}
44
+ id={model.id}
45
+ hideSpeaker={true}
46
+ on={{
47
+ progress: on?.progress
48
+ }} />
36
49
  {:else}
37
50
  <Image src={model.media.url} />
38
51
  {/if}
@@ -45,9 +58,21 @@ $effect(() => {
45
58
  {#if uiManager.showAttachments}
46
59
  <AttachmentsInline model={model} />
47
60
  {/if}
61
+ {#if uiManager.showControls}
62
+ <ShortVideoControls model={model} on={{ attachmentsClicked: uiManager.toggleEnableAttachments }} />
63
+ {/if}
48
64
  </div>
49
65
  <div class="short-video-viewer__description">
50
- <ShortVideoDescription model={model} localization={localization} />
66
+ {#if model.heading}
67
+ <div class="short-video-viewer__heading">
68
+ <ShortVideoHeading model={model.heading} localization={localization} />
69
+ </div>
70
+ {/if}
71
+ <div class="short-video-viewer__text">
72
+ {#if model.text}
73
+ <LineClamp value={model.text} maxLines={2} />
74
+ {/if}
75
+ </div>
51
76
  </div>
52
77
  </div>
53
78
 
@@ -65,8 +90,6 @@ $effect(() => {
65
90
  .short-video-viewer {
66
91
  --_short-video-viewer--actions-panel--top: var(--short-video-viewer--actions-panel--top, 0.9375rem);
67
92
  --_short-video-viewer--actions-panel--bottom: var(--short-video-viewer--actions-panel--bottom, 0.9375rem);
68
- --_short-video-viewer--button--background-color: #f3f4f6;
69
- --_short-video-viewer--button--font-color: #2e2e2e;
70
93
  --video--media-fit: cover;
71
94
  width: 100%;
72
95
  min-width: 100%;
@@ -75,13 +98,6 @@ $effect(() => {
75
98
  min-height: 100%;
76
99
  max-height: 100%;
77
100
  position: relative;
78
- /* Set 'container-type: inline-size;' to reference container*/
79
- }
80
- @container (width < 576px) {
81
- .short-video-viewer {
82
- --_short-video-viewer--button--background-color: #ffffff;
83
- --_short-video-viewer--button--font-color: #2e2e2e;
84
- }
85
101
  }
86
102
  .short-video-viewer__media {
87
103
  height: 100%;
@@ -122,12 +138,21 @@ $effect(() => {
122
138
  }
123
139
  .short-video-viewer__description {
124
140
  position: absolute;
125
- bottom: 0.9375rem;
126
- left: 0.625rem;
127
- right: calc(1.25rem + var(--_short-video-viewer--button--size));
128
- --short-video-viewer--description--font--primary-color: #ffffff;
129
- --short-video-viewer--description--font--secondary-color: #ffffff;
141
+ bottom: 0;
142
+ left: 0;
143
+ right: 0;
144
+ padding: 1.875rem 1.25rem;
145
+ padding-right: calc(1.25rem + var(--_short-video-viewer--button--size));
146
+ background: linear-gradient(0deg, #000 0%, rgba(0, 0, 0, 0) 100%);
147
+ border-radius: 0rem 0.375rem 0.375rem;
148
+ }
149
+ .short-video-viewer__heading {
150
+ --short-video-heading--padding: 0 0 0.875rem 0;
151
+ --short-video-heading--text--color: #ffffff;
152
+ --short-video-heading--meta--color: #ffffff;
130
153
  }
131
- :global([data-theme="dark"]) .short-video-viewer__description {
132
- --short-video-viewer--description--font--primary-color: #ffffff;
154
+ .short-video-viewer__text {
155
+ color: #ffffff;
156
+ font-size: 1.125rem;
157
+ font-weight: 400;
133
158
  }</style>
@@ -5,6 +5,7 @@ type Props = {
5
5
  model: ShortVideoViewerModel;
6
6
  availableSideSpace?: number;
7
7
  showAttachments?: boolean;
8
+ showControls?: boolean;
8
9
  autoplay?: true | false | 'on-appearance';
9
10
  localization: IShortVideoViewerLocalization | Locale;
10
11
  on?: {
@@ -1,12 +1,8 @@
1
1
  export { default as ShortVideoViewer } from './cmp.short-video-viewer.svelte';
2
- export { default as ShortVideoAdViewer } from './cmp.ad.svelte';
3
- export { default as ShortVideoProductViewer } from './cmp.product.svelte';
4
- export { default as ShortVideoSocialInteractions } from './cmp.social-interactions.svelte';
2
+ export { default as ShortVideoControls } from './cmp.short-video-controls.svelte';
5
3
  export { default as ShortVideoViewerAttachments } from './cmp.attachments.svelte';
6
4
  export { default as ShortVideoViewerAttachmentsInline } from './cmp.attachments-inline.svelte';
7
- export { default as ShortVideoDetails } from './cmp.short-video-details.svelte';
8
- export type { ShortVideoViewerModel, ShortVideoViewerAdModel, ShortVideoViewerProductModel } from './types';
5
+ export type { ShortVideoViewerModel, ShortVideoSocialInteractions } from './types';
9
6
  export type { IShortVideoViewerLocalization } from './short-video-viewer-localization';
10
- export type { IShortVideoDetailsLocalization } from './short-video-details-localization';
11
7
  export type { IShortVideoAttachmentsLocalization } from './short-video-attachments-localization';
12
8
  export { mapShortVideoViewerModel } from './mapper';
@@ -1,8 +1,5 @@
1
1
  export { default as ShortVideoViewer } from './cmp.short-video-viewer.svelte';
2
- export { default as ShortVideoAdViewer } from './cmp.ad.svelte';
3
- export { default as ShortVideoProductViewer } from './cmp.product.svelte';
4
- export { default as ShortVideoSocialInteractions } from './cmp.social-interactions.svelte';
2
+ export { default as ShortVideoControls } from './cmp.short-video-controls.svelte';
5
3
  export { default as ShortVideoViewerAttachments } from './cmp.attachments.svelte';
6
4
  export { default as ShortVideoViewerAttachmentsInline } from './cmp.attachments-inline.svelte';
7
- export { default as ShortVideoDetails } from './cmp.short-video-details.svelte';
8
5
  export { mapShortVideoViewerModel } from './mapper';
@@ -1,6 +1,6 @@
1
+ import { mapToAdCardModel } from '../../ads/ad-card';
1
2
  import { MediaType } from '../../core/enums';
2
- import { getMediaItemImageUrl } from '../../core/media';
3
- import { shouldUseSalePrice } from '../../products/price-helper';
3
+ import { mapToProductCard } from '../../products/product-card';
4
4
  export const mapShortVideoViewerModel = (payload) => {
5
5
  const mediaBlob = payload.postData.media[0];
6
6
  return {
@@ -13,46 +13,16 @@ export const mapShortVideoViewerModel = (payload) => {
13
13
  thumbnailUrl: mediaBlob.thumbnailUrl
14
14
  },
15
15
  text: payload.postData.shortVideoData.text,
16
- heading: {
17
- image: payload.postHeading.sourceImage,
18
- name: payload.postHeading.sourceName,
19
- displayDate: payload.postHeading.postDisplayDate,
20
- viewsCount: payload.postHeading.postViewsCount
21
- },
22
- ad: payload.ad ? mapToShortVideoViewerAdModel(payload.ad) : null,
23
- products: payload.allProducts.map(mapToShortVideoViewerProductModel)
16
+ enableSocialInteractions: payload.enableSocialInteractions,
17
+ heading: null,
18
+ ad: payload.ad ? mapToAdCardModel(payload.ad) : null,
19
+ products: payload.allProducts.map((x) => mapToProductCard(x))
24
20
  // uncomment if you want to test many products behavior
25
- /*.flatMap((x) => [
26
- { ...x, id: x.id + '1' },
27
- { ...x, id: x.id + '2' },
28
- { ...x, id: x.id + '3' },
29
- { ...x, id: x.id + '4' },
30
- { ...x, id: x.id + '5' }
31
- ])*/
32
- };
33
- };
34
- const mapToShortVideoViewerAdModel = (payload) => {
35
- return {
36
- id: payload.id,
37
- type: payload.type,
38
- image: getMediaItemImageUrl(payload.media[0]),
39
- title: payload.title,
40
- description: payload.description,
41
- price: payload.price,
42
- currency: payload.currency,
43
- ctaButton: payload.ctaButton
44
- };
45
- };
46
- const mapToShortVideoViewerProductModel = (payload) => {
47
- const effectiveSalePrice = payload.priceAndAvailability.productSalePrices?.find((x) => shouldUseSalePrice(payload.priceAndAvailability.price, x.salePrice, x.salePriceEffectiveDateFrom, x.salePriceEffectiveDateTo));
48
- return {
49
- id: payload.id,
50
- title: payload.title,
51
- image: getMediaItemImageUrl(payload.media[0]),
52
- link: payload.link,
53
- currency: payload.priceAndAvailability.currency,
54
- price: payload.priceAndAvailability.price,
55
- salePrice: effectiveSalePrice?.salePrice ?? null,
56
- shortDescription: payload.shortDescription ?? null
21
+ // .flatMap(x =>
22
+ // Array.from({ length: 20 }, (_, i) => ({
23
+ // ...x,
24
+ // id: x.id + String(i + 1)
25
+ // }))
26
+ // )
57
27
  };
58
28
  };
@@ -3,22 +3,19 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
3
3
  export type ShortVideoViewerPayloadFragment = {
4
4
  id: string;
5
5
  enableSocialInteractions: boolean;
6
- postHeading: {
7
- sourceImage: string | null;
8
- sourceName: string;
9
- postDisplayDate: any;
10
- postViewsCount: number;
11
- };
12
6
  allProducts: Array<{
13
- title: string;
14
7
  id: string;
15
- link: string | null;
8
+ title: string;
16
9
  shortDescription: string | null;
10
+ link: string | null;
17
11
  media: Array<{
18
12
  url: string;
19
13
  thumbnailUrl: string | null;
20
14
  type: SchemaTypes.MediaType;
21
15
  }>;
16
+ brand: {
17
+ name: string;
18
+ } | null;
22
19
  priceAndAvailability: {
23
20
  currency: SchemaTypes.Currency;
24
21
  price: number;