@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.
Files changed (29) hide show
  1. package/dist/media-center/media-center/discover/community-features.svelte +2 -1
  2. package/dist/media-center/media-center/discover/data-loading.js +1 -1
  3. package/dist/media-center/media-center/discover/discover-header.svelte +1 -0
  4. package/dist/media-center/media-center/discover/discover-view-handler.svelte.d.ts +1 -0
  5. package/dist/media-center/media-center/discover/discover-view-handler.svelte.js +15 -2
  6. package/dist/media-center/media-center/discover/discover-view-localization.d.ts +1 -0
  7. package/dist/media-center/media-center/discover/discover-view-localization.js +6 -0
  8. package/dist/media-center/media-center/discover/discover-view.svelte +69 -44
  9. package/dist/media-center/media-center/media-center-context.svelte.js +1 -0
  10. package/dist/posts/post-viewer/attachments-horizontal.svelte +1 -0
  11. package/dist/posts/post-viewer/media/post-media.svelte +3 -3
  12. package/dist/ui/player/slider/cmp.player-slider.svelte +22 -15
  13. package/dist/ui/{slider → player/slider-horizontal}/cmp.slider.svelte +51 -28
  14. package/dist/ui/{slider → player/slider-horizontal}/cmp.slider.svelte.d.ts +1 -1
  15. package/dist/ui/player/slider-horizontal/index.d.ts +2 -0
  16. package/dist/ui/player/slider-horizontal/index.js +2 -0
  17. package/dist/ui/{slider → player/slider-horizontal}/slider-localization.d.ts +1 -1
  18. package/dist/ui/{slider → player/slider-horizontal}/slider-localization.js +1 -1
  19. package/dist/ui/player/utils/index.d.ts +1 -0
  20. package/dist/ui/player/utils/index.js +1 -0
  21. package/dist/ui/player/utils/touch-synchronizer.d.ts +7 -0
  22. package/dist/ui/player/utils/touch-synchronizer.js +21 -0
  23. package/dist/ui/shadow-dom/cmp.shadow-root.svelte +2 -0
  24. package/dist/ui/shadow-dom/colors.scss +2 -0
  25. package/package.json +1 -1
  26. package/dist/ui/slider/index.d.ts +0 -2
  27. package/dist/ui/slider/index.js +0 -2
  28. /package/dist/ui/{slider → player/slider-horizontal}/types.d.ts +0 -0
  29. /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: 6,
14
+ limit: 5,
15
15
  continuationToken: holder.continuationToken
16
16
  });
17
17
  holder.streams = [...holder.streams, ...streamsResponse.items];
@@ -139,6 +139,7 @@ let { context } = $props();
139
139
  max-width: 4rem;
140
140
  }
