@streamscloud/embeddable 13.1.0 → 13.3.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/media-center/media-center/discover/community-features.svelte +2 -1
- package/dist/media-center/media-center/discover/data-loading.js +1 -1
- package/dist/media-center/media-center/discover/discover-header.svelte +1 -0
- package/dist/media-center/media-center/discover/discover-view-handler.svelte.d.ts +1 -0
- package/dist/media-center/media-center/discover/discover-view-handler.svelte.js +15 -2
- package/dist/media-center/media-center/discover/discover-view-localization.d.ts +1 -0
- package/dist/media-center/media-center/discover/discover-view-localization.js +6 -0
- package/dist/media-center/media-center/discover/discover-view.svelte +69 -44
- package/dist/media-center/media-center/media-center-context.svelte.js +1 -0
- package/dist/posts/post-viewer/attachments-horizontal.svelte +1 -0
- package/dist/posts/post-viewer/media/post-media.svelte +3 -3
- package/dist/ui/player/slider/cmp.player-slider.svelte +22 -15
- package/dist/ui/{slider → player/slider-horizontal}/cmp.slider.svelte +51 -28
- package/dist/ui/{slider → player/slider-horizontal}/cmp.slider.svelte.d.ts +1 -1
- package/dist/ui/player/slider-horizontal/index.d.ts +2 -0
- package/dist/ui/player/slider-horizontal/index.js +2 -0
- package/dist/ui/{slider → player/slider-horizontal}/slider-localization.d.ts +1 -1
- package/dist/ui/{slider → player/slider-horizontal}/slider-localization.js +1 -1
- package/dist/ui/player/utils/index.d.ts +1 -0
- package/dist/ui/player/utils/index.js +1 -0
- package/dist/ui/player/utils/touch-synchronizer.d.ts +7 -0
- package/dist/ui/player/utils/touch-synchronizer.js +21 -0
- package/dist/ui/shadow-dom/cmp.shadow-root.svelte +2 -0
- package/dist/ui/shadow-dom/colors.scss +2 -0
- package/package.json +1 -1
- package/dist/ui/slider/index.d.ts +0 -2
- package/dist/ui/slider/index.js +0 -2
- /package/dist/ui/{slider → player/slider-horizontal}/types.d.ts +0 -0
- /package/dist/ui/{slider → player/slider-horizontal}/types.js +0 -0
|
@@ -111,8 +111,9 @@ const onJoinMembershipClick = () => __awaiter(void 0, void 0, void 0, function*
|
|
|
111
111
|
display: flex;
|
|
112
112
|
flex-wrap: nowrap;
|
|
113
113
|
margin-top: 0.25rem;
|
|
114
|
-
gap: 8.125rem;
|
|
115
114
|
align-items: center;
|
|
115
|
+
max-width: 19.6875rem;
|
|
116
|
+
justify-content: space-between;
|
|
116
117
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
117
118
|
}
|
|
118
119
|
@container (width < 576px) {
|
|
@@ -11,7 +11,7 @@ export class DiscoverDataLoader {
|
|
|
11
11
|
}
|
|
12
12
|
const streamsResponse = await this.dataProvider.streamPlayer.getStreamsCursor({
|
|
13
13
|
filter: { categoryId: holder.categoryId ?? undefined },
|
|
14
|
-
limit:
|
|
14
|
+
limit: 5,
|
|
15
15
|
continuationToken: holder.continuationToken
|
|
16
16
|
});
|
|
17
17
|
holder.streams = [...holder.streams, ...streamsResponse.items];
|
|
@@ -51,6 +51,10 @@ export class DiscoverViewHandler {
|
|
|
51
51
|
}
|
|
52
52
|
index++;
|
|
53
53
|
}
|
|
54
|
+
const holdersWithContent = this._shortVideosInCategory.filter((x) => x.shortVideos.length > 0);
|
|
55
|
+
if (holdersWithContent.length === 1) {
|
|
56
|
+
await this._dataLoader.loadShortVideos(holdersWithContent[0]);
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
finally {
|
|
56
60
|
this._shortVideosLoadingDeferred.resolve();
|
|
@@ -58,7 +62,7 @@ export class DiscoverViewHandler {
|
|
|
58
62
|
}
|
|
59
63
|
};
|
|
60
64
|
activate = async (data) => {
|
|
61
|
-
const { activeCategoryId, childCategories } = data;
|
|
65
|
+
const { activeCategoryId, activeCategoryName, childCategories } = data;
|
|
62
66
|
this._state = 'loading';
|
|
63
67
|
try {
|
|
64
68
|
const promises = [];
|
|
@@ -72,13 +76,22 @@ export class DiscoverViewHandler {
|
|
|
72
76
|
if (streamsInCategory.continuationToken === undefined) {
|
|
73
77
|
promises.push(this._dataLoader.loadStreams(streamsInCategory));
|
|
74
78
|
}
|
|
75
|
-
// Init short videos in categories
|
|
79
|
+
// Init short videos in child categories
|
|
76
80
|
const nonExistingShortVideosInCategory = childCategories.filter((cc) => !this._shortVideosInCategoryCache.some((x) => x.categoryId === cc.id));
|
|
77
81
|
for (const category of nonExistingShortVideosInCategory) {
|
|
78
82
|
const newShortVideosInCategory = new ShortVideosInCategory({ categoryId: category.id, categoryName: category.name });
|
|
79
83
|
this._shortVideosInCategoryCache.push(newShortVideosInCategory);
|
|
80
84
|
}
|
|
81
85
|
this._shortVideosInCategory = childCategories.map((c) => this._shortVideosInCategoryCache.find((b) => b.categoryId === c.id));
|
|
86
|
+
// Init short videos in main category
|
|
87
|
+
if (activeCategoryId) {
|
|
88
|
+
let shortVideosInCategory = this._shortVideosInCategoryCache.find((x) => x.categoryId === activeCategoryId);
|
|
89
|
+
if (!shortVideosInCategory) {
|
|
90
|
+
shortVideosInCategory = new ShortVideosInCategory({ categoryId: activeCategoryId, categoryName: activeCategoryName || '' });
|
|
91
|
+
this._shortVideosInCategoryCache.push(shortVideosInCategory);
|
|
92
|
+
}
|
|
93
|
+
this._shortVideosInCategory.unshift(shortVideosInCategory);
|
|
94
|
+
}
|
|
82
95
|
const pushShortVideoPromise = (holder) => {
|
|
83
96
|
if (holder && holder.continuationToken === undefined) {
|
|
84
97
|
promises.push(this._dataLoader.loadShortVideos(holder));
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {} from '../../../core/locale';
|
|
2
2
|
export class DiscoverViewLocalization {
|
|
3
3
|
latestStreamsSectionTitle;
|
|
4
|
+
latestShortVideosSectionTitle;
|
|
4
5
|
locale;
|
|
5
6
|
constructor(locale) {
|
|
6
7
|
this.latestStreamsSectionTitle = loc.streamsSectionTitle[locale];
|
|
8
|
+
this.latestShortVideosSectionTitle = loc.shortVideosSectionTitle[locale];
|
|
7
9
|
this.locale = locale;
|
|
8
10
|
}
|
|
9
11
|
}
|
|
@@ -11,5 +13,9 @@ const loc = {
|
|
|
11
13
|
streamsSectionTitle: {
|
|
12
14
|
en: 'Latest Streams',
|
|
13
15
|
no: 'Siste Streams'
|
|
16
|
+
},
|
|
17
|
+
shortVideosSectionTitle: {
|
|
18
|
+
en: 'Latest Short Videos',
|
|
19
|
+
no: 'Siste Korte Videoer'
|
|
14
20
|
}
|
|
15
21
|
};
|
|
@@ -10,7 +10,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<div class="media-center-discover">
|
|
13
|
-
<div class="media-center-discover__content">
|
|
13
|
+
<div class="media-center-discover__content media-center-discover__feed">
|
|
14
14
|
<Header context={context} />
|
|
15
15
|
{#if handler.streamsHolder?.streams.length}
|
|
16
16
|
<div class="media-center-discover__section">
|
|
@@ -25,35 +25,38 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
25
25
|
</div>
|
|
26
26
|
{/if}
|
|
27
27
|
{#if !handler.loading}
|
|
28
|
-
{
|
|
29
|
-
<
|
|
30
|
-
{#
|
|
31
|
-
|
|
32
|
-
<div class="media-center-discover__section
|
|
33
|
-
|
|
28
|
+
<InfiniteScrolling loadMore={handler.loadMoreShortVideos}>
|
|
29
|
+
<div class="media-center-discover__feed">
|
|
30
|
+
{#each handler.shortVideosHolders as holder, index (holder)}
|
|
31
|
+
{#if holder.shortVideos.length}
|
|
32
|
+
<div class="media-center-discover__section">
|
|
33
|
+
<div class="media-center-discover__section-header">
|
|
34
|
+
{holder.categoryId === context.categoriesHandler.selectedCategoryId ? localization.latestShortVideosSectionTitle : holder.categoryName}
|
|
35
|
+
</div>
|
|
36
|
+
<div
|
|
37
|
+
class="media-center-discover__section-content"
|
|
38
|
+
class:media-center-discover__section-content--5={index % 2 !== 0}
|
|
39
|
+
class:media-center-discover__section-content--4={index % 2 === 0}
|
|
40
|
+
class:media-center-discover__section-content--not-alone={handler.shortVideosHolders.length > 1}>
|
|
41
|
+
{#each holder.shortVideos as item (item.id)}
|
|
42
|
+
<ShortVideoCard
|
|
43
|
+
shortVideo={{
|
|
44
|
+
id: item.id,
|
|
45
|
+
text: item.text,
|
|
46
|
+
cover: getPostCoverImage(item),
|
|
47
|
+
displayDate: item.displayDate,
|
|
48
|
+
viewsCount: item.viewsCount,
|
|
49
|
+
products: item.products
|
|
50
|
+
}}
|
|
51
|
+
locale={context.locale}
|
|
52
|
+
on={{ click: () => on.shortVideoSelected(item) }} />
|
|
53
|
+
{/each}
|
|
54
|
+
</div>
|
|
34
55
|
</div>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{#each holder.shortVideos as item (item.id)}
|
|
40
|
-
<ShortVideoCard
|
|
41
|
-
shortVideo={{
|
|
42
|
-
id: item.id,
|
|
43
|
-
text: item.text,
|
|
44
|
-
cover: getPostCoverImage(item),
|
|
45
|
-
displayDate: item.displayDate,
|
|
46
|
-
viewsCount: item.viewsCount,
|
|
47
|
-
products: item.products
|
|
48
|
-
}}
|
|
49
|
-
locale={context.locale}
|
|
50
|
-
on={{ click: () => on.shortVideoSelected(item) }} />
|
|
51
|
-
{/each}
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
{/if}
|
|
55
|
-
</InfiniteScrolling>
|
|
56
|
-
{/each}
|
|
56
|
+
{/if}
|
|
57
|
+
{/each}
|
|
58
|
+
</div>
|
|
59
|
+
</InfiniteScrolling>
|
|
57
60
|
{/if}
|
|
58
61
|
</div>
|
|
59
62
|
</div>
|
|
@@ -74,6 +77,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
74
77
|
flex: 1;
|
|
75
78
|
scrollbar-gutter: stable both-edges;
|
|
76
79
|
padding-inline: 1.875rem;
|
|
80
|
+
overflow-x: hidden;
|
|
77
81
|
overflow-y: auto;
|
|
78
82
|
--_cross-browser-scrollbar--thumb-color: transparent;
|
|
79
83
|
--_cross-browser-scrollbar--track-color: transparent;
|
|
@@ -110,9 +114,6 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
110
114
|
width: 100%;
|
|
111
115
|
max-width: 73.75rem;
|
|
112
116
|
margin: 0 auto;
|
|
113
|
-
display: flex;
|
|
114
|
-
flex-direction: column;
|
|
115
|
-
gap: 3rem;
|
|
116
117
|
padding-bottom: 1.25rem;
|
|
117
118
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
118
119
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
@@ -120,22 +121,42 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
120
121
|
}
|
|
121
122
|
@container (width < 992px) {
|
|
122
123
|
.media-center-discover__content {
|
|
123
|
-
gap: 2.5rem;
|
|
124
124
|
padding-bottom: 1rem;
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
@container (width < 768px) {
|
|
128
128
|
.media-center-discover__content {
|
|
129
|
-
gap: 2rem;
|
|
130
129
|
padding-bottom: 0.8125rem;
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
@container (width < 576px) {
|
|
134
133
|
.media-center-discover__content {
|
|
135
|
-
gap: 1.5rem;
|
|
136
134
|
padding-bottom: 0.625rem;
|
|
137
135
|
}
|
|
138
136
|
}
|
|
137
|
+
.media-center-discover__feed {
|
|
138
|
+
display: flex;
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
gap: 3rem;
|
|
141
|
+
/* Set 'container-type: inline-size;' to reference container*/
|
|
142
|
+
/* Set 'container-type: inline-size;' to reference container*/
|
|
143
|
+
/* Set 'container-type: inline-size;' to reference container*/
|
|
144
|
+
}
|
|
145
|
+
@container (width < 992px) {
|
|
146
|
+
.media-center-discover__feed {
|
|
147
|
+
gap: 2.5rem;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
@container (width < 768px) {
|
|
151
|
+
.media-center-discover__feed {
|
|
152
|
+
gap: 2rem;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
@container (width < 576px) {
|
|
156
|
+
.media-center-discover__feed {
|
|
157
|
+
gap: 1.5rem;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
139
160
|
.media-center-discover__section {
|
|
140
161
|
display: flex;
|
|
141
162
|
flex-direction: column;
|
|
@@ -154,11 +175,15 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
154
175
|
font-size: 1.125rem;
|
|
155
176
|
line-height: 1.75rem;
|
|
156
177
|
font-weight: 600;
|
|
157
|
-
color: var(--sc-mc-color--text-
|
|
158
|
-
text-shadow: var(--sc-mc-color--text-
|
|
178
|
+
color: var(--sc-mc-color--text-primary);
|
|
179
|
+
text-shadow: var(--sc-mc-color--text-primary-shadow);
|
|
159
180
|
}
|
|
160
181
|
.media-center-discover__section-content {
|
|
161
182
|
display: grid;
|
|
183
|
+
--_section-content--more-items--display: block;
|
|
184
|
+
}
|
|
185
|
+
.media-center-discover__section-content--not-alone {
|
|
186
|
+
--_section-content--more-items--display: none;
|
|
162
187
|
}
|
|
163
188
|
.media-center-discover__section-content--4 {
|
|
164
189
|
gap: 1.25rem;
|
|
@@ -168,7 +193,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
168
193
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
169
194
|
}
|
|
170
195
|
.media-center-discover__section-content--4 :global(> :nth-child(n + 5)) {
|
|
171
|
-
display:
|
|
196
|
+
display: var(--_section-content--more-items--display);
|
|
172
197
|
}
|
|
173
198
|
@container (width < 992px) {
|
|
174
199
|
.media-center-discover__section-content--4 {
|
|
@@ -176,7 +201,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
176
201
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
177
202
|
}
|
|
178
203
|
.media-center-discover__section-content--4 :global(> :nth-child(n + 4)) {
|
|
179
|
-
display:
|
|
204
|
+
display: var(--_section-content--more-items--display);
|
|
180
205
|
}
|
|
181
206
|
}
|
|
182
207
|
@container (width < 768px) {
|
|
@@ -188,7 +213,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
188
213
|
display: block;
|
|
189
214
|
}
|
|
190
215
|
.media-center-discover__section-content--4 :global(> :nth-child(n + 5)) {
|
|
191
|
-
display:
|
|
216
|
+
display: var(--_section-content--more-items--display);
|
|
192
217
|
}
|
|
193
218
|
}
|
|
194
219
|
@container (width < 576px) {
|
|
@@ -204,7 +229,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
204
229
|
/* Set 'container-type: inline-size;' to reference container*/
|
|
205
230
|
}
|
|
206
231
|
.media-center-discover__section-content--5 :global(> :nth-child(n + 6)) {
|
|
207
|
-
display:
|
|
232
|
+
display: var(--_section-content--more-items--display);
|
|
208
233
|
}
|
|
209
234
|
@container (width < 992px) {
|
|
210
235
|
.media-center-discover__section-content--5 {
|
|
@@ -212,7 +237,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
212
237
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
213
238
|
}
|
|
214
239
|
.media-center-discover__section-content--5 :global(> :nth-child(n + 5)) {
|
|
215
|
-
display:
|
|
240
|
+
display: var(--_section-content--more-items--display);
|
|
216
241
|
}
|
|
217
242
|
}
|
|
218
243
|
@container (width < 768px) {
|
|
@@ -221,7 +246,7 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
221
246
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
222
247
|
}
|
|
223
248
|
.media-center-discover__section-content--5 :global(> :nth-child(n + 4)) {
|
|
224
|
-
display:
|
|
249
|
+
display: var(--_section-content--more-items--display);
|
|
225
250
|
}
|
|
226
251
|
}
|
|
227
252
|
@container (width < 576px) {
|
|
@@ -233,6 +258,6 @@ const localization = $derived(new DiscoverViewLocalization(context.locale));
|
|
|
233
258
|
display: block;
|
|
234
259
|
}
|
|
235
260
|
.media-center-discover__section-content--5 :global(> :nth-child(n + 5)) {
|
|
236
|
-
display:
|
|
261
|
+
display: var(--_section-content--more-items--display);
|
|
237
262
|
}
|
|
238
263
|
}</style>
|
|
@@ -124,6 +124,7 @@ export class MediaCenterContext {
|
|
|
124
124
|
const childCategories = this.categoriesHandler.allCategories.filter((c) => c.parentId === this.categoriesHandler.selectedCategoryId);
|
|
125
125
|
await this.discoverHandler.activate({
|
|
126
126
|
activeCategoryId: this.categoriesHandler.selectedCategoryId,
|
|
127
|
+
activeCategoryName: this.categoriesHandler.allCategories.find((c) => c.id === this.categoriesHandler.selectedCategoryId)?.name || null,
|
|
127
128
|
childCategories: childCategories.map((c) => ({ id: c.id, name: c.name }))
|
|
128
129
|
});
|
|
129
130
|
};
|
|
@@ -168,6 +168,7 @@ const variables = $derived.by(() => {
|
|
|
168
168
|
overflow-x: auto;
|
|
169
169
|
padding-inline: var(--_post-viewer--attachments-horizontal--padding-inline);
|
|
170
170
|
scrollbar-width: none;
|
|
171
|
+
overscroll-behavior: none;
|
|
171
172
|
}
|
|
172
173
|
.attachments-horizontal__item {
|
|
173
174
|
position: relative;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">import { Image } from '../../../ui/image';
|
|
2
|
-
import {
|
|
2
|
+
import { SliderHorizontal } from '../../../ui/player/slider-horizontal';
|
|
3
3
|
import { Video } from '../../../ui/video';
|
|
4
4
|
let { id, media, locale, autoplay = 'on-appearance', on } = $props();
|
|
5
5
|
</script>
|
|
@@ -26,11 +26,11 @@ let { id, media, locale, autoplay = 'on-appearance', on } = $props();
|
|
|
26
26
|
{#if media.items.length === 1}
|
|
27
27
|
{@render mediaItem(media.items[0])}
|
|
28
28
|
{:else if media.items.length > 1}
|
|
29
|
-
<
|
|
29
|
+
<SliderHorizontal items={media.items} initialIndex={media.currentIndex} locale={locale} on={{ indexChanged: (index) => (media.currentIndex = index) }}>
|
|
30
30
|
{#snippet children(item)}
|
|
31
31
|
{@render mediaItem(item)}
|
|
32
32
|
{/snippet}
|
|
33
|
-
</
|
|
33
|
+
</SliderHorizontal>
|
|
34
34
|
{/if}
|
|
35
35
|
</div>
|
|
36
36
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
<script lang="ts">import {
|
|
1
|
+
<script lang="ts">import { TouchSynchronizer } from '../utils';
|
|
2
|
+
import { createWheelAdapter } from './wheel-gestures-adapter';
|
|
2
3
|
import { onDestroy, onMount, untrack } from 'svelte';
|
|
3
4
|
let { buffer, on, children } = $props();
|
|
4
5
|
let slidesRef;
|
|
@@ -43,21 +44,27 @@ onMount(() => {
|
|
|
43
44
|
let touchStartY = 0;
|
|
44
45
|
let touchMoveY = 0;
|
|
45
46
|
let touchStartTime = 0;
|
|
46
|
-
let
|
|
47
|
+
let swipeState = 'not-initialized';
|
|
47
48
|
window.addEventListener(`keydown`, onKeyPress);
|
|
48
49
|
slidesRef.addEventListener('touchstart', (e) => {
|
|
50
|
+
TouchSynchronizer.touchStarted(slidesRef);
|
|
49
51
|
// The movement gets all janky if there's a transition on the elements.
|
|
50
52
|
slidesRef.classList.toggle('animate', false);
|
|
51
53
|
touchStartX = e.changedTouches[0].screenX;
|
|
52
54
|
touchStartY = e.changedTouches[0].screenY;
|
|
53
55
|
touchStartTime = Date.now();
|
|
54
|
-
|
|
56
|
+
swipeState = 'not-initialized';
|
|
55
57
|
}, { passive: true });
|
|
56
58
|
slidesRef.addEventListener('touchmove', (e) => {
|
|
57
|
-
if (
|
|
59
|
+
if (swipeState === 'vertical') {
|
|
58
60
|
// Already determined this is a vertical swipe
|
|
59
61
|
e.preventDefault();
|
|
60
62
|
e.stopPropagation();
|
|
63
|
+
TouchSynchronizer.touchMovePropagationBlocked(slidesRef);
|
|
64
|
+
}
|
|
65
|
+
else if (swipeState === 'horizontal') {
|
|
66
|
+
// Already determined this is a horizontal swipe
|
|
67
|
+
return;
|
|
61
68
|
}
|
|
62
69
|
const touchCurrentX = e.changedTouches[0].screenX;
|
|
63
70
|
const touchCurrentY = e.changedTouches[0].screenY;
|
|
@@ -67,9 +74,10 @@ onMount(() => {
|
|
|
67
74
|
if (diffX > 10 || diffY > 10) {
|
|
68
75
|
if (diffY > diffX) {
|
|
69
76
|
// Vertical swipe - handle it
|
|
70
|
-
|
|
77
|
+
swipeState = 'vertical';
|
|
71
78
|
e.preventDefault();
|
|
72
79
|
e.stopPropagation();
|
|
80
|
+
TouchSynchronizer.touchMovePropagationBlocked(slidesRef);
|
|
73
81
|
const newPosition = touchCurrentY;
|
|
74
82
|
const diff = newPosition - touchStartY;
|
|
75
83
|
if ((diff > 0 && buffer.canLoadPrevious) || (diff < 0 && buffer.canLoadNext)) {
|
|
@@ -79,28 +87,27 @@ onMount(() => {
|
|
|
79
87
|
}
|
|
80
88
|
else {
|
|
81
89
|
// Horizontal swipe - allow child to handle
|
|
82
|
-
|
|
90
|
+
swipeState = 'horizontal';
|
|
83
91
|
return;
|
|
84
92
|
}
|
|
85
93
|
}
|
|
86
94
|
}, { passive: false });
|
|
87
95
|
slidesRef.addEventListener('touchend', (e) => {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
const reset = () => {
|
|
97
|
+
swipeTransition = 0;
|
|
98
|
+
touchMoveY = 0;
|
|
99
|
+
};
|
|
92
100
|
// Check if this is a vertical swipe
|
|
93
|
-
if (
|
|
101
|
+
if (swipeState !== 'vertical') {
|
|
94
102
|
// Horizontal swipe - don't handle and don't block
|
|
103
|
+
reset();
|
|
95
104
|
return;
|
|
96
105
|
}
|
|
97
106
|
// This is a vertical swipe - block propagation
|
|
98
107
|
e.stopPropagation();
|
|
108
|
+
TouchSynchronizer.touchEndPropatationBlocked(slidesRef);
|
|
109
|
+
TouchSynchronizer.touchEnded(slidesRef);
|
|
99
110
|
slidesRef.classList.toggle('animate', true);
|
|
100
|
-
const reset = () => {
|
|
101
|
-
swipeTransition = 0;
|
|
102
|
-
touchMoveY = 0;
|
|
103
|
-
};
|
|
104
111
|
const isFastSwipe = Date.now() - touchStartTime < 300;
|
|
105
112
|
if (!touchMoveY || (!isFastSwipe && Math.abs(touchMoveY) < sliderHeight / 6)) {
|
|
106
113
|
return reset();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
<script lang="ts">import { runningInBrowser } from '
|
|
2
|
-
import { Utils } from '
|
|
3
|
-
import { HtmlHelper } from '
|
|
4
|
-
import { Icon } from '
|
|
1
|
+
<script lang="ts">import { runningInBrowser } from '../../../core/browser';
|
|
2
|
+
import { Utils } from '../../../core/utils';
|
|
3
|
+
import { HtmlHelper } from '../../../core/utils/html-helper';
|
|
4
|
+
import { Icon } from '../../icon';
|
|
5
|
+
import { TouchSynchronizer } from '../utils';
|
|
5
6
|
import { SliderLocalization } from './slider-localization';
|
|
6
7
|
import { SliderMode } from './types';
|
|
7
8
|
import IconChevronLeft from '@fluentui/svg-icons/icons/chevron_left_20_regular.svg?raw';
|
|
@@ -13,13 +14,14 @@ let { items, sliderMode = SliderMode.ArrowsWithCounts, dotsConfig = {
|
|
|
13
14
|
}, locale, initialIndex, autoSlideMs = 0, on, children } = $props();
|
|
14
15
|
const localization = $derived.by(() => new SliderLocalization(locale));
|
|
15
16
|
const itemIndices = $derived(items.map((_, index) => index));
|
|
16
|
-
const animationDuration =
|
|
17
|
+
const animationDuration = 300;
|
|
17
18
|
let previousItems = $state.raw(undefined);
|
|
18
19
|
let selectedIndex = $state(initialIndex);
|
|
19
20
|
let animationIndex = $state(null);
|
|
20
21
|
let interval;
|
|
21
22
|
let slidesRef;
|
|
22
23
|
let sliderWidth = $state(0);
|
|
24
|
+
let swipeTransition = $state(0);
|
|
23
25
|
let resizeObserver;
|
|
24
26
|
onMount(() => {
|
|
25
27
|
notifyIndexChanged();
|
|
@@ -28,19 +30,26 @@ onMount(() => {
|
|
|
28
30
|
});
|
|
29
31
|
let touchStartX = 0;
|
|
30
32
|
let touchStartY = 0;
|
|
33
|
+
let touchMoveX = 0;
|
|
31
34
|
let touchStartTime = 0;
|
|
32
|
-
let
|
|
35
|
+
let swipeState = 'not-initialized';
|
|
33
36
|
slidesRef.addEventListener('touchstart', (e) => {
|
|
37
|
+
TouchSynchronizer.touchStarted(slidesRef);
|
|
38
|
+
slidesRef.classList.toggle('animate', false);
|
|
34
39
|
touchStartX = e.changedTouches[0].screenX;
|
|
35
40
|
touchStartY = e.changedTouches[0].screenY;
|
|
36
41
|
touchStartTime = Date.now();
|
|
37
|
-
|
|
42
|
+
swipeState = 'not-initialized';
|
|
38
43
|
}, { passive: true });
|
|
39
44
|
slidesRef.addEventListener('touchmove', (e) => {
|
|
40
|
-
if (
|
|
45
|
+
if (swipeState === 'horizontal') {
|
|
41
46
|
// Already determined this is a horizontal swipe
|
|
42
47
|
e.preventDefault();
|
|
43
48
|
e.stopPropagation();
|
|
49
|
+
TouchSynchronizer.touchMovePropagationBlocked(slidesRef);
|
|
50
|
+
}
|
|
51
|
+
else if (swipeState === 'vertical') {
|
|
52
|
+
// Already determined this is a vertical swipe
|
|
44
53
|
return;
|
|
45
54
|
}
|
|
46
55
|
const touchCurrentX = e.changedTouches[0].screenX;
|
|
@@ -51,39 +60,49 @@ onMount(() => {
|
|
|
51
60
|
if (diffX > 10 || diffY > 10) {
|
|
52
61
|
if (diffX > diffY) {
|
|
53
62
|
// Horizontal swipe - block vertical scroll
|
|
54
|
-
|
|
63
|
+
swipeState = 'horizontal';
|
|
55
64
|
e.preventDefault();
|
|
56
65
|
e.stopPropagation();
|
|
66
|
+
TouchSynchronizer.touchMovePropagationBlocked(slidesRef);
|
|
67
|
+
touchMoveX = touchCurrentX - touchStartX;
|
|
68
|
+
const basePosition = -sliderWidth * (animationIndex === null ? selectedIndex + 1 : animationIndex);
|
|
69
|
+
swipeTransition = basePosition + touchMoveX;
|
|
57
70
|
}
|
|
58
71
|
else {
|
|
59
72
|
// Vertical swipe - allow parent to handle
|
|
60
|
-
|
|
73
|
+
swipeState = 'vertical';
|
|
61
74
|
}
|
|
62
75
|
}
|
|
63
|
-
});
|
|
76
|
+
}, { passive: false });
|
|
64
77
|
slidesRef.addEventListener('touchend', (e) => {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
const reset = () => {
|
|
79
|
+
swipeTransition = 0;
|
|
80
|
+
touchMoveX = 0;
|
|
81
|
+
};
|
|
69
82
|
// Check if this is a horizontal swipe
|
|
70
|
-
if (
|
|
83
|
+
if (swipeState !== 'horizontal') {
|
|
84
|
+
reset();
|
|
71
85
|
return;
|
|
72
86
|
}
|
|
73
87
|
// This is a horizontal swipe - block propagation
|
|
74
88
|
e.stopPropagation();
|
|
75
89
|
e.preventDefault();
|
|
90
|
+
slidesRef.classList.toggle('animate', true);
|
|
91
|
+
TouchSynchronizer.touchEndPropatationBlocked(slidesRef);
|
|
92
|
+
TouchSynchronizer.touchEnded(slidesRef);
|
|
76
93
|
const isFastSwipe = Date.now() - touchStartTime < 300;
|
|
77
|
-
if (!isFastSwipe &&
|
|
94
|
+
if (!isFastSwipe && Math.abs(touchMoveX) < sliderWidth / 6) {
|
|
95
|
+
reset();
|
|
78
96
|
return;
|
|
79
97
|
}
|
|
80
|
-
if (
|
|
98
|
+
if (touchMoveX < 0) {
|
|
81
99
|
loadNext();
|
|
82
100
|
}
|
|
83
|
-
|
|
101
|
+
else {
|
|
84
102
|
loadPrevious();
|
|
85
103
|
}
|
|
86
|
-
|
|
104
|
+
reset();
|
|
105
|
+
}, { passive: false });
|
|
87
106
|
slidesRef.addEventListener('transitionend', (e) => {
|
|
88
107
|
e.stopPropagation();
|
|
89
108
|
if (animationIndex !== null && animationIndex > items.length - 1) {
|
|
@@ -166,16 +185,23 @@ const setIndex = (index) => {
|
|
|
166
185
|
};
|
|
167
186
|
const loadPrevious = Utils.throttle(() => {
|
|
168
187
|
setIndex(--selectedIndex);
|
|
169
|
-
},
|
|
188
|
+
}, animationDuration);
|
|
170
189
|
const loadNext = Utils.throttle(() => {
|
|
171
190
|
setIndex(++selectedIndex);
|
|
172
|
-
},
|
|
191
|
+
}, animationDuration);
|
|
173
192
|
$effect(() => {
|
|
174
193
|
if (previousItems && items !== previousItems) {
|
|
175
194
|
setIndex(0);
|
|
176
195
|
}
|
|
177
196
|
previousItems = items;
|
|
178
197
|
});
|
|
198
|
+
const styles = $derived.by(() => {
|
|
199
|
+
const values = [
|
|
200
|
+
`transform: translateX(${swipeTransition || (items.length > 1 ? -sliderWidth * (animationIndex === null ? selectedIndex + 1 : animationIndex) : 0)}px)`,
|
|
201
|
+
`--_slider-horizontal--animation: ${animationDuration}ms ease-out transform`
|
|
202
|
+
];
|
|
203
|
+
return values.join(';');
|
|
204
|
+
});
|
|
179
205
|
const showClassicArrowsAndDots = $derived([SliderMode.ArrowsAndDots, SliderMode.ArrowsOnly, SliderMode.DotsOnly, SliderMode.DotsOnlyBelow].includes(sliderMode));
|
|
180
206
|
</script>
|
|
181
207
|
|
|
@@ -186,12 +212,9 @@ const showClassicArrowsAndDots = $derived([SliderMode.ArrowsAndDots, SliderMode.
|
|
|
186
212
|
class:slider--dots-only={sliderMode === SliderMode.DotsOnly}
|
|
187
213
|
class:slider--dots-only-below={sliderMode === SliderMode.DotsOnlyBelow}
|
|
188
214
|
class:slider--arrows-with-counts={sliderMode === SliderMode.ArrowsWithCounts}>
|
|
189
|
-
<div
|
|
190
|
-
class="slider__slides"
|
|
191
|
-
bind:this={slidesRef}
|
|
192
|
-
style={`transform: translateX(${items.length > 1 ? -sliderWidth * (animationIndex === null ? selectedIndex + 1 : animationIndex) : 0}px)`}>
|
|
215
|
+
<div class="slider__slides" bind:this={slidesRef} style={styles}>
|
|
193
216
|
{#if items.length > 1}
|
|
194
|
-
<div class="slider__slide"
|
|
217
|
+
<div class="slider__slide">
|
|
195
218
|
{#if children}
|
|
196
219
|
{@render children(items[items.length - 1])}
|
|
197
220
|
{/if}
|
|
@@ -268,7 +291,7 @@ const showClassicArrowsAndDots = $derived([SliderMode.ArrowsAndDots, SliderMode.
|
|
|
268
291
|
height: 100%;
|
|
269
292
|
}
|
|
270
293
|
.slider__slides:global(.animate) {
|
|
271
|
-
transition:
|
|
294
|
+
transition: var(--_slider-horizontal--animation);
|
|
272
295
|
}
|
|
273
296
|
.slider__slide {
|
|
274
297
|
display: flex;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TouchSynchronizer } from './touch-synchronizer';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TouchSynchronizer } from './touch-synchronizer';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class TouchSynchronizer {
|
|
2
|
+
private static listeners;
|
|
3
|
+
static touchStarted: (ref: HTMLElement) => void;
|
|
4
|
+
static touchMovePropagationBlocked: (ref: HTMLElement) => void;
|
|
5
|
+
static touchEndPropatationBlocked: (ref: HTMLElement) => void;
|
|
6
|
+
static touchEnded: (ref: HTMLElement) => void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class TouchSynchronizer {
|
|
2
|
+
static listeners = [];
|
|
3
|
+
static touchStarted = (ref) => {
|
|
4
|
+
TouchSynchronizer.listeners.push(ref);
|
|
5
|
+
};
|
|
6
|
+
static touchMovePropagationBlocked = (ref) => {
|
|
7
|
+
const otherListeners = TouchSynchronizer.listeners.filter((r) => r !== ref);
|
|
8
|
+
otherListeners.forEach((r) => {
|
|
9
|
+
r.dispatchEvent(new TouchEvent('touchend', { cancelable: false }));
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
static touchEndPropatationBlocked = (ref) => {
|
|
13
|
+
const otherListeners = TouchSynchronizer.listeners.filter((r) => r !== ref);
|
|
14
|
+
otherListeners.forEach((r) => {
|
|
15
|
+
r.dispatchEvent(new TouchEvent('touchend', { cancelable: false }));
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
static touchEnded = (ref) => {
|
|
19
|
+
TouchSynchronizer.listeners = TouchSynchronizer.listeners.filter((r) => r !== ref);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -77,6 +77,7 @@ const styles = $derived.by(() => {
|
|
|
77
77
|
--sc-mc-color--text-brand: #144ab0;
|
|
78
78
|
--sc-mc-color--text-inverted: #ffffff;
|
|
79
79
|
--sc-mc-color--text-primary: #000000;
|
|
80
|
+
--sc-mc-color--text-primary-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 3px rgba(255, 255, 255, 0.1), 0 1px 6px rgba(255, 255, 255, 0.05);
|
|
80
81
|
--sc-mc-color--text-secondary: #6b7280;
|
|
81
82
|
--sc-mc-color--text-white: #ffffff;
|
|
82
83
|
--sc-mc-color--text-white-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
|
|
@@ -109,6 +110,7 @@ const styles = $derived.by(() => {
|
|
|
109
110
|
--sc-mc-color--text-brand: #5a8dec;
|
|
110
111
|
--sc-mc-color--text-inverted: #ffffff;
|
|
111
112
|
--sc-mc-color--text-primary: #ffffff;
|
|
113
|
+
--sc-mc-color--text-primary-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
|
|
112
114
|
--sc-mc-color--text-secondary: #d1d5db;
|
|
113
115
|
--sc-mc-color--text-white: #ffffff;
|
|
114
116
|
--sc-mc-color--text-white-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
--sc-mc-color--text-brand: #{colors.$color-primary-500};
|
|
32
32
|
--sc-mc-color--text-inverted: #{colors.$color-white};
|
|
33
33
|
--sc-mc-color--text-primary: #{colors.$color-black};
|
|
34
|
+
--sc-mc-color--text-primary-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 3px rgba(255, 255, 255, 0.1), 0 1px 6px rgba(255, 255, 255, 0.05);
|
|
34
35
|
--sc-mc-color--text-secondary: #{colors.$color-neutral-500};
|
|
35
36
|
--sc-mc-color--text-white: #{colors.$color-white};
|
|
36
37
|
--sc-mc-color--text-white-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
--sc-mc-color--text-brand: #{colors.$color-primary-400};
|
|
68
69
|
--sc-mc-color--text-inverted: #{colors.$color-white};
|
|
69
70
|
--sc-mc-color--text-primary: #{colors.$color-white};
|
|
71
|
+
--sc-mc-color--text-primary-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
|
|
70
72
|
--sc-mc-color--text-secondary: #{colors.$color-neutral-300};
|
|
71
73
|
--sc-mc-color--text-white: #{colors.$color-white};
|
|
72
74
|
--sc-mc-color--text-white-shadow: 0 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 6px rgba(0, 0, 0, 0.05);
|
package/package.json
CHANGED
package/dist/ui/slider/index.js
DELETED
|
File without changes
|
|
File without changes
|