@streamscloud/embeddable 17.0.4 → 18.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.
- package/package.json +1 -5
- package/dist/feed-player/cmp.close-button.svelte +0 -40
- package/dist/feed-player/cmp.close-button.svelte.d.ts +0 -19
- package/dist/feed-player/cmp.feed-player.svelte +0 -542
- package/dist/feed-player/cmp.feed-player.svelte.d.ts +0 -15
- package/dist/feed-player/feed-player-localization.d.ts +0 -16
- package/dist/feed-player/feed-player-localization.js +0 -70
- package/dist/feed-player/index.d.ts +0 -3
- package/dist/feed-player/index.js +0 -2
- package/dist/feed-player/sidebar/article-tab.svelte +0 -98
- package/dist/feed-player/sidebar/article-tab.svelte.d.ts +0 -19
- package/dist/feed-player/sidebar/information-tab.svelte +0 -98
- package/dist/feed-player/sidebar/information-tab.svelte.d.ts +0 -26
- package/dist/feed-player/sidebar/playlist-tab.svelte +0 -115
- package/dist/feed-player/sidebar/playlist-tab.svelte.d.ts +0 -19
- package/dist/feed-player/sidebar/post-card.svelte +0 -127
- package/dist/feed-player/sidebar/post-card.svelte.d.ts +0 -24
- package/dist/feed-player/sidebar/recommended-tab.svelte +0 -169
- package/dist/feed-player/sidebar/recommended-tab.svelte.d.ts +0 -19
- package/dist/feed-player/sidebar/sidebar-panel.svelte +0 -71
- package/dist/feed-player/sidebar/sidebar-panel.svelte.d.ts +0 -28
- package/dist/feed-player/sidebar/sidebar-tab-bar.svelte +0 -49
- package/dist/feed-player/sidebar/sidebar-tab-bar.svelte.d.ts +0 -21
- package/dist/feed-player/sidebar/types.d.ts +0 -4
- package/dist/feed-player/sidebar/types.js +0 -1
- package/dist/feed-player/types.d.ts +0 -67
- package/dist/feed-player/types.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/embeddable",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.0.0",
|
|
4
4
|
"author": "StreamsCloud",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,10 +42,6 @@
|
|
|
42
42
|
"types": "./dist/external-api/index.d.ts",
|
|
43
43
|
"svelte": "./dist/external-api/index.js"
|
|
44
44
|
},
|
|
45
|
-
"./feed-player": {
|
|
46
|
-
"types": "./dist/feed-player/index.d.ts",
|
|
47
|
-
"svelte": "./dist/feed-player/index.js"
|
|
48
|
-
},
|
|
49
45
|
"./media-center": {
|
|
50
46
|
"types": "./dist/media-center/index.d.ts",
|
|
51
47
|
"svelte": "./dist/media-center/index.js"
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
<script lang="ts">import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
|
|
2
|
-
import { Icon } from '@streamscloud/kit/ui/icon';
|
|
3
|
-
const { on } = $props();
|
|
4
|
-
</script>
|
|
5
|
-
|
|
6
|
-
<button type="button" class="close-button" onclick={() => on?.click?.()}>
|
|
7
|
-
<Icon src={IconDismiss} />
|
|
8
|
-
</button>
|
|
9
|
-
|
|
10
|
-
<!--
|
|
11
|
-
@component
|
|
12
|
-
Round close button — 32px circle with dismiss icon.
|
|
13
|
-
|
|
14
|
-
### CSS Custom Properties
|
|
15
|
-
| Property | Description | Default |
|
|
16
|
-
|---|---|---|
|
|
17
|
-
| `--sc-fp--close-button--size` | Button size | `32px` |
|
|
18
|
-
| `--sc-fp--close-button--background` | Button background | `light-dark(#f1f6fd, #2a2a2a)` |
|
|
19
|
-
| `--sc-fp--close-button--color` | Icon color | `currentColor` |
|
|
20
|
-
| `--sc-fp--close-button--icon-size` | Icon size | `16px` |
|
|
21
|
-
-->
|
|
22
|
-
|
|
23
|
-
<style>.close-button {
|
|
24
|
-
--_close-button--size: var(--sc-fp--close-button--size, 2rem);
|
|
25
|
-
--_close-button--background: var(--sc-fp--close-button--background, light-dark(#f1f6fd, #2a2a2a));
|
|
26
|
-
--_close-button--color: var(--sc-fp--close-button--color, light-dark(#000000, #ffffff));
|
|
27
|
-
--_close-button--icon-size: var(--sc-fp--close-button--icon-size, 1rem);
|
|
28
|
-
display: flex;
|
|
29
|
-
align-items: center;
|
|
30
|
-
justify-content: center;
|
|
31
|
-
width: var(--_close-button--size);
|
|
32
|
-
height: var(--_close-button--size);
|
|
33
|
-
padding: 0;
|
|
34
|
-
background: var(--_close-button--background);
|
|
35
|
-
border: none;
|
|
36
|
-
border-radius: 50%;
|
|
37
|
-
cursor: pointer;
|
|
38
|
-
--sc-kit--icon--size: var(--_close-button--icon-size);
|
|
39
|
-
--sc-kit--icon--color: var(--_close-button--color);
|
|
40
|
-
}</style>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
type Props = {
|
|
2
|
-
on?: {
|
|
3
|
-
click?: () => void;
|
|
4
|
-
};
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Round close button — 32px circle with dismiss icon.
|
|
8
|
-
*
|
|
9
|
-
* ### CSS Custom Properties
|
|
10
|
-
* | Property | Description | Default |
|
|
11
|
-
* |---|---|---|
|
|
12
|
-
* | `--sc-fp--close-button--size` | Button size | `32px` |
|
|
13
|
-
* | `--sc-fp--close-button--background` | Button background | `light-dark(#f1f6fd, #2a2a2a)` |
|
|
14
|
-
* | `--sc-fp--close-button--color` | Icon color | `currentColor` |
|
|
15
|
-
* | `--sc-fp--close-button--icon-size` | Icon size | `16px` |
|
|
16
|
-
*/
|
|
17
|
-
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
18
|
-
type Cmp = ReturnType<typeof Cmp>;
|
|
19
|
-
export default Cmp;
|
|
@@ -1,542 +0,0 @@
|
|
|
1
|
-
<script lang="ts">import { PostActionsGenerator } from '../posts/controls';
|
|
2
|
-
import { getPostCoverImage, PostModel } from '../posts/model';
|
|
3
|
-
import { PostViewer } from '../posts/post-viewer';
|
|
4
|
-
import { default as CloseButton } from './cmp.close-button.svelte';
|
|
5
|
-
import { FeedPlayerLocalization } from './feed-player-localization';
|
|
6
|
-
import { default as SidebarPanel } from './sidebar/sidebar-panel.svelte';
|
|
7
|
-
import IconChevronDown from '@fluentui/svg-icons/icons/chevron_down_28_regular.svg?raw';
|
|
8
|
-
import IconChevronUp from '@fluentui/svg-icons/icons/chevron_up_28_regular.svg?raw';
|
|
9
|
-
import IconInfo from '@fluentui/svg-icons/icons/info_24_regular.svg?raw';
|
|
10
|
-
import { AppLocale } from '@streamscloud/kit/core/locale';
|
|
11
|
-
import { slideHorizontally } from '@streamscloud/kit/core/transitions';
|
|
12
|
-
import { preloadImage } from '@streamscloud/kit/core/utils';
|
|
13
|
-
import { Loading } from '@streamscloud/kit/ui/loading';
|
|
14
|
-
import { PlayerButtons } from '@streamscloud/kit/ui/player/buttons';
|
|
15
|
-
import { FeedSlider } from '@streamscloud/kit/ui/player/feed-slider';
|
|
16
|
-
import { initBufferFromProvider } from '@streamscloud/kit/ui/player/providers';
|
|
17
|
-
import { untrack } from 'svelte';
|
|
18
|
-
let { dataProvider, socialInteractionsHandler, sharingHandler, analyticsHandler, recommendedHandler, playlistHandler, trackingParams: externalTrackingParams, settings, header, on } = $props();
|
|
19
|
-
$effect(() => {
|
|
20
|
-
if (settings?.locale) {
|
|
21
|
-
AppLocale.change(settings.locale);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
const localization = new FeedPlayerLocalization();
|
|
25
|
-
// ── Mode ──
|
|
26
|
-
const isPreview = $derived(settings?.mode === 'preview');
|
|
27
|
-
// ── Background mode ──
|
|
28
|
-
const isGlassBackground = $derived(settings?.background === 'glass');
|
|
29
|
-
const isTransparentBackground = $derived(settings?.background === 'transparent');
|
|
30
|
-
let backgroundImageUrl = $state(null);
|
|
31
|
-
// ── Buffer ──
|
|
32
|
-
let buffer = $state.raw(null);
|
|
33
|
-
$effect(() => {
|
|
34
|
-
void dataProvider;
|
|
35
|
-
untrack(() => {
|
|
36
|
-
buffer = null;
|
|
37
|
-
backgroundImageUrl = null;
|
|
38
|
-
mappedPostsCache = {};
|
|
39
|
-
initBuffer();
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
const initBuffer = async () => {
|
|
43
|
-
const newBuffer = initBufferFromProvider(dataProvider);
|
|
44
|
-
await newBuffer.ensureWarmedUp();
|
|
45
|
-
if (newBuffer.loaded.length) {
|
|
46
|
-
const coverUrl = getPostCoverImage(newBuffer.loaded[0]);
|
|
47
|
-
await preloadImage(coverUrl);
|
|
48
|
-
backgroundImageUrl = coverUrl;
|
|
49
|
-
}
|
|
50
|
-
buffer = newBuffer;
|
|
51
|
-
};
|
|
52
|
-
// ── PostModel cache ──
|
|
53
|
-
let mappedPostsCache = {};
|
|
54
|
-
const itemAsPostModel = (item) => {
|
|
55
|
-
if (mappedPostsCache[item.id]) {
|
|
56
|
-
return mappedPostsCache[item.id];
|
|
57
|
-
}
|
|
58
|
-
const postModel = new PostModel(item);
|
|
59
|
-
mappedPostsCache[item.id] = postModel;
|
|
60
|
-
return postModel;
|
|
61
|
-
};
|
|
62
|
-
// ── Current post ──
|
|
63
|
-
const currentPostModel = $derived.by(() => {
|
|
64
|
-
if (!buffer?.current) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
return itemAsPostModel(buffer.current);
|
|
68
|
-
});
|
|
69
|
-
const currentTrackingParams = $derived.by(() => {
|
|
70
|
-
if (externalTrackingParams === false) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
const shortVideoId = currentPostModel?.postType === 'SHORT_VIDEO' ? currentPostModel.id : undefined;
|
|
74
|
-
if (!externalTrackingParams && !shortVideoId) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
return { ...externalTrackingParams, shortVideoId };
|
|
78
|
-
});
|
|
79
|
-
const hasProducts = $derived(!!currentPostModel?.attachments && currentPostModel.attachments.products.length > 0);
|
|
80
|
-
const isArticle = $derived(currentPostModel?.postType === 'ARTICLE' && !!currentPostModel.articleId);
|
|
81
|
-
const hasAttachments = $derived(!!currentPostModel?.attachments);
|
|
82
|
-
// ── Sidebar tabs ──
|
|
83
|
-
let preferredTab = $state('information');
|
|
84
|
-
let activeSidebarTab = $state('information');
|
|
85
|
-
let selectedProductId = $state(null);
|
|
86
|
-
const visibleTabs = $derived.by(() => {
|
|
87
|
-
const tabs = [];
|
|
88
|
-
if (hasAttachments) {
|
|
89
|
-
tabs.push({ id: 'information', label: localization.tabInformation });
|
|
90
|
-
}
|
|
91
|
-
if (hasProducts) {
|
|
92
|
-
tabs.push({ id: 'product', label: localization.tabProduct });
|
|
93
|
-
}
|
|
94
|
-
if (isArticle) {
|
|
95
|
-
tabs.push({ id: 'article', label: localization.tabArticle });
|
|
96
|
-
}
|
|
97
|
-
if (recommendedHandler) {
|
|
98
|
-
tabs.push({ id: 'recommended', label: localization.tabRecommended });
|
|
99
|
-
}
|
|
100
|
-
if (playlistHandler) {
|
|
101
|
-
tabs.push({ id: 'playlist', label: localization.tabPlaylist });
|
|
102
|
-
}
|
|
103
|
-
return tabs;
|
|
104
|
-
});
|
|
105
|
-
const sidebarVisible = $derived(visibleTabs.length > 0);
|
|
106
|
-
const handleTabChange = (id) => {
|
|
107
|
-
preferredTab = id;
|
|
108
|
-
activeSidebarTab = id;
|
|
109
|
-
};
|
|
110
|
-
// Tab resolution: preferred tab if available, otherwise first visible
|
|
111
|
-
$effect(() => {
|
|
112
|
-
const tabs = visibleTabs;
|
|
113
|
-
if (tabs.length === 0) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (tabs.some((t) => t.id === preferredTab)) {
|
|
117
|
-
activeSidebarTab = preferredTab;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
activeSidebarTab = tabs[0].id;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
// Reset selectedProductId on post change
|
|
124
|
-
$effect(() => {
|
|
125
|
-
const post = currentPostModel;
|
|
126
|
-
untrack(() => {
|
|
127
|
-
if (post?.attachments && post.attachments.products.length > 0) {
|
|
128
|
-
selectedProductId = post.attachments.products[0].id;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
selectedProductId = null;
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
// ── Sidebar collapsed (user toggle) ──
|
|
136
|
-
let sidebarCollapsed = $state(false);
|
|
137
|
-
// ── Layout ──
|
|
138
|
-
const SAFE_AREA_SIZE = 80;
|
|
139
|
-
const SIDEBAR_WIDTH = 378;
|
|
140
|
-
const MOBILE_BREAKPOINT = 576;
|
|
141
|
-
// Measured from the root element
|
|
142
|
-
let totalWidth = $state(0);
|
|
143
|
-
let isMobileView = $derived(totalWidth <= MOBILE_BREAKPOINT);
|
|
144
|
-
const handleRootResize = (node) => {
|
|
145
|
-
const observer = new ResizeObserver(([entry]) => {
|
|
146
|
-
totalWidth = entry.contentRect.width;
|
|
147
|
-
});
|
|
148
|
-
observer.observe(node);
|
|
149
|
-
return { destroy: () => observer.disconnect() };
|
|
150
|
-
};
|
|
151
|
-
// Measured from header
|
|
152
|
-
let headerHeight = $state(0);
|
|
153
|
-
const handleHeaderResize = (node) => {
|
|
154
|
-
const observer = new ResizeObserver(([entry]) => {
|
|
155
|
-
headerHeight = entry.contentRect.height;
|
|
156
|
-
});
|
|
157
|
-
observer.observe(node);
|
|
158
|
-
return { destroy: () => observer.disconnect() };
|
|
159
|
-
};
|
|
160
|
-
// Measured from video-area
|
|
161
|
-
let contentViewWidth = $state(0);
|
|
162
|
-
let videoAreaWidth = $state(0);
|
|
163
|
-
let videoAreaHeight = $state(0);
|
|
164
|
-
// Priority: sidebar hides first, then video shrinks, then controls go overlay
|
|
165
|
-
// Step 1: Would sidebar + video + safe areas fit?
|
|
166
|
-
const sidebarCanBeShown = $derived.by(() => {
|
|
167
|
-
if (!sidebarVisible || isMobileView || videoAreaHeight === 0) {
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
// Estimate video width from height at 9:16
|
|
171
|
-
const videoW = videoAreaHeight * (9 / 16);
|
|
172
|
-
const areaWithSidebar = totalWidth - SIDEBAR_WIDTH;
|
|
173
|
-
const sideSpace = (areaWithSidebar - videoW) / 2;
|
|
174
|
-
return sideSpace >= SAFE_AREA_SIZE;
|
|
175
|
-
});
|
|
176
|
-
const sidebarWidth = $derived(sidebarCanBeShown && !sidebarCollapsed ? SIDEBAR_WIDTH : 0);
|
|
177
|
-
// Step 2: Controls overlay — only when side space without sidebar is still too small
|
|
178
|
-
const sidePanelsMaxWidth = $derived((videoAreaWidth - contentViewWidth) / 2);
|
|
179
|
-
const showControlsOverlay = $derived(isMobileView);
|
|
180
|
-
// ResizeObserver on video-area — same logic as generic player's handleSliderMounted
|
|
181
|
-
const handleVideoAreaResize = (node) => {
|
|
182
|
-
const observer = new ResizeObserver(([entry]) => {
|
|
183
|
-
const { width: areaW, height: areaH } = entry.contentRect;
|
|
184
|
-
videoAreaWidth = areaW;
|
|
185
|
-
videoAreaHeight = areaH;
|
|
186
|
-
const ratio = 9 / 16;
|
|
187
|
-
let width;
|
|
188
|
-
let height;
|
|
189
|
-
let margin;
|
|
190
|
-
let contentWidthNumber = areaW;
|
|
191
|
-
if (isMobileView) {
|
|
192
|
-
width = '100%';
|
|
193
|
-
height = '100%';
|
|
194
|
-
margin = '0';
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
// On desktop, reserve safe area on each side for controls
|
|
198
|
-
const maxContentWidth = areaW - 2 * SAFE_AREA_SIZE;
|
|
199
|
-
const heightBasedWidth = areaH * ratio;
|
|
200
|
-
contentWidthNumber = Math.min(maxContentWidth, heightBasedWidth);
|
|
201
|
-
width = `${contentWidthNumber}px`;
|
|
202
|
-
height = `${contentWidthNumber / ratio}px`;
|
|
203
|
-
margin = 'auto';
|
|
204
|
-
}
|
|
205
|
-
node.style.setProperty('--_feed-player--content--width', width);
|
|
206
|
-
node.style.setProperty('--_feed-player--content--height', height);
|
|
207
|
-
node.style.setProperty('--_feed-player--content--margin', margin);
|
|
208
|
-
contentViewWidth = contentWidthNumber;
|
|
209
|
-
});
|
|
210
|
-
observer.observe(node);
|
|
211
|
-
return { destroy: () => observer.disconnect() };
|
|
212
|
-
};
|
|
213
|
-
// ── Callbacks ──
|
|
214
|
-
const handleItemActivated = (id) => {
|
|
215
|
-
if (buffer) {
|
|
216
|
-
const item = buffer.loaded.find((i) => i.id === id);
|
|
217
|
-
if (item) {
|
|
218
|
-
backgroundImageUrl = getPostCoverImage(item);
|
|
219
|
-
if (item.postType === 'SHORT_VIDEO') {
|
|
220
|
-
analyticsHandler?.trackShortVideoView(id);
|
|
221
|
-
}
|
|
222
|
-
else if (item.postType) {
|
|
223
|
-
analyticsHandler?.trackPostOpened(item.postType, id);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
on?.postActivated?.(id);
|
|
228
|
-
};
|
|
229
|
-
// ── Analytics callbacks ──
|
|
230
|
-
const onProductClick = (productId) => {
|
|
231
|
-
if (!currentPostModel) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (currentPostModel.postType === 'SHORT_VIDEO') {
|
|
235
|
-
analyticsHandler?.trackShortVideoProductClick(productId, currentPostModel.id);
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
const onProductImpression = (productId) => {
|
|
239
|
-
if (!currentPostModel) {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
if (currentPostModel.postType === 'SHORT_VIDEO') {
|
|
243
|
-
analyticsHandler?.trackShortVideoProductImpression(productId, currentPostModel.id);
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
const onAdClick = (adId) => {
|
|
247
|
-
analyticsHandler?.trackAdClick(adId);
|
|
248
|
-
};
|
|
249
|
-
const onAdImpression = (adId) => {
|
|
250
|
-
analyticsHandler?.trackAdImpression(adId);
|
|
251
|
-
};
|
|
252
|
-
// ── Action buttons (like, share, attachments toggle) ──
|
|
253
|
-
const postActionsGenerator = untrack(() => new PostActionsGenerator({
|
|
254
|
-
get socialInteractionsHandler() {
|
|
255
|
-
return socialInteractionsHandler;
|
|
256
|
-
},
|
|
257
|
-
get sharingHandler() {
|
|
258
|
-
return sharingHandler;
|
|
259
|
-
},
|
|
260
|
-
on: { attachmentClicked: () => (sidebarCollapsed = !sidebarCollapsed) }
|
|
261
|
-
}));
|
|
262
|
-
const itemActions = $derived.by(() => {
|
|
263
|
-
if (!currentPostModel) {
|
|
264
|
-
return [];
|
|
265
|
-
}
|
|
266
|
-
const handler = postActionsGenerator.getPostActionsHandler(currentPostModel);
|
|
267
|
-
const actions = [...handler.actions];
|
|
268
|
-
if (sidebarCanBeShown) {
|
|
269
|
-
actions.push({ icon: IconInfo, callback: () => (sidebarCollapsed = !sidebarCollapsed) });
|
|
270
|
-
}
|
|
271
|
-
return actions;
|
|
272
|
-
});
|
|
273
|
-
</script>
|
|
274
|
-
|
|
275
|
-
<div
|
|
276
|
-
class="feed-player"
|
|
277
|
-
class:feed-player--glass={isGlassBackground}
|
|
278
|
-
class:feed-player--glass-active={isGlassBackground && backgroundImageUrl}
|
|
279
|
-
class:feed-player--transparent={isTransparentBackground}
|
|
280
|
-
style:--_feed-player--background-image-url={backgroundImageUrl ? `url(${backgroundImageUrl})` : undefined}
|
|
281
|
-
use:handleRootResize>
|
|
282
|
-
<div class="feed-player__main">
|
|
283
|
-
{#if header}
|
|
284
|
-
<div class="feed-player__header" use:handleHeaderResize>
|
|
285
|
-
{@render header()}
|
|
286
|
-
</div>
|
|
287
|
-
{/if}
|
|
288
|
-
{#if buffer}
|
|
289
|
-
<div class="feed-player__video-area" class:feed-player__video-area--with-header={!!header} use:handleVideoAreaResize>
|
|
290
|
-
<FeedSlider buffer={buffer} on={{ itemActivated: handleItemActivated }}>
|
|
291
|
-
{#snippet children({ item })}
|
|
292
|
-
{@const postModel = itemAsPostModel(item)}
|
|
293
|
-
<div class="feed-player__content">
|
|
294
|
-
<PostViewer
|
|
295
|
-
model={postModel}
|
|
296
|
-
controlActions={itemActions}
|
|
297
|
-
trackingParams={externalTrackingParams ?? null}
|
|
298
|
-
enableAttachments={false}
|
|
299
|
-
enableControls={showControlsOverlay}
|
|
300
|
-
autoplay="on-appearance"
|
|
301
|
-
on={isPreview
|
|
302
|
-
? {}
|
|
303
|
-
: {
|
|
304
|
-
productClick: onProductClick,
|
|
305
|
-
productImpression: onProductImpression,
|
|
306
|
-
adClick: onAdClick,
|
|
307
|
-
adImpression: onAdImpression,
|
|
308
|
-
articleReadMore: (id) => on?.articleReadMore?.(id)
|
|
309
|
-
}} />
|
|
310
|
-
</div>
|
|
311
|
-
{/snippet}
|
|
312
|
-
</FeedSlider>
|
|
313
|
-
|
|
314
|
-
{#if !showControlsOverlay}
|
|
315
|
-
<div class="feed-player__controls" style:--_feed-player--controls-width="{sidePanelsMaxWidth}px">
|
|
316
|
-
<div class="feed-player__controls-spacer"> </div>
|
|
317
|
-
<div class="feed-player__controls-left">
|
|
318
|
-
<PlayerButtons actions={itemActions} scaleEffect={true} />
|
|
319
|
-
|
|
320
|
-
{#if buffer && buffer.loaded.length > 1}
|
|
321
|
-
<div class="feed-player__navigation">
|
|
322
|
-
<PlayerButtons actions={[{ icon: IconChevronUp, callback: buffer.loadPrevious, disabled: !buffer.canLoadPrevious }]} scaleEffect={true} />
|
|
323
|
-
<PlayerButtons actions={[{ icon: IconChevronDown, callback: buffer.loadNext, disabled: !buffer.canLoadNext }]} scaleEffect={true} />
|
|
324
|
-
</div>
|
|
325
|
-
{/if}
|
|
326
|
-
</div>
|
|
327
|
-
<div class="feed-player__controls-spacer feed-player__controls-spacer--right"> </div>
|
|
328
|
-
</div>
|
|
329
|
-
{/if}
|
|
330
|
-
</div>
|
|
331
|
-
{/if}
|
|
332
|
-
</div>
|
|
333
|
-
{#if buffer}
|
|
334
|
-
<div class="feed-player__sidebar-container">
|
|
335
|
-
{#if on?.closed}
|
|
336
|
-
<div class="feed-player__close">
|
|
337
|
-
<CloseButton on={{ click: on.closed }} />
|
|
338
|
-
</div>
|
|
339
|
-
{/if}
|
|
340
|
-
|
|
341
|
-
{#if sidebarWidth > 0 && currentPostModel}
|
|
342
|
-
<div
|
|
343
|
-
class="feed-player__sidebar"
|
|
344
|
-
class:feed-player__sidebar--with-close={on?.closed}
|
|
345
|
-
class:feed-player__sidebar--with-header={!on?.closed && !!header}
|
|
346
|
-
style:--_--feed-player--sidebar--width={SIDEBAR_WIDTH + 'px'}
|
|
347
|
-
style:--_--feed-player--header-height={headerHeight + 'px'}
|
|
348
|
-
transition:slideHorizontally|local>
|
|
349
|
-
<SidebarPanel
|
|
350
|
-
tabs={visibleTabs}
|
|
351
|
-
activeTabId={activeSidebarTab}
|
|
352
|
-
model={currentPostModel}
|
|
353
|
-
trackingParams={currentTrackingParams}
|
|
354
|
-
selectedProductId={selectedProductId}
|
|
355
|
-
interactionsDisabled={isPreview}
|
|
356
|
-
recommendedHandler={recommendedHandler}
|
|
357
|
-
playlistHandler={playlistHandler}
|
|
358
|
-
on={isPreview
|
|
359
|
-
? { tabChange: () => {} }
|
|
360
|
-
: {
|
|
361
|
-
tabChange: handleTabChange,
|
|
362
|
-
productClick: onProductClick,
|
|
363
|
-
productImpression: onProductImpression,
|
|
364
|
-
productBuy: on?.productBuy,
|
|
365
|
-
productSelect: (id) => {
|
|
366
|
-
selectedProductId = id;
|
|
367
|
-
handleTabChange('product');
|
|
368
|
-
},
|
|
369
|
-
adClick: onAdClick,
|
|
370
|
-
adImpression: onAdImpression,
|
|
371
|
-
articleReadMore: on?.articleReadMore,
|
|
372
|
-
postActivate: (id) => buffer?.tryActivateItemById(id)
|
|
373
|
-
}} />
|
|
374
|
-
</div>
|
|
375
|
-
{/if}
|
|
376
|
-
</div>
|
|
377
|
-
{/if}
|
|
378
|
-
{#if !buffer}
|
|
379
|
-
<Loading positionAbsoluteCenter={true} timeout={500} />
|
|
380
|
-
{/if}
|
|
381
|
-
</div>
|
|
382
|
-
|
|
383
|
-
<!--
|
|
384
|
-
@component
|
|
385
|
-
Vertical post feed player with a tabbed sidebar for products, articles, and recommendations.
|
|
386
|
-
|
|
387
|
-
### CSS Custom Properties
|
|
388
|
-
| Property | Description | Default |
|
|
389
|
-
|---|---|---|
|
|
390
|
-
| `--sc-fp--background` | Player background | `light-dark(white, black)` |
|
|
391
|
-
| `--sc-fp--padding-left` | Left padding for video area offset | `0px` |
|
|
392
|
-
| `--sc-fp--button--color` | Control button color | inherited |
|
|
393
|
-
| `--sc-fp--button--color--inactive` | Inactive control button color | inherited |
|
|
394
|
-
-->
|
|
395
|
-
|
|
396
|
-
<style>.feed-player {
|
|
397
|
-
--_feed-player--background: var(--sc-fp--background, light-dark(#ffffff, #000000));
|
|
398
|
-
--_feed-player--padding-left: var(--sc-fp--padding-left, 0px);
|
|
399
|
-
--_feed-player--button--color: var(--sc-fp--button--color);
|
|
400
|
-
--_feed-player--button--color--inactive: var(--sc-fp--button--color--inactive);
|
|
401
|
-
--post-viewer--button--color: var(--_feed-player--button--color);
|
|
402
|
-
--post-viewer--button--color--inactive: var(--_feed-player--button--color--inactive);
|
|
403
|
-
position: relative;
|
|
404
|
-
width: 100%;
|
|
405
|
-
height: 100%;
|
|
406
|
-
display: flex;
|
|
407
|
-
padding-left: var(--_feed-player--padding-left);
|
|
408
|
-
background: var(--_feed-player--background);
|
|
409
|
-
overflow: hidden;
|
|
410
|
-
container-type: inline-size;
|
|
411
|
-
}
|
|
412
|
-
.feed-player::before {
|
|
413
|
-
content: "";
|
|
414
|
-
position: absolute;
|
|
415
|
-
inset: 0;
|
|
416
|
-
backdrop-filter: blur(1.875rem);
|
|
417
|
-
background-color: rgb(from var(--_feed-player--background) r g b/50%);
|
|
418
|
-
display: none;
|
|
419
|
-
z-index: 0;
|
|
420
|
-
}
|
|
421
|
-
.feed-player--glass::before {
|
|
422
|
-
display: block;
|
|
423
|
-
}
|
|
424
|
-
.feed-player--glass-active {
|
|
425
|
-
background-image: var(--_feed-player--background-image-url);
|
|
426
|
-
background-size: cover;
|
|
427
|
-
background-position: center;
|
|
428
|
-
}
|
|
429
|
-
.feed-player--transparent {
|
|
430
|
-
background: transparent;
|
|
431
|
-
}
|
|
432
|
-
.feed-player__main {
|
|
433
|
-
display: flex;
|
|
434
|
-
flex-direction: column;
|
|
435
|
-
width: 100%;
|
|
436
|
-
flex: 1;
|
|
437
|
-
min-height: 0;
|
|
438
|
-
position: relative;
|
|
439
|
-
z-index: 1;
|
|
440
|
-
}
|
|
441
|
-
.feed-player__header {
|
|
442
|
-
position: relative;
|
|
443
|
-
flex-shrink: 0;
|
|
444
|
-
display: flex;
|
|
445
|
-
justify-content: center;
|
|
446
|
-
align-items: center;
|
|
447
|
-
z-index: 1;
|
|
448
|
-
pointer-events: none;
|
|
449
|
-
/* Set 'container-type: inline-size;' to reference container*/
|
|
450
|
-
}
|
|
451
|
-
@container (width < 576px) {
|
|
452
|
-
.feed-player__header {
|
|
453
|
-
position: absolute;
|
|
454
|
-
top: 0;
|
|
455
|
-
left: 0;
|
|
456
|
-
right: 0;
|
|
457
|
-
z-index: 2;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
.feed-player__video-area {
|
|
461
|
-
flex: 1;
|
|
462
|
-
min-width: 0;
|
|
463
|
-
min-height: 0;
|
|
464
|
-
position: relative;
|
|
465
|
-
padding-bottom: 0.625rem;
|
|
466
|
-
overflow: hidden;
|
|
467
|
-
}
|
|
468
|
-
.feed-player__video-area--with-header {
|
|
469
|
-
padding-top: 0;
|
|
470
|
-
}
|
|
471
|
-
.feed-player__video-area {
|
|
472
|
-
/* Set 'container-type: inline-size;' to reference container*/
|
|
473
|
-
}
|
|
474
|
-
@container (width < 576px) {
|
|
475
|
-
.feed-player__video-area {
|
|
476
|
-
padding: 0;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
.feed-player__content {
|
|
480
|
-
width: var(--_feed-player--content--width, 100%);
|
|
481
|
-
height: var(--_feed-player--content--height, 100%);
|
|
482
|
-
margin: var(--_feed-player--content--margin, 0);
|
|
483
|
-
position: relative;
|
|
484
|
-
display: flex;
|
|
485
|
-
justify-content: center;
|
|
486
|
-
align-items: center;
|
|
487
|
-
}
|
|
488
|
-
.feed-player__controls {
|
|
489
|
-
position: absolute;
|
|
490
|
-
top: 0;
|
|
491
|
-
right: 0;
|
|
492
|
-
height: 100%;
|
|
493
|
-
width: var(--_feed-player--controls-width);
|
|
494
|
-
display: flex;
|
|
495
|
-
padding: 0.625rem 0 1.875rem;
|
|
496
|
-
pointer-events: none;
|
|
497
|
-
}
|
|
498
|
-
.feed-player__controls-spacer {
|
|
499
|
-
flex: 0 0 1rem;
|
|
500
|
-
}
|
|
501
|
-
.feed-player__controls-spacer--right {
|
|
502
|
-
flex: 1;
|
|
503
|
-
}
|
|
504
|
-
.feed-player__controls-left {
|
|
505
|
-
display: flex;
|
|
506
|
-
flex-direction: column;
|
|
507
|
-
gap: 2.3125rem;
|
|
508
|
-
justify-content: flex-end;
|
|
509
|
-
align-items: center;
|
|
510
|
-
pointer-events: auto;
|
|
511
|
-
}
|
|
512
|
-
.feed-player__navigation {
|
|
513
|
-
display: flex;
|
|
514
|
-
flex-direction: column;
|
|
515
|
-
gap: 1rem;
|
|
516
|
-
}
|
|
517
|
-
.feed-player__sidebar-container {
|
|
518
|
-
flex-shrink: 0;
|
|
519
|
-
display: flex;
|
|
520
|
-
flex-direction: column;
|
|
521
|
-
align-items: flex-end;
|
|
522
|
-
position: relative;
|
|
523
|
-
z-index: 1;
|
|
524
|
-
}
|
|
525
|
-
.feed-player__close {
|
|
526
|
-
position: absolute;
|
|
527
|
-
top: 0.75rem;
|
|
528
|
-
right: 1rem;
|
|
529
|
-
z-index: 1;
|
|
530
|
-
}
|
|
531
|
-
.feed-player__sidebar {
|
|
532
|
-
flex: 1;
|
|
533
|
-
min-height: 0;
|
|
534
|
-
width: var(--_--feed-player--sidebar--width);
|
|
535
|
-
padding: 0.625rem 0.625rem 0.625rem 0;
|
|
536
|
-
}
|
|
537
|
-
.feed-player__sidebar--with-close {
|
|
538
|
-
padding-top: 3.75rem;
|
|
539
|
-
}
|
|
540
|
-
.feed-player__sidebar--with-header {
|
|
541
|
-
padding-top: var(--_--feed-player--header-height, 0px);
|
|
542
|
-
}</style>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { FeedPlayerProps } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Vertical post feed player with a tabbed sidebar for products, articles, and recommendations.
|
|
4
|
-
*
|
|
5
|
-
* ### CSS Custom Properties
|
|
6
|
-
* | Property | Description | Default |
|
|
7
|
-
* |---|---|---|
|
|
8
|
-
* | `--sc-fp--background` | Player background | `light-dark(white, black)` |
|
|
9
|
-
* | `--sc-fp--padding-left` | Left padding for video area offset | `0px` |
|
|
10
|
-
* | `--sc-fp--button--color` | Control button color | inherited |
|
|
11
|
-
* | `--sc-fp--button--color--inactive` | Inactive control button color | inherited |
|
|
12
|
-
*/
|
|
13
|
-
declare const Cmp: import("svelte").Component<FeedPlayerProps, {}, "">;
|
|
14
|
-
type Cmp = ReturnType<typeof Cmp>;
|
|
15
|
-
export default Cmp;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export declare class FeedPlayerLocalization {
|
|
2
|
-
get tabInformation(): string;
|
|
3
|
-
get tabProduct(): string;
|
|
4
|
-
get tabArticle(): string;
|
|
5
|
-
get tabRecommended(): string;
|
|
6
|
-
get tabPlaylist(): string;
|
|
7
|
-
get show(): string;
|
|
8
|
-
get relatedPosts(): string;
|
|
9
|
-
get suggestedPlaylist(): string;
|
|
10
|
-
get suggestedProducts(): string;
|
|
11
|
-
get showList(): string;
|
|
12
|
-
get updatedLabel(): string;
|
|
13
|
-
postsCount(count: number): string;
|
|
14
|
-
postOf(current: number, total: number): string;
|
|
15
|
-
viewsLabel(count: number): string;
|
|
16
|
-
}
|