@streamscloud/embeddable 7.2.0-1759185799768 → 7.2.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/content-player/cmp.content-player.svelte +46 -83
- package/dist/content-player/controls-and-attachments.svelte +6 -9
- package/dist/content-player/ui-manager.svelte.d.ts +6 -6
- package/dist/content-player/ui-manager.svelte.js +11 -11
- package/dist/streams/stream-player/stream-player-view.svelte +14 -11
- package/dist/ui/spotlight-layout/cmp.spotlight-layout.svelte +120 -0
- package/dist/ui/spotlight-layout/cmp.spotlight-layout.svelte.d.ts +18 -0
- package/dist/ui/spotlight-layout/index.d.ts +1 -0
- package/dist/ui/spotlight-layout/index.js +1 -0
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ import { PostViewer } from '../posts/post-viewer';
|
|
|
12
12
|
import { Icon } from '../ui/icon';
|
|
13
13
|
import { Loading } from '../ui/loading';
|
|
14
14
|
import { PlayerSlider } from '../ui/player-slider';
|
|
15
|
+
import { SpotlightLayout } from '../ui/spotlight-layout';
|
|
15
16
|
import { SwipeIndicator } from '../ui/swipe-indicator';
|
|
16
17
|
import { default as ControlsAndAttachments } from './controls-and-attachments.svelte';
|
|
17
18
|
import { default as OverviewPanel } from './overview-panel.svelte';
|
|
@@ -32,15 +33,18 @@ $effect(() => {
|
|
|
32
33
|
}
|
|
33
34
|
});
|
|
34
35
|
});
|
|
36
|
+
const handleDimensionsChanged = (dimensions) => {
|
|
37
|
+
uiManager.setDialogDimensions({
|
|
38
|
+
mainViewColumnWidth: dimensions.mainSceneWidth,
|
|
39
|
+
viewTotalWidth: dimensions.totalWidth
|
|
40
|
+
});
|
|
41
|
+
};
|
|
35
42
|
const handleMediaCenterHeaderMounted = (data) => {
|
|
36
43
|
uiManager.setMediaCenterHeaderHeight(data.height);
|
|
37
44
|
};
|
|
38
|
-
const
|
|
45
|
+
const handleContentAreaMounted = (node) => {
|
|
39
46
|
const markAsTouched = () => {
|
|
40
47
|
everTouched = true;
|
|
41
|
-
removeListeners();
|
|
42
|
-
};
|
|
43
|
-
const removeListeners = () => {
|
|
44
48
|
node.removeEventListener('touchstart', markAsTouched);
|
|
45
49
|
node.removeEventListener('wheel', markAsTouched);
|
|
46
50
|
node.removeEventListener('click', markAsTouched);
|
|
@@ -50,48 +54,6 @@ const handleContentPlayerMounted = (node) => {
|
|
|
50
54
|
node.addEventListener('wheel', markAsTouched);
|
|
51
55
|
node.addEventListener('click', markAsTouched);
|
|
52
56
|
node.addEventListener('keypress', markAsTouched);
|
|
53
|
-
let resizeObserver = new ResizeObserver(() => {
|
|
54
|
-
const { width: playerWidth, height: playerHeight } = node.getBoundingClientRect();
|
|
55
|
-
const parentAspectRatio = playerWidth / playerHeight;
|
|
56
|
-
const ratio = 9 / 16;
|
|
57
|
-
let width;
|
|
58
|
-
let height;
|
|
59
|
-
let margin;
|
|
60
|
-
let contentWidthNumber = playerWidth;
|
|
61
|
-
if (parentAspectRatio > ratio) {
|
|
62
|
-
// container is wider than main content
|
|
63
|
-
contentWidthNumber = playerHeight * ratio;
|
|
64
|
-
width = `${contentWidthNumber}px`;
|
|
65
|
-
height = '100%';
|
|
66
|
-
margin = '0 auto';
|
|
67
|
-
}
|
|
68
|
-
else if (parentAspectRatio < ratio) {
|
|
69
|
-
// main content is wider than container
|
|
70
|
-
width = '100%';
|
|
71
|
-
height = `${playerWidth / ratio}px`;
|
|
72
|
-
margin = 'auto 0';
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
// fallback case
|
|
76
|
-
width = '100%';
|
|
77
|
-
height = '100%';
|
|
78
|
-
margin = '0';
|
|
79
|
-
}
|
|
80
|
-
node.style.setProperty('--_content-player--content--width', width);
|
|
81
|
-
node.style.setProperty('--_content-player--content--height', height);
|
|
82
|
-
node.style.setProperty('--_content-player--content--margin', margin);
|
|
83
|
-
uiManager.setDialogDimensions({
|
|
84
|
-
contentViewWidth: contentWidthNumber,
|
|
85
|
-
playerTotalWidth: node.getBoundingClientRect().width
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
resizeObserver.observe(node);
|
|
89
|
-
return {
|
|
90
|
-
destroy: () => {
|
|
91
|
-
removeListeners();
|
|
92
|
-
resizeObserver.disconnect();
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
57
|
};
|
|
96
58
|
</script>
|
|
97
59
|
|
|
@@ -111,13 +73,13 @@ const handleContentPlayerMounted = (node) => {
|
|
|
111
73
|
{/snippet}
|
|
112
74
|
<div class="content-player-container__media-center">
|
|
113
75
|
{@render config.mediaCenterControlsPanel({
|
|
114
|
-
maxItemsWidth: Math.min(uiManager.
|
|
76
|
+
maxItemsWidth: Math.min(uiManager.mainViewColumnWidth * 1.4, uiManager.dialogTotalWidth),
|
|
115
77
|
onMounted: handleMediaCenterHeaderMounted,
|
|
116
78
|
closeButton: config.callbacks?.close && closeButton
|
|
117
79
|
})}
|
|
118
80
|
</div>
|
|
119
81
|
{/if}
|
|
120
|
-
<div class="content-player"
|
|
82
|
+
<div class="content-player">
|
|
121
83
|
{#if config.showStreamsCloudWatermark}
|
|
122
84
|
<div class="content-player__watermark">
|
|
123
85
|
<a href="https://streamscloud.com/" tabindex="-1" aria-label="none">
|
|
@@ -126,35 +88,36 @@ const handleContentPlayerMounted = (node) => {
|
|
|
126
88
|
</div>
|
|
127
89
|
{/if}
|
|
128
90
|
{#if config.playerBuffer}
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
91
|
+
<SpotlightLayout ratio={9 / 16} on={{ dimensionsChanged: handleDimensionsChanged }}>
|
|
92
|
+
<div class="content-player__content" use:handleContentAreaMounted>
|
|
93
|
+
<PlayerSlider buffer={config.playerBuffer} on={config.playerSliderCallbacks}>
|
|
94
|
+
{#snippet children({ item })}
|
|
95
|
+
{@const postModel = config.itemAsPostViewerModel(item)}
|
|
96
|
+
{#if postModel}
|
|
97
|
+
<PostViewer
|
|
98
|
+
model={postModel}
|
|
99
|
+
socialInteractionsHandler={config.socialInteractionsHandler}
|
|
100
|
+
showAttachments={config.uiManager.showPostOverlayAttachments}
|
|
101
|
+
showControls={config.uiManager.showPostOverlayControls}
|
|
102
|
+
autoplay="on-appearance"
|
|
103
|
+
locale={config.locale}
|
|
104
|
+
on={{
|
|
105
|
+
progress: (progress) => config.callbacks?.videoProgress?.(item.id, postModel.id, progress),
|
|
106
|
+
productClick: (productId) => config.callbacks?.productClick?.(productId, postModel.id),
|
|
107
|
+
productImpression: (productId) => config.callbacks?.productImpression?.(productId, postModel.id),
|
|
108
|
+
adClick: (adId) => config.callbacks?.adClick?.(adId, postModel.id),
|
|
109
|
+
adImpression: (adId) => config.callbacks?.adImpression?.(adId, postModel.id)
|
|
110
|
+
}} />
|
|
111
|
+
{:else if nonPostItemView}
|
|
112
|
+
{@render nonPostItemView({ item })}
|
|
113
|
+
{/if}
|
|
114
|
+
{/snippet}
|
|
115
|
+
</PlayerSlider>
|
|
116
|
+
{#if uiManager.isMobileView && config.playerBuffer.loaded.length > 1 && !everTouched}
|
|
117
|
+
<SwipeIndicator locale={config.locale} />
|
|
118
|
+
{/if}
|
|
119
|
+
</div>
|
|
120
|
+
</SpotlightLayout>
|
|
158
121
|
{#if overviewPanelContent}
|
|
159
122
|
<OverviewPanel contentFaded={config.fadeContent} uiManager={uiManager}>
|
|
160
123
|
{@render overviewPanelContent()}
|
|
@@ -216,8 +179,6 @@ const handleContentPlayerMounted = (node) => {
|
|
|
216
179
|
padding: var(--content-player--padding);
|
|
217
180
|
backdrop-filter: blur(30px);
|
|
218
181
|
position: relative;
|
|
219
|
-
opacity: var(--content-player--elements-opacity);
|
|
220
|
-
transition: opacity 0.3s ease-in-out;
|
|
221
182
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
222
183
|
}
|
|
223
184
|
@container (width < 576px) {
|
|
@@ -229,12 +190,14 @@ const handleContentPlayerMounted = (node) => {
|
|
|
229
190
|
position: absolute;
|
|
230
191
|
bottom: 5rem;
|
|
231
192
|
left: calc(var(--content-player--overview--width) + 5rem);
|
|
193
|
+
opacity: var(--content-player--elements-opacity);
|
|
194
|
+
transition: opacity 0.3s ease-in-out;
|
|
232
195
|
}
|
|
233
196
|
.content-player__content {
|
|
234
|
-
width:
|
|
235
|
-
height:
|
|
236
|
-
|
|
237
|
-
|
|
197
|
+
width: 100%;
|
|
198
|
+
height: 100%;
|
|
199
|
+
opacity: var(--content-player--elements-opacity);
|
|
200
|
+
transition: opacity 0.3s ease-in-out;
|
|
238
201
|
}
|
|
239
202
|
|
|
240
203
|
.close-button {
|
|
@@ -49,7 +49,7 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
49
49
|
{#if !uiManager.showPostOverlayControls}
|
|
50
50
|
<div class="controls-and-attachments__left">
|
|
51
51
|
{#if currentItemPostContainer}
|
|
52
|
-
<div class="controls-and-
|
|
52
|
+
<div class="controls-and-attachments__short-video-controls">
|
|
53
53
|
<PostControls
|
|
54
54
|
model={currentItemPostContainer}
|
|
55
55
|
socialInteractionsHandler={config.socialInteractionsHandler}
|
|
@@ -80,7 +80,7 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
80
80
|
</div>
|
|
81
81
|
{/if}
|
|
82
82
|
{#if currentItemPostContainer && uiManager.showAttachments && (currentItemPostContainer.products.length || currentItemPostContainer.ads.length)}
|
|
83
|
-
<div class="controls-and-
|
|
83
|
+
<div class="controls-and-attachments__short-video-attachments" transition:slideHorizontally|local>
|
|
84
84
|
<PostAttachments
|
|
85
85
|
container={currentItemPostContainer}
|
|
86
86
|
locale={config.locale}
|
|
@@ -124,7 +124,6 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
124
124
|
justify-content: space-between;
|
|
125
125
|
padding: var(--content-player--controls--padding);
|
|
126
126
|
container-type: inline-size;
|
|
127
|
-
pointer-events: none;
|
|
128
127
|
}
|
|
129
128
|
.controls-and-attachments--with-logo {
|
|
130
129
|
padding-top: 0;
|
|
@@ -141,6 +140,7 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
141
140
|
.controls-and-attachments__right {
|
|
142
141
|
flex: 1;
|
|
143
142
|
display: flex;
|
|
143
|
+
justify-content: space-between;
|
|
144
144
|
align-items: flex-end;
|
|
145
145
|
flex-direction: column;
|
|
146
146
|
overflow-x: hidden;
|
|
@@ -169,7 +169,7 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
169
169
|
.controls-and-attachments__right:hover::-webkit-scrollbar-thumb {
|
|
170
170
|
background: var(--custom-scrollbar-color, #7d7d7d);
|
|
171
171
|
}
|
|
172
|
-
.controls-and-
|
|
172
|
+
.controls-and-attachments__short-video-controls {
|
|
173
173
|
gap: 2.5rem;
|
|
174
174
|
display: flex;
|
|
175
175
|
flex: 1;
|
|
@@ -177,8 +177,7 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
177
177
|
flex-direction: column;
|
|
178
178
|
align-items: flex-start;
|
|
179
179
|
justify-content: flex-end;
|
|
180
|
-
|
|
181
|
-
--post-controls--icon--size: 1.75rem;
|
|
180
|
+
--short-video-controls--icon--size: 1.75rem;
|
|
182
181
|
}
|
|
183
182
|
.controls-and-attachments__player-logo {
|
|
184
183
|
width: var(--content-player--controls--logo--max-width);
|
|
@@ -195,11 +194,10 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
195
194
|
min-width: 6.25rem;
|
|
196
195
|
max-width: 6.25rem;
|
|
197
196
|
}
|
|
198
|
-
.controls-and-
|
|
197
|
+
.controls-and-attachments__short-video-attachments {
|
|
199
198
|
flex: 1;
|
|
200
199
|
width: var(--content-player--controls--attachments--max-width);
|
|
201
200
|
max-width: var(--content-player--controls--attachments--max-width);
|
|
202
|
-
pointer-events: auto;
|
|
203
201
|
opacity: var(--content-player--elements-opacity);
|
|
204
202
|
transition: opacity 0.3s ease-in-out;
|
|
205
203
|
}
|
|
@@ -208,7 +206,6 @@ const trackNavigationButtonsSize = (node) => {
|
|
|
208
206
|
flex-direction: column;
|
|
209
207
|
gap: 1rem;
|
|
210
208
|
z-index: 1;
|
|
211
|
-
pointer-events: auto;
|
|
212
209
|
}
|
|
213
210
|
|
|
214
211
|
.close-button {
|
|
@@ -7,21 +7,21 @@ export declare class ContentPlayerUIManager {
|
|
|
7
7
|
showPostOverlayAttachments: boolean;
|
|
8
8
|
showPostOverlayControls: boolean;
|
|
9
9
|
private _backgroundImageUrl;
|
|
10
|
-
private
|
|
11
|
-
private
|
|
10
|
+
private _dialogTotalWidth;
|
|
11
|
+
private _mainViewColumnWidth;
|
|
12
12
|
private _mediaCenterHeaderHeight;
|
|
13
13
|
private _overviewWidth;
|
|
14
14
|
private _controlsAttachmentsPanelWidth;
|
|
15
15
|
private _controlsNavitagionButtonsWidth;
|
|
16
16
|
private _controlsAvailableSpace;
|
|
17
17
|
private _controlsContentWidth;
|
|
18
|
-
get
|
|
19
|
-
get
|
|
18
|
+
get dialogTotalWidth(): number;
|
|
19
|
+
get mainViewColumnWidth(): number;
|
|
20
20
|
get backgroundImageUrl(): string | null;
|
|
21
21
|
get overviewWidth(): number;
|
|
22
22
|
setDialogDimensions: (dimensions: {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
viewTotalWidth: number;
|
|
24
|
+
mainViewColumnWidth: number;
|
|
25
25
|
}) => void;
|
|
26
26
|
setOverviewWidth: (width: number) => void;
|
|
27
27
|
setMediaCenterHeaderHeight: (height: number) => void;
|
|
@@ -21,24 +21,24 @@ export class ContentPlayerUIManager {
|
|
|
21
21
|
}
|
|
22
22
|
return values.join(';');
|
|
23
23
|
});
|
|
24
|
-
isMobileView = $derived.by(() => this.
|
|
25
|
-
viewInitialized = $derived.by(() => !!this.
|
|
24
|
+
isMobileView = $derived.by(() => this._dialogTotalWidth <= 576);
|
|
25
|
+
viewInitialized = $derived.by(() => !!this._dialogTotalWidth && !!this._mainViewColumnWidth);
|
|
26
26
|
showPostOverlayAttachments = $derived.by(() => this.viewInitialized && this._controlsAttachmentsPanelWidth < CONTROLS_ATTACHMENTS_MAX_WIDTH + 10);
|
|
27
27
|
showPostOverlayControls = $derived.by(() => this.viewInitialized && this._controlsContentWidth < this._controlsNavitagionButtonsWidth);
|
|
28
28
|
_backgroundImageUrl = $state(null);
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
_dialogTotalWidth = $state(0);
|
|
30
|
+
_mainViewColumnWidth = $state(0);
|
|
31
31
|
_mediaCenterHeaderHeight = $state(0);
|
|
32
32
|
_overviewWidth = $state(0);
|
|
33
33
|
_controlsAttachmentsPanelWidth = $state(0);
|
|
34
34
|
_controlsNavitagionButtonsWidth = $state(0);
|
|
35
|
-
_controlsAvailableSpace = $derived.by(() => (this.
|
|
35
|
+
_controlsAvailableSpace = $derived.by(() => (this._dialogTotalWidth - this._mainViewColumnWidth) / 2);
|
|
36
36
|
_controlsContentWidth = $derived.by(() => this._controlsAvailableSpace - CONTROLS_PADDING_HORIZONTAL * 2);
|
|
37
|
-
get
|
|
38
|
-
return this.
|
|
37
|
+
get dialogTotalWidth() {
|
|
38
|
+
return this._dialogTotalWidth;
|
|
39
39
|
}
|
|
40
|
-
get
|
|
41
|
-
return this.
|
|
40
|
+
get mainViewColumnWidth() {
|
|
41
|
+
return this._mainViewColumnWidth;
|
|
42
42
|
}
|
|
43
43
|
get backgroundImageUrl() {
|
|
44
44
|
return this._backgroundImageUrl;
|
|
@@ -47,8 +47,8 @@ export class ContentPlayerUIManager {
|
|
|
47
47
|
return this._overviewWidth;
|
|
48
48
|
}
|
|
49
49
|
setDialogDimensions = (dimensions) => {
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
50
|
+
this._dialogTotalWidth = dimensions.viewTotalWidth;
|
|
51
|
+
this._mainViewColumnWidth = dimensions.mainViewColumnWidth;
|
|
52
52
|
};
|
|
53
53
|
setOverviewWidth = (width) => {
|
|
54
54
|
this._overviewWidth = width;
|
|
@@ -30,21 +30,13 @@ let maxPageIndexViewed = 0;
|
|
|
30
30
|
$effect(() => {
|
|
31
31
|
void streamId;
|
|
32
32
|
untrack(() => {
|
|
33
|
+
if (currentStreamModel) {
|
|
34
|
+
finalizeStreamTracking(currentStreamModel.id);
|
|
35
|
+
}
|
|
33
36
|
buffer = null;
|
|
34
37
|
contentPlayerConfig.playerBuffer = null;
|
|
35
38
|
initNewStream(streamId);
|
|
36
39
|
});
|
|
37
|
-
return () => {
|
|
38
|
-
console.warn('finalize');
|
|
39
|
-
stopActivityTracking();
|
|
40
|
-
if (currentStreamModel) {
|
|
41
|
-
analyticsHandler === null || analyticsHandler === void 0 ? void 0 : analyticsHandler.trackStreamEngagementTime(currentStreamModel.id, totalEngagementTimeSeconds);
|
|
42
|
-
if (buffer && buffer.loaded.length > 0) {
|
|
43
|
-
let scrollDepth = Math.round(((maxPageIndexViewed + 1) / buffer.loaded.length) * 100);
|
|
44
|
-
analyticsHandler === null || analyticsHandler === void 0 ? void 0 : analyticsHandler.trackStreamScrollDepth(currentStreamModel.id, scrollDepth);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
40
|
});
|
|
49
41
|
$effect(() => contentPlayerConfig.updateMediaCenterData(mediaCenterData));
|
|
50
42
|
const initNewStream = (streamId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -147,8 +139,19 @@ const onStreamProductClick = (productId) => {
|
|
|
147
139
|
const onStreamProductImpression = (productId) => {
|
|
148
140
|
analyticsHandler === null || analyticsHandler === void 0 ? void 0 : analyticsHandler.trackStreamProductImpression(productId, streamId);
|
|
149
141
|
};
|
|
142
|
+
const finalizeStreamTracking = (id) => {
|
|
143
|
+
stopActivityTracking();
|
|
144
|
+
analyticsHandler === null || analyticsHandler === void 0 ? void 0 : analyticsHandler.trackStreamEngagementTime(id, totalEngagementTimeSeconds);
|
|
145
|
+
if (buffer && buffer.loaded.length > 0) {
|
|
146
|
+
let scrollDepth = Math.round(((maxPageIndexViewed + 1) / buffer.loaded.length) * 100);
|
|
147
|
+
analyticsHandler === null || analyticsHandler === void 0 ? void 0 : analyticsHandler.trackStreamScrollDepth(id, scrollDepth);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
150
|
const onPlayerClose = () => {
|
|
151
151
|
var _a;
|
|
152
|
+
if (currentStreamModel) {
|
|
153
|
+
finalizeStreamTracking(currentStreamModel.id);
|
|
154
|
+
}
|
|
152
155
|
(_a = on === null || on === void 0 ? void 0 : on.playerClosed) === null || _a === void 0 ? void 0 : _a.call(on);
|
|
153
156
|
};
|
|
154
157
|
const onProgress = (pageId, videoId, progress) => {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<script lang="ts">let { ratio, children, left = undefined, right = undefined, on } = $props();
|
|
2
|
+
let containerRef = $state.raw(null);
|
|
3
|
+
let mainSceneRef = $state.raw(null);
|
|
4
|
+
let leftSidebarRef = $state.raw(null);
|
|
5
|
+
let rightSidebarRef = $state.raw(null);
|
|
6
|
+
const notifyDimensionChanged = () => {
|
|
7
|
+
var _a;
|
|
8
|
+
if (!containerRef || !mainSceneRef || !leftSidebarRef || !rightSidebarRef) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
(_a = on === null || on === void 0 ? void 0 : on.dimensionsChanged) === null || _a === void 0 ? void 0 : _a.call(on, {
|
|
12
|
+
mainSceneWidth: mainSceneRef.getBoundingClientRect().width,
|
|
13
|
+
leftSidebarWidth: leftSidebarRef.getBoundingClientRect().width,
|
|
14
|
+
rightSidebarWidth: rightSidebarRef.getBoundingClientRect().width,
|
|
15
|
+
totalWidth: containerRef.getBoundingClientRect().width
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
let computedMainSceneStyle = $state('width: 100%; height: 100%');
|
|
19
|
+
const calcMainSceneSize = () => {
|
|
20
|
+
if (!containerRef) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const { width: parentWidth, height: parentHeight } = containerRef.getBoundingClientRect();
|
|
24
|
+
const parentAspectRatio = parentWidth / parentHeight;
|
|
25
|
+
let width;
|
|
26
|
+
let height;
|
|
27
|
+
let margin;
|
|
28
|
+
if (parentAspectRatio > ratio) {
|
|
29
|
+
// container is wider than main content
|
|
30
|
+
width = `${parentHeight * ratio}px`;
|
|
31
|
+
height = '100%';
|
|
32
|
+
margin = '0 auto';
|
|
33
|
+
}
|
|
34
|
+
else if (parentAspectRatio < ratio) {
|
|
35
|
+
// main content is wider than container
|
|
36
|
+
width = '100%';
|
|
37
|
+
height = `${parentWidth / ratio}px`;
|
|
38
|
+
margin = 'auto 0';
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// fallback case
|
|
42
|
+
width = '100%';
|
|
43
|
+
height = '100%';
|
|
44
|
+
margin = '0';
|
|
45
|
+
}
|
|
46
|
+
computedMainSceneStyle = `--_spotlight-layout--main--width: ${width}; --_spotlight-layout--main--height: ${height}; --_spotlight-layout--main--margin: ${margin};`;
|
|
47
|
+
};
|
|
48
|
+
$effect(() => {
|
|
49
|
+
let mainSceneSizeCalculationObserver = new ResizeObserver(calcMainSceneSize);
|
|
50
|
+
let dimentionsChangedObserver = new ResizeObserver(notifyDimensionChanged);
|
|
51
|
+
if (containerRef && mainSceneRef && leftSidebarRef && rightSidebarRef) {
|
|
52
|
+
mainSceneSizeCalculationObserver.observe(mainSceneRef);
|
|
53
|
+
dimentionsChangedObserver.observe(containerRef);
|
|
54
|
+
dimentionsChangedObserver.observe(mainSceneRef);
|
|
55
|
+
dimentionsChangedObserver.observe(leftSidebarRef);
|
|
56
|
+
dimentionsChangedObserver.observe(rightSidebarRef);
|
|
57
|
+
}
|
|
58
|
+
return () => {
|
|
59
|
+
mainSceneSizeCalculationObserver.disconnect();
|
|
60
|
+
dimentionsChangedObserver.disconnect();
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
export {};
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<div class="spotlight-layout" bind:this={containerRef}>
|
|
67
|
+
<div class="spotlight-layout__sidebar" bind:this={leftSidebarRef}>
|
|
68
|
+
{#if left}
|
|
69
|
+
{@render left()}
|
|
70
|
+
{/if}
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="spotlight-layout__main-scene" bind:this={mainSceneRef} style={computedMainSceneStyle}>
|
|
74
|
+
{@render children()}
|
|
75
|
+
</div>
|
|
76
|
+
<div class="spotlight-layout__sidebar" bind:this={rightSidebarRef}>
|
|
77
|
+
{#if right}
|
|
78
|
+
{@render right()}
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<style>@keyframes fadeIn {
|
|
84
|
+
0% {
|
|
85
|
+
opacity: 1;
|
|
86
|
+
}
|
|
87
|
+
50% {
|
|
88
|
+
opacity: 0.4;
|
|
89
|
+
}
|
|
90
|
+
100% {
|
|
91
|
+
opacity: 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
.spotlight-layout {
|
|
95
|
+
width: 100%;
|
|
96
|
+
min-width: 100%;
|
|
97
|
+
max-width: 100%;
|
|
98
|
+
height: 100%;
|
|
99
|
+
min-height: 100%;
|
|
100
|
+
max-height: 100%;
|
|
101
|
+
container-type: inline-size;
|
|
102
|
+
display: flex;
|
|
103
|
+
}
|
|
104
|
+
.spotlight-layout__main-scene {
|
|
105
|
+
width: var(--_spotlight-layout--main--width);
|
|
106
|
+
height: var(--_spotlight-layout--main--height);
|
|
107
|
+
margin: var(--_spotlight-layout--main--margin);
|
|
108
|
+
position: relative;
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: center;
|
|
112
|
+
/* Set 'container-type: inline-size;' to reference container*/
|
|
113
|
+
}
|
|
114
|
+
@container (width < 576px) {
|
|
115
|
+
.spotlight-layout__main-scene {
|
|
116
|
+
width: 100%;
|
|
117
|
+
height: 100%;
|
|
118
|
+
margin: 0;
|
|
119
|
+
}
|
|
120
|
+
}</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type Props = {
|
|
3
|
+
ratio: number;
|
|
4
|
+
children: Snippet;
|
|
5
|
+
left?: Snippet;
|
|
6
|
+
right?: Snippet;
|
|
7
|
+
on?: {
|
|
8
|
+
dimensionsChanged?: (dimensions: {
|
|
9
|
+
mainSceneWidth: number;
|
|
10
|
+
leftSidebarWidth: number;
|
|
11
|
+
rightSidebarWidth: number;
|
|
12
|
+
totalWidth: number;
|
|
13
|
+
}) => void;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type Cmp = ReturnType<typeof Cmp>;
|
|
18
|
+
export default Cmp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as SpotlightLayout } from './cmp.spotlight-layout.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as SpotlightLayout } from './cmp.spotlight-layout.svelte';
|