@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.
- package/dist/ads/ad-card/cmp.ad-card.svelte +5 -19
- package/dist/ads/ad-card/cmp.ad-card.svelte.d.ts +0 -3
- package/dist/core/actions/horizontal-wheel-scroll.d.ts +4 -0
- package/dist/core/actions/horizontal-wheel-scroll.js +22 -0
- package/dist/core/actions/index.d.ts +2 -0
- package/dist/core/actions/index.js +2 -0
- package/dist/core/actions/swallow-touch.d.ts +2 -0
- package/dist/core/actions/swallow-touch.js +17 -0
- package/dist/media-center/media-center/cmp.media-center.svelte +10 -5
- package/dist/products/price-helper.d.ts +8 -1
- package/dist/products/price-helper.js +69 -24
- package/dist/products/product-card/cmp.product-card.svelte +2 -2
- package/dist/products/product-card/types.d.ts +1 -1
- package/dist/short-videos/short-video-viewer/cmp.attachments-horizontal.svelte +220 -0
- package/dist/short-videos/short-video-viewer/{cmp.attachments-inline.svelte.d.ts → cmp.attachments-horizontal.svelte.d.ts} +0 -2
- package/dist/short-videos/short-video-viewer/cmp.attachments.svelte +2 -8
- package/dist/short-videos/short-video-viewer/cmp.attachments.svelte.d.ts +0 -2
- package/dist/short-videos/short-video-viewer/cmp.short-video-viewer.svelte +63 -67
- package/dist/short-videos/short-video-viewer/cmp.short-video-viewer.svelte.d.ts +0 -2
- package/dist/short-videos/short-video-viewer/index.d.ts +0 -1
- package/dist/short-videos/short-video-viewer/index.js +0 -1
- package/dist/short-videos/short-video-viewer/types.d.ts +1 -1
- package/dist/short-videos/short-video-viewer/ui-manager.svelte.d.ts +2 -1
- package/dist/short-videos/short-video-viewer/ui-manager.svelte.js +5 -2
- package/dist/short-videos/short-videos-player/controls.svelte +85 -93
- package/dist/short-videos/short-videos-player/controls.svelte.d.ts +0 -2
- package/dist/short-videos/short-videos-player/internal-short-video-analytics-handler.d.ts +0 -2
- package/dist/short-videos/short-videos-player/internal-short-video-analytics-handler.js +0 -2
- package/dist/short-videos/short-videos-player/short-videos-player-view.svelte +4 -14
- package/dist/short-videos/short-videos-player/types.d.ts +0 -2
- package/dist/short-videos/short-videos-player/ui-manager.svelte.d.ts +8 -1
- package/dist/short-videos/short-videos-player/ui-manager.svelte.js +15 -6
- package/dist/streams/layout/element-views/price-element-view.svelte +7 -5
- package/dist/streams/layout/models/stream-layout-short-video-model.d.ts +4 -4
- package/dist/streams/stream-player/controls.svelte +131 -103
- package/dist/streams/stream-player/controls.svelte.d.ts +1 -2
- package/dist/streams/stream-player/fade-mixins.scss +12 -0
- package/dist/streams/stream-player/internal-stream-analytics-handler.d.ts +0 -2
- package/dist/streams/stream-player/internal-stream-analytics-handler.js +0 -2
- package/dist/streams/stream-player/stream-overview.svelte +1 -3
- package/dist/streams/stream-player/stream-player.svelte +24 -15
- package/dist/streams/stream-player/stream-player.svelte.d.ts +12 -1
- package/dist/streams/stream-player/types.d.ts +0 -2
- package/dist/streams/stream-player/ui-manager.svelte.d.ts +15 -8
- package/dist/streams/stream-player/ui-manager.svelte.js +40 -19
- package/package.json +1 -1
- package/dist/short-videos/short-video-viewer/cmp.attachments-inline.svelte +0 -144
- package/dist/short-videos/short-video-viewer/cmp.short-video-ad-card.svelte +0 -26
- 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
|
|
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:
|
|
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:
|
|
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:
|
|
149
|
+
min-width: 0;
|
|
164
150
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
165
151
|
}
|
|
166
152
|
@container (width < 230px) {
|
|
@@ -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,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
|
-
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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,
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
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:
|
|
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;
|
|
@@ -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> </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 {
|
|
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
|
-
<
|
|
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, {}, "">;
|