141
141
  .header-mobile__texts {
142
+ flex: 1;
142
143
  display: flex;
143
144
  flex-direction: column;
144
145
  gap: 0.25rem;
@@ -22,6 +22,7 @@ export declare class DiscoverViewHandler {
22
22
  loadMoreShortVideos: () => Promise<void>;
23
23
  activate: (data: {
24
24
  activeCategoryId: string | null;
25
+ activeCategoryName: string | null;
25
26
  childCategories: {
26
27
  id: string;
27
28
  name: string;
@@ -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,6 +1,7 @@
1
1
  import { type Locale } from '../../../core/locale';
2
2
  export declare class DiscoverViewLocalization {
3
3
  latestStreamsSectionTitle: string;
4
+ latestShortVideosSectionTitle: string;
4
5
  locale: Locale;
5
6
  constructor(locale: Locale);
6
7
  }
@@ -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
- {#each handler.shortVideosHolders as holder, index (holder)}
29
- <InfiniteScrolling loadMore={handler.loadMoreShortVideos}>
30
- {#if holder.shortVideos.length}
31
- <div class="media-center-discover__section">
32
- <div class="media-center-discover__section-header">
33
- {holder.categoryName}
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
- <div
36
- class="media-center-discover__section-content"
37
- class:media-center-discover__section-content--5={index % 2 !== 0}
38
- class:media-center-discover__section-content--4={index % 2 === 0}>
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-white);
158
- text-shadow: var(--sc-mc-color--text-white-shadow);
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: none;
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: none;
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: none;
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: none;
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: none;
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: none;
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: none;
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 { Slider } from '../../../ui/slider';
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
- <Slider items={media.items} initialIndex={media.currentIndex} locale={locale} on={{ indexChanged: (index) => (media.currentIndex = index) }}>
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
- </Slider>
33
+ </SliderHorizontal>
34
34
  {/if}
35
35
  </div>
36
36
 
@@ -1,4 +1,5 @@
1
- <script lang="ts">import { createWheelAdapter } from './wheel-gestures-adapter';
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 isVerticalSwipe = false;
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
- isVerticalSwipe = false;
56
+ swipeState = 'not-initialized';
55
57
  }, { passive: true });
56
58
  slidesRef.addEventListener('touchmove', (e) => {
57
- if (isVerticalSwipe) {
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
- isVerticalSwipe = true;
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
- isVerticalSwipe = false;
90
+ swipeState = 'horizontal';
83
91
  return;
84
92
  }
85
93
  }
86
94
  }, { passive: false });
87
95
  slidesRef.addEventListener('touchend', (e) => {
88
- const touchEndX = e.changedTouches[0].screenX;
89
- const touchEndY = e.changedTouches[0].screenY;
90
- const diffX = Math.abs(touchEndX - touchStartX);
91
- const diffY = Math.abs(touchEndY - touchStartY);
96
+ const reset = () => {
97
+ swipeTransition = 0;
98
+ touchMoveY = 0;
99
+ };
92
100
  // Check if this is a vertical swipe
93
- if (diffX > diffY) {
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 '../../core/browser';
2
- import { Utils } from '../../core/utils';
3
- import { HtmlHelper } from '../../core/utils/html-helper';
4
- import { Icon } from '../icon';
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 = 850;
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 isHorizontalSwipe = false;
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
- isHorizontalSwipe = false;
42
+ swipeState = 'not-initialized';
38
43
  }, { passive: true });
39
44
  slidesRef.addEventListener('touchmove', (e) => {
40
- if (isHorizontalSwipe) {
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
- isHorizontalSwipe = true;
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
- isHorizontalSwipe = false;
73
+ swipeState = 'vertical';
61
74
  }
62
75
  }
63
- });
76
+ }, { passive: false });
64
77
  slidesRef.addEventListener('touchend', (e) => {
65
- const touchEndX = e.changedTouches[0].screenX;
66
- const touchEndY = e.changedTouches[0].screenY;
67
- const diffX = Math.abs(touchEndX - touchStartX);
68
- const diffY = Math.abs(touchEndY - touchStartY);
78
+ const reset = () => {
79
+ swipeTransition = 0;
80
+ touchMoveX = 0;
81
+ };
69
82
  // Check if this is a horizontal swipe
70
- if (diffY > diffX) {
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 && diffX < sliderWidth / 6) {
94
+ if (!isFastSwipe && Math.abs(touchMoveX) < sliderWidth / 6) {
95
+ reset();
78
96
  return;
79
97
  }
80
- if (touchEndX < touchStartX) {
98
+ if (touchMoveX < 0) {
81
99
  loadNext();
82
100
  }
83
- if (touchEndX > touchStartX) {
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
- }, 800);
188
+ }, animationDuration);
170
189
  const loadNext = Utils.throttle(() => {
171
190
  setIndex(++selectedIndex);
172
- }, 800);
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" data-before>
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: 800ms ease-out transform;
294
+ transition: var(--_slider-horizontal--animation);
272
295
  }
273
296
  .slider__slide {
274
297
  display: flex;
@@ -1,4 +1,4 @@
1
- import type { Locale } from '../../core/locale';
1
+ import type { Locale } from '../../../core/locale';
2
2
  import { type SliderDotsConfig, SliderMode } from './types';
3
3
  import { type Snippet } from 'svelte';
4
4
  declare class __sveltets_Render<T> {
@@ -0,0 +1,2 @@
1
+ export { default as SliderHorizontal } from './cmp.slider.svelte';
2
+ export { SliderMode, type SliderDotsConfig } from './types';
@@ -0,0 +1,2 @@
1
+ export { default as SliderHorizontal } from './cmp.slider.svelte';
2
+ export { SliderMode } from './types';
@@ -1,4 +1,4 @@
1
- import { type Locale } from '../../core/locale';
1
+ import { type Locale } from '../../../core/locale';
2
2
  export declare class SliderLocalization {
3
3
  nOfM: (n: number, m: number) => string;
4
4
  constructor(locale: Locale);
@@ -1,4 +1,4 @@
1
- import {} from '../../core/locale';
1
+ import {} from '../../../core/locale';
2
2
  export class SliderLocalization {
3
3
  nOfM;
4
4
  constructor(locale) {
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/embeddable",
3
- "version": "13.1.0",
3
+ "version": "13.3.0",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,2 +0,0 @@
1
- export { default as Slider } from './cmp.slider.svelte';
2
- export { SliderMode, type SliderDotsConfig } from './types';
@@ -1,2 +0,0 @@
1
- export { default as Slider } from './cmp.slider.svelte';
2
- export { SliderMode } from './types';