@streamscloud/embeddable 6.4.3 → 6.5.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 (49) hide show
  1. package/dist/ads/ad-card/cmp.ad-card.svelte +5 -19
  2. package/dist/ads/ad-card/cmp.ad-card.svelte.d.ts +0 -3
  3. package/dist/core/actions/horizontal-wheel-scroll.d.ts +4 -0
  4. package/dist/core/actions/horizontal-wheel-scroll.js +22 -0
  5. package/dist/core/actions/index.d.ts +2 -0
  6. package/dist/core/actions/index.js +2 -0
  7. package/dist/core/actions/swallow-touch.d.ts +2 -0
  8. package/dist/core/actions/swallow-touch.js +17 -0
  9. package/dist/media-center/media-center/cmp.media-center.svelte +10 -5
  10. package/dist/products/price-helper.d.ts +8 -1
  11. package/dist/products/price-helper.js +69 -24
  12. package/dist/products/product-card/cmp.product-card.svelte +2 -2
  13. package/dist/products/product-card/types.d.ts +1 -1
  14. package/dist/short-videos/short-video-viewer/cmp.attachments-horizontal.svelte +220 -0
  15. package/dist/short-videos/short-video-viewer/{cmp.attachments-inline.svelte.d.ts → cmp.attachments-horizontal.svelte.d.ts} +0 -2
  16. package/dist/short-videos/short-video-viewer/cmp.attachments.svelte +2 -8
  17. package/dist/short-videos/short-video-viewer/cmp.attachments.svelte.d.ts +0 -2
  18. package/dist/short-videos/short-video-viewer/cmp.short-video-viewer.svelte +63 -67
  19. package/dist/short-videos/short-video-viewer/cmp.short-video-viewer.svelte.d.ts +0 -2
  20. package/dist/short-videos/short-video-viewer/index.d.ts +0 -1
  21. package/dist/short-videos/short-video-viewer/index.js +0 -1
  22. package/dist/short-videos/short-video-viewer/types.d.ts +1 -1
  23. package/dist/short-videos/short-video-viewer/ui-manager.svelte.d.ts +2 -1
  24. package/dist/short-videos/short-video-viewer/ui-manager.svelte.js +5 -2
  25. package/dist/short-videos/short-videos-player/controls.svelte +85 -93
  26. package/dist/short-videos/short-videos-player/controls.svelte.d.ts +0 -2
  27. package/dist/short-videos/short-videos-player/internal-short-video-analytics-handler.d.ts +0 -2
  28. package/dist/short-videos/short-videos-player/internal-short-video-analytics-handler.js +0 -2
  29. package/dist/short-videos/short-videos-player/short-videos-player-view.svelte +4 -14
  30. package/dist/short-videos/short-videos-player/types.d.ts +0 -2
  31. package/dist/short-videos/short-videos-player/ui-manager.svelte.d.ts +8 -1
  32. package/dist/short-videos/short-videos-player/ui-manager.svelte.js +15 -6
  33. package/dist/streams/layout/element-views/price-element-view.svelte +7 -5
  34. package/dist/streams/layout/models/stream-layout-short-video-model.d.ts +4 -4
  35. package/dist/streams/stream-player/controls.svelte +131 -103
  36. package/dist/streams/stream-player/controls.svelte.d.ts +1 -2
  37. package/dist/streams/stream-player/fade-mixins.scss +12 -0
  38. package/dist/streams/stream-player/internal-stream-analytics-handler.d.ts +0 -2
  39. package/dist/streams/stream-player/internal-stream-analytics-handler.js +0 -2
  40. package/dist/streams/stream-player/stream-overview.svelte +1 -3
  41. package/dist/streams/stream-player/stream-player.svelte +24 -15
  42. package/dist/streams/stream-player/stream-player.svelte.d.ts +12 -1
  43. package/dist/streams/stream-player/types.d.ts +0 -2
  44. package/dist/streams/stream-player/ui-manager.svelte.d.ts +15 -8
  45. package/dist/streams/stream-player/ui-manager.svelte.js +40 -19
  46. package/package.json +1 -1
  47. package/dist/short-videos/short-video-viewer/cmp.attachments-inline.svelte +0 -144
  48. package/dist/short-videos/short-video-viewer/cmp.short-video-ad-card.svelte +0 -26
  49. package/dist/short-videos/short-video-viewer/cmp.short-video-ad-card.svelte.d.ts +0 -13
@@ -2,19 +2,7 @@
2
2
  import { Button, ButtonSize } from '../../ui/button';
3
3
  import { Image } from '../../ui/image';
4
4
  import { LineClamp } from '../../ui/line-clamp';
5
- let { ad, inert = false, on } = $props();
6
- const onAdClicked = (event) => {
7
- var _a;
8
- event.preventDefault();
9
- event.stopPropagation();
10
- if (on === null || on === void 0 ? void 0 : on.adClick) {
11
- on.adClick(ad.id);
12
- }
13
- if (!((_a = ad.ctaButton) === null || _a === void 0 ? void 0 : _a.url)) {
14
- return;
15
- }
16
- window.open(ad.ctaButton.url, '_blank', 'noopener noreferrer');
17
- };
5
+ let { ad, inert = false } = $props();
18
6
  </script>
19
7
 
20
8
  <div class="ad-card" inert={inert}>
@@ -49,7 +37,7 @@ const onAdClicked = (event) => {
49
37
  <div class="ad-card__button">
50
38
  <Button
51
39
  size={ButtonSize.Standard}
52
- on={{ click: onAdClicked }}
40
+ on={{ click: () => ad.ctaButton && window.open(ad.ctaButton.url, '_blank') }}
53
41
  --button--font--size="1em"
54
42
  --button--font--color={ad.ctaButton.textColor}
55
43
  --button--background={ad.ctaButton.background}
@@ -74,9 +62,6 @@ const onAdClicked = (event) => {
74
62
  }
75
63
  }
76
64
  .ad-card {
77
- --image--object-fit: cover;
78
- --image--width: auto;
79
- --image--height: auto;
80
65
  width: 100%;
81
66
  height: max-content;
82
67
  display: flex;
@@ -86,7 +71,7 @@ const onAdClicked = (event) => {
86
71
  aspect-ratio: 9/16;
87
72
  color: #000000;
88
73
  background-color: rgba(255, 255, 255, 0.9);
89
- border: 0.038125rem solid #f2f2f3;
74
+ border: 1px solid #f2f2f3;
90
75
  border-radius: 0.5rem;
91
76
  padding: 0.75rem 0.75rem 1.125rem;
92
77
  justify-content: flex-start;
@@ -108,6 +93,7 @@ const onAdClicked = (event) => {
108
93
  overflow: hidden;
109
94
  aspect-ratio: 1/1;
110
95
  width: 100%;
96
+ --image--object-fit: cover;
111
97
  --image--width: 100%;
112
98
  --image--height: 100%;
113
99
  }
@@ -160,7 +146,7 @@ const onAdClicked = (event) => {
160
146
  align-items: end;
161
147
  min-height: 2.5rem;
162
148
  margin-top: 0.125rem;
163
- min-width: 0px;
149
+ min-width: 0;
164
150
  /* Set 'container-type: inline-size;' to reference container*/
165
151
  }
166
152
  @container (width < 230px) {
@@ -2,9 +2,6 @@ import type { AdCardModel } from './types';
2
2
  type Props = {
3
3
  ad: AdCardModel;
4
4
  inert?: boolean;
5
- on?: {
6
- adClick?: (id: string) => void;
7
- };
8
5
  };
9
6
  declare const Cmp: import("svelte").Component<Props, {}, "">;
10
7
  type Cmp = ReturnType<typeof Cmp>;
@@ -0,0 +1,4 @@
1
+ import type { Action } from 'svelte/action';
2
+ export declare const horizontalWheelScroll: Action<HTMLElement, {
3
+ isolateScroll: boolean;
4
+ } | undefined>;
@@ -0,0 +1,22 @@
1
+ export const horizontalWheelScroll = (node, param = { isolateScroll: true }) => {
2
+ const onWheel = (event) => {
3
+ const canScrollX = node.scrollWidth > node.clientWidth;
4
+ if (!canScrollX) {
5
+ return;
6
+ }
7
+ const delta = event.deltaX !== 0 ? event.deltaX : event.deltaY;
8
+ if (delta !== 0) {
9
+ node.scrollLeft += delta;
10
+ if (param.isolateScroll) {
11
+ event.preventDefault();
12
+ event.stopPropagation();
13
+ }
14
+ }
15
+ };
16
+ node.addEventListener('wheel', onWheel, { passive: false });
17
+ return {
18
+ destroy() {
19
+ node.removeEventListener('wheel', onWheel);
20
+ }
21
+ };
22
+ };
@@ -0,0 +1,2 @@
1
+ export { horizontalWheelScroll } from './horizontal-wheel-scroll';
2
+ export { swallowTouch } from './swallow-touch';
@@ -0,0 +1,2 @@
1
+ export { horizontalWheelScroll } from './horizontal-wheel-scroll';
2
+ export { swallowTouch } from './swallow-touch';
@@ -0,0 +1,2 @@
1
+ import type { Action } from 'svelte/action';
2
+ export declare const swallowTouch: Action;
@@ -0,0 +1,17 @@
1
+ export const swallowTouch = (node) => {
2
+ const stopAll = (e) => {
3
+ e.stopPropagation();
4
+ };
5
+ node.addEventListener('touchstart', stopAll, { passive: false });
6
+ node.addEventListener('touchmove', stopAll, { passive: false });
7
+ node.addEventListener('touchend', stopAll, { passive: false });
8
+ node.addEventListener('touchcancel', stopAll, { passive: false });
9
+ return {
10
+ destroy() {
11
+ node.removeEventListener('touchstart', stopAll);
12
+ node.removeEventListener('touchmove', stopAll);
13
+ node.removeEventListener('touchend', stopAll);
14
+ node.removeEventListener('touchcancel', stopAll);
15
+ }
16
+ };
17
+ };
@@ -7,6 +7,7 @@
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { horizontalWheelScroll } from '../../core/actions';
10
11
  import { Utils } from '../../core/utils';
11
12
  import { default as ShortVideosPlayerView } from '../../short-videos/short-videos-player/short-videos-player-view.svelte';
12
13
  import { default as StreamPlayer } from '../../streams/stream-player/stream-player.svelte';
@@ -194,6 +195,7 @@ const onScrollMounted = (node) => {
194
195
  class:media-center__scroll--has-right={scrollHasRight}
195
196
  class:media-center__scroll--has-both={scrollHasRight && scrollHasLeft}
196
197
  use:onScrollMounted
198
+ use:horizontalWheelScroll
197
199
  onscroll={updateScrollShadows}>
198
200
  <button type="button" class="media-center__overview-button" class:media-center__overview-button--active={overviewOpened} onclick={toggleOverview}>
199
201
  <Icon src={IconTextColumnThree} />
@@ -302,7 +304,7 @@ const onScrollMounted = (node) => {
302
304
  min-width: 0;
303
305
  overflow-x: auto;
304
306
  overflow-y: hidden;
305
- -webkit-overflow-scrolling: touch;
307
+ scrollbar-color: var(--custom-scrollbar-color, #7d7d7d) var(--custom-scrollbar-background, transparent);
306
308
  scrollbar-width: none;
307
309
  display: flex;
308
310
  align-items: center;
@@ -311,7 +313,12 @@ const onScrollMounted = (node) => {
311
313
  mask-image: none;
312
314
  }
313
315
  .media-center__scroll::-webkit-scrollbar {
314
- display: none;
316
+ width: 0;
317
+ height: 0;
318
+ background: var(--custom-scrollbar-background, transparent);
319
+ }
320
+ .media-center__scroll::-webkit-scrollbar-thumb {
321
+ background: var(--custom-scrollbar-color, #7d7d7d);
315
322
  }
316
323
  .media-center__scroll--has-left {
317
324
  mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, rgb(0, 0, 0) 32px, rgb(0, 0, 0) 100%);
@@ -428,8 +435,7 @@ const onScrollMounted = (node) => {
428
435
  .media-center-overview::-webkit-scrollbar {
429
436
  width: 3px;
430
437
  height: 3px;
431
- background: var(--custom-scrollbar-background, transparent);
432
- visibility: hidden;
438
+ background: transparent;
433
439
  }
434
440
  .media-center-overview::-webkit-scrollbar-thumb {
435
441
  background: transparent;
@@ -442,7 +448,6 @@ const onScrollMounted = (node) => {
442
448
  width: 3px;
443
449
  height: 3px;
444
450
  background: var(--custom-scrollbar-background, transparent);
445
- visibility: hidden;
446
451
  }
447
452
  .media-center-overview:hover::-webkit-scrollbar-thumb {
448
453
  background: var(--custom-scrollbar-color, #7d7d7d);
@@ -1,8 +1,14 @@
1
1
  import { Currency } from '../core/enums';
2
+ type CurrecyMode = 'none' | 'symbol' | 'code';
3
+ export type PriceRepresentationOptions = {
4
+ currencyMode?: CurrecyMode;
5
+ missingFractionMode?: 'hide' | 'currency-placeholder' | 'zeros' | 'zeros-or-placeholder';
6
+ locale?: string;
7
+ };
2
8
  export declare const toPriceRepresentation: (data: {
3
9
  amount: number;
4
10
  currency: Currency;
5
- includeCurrency?: boolean;
11
+ options?: PriceRepresentationOptions;
6
12
  }) => string;
7
13
  export declare const shouldUseSalePrice: (data: {
8
14
  price: number;
@@ -17,3 +23,4 @@ export declare const isSalePriceEffective: (data: {
17
23
  effectiveDateTo: number | null;
18
24
  referenceDate?: string | Date | null;
19
25
  }) => boolean;
26
+ export {};
@@ -1,32 +1,77 @@
1
1
  import { Currency } from '../core/enums';
2
2
  import { Utils } from '../core/utils';
3
3
  export const toPriceRepresentation = (data) => {
4
- const { amount, currency, includeCurrency = true } = data;
5
- const formatNumber = (value) => {
6
- const noDecimals = Number.isInteger(value);
7
- const options = {
8
- minimumFractionDigits: noDecimals ? 0 : 2,
9
- maximumFractionDigits: noDecimals ? 0 : 2
10
- };
11
- return value.toLocaleString('no-NO', options);
12
- };
13
- switch (currency) {
14
- case Currency.Eur: {
15
- const value = formatNumber(amount);
16
- return includeCurrency ? `€ ${value}` : value;
17
- }
18
- case Currency.Nok: {
19
- const formattedValue = Number.isInteger(amount) ? `${amount},-` : formatNumber(amount);
20
- return includeCurrency ? `kr ${formatNumber(amount)}` : formattedValue;
21
- }
22
- case Currency.Usd: {
23
- const value = formatNumber(amount);
24
- return includeCurrency ? `$ ${value}` : value;
25
- }
26
- default: {
27
- Utils.assertUnreachable(currency);
4
+ const { amount, currency, options } = data;
5
+ const { currencyMode = 'symbol', missingFractionMode = 'zeros-or-placeholder', locale = 'nb-NO' } = options ?? {};
6
+ const cfg = currencyDefaults[currency];
7
+ // Helper to attach currency per mode
8
+ const decorateWithCurrency = (value) => {
9
+ switch (currencyMode) {
10
+ case 'symbol':
11
+ return `${cfg.symbol} ${value}`;
12
+ case 'code':
13
+ return `${value} ${cfg.code}`;
14
+ case 'none':
15
+ return `${value}`;
16
+ default:
17
+ Utils.assertUnreachable(currencyMode);
28
18
  }
19
+ };
20
+ // If amount has a fractional part, show localized number with 2 fraction digits
21
+ if (hasFraction(amount)) {
22
+ const nf = new Intl.NumberFormat(locale, {
23
+ minimumFractionDigits: 2,
24
+ maximumFractionDigits: 2
25
+ });
26
+ return decorateWithCurrency(nf.format(amount));
29
27
  }
28
+ // Fraction is missing: build numeric part according to the selected missingFractionMode
29
+ const formattedInteger = formatIntegerWithGrouping(amount, locale);
30
+ const decimalSeparator = getDecimalSeparator(locale);
31
+ switch (missingFractionMode) {
32
+ case 'hide':
33
+ return decorateWithCurrency(formattedInteger);
34
+ case 'currency-placeholder':
35
+ if (cfg.missingFractionPlaceholder) {
36
+ return decorateWithCurrency(`${formattedInteger}${decimalSeparator}${cfg.missingFractionPlaceholder}`);
37
+ }
38
+ else {
39
+ return decorateWithCurrency(formattedInteger);
40
+ }
41
+ case 'zeros':
42
+ return decorateWithCurrency(`${formattedInteger}${decimalSeparator}00`);
43
+ case 'zeros-or-placeholder':
44
+ if (cfg.missingFractionPlaceholder) {
45
+ return decorateWithCurrency(`${formattedInteger}${decimalSeparator}${cfg.missingFractionPlaceholder}`);
46
+ }
47
+ else {
48
+ return decorateWithCurrency(`${formattedInteger}${decimalSeparator}00`);
49
+ }
50
+ default:
51
+ Utils.assertUnreachable(missingFractionMode);
52
+ }
53
+ };
54
+ const currencyDefaults = {
55
+ [Currency.Nok]: { symbol: 'kr', code: 'NOK', missingFractionPlaceholder: '-' },
56
+ [Currency.Usd]: { symbol: '$', code: 'USD' },
57
+ [Currency.Eur]: { symbol: '€', code: 'EUR' }
58
+ };
59
+ const getDecimalSeparator = (locale) => {
60
+ const nf = new Intl.NumberFormat(locale);
61
+ const parts = nf.formatToParts(1.1);
62
+ const decimalPart = parts.find((p) => p.type === 'decimal');
63
+ return decimalPart ? decimalPart.value : '.';
64
+ };
65
+ const formatIntegerWithGrouping = (amount, locale) => {
66
+ const nf = new Intl.NumberFormat(locale, {
67
+ minimumFractionDigits: 0,
68
+ maximumFractionDigits: 0,
69
+ useGrouping: true
70
+ });
71
+ return nf.format(Math.trunc(amount));
72
+ };
73
+ const hasFraction = (amount) => {
74
+ return Math.abs(amount % 1) > Number.EPSILON;
30
75
  };
31
76
  export const shouldUseSalePrice = (data) => {
32
77
  const { price, salePrice, effectiveDateFrom, effectiveDateTo, referenceDate } = data;
@@ -69,7 +69,7 @@ const onProductClicked = (event) => {
69
69
  --_product-card--aspect-ratio: var(--product-card--aspect-ratio, 10/16);
70
70
  --_product-card--border-radius: var(--product-card--border-radius, 0.5rem);
71
71
  --image--border-radius: 0.25rem;
72
- --image--object-fit: fit;
72
+ --image--object-fit: contain;
73
73
  --image--width: auto;
74
74
  --image--height: auto;
75
75
  width: 100%;
@@ -81,7 +81,7 @@ const onProductClicked = (event) => {
81
81
  aspect-ratio: var(--_product-card--aspect-ratio);
82
82
  color: #000000;
83
83
  background-color: rgba(255, 255, 255, 0.9);
84
- border: 0.038125rem solid #f2f2f3;
84
+ border: 1px solid #f2f2f3;
85
85
  border-radius: var(--_product-card--border-radius);
86
86
  padding: 0.75rem 0.75rem 1.125rem;
87
87
  justify-content: space-between;
@@ -2,7 +2,7 @@ import type { Currency } from '../..';
2
2
  export type ProductCardModel = {
3
3
  id: string;
4
4
  title: string;
5
- shortDescription?: string | null;
5
+ shortDescription: string | null;
6
6
  link: string | null;
7
7
  image: string | null;
8
8
  brandName: string | null;
@@ -0,0 +1,220 @@
1
+ <script lang="ts">import { horizontalWheelScroll, swallowTouch } from '../../core/actions';
2
+ import { Currency } from '../../core/enums';
3
+ import { toPriceRepresentation } from '../../products/price-helper';
4
+ import { Icon, IconColor } from '../../ui/icon';
5
+ import { ImageRounded } from '../../ui/image';
6
+ import IconTargetArrow from '@fluentui/svg-icons/icons/target_arrow_20_regular.svg?raw';
7
+ let { model, on } = $props();
8
+ const attachmentsToShow = $derived.by(() => {
9
+ const products = model.products
10
+ .filter((p) => !!p.image)
11
+ .map((p) => ({
12
+ isAd: false,
13
+ image: p.image,
14
+ link: p.link,
15
+ productId: p.id,
16
+ price: {
17
+ amount: p.salePrice || p.price,
18
+ currency: p.currency
19
+ },
20
+ title: p.title,
21
+ description: p.shortDescription
22
+ }));
23
+ const ads = (model.ad ? [model.ad] : [])
24
+ .filter((a) => !!a.image)
25
+ .map((a) => {
26
+ var _a;
27
+ return ({
28
+ isAd: true,
29
+ image: a.image,
30
+ link: ((_a = a.ctaButton) === null || _a === void 0 ? void 0 : _a.url) || null,
31
+ productId: null,
32
+ price: a.price && a.currency
33
+ ? {
34
+ amount: a.price,
35
+ currency: a.currency,
36
+ label: a.priceInfoLabel
37
+ }
38
+ : null,
39
+ title: a.title,
40
+ description: a.description
41
+ });
42
+ });
43
+ const result = [...ads, ...products];
44
+ return result;
45
+ });
46
+ const handleAttachmentClick = (attachment) => {
47
+ if (attachment.productId && (on === null || on === void 0 ? void 0 : on.productClick)) {
48
+ on.productClick(attachment.productId);
49
+ }
50
+ if (attachment.link) {
51
+ window.open(attachment.link, '_blank');
52
+ }
53
+ };
54
+ let attachmentElements = $state({});
55
+ let observer = null;
56
+ $effect(() => {
57
+ if ((on === null || on === void 0 ? void 0 : on.productImpression) && Object.keys(attachmentElements).length > 0) {
58
+ if (observer) {
59
+ observer.disconnect();
60
+ }
61
+ observer = new IntersectionObserver((entries) => {
62
+ entries.forEach((entry) => {
63
+ if (entry.isIntersecting && entry.intersectionRatio >= 0.1) {
64
+ const productId = entry.target.getAttribute('data-product-id');
65
+ if (productId) {
66
+ on.productImpression(productId, model.id);
67
+ observer === null || observer === void 0 ? void 0 : observer.unobserve(entry.target); // Only track once per session
68
+ }
69
+ }
70
+ });
71
+ }, { threshold: 0.1 });
72
+ Object.entries(attachmentElements).forEach(([key, element]) => {
73
+ if (element && key.startsWith('product-')) {
74
+ observer === null || observer === void 0 ? void 0 : observer.observe(element);
75
+ }
76
+ });
77
+ }
78
+ return () => {
79
+ if (observer) {
80
+ observer.disconnect();
81
+ observer = null;
82
+ }
83
+ };
84
+ });
85
+ </script>
86
+
87
+ <div class="attachments-horizontal" use:horizontalWheelScroll use:swallowTouch>
88
+ {#each attachmentsToShow as attachment, index (attachment)}
89
+ <div
90
+ class="attachments-horizontal__item"
91
+ class:attachments-horizontal__item--single={attachmentsToShow.length === 1}
92
+ onclick={() => handleAttachmentClick(attachment)}
93
+ onkeydown={() => {}}
94
+ role="none"
95
+ bind:this={attachmentElements[attachment.productId ? `product-${attachment.productId}` : `ad-${index}`]}
96
+ data-product-id={attachment.productId || undefined}>
97
+ <div class="attachments-card" class:attachments-card--single={attachmentsToShow.length === 1}>
98
+ <div class="attachments-card__thumb">
99
+ <ImageRounded src={attachment.image} alt="" noBorders={true} />
100
+ </div>
101
+ <div class="attachments-card__content">
102
+ <div class="attachments-card__title" title={attachment.title}>
103
+ {attachment.title}
104
+ </div>
105
+ {#if attachment.price}
106
+ <div class="attachments-card__extra-info">
107
+ {#if attachment.price.label}
108
+ {attachment.price.label}<span>&nbsp;</span>
109
+ {/if}
110
+ {toPriceRepresentation({ amount: attachment.price.amount, currency: attachment.price.currency, options: { currencyMode: 'code' } })}
111
+ </div>
112
+ {:else if attachment.description}
113
+ <div class="attachments-card__extra-info" title={attachment.description}>
114
+ {attachment.description}
115
+ </div>
116
+ {/if}
117
+ </div>
118
+ </div>
119
+ {#if attachment.isAd}
120
+ <div class="attachments-horizontal__item-icon">
121
+ <Icon src={IconTargetArrow} color={IconColor.White} />
122
+ </div>
123
+ {/if}
124
+ </div>
125
+ {/each}
126
+ </div>
127
+
128
+ <style>@keyframes fadeIn {
129
+ 0% {
130
+ opacity: 1;
131
+ }
132
+ 50% {
133
+ opacity: 0.4;
134
+ }
135
+ 100% {
136
+ opacity: 1;
137
+ }
138
+ }
139
+ .attachments-horizontal {
140
+ --_short-video-attachments-horizontal--padding-horizontal: var(--short-video-attachments-horizontal--padding-horizontal, 0);
141
+ display: flex;
142
+ gap: 0.5rem;
143
+ flex-wrap: nowrap;
144
+ width: 100%;
145
+ justify-content: flex-start;
146
+ overflow-x: auto;
147
+ padding: 0 var(--_short-video-attachments-horizontal--padding-horizontal);
148
+ scrollbar-color: var(--custom-scrollbar-color, #7d7d7d) var(--custom-scrollbar-background, transparent);
149
+ scrollbar-width: none;
150
+ }
151
+ .attachments-horizontal::-webkit-scrollbar {
152
+ width: 0;
153
+ height: 0;
154
+ background: var(--custom-scrollbar-background, transparent);
155
+ }
156
+ .attachments-horizontal::-webkit-scrollbar-thumb {
157
+ background: var(--custom-scrollbar-color, #7d7d7d);
158
+ }
159
+ .attachments-horizontal__item {
160
+ position: relative;
161
+ cursor: pointer;
162
+ width: min-content;
163
+ }
164
+ .attachments-horizontal__item--single {
165
+ flex: 1 1 auto;
166
+ width: 100%;
167
+ }
168
+ .attachments-horizontal__item-icon {
169
+ position: absolute;
170
+ bottom: 2px;
171
+ right: 2px;
172
+ --icon--size: 1rem;
173
+ }
174
+
175
+ .attachments-card {
176
+ display: grid;
177
+ grid-template-columns: 2.375rem 1fr;
178
+ grid-column-gap: 0.75rem;
179
+ align-items: center;
180
+ width: 15.625rem;
181
+ padding: 0.375rem;
182
+ background-color: rgba(255, 255, 255, 0.9);
183
+ color: #000000;
184
+ border: 0.0625rem solid #f2f2f3;
185
+ border-radius: 0.25rem;
186
+ }
187
+ .attachments-card--single {
188
+ width: 100%;
189
+ }
190
+ :global([data-theme="dark"]) .attachments-card {
191
+ background-color: rgba(18, 18, 18, 0.9);
192
+ color: #ffffff;
193
+ border-color: #1e1e1e;
194
+ }
195
+ .attachments-card__thumb {
196
+ --image--rounded--width: 2.375rem;
197
+ --image--rounded--height: 2.375rem;
198
+ overflow: hidden;
199
+ border-radius: 0.125rem;
200
+ }
201
+ .attachments-card__content {
202
+ min-width: 0;
203
+ display: flex;
204
+ flex-direction: column;
205
+ }
206
+ .attachments-card__title {
207
+ font-size: 0.875rem;
208
+ font-weight: 600;
209
+ line-height: 1.0625rem;
210
+ white-space: nowrap;
211
+ overflow: hidden;
212
+ text-overflow: ellipsis;
213
+ }
214
+ .attachments-card__extra-info {
215
+ font-size: 0.625rem;
216
+ color: #6b7280;
217
+ white-space: nowrap;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
220
+ }</style>
@@ -4,8 +4,6 @@ type Props = {
4
4
  on?: {
5
5
  productClick?: (productId: string) => void;
6
6
  productImpression?: (productId: string, videoId: string) => void;
7
- adClick?: (adId: string) => void;
8
- adImpression?: (adId: string, videoId: string) => void;
9
7
  };
10
8
  };
11
9
  declare const Cmp: import("svelte").Component<Props, {}, "">;
@@ -1,4 +1,4 @@
1
- <script lang="ts">import { default as ShortVideoAdCard } from './cmp.short-video-ad-card.svelte';
1
+ <script lang="ts">import { AdCard } from '../../ads/ad-card';
2
2
  import { default as ShortVideoProductCard } from './cmp.short-video-product-card.svelte';
3
3
  import { ShortVideoAttachmentsLocalization } from './short-video-attachments-localization';
4
4
  import {} from './types';
@@ -9,13 +9,7 @@ const localization = $derived(new ShortVideoAttachmentsLocalization(localization
9
9
  {#if shortVideo.hasAttachments}
10
10
  <div class="short-video-attachments">
11
11
  {#if shortVideo.ad}
12
- <ShortVideoAdCard
13
- ad={shortVideo.ad}
14
- videoId={shortVideo.id}
15
- on={{
16
- adClick: on?.adClick,
17
- impression: on?.adImpression
18
- }} />
12
+ <AdCard ad={shortVideo.ad} />
19
13
  {/if}
20
14
 
21
15
  {#if shortVideo.products.length}
@@ -7,8 +7,6 @@ type Props = {
7
7
  on?: {
8
8
  productClick?: (productId: string) => void;
9
9
  productImpression?: (productId: string, videoId: string) => void;
10
- adClick?: (adId: string) => void;
11
- adImpression?: (adId: string, videoId: string) => void;
12
10
  };
13
11
  };
14
12
  declare const Cmp: import("svelte").Component<Props, {}, "">;