@playpilot/tpi 8.4.3 → 8.5.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.4.3",
3
+ "version": "8.5.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -18,3 +18,8 @@
18
18
  @return var(--playpilot-#{$scope}, $fallback);
19
19
  }
20
20
  }
21
+
22
+ // Alternative to `vw` but scaled by the explore width instead.
23
+ @function explore-width($percentage) {
24
+ @return calc(theme(explore-width, 1000px) * ($percentage / 100))
25
+ }
@@ -20,7 +20,7 @@
20
20
  let data = $state(dataToReadable())
21
21
  let shown = $state(false)
22
22
  let interval: ReturnType<typeof setInterval> | null = null
23
- let railSize = $state(136)
23
+ let railSize = $state(184)
24
24
 
25
25
  onDestroy(() => {
26
26
  if (interval) clearInterval(interval)
@@ -220,7 +220,7 @@
220
220
  max={240}
221
221
  step={8}
222
222
  bind:value={railSize}
223
- oninput={() => document.body.style.setProperty('--playpilot-rail-size-huge', `${railSize}px`)} />
223
+ oninput={() => document.body.style.setProperty('--playpilot-rail-size-flexible', `${railSize}px`)} />
224
224
  <small>{railSize}</small>
225
225
  </div>
226
226
 
@@ -22,6 +22,7 @@
22
22
 
23
23
  let element: HTMLElement | null = null
24
24
  let height: string | null = $state(null)
25
+ let clientWidth: number = $state(0)
25
26
 
26
27
  $effect(() => {
27
28
  // Set the height of this element to match that of it's container. That way we can
@@ -36,7 +37,7 @@
36
37
  })
37
38
  </script>
38
39
 
39
- <div class="explore playpilot-styled-scrollbar" bind:this={element} style:height>
40
+ <div class="explore playpilot-styled-scrollbar" bind:this={element} bind:clientWidth style:height style:--playpilot-explore-width="{clientWidth}px">
40
41
  <div class="header" role="banner">
41
42
  <div>
42
43
  <div class="divider"></div>
@@ -4,14 +4,10 @@
4
4
  import TitlesRail from '../../Rails/TitlesRail.svelte'
5
5
  import { t } from '$lib/localization'
6
6
  import { Sorting } from '$lib/enums/Sorting'
7
- import { mobileBreakpoint } from '$lib/constants'
8
7
 
9
- let windowWidth = $state(0)
10
8
  let expandedTitle: TitleData | null = $state(null)
11
9
  let expandedRailKey: string | null = $state(null)
12
10
 
13
- const size = $derived(windowWidth >= mobileBreakpoint ? 'huge' : null)
14
-
15
11
  const rails: { heading: string, params: Record<string, any>, properties: Record<string, any> }[] = [{
16
12
  heading: t('List: Trending'),
17
13
  params: { ordering: Sorting.Popular },
@@ -19,7 +15,7 @@
19
15
  }, {
20
16
  heading: t('List: Upcoming'),
21
17
  params: { from_playlist_sid: 'li42wf', region: null, no_region_filter: true },
22
- properties: { aside: true, size: 'large' },
18
+ properties: { aside: true },
23
19
  }, {
24
20
  heading: t('List: New'),
25
21
  params: { from_playlist_sid: 'li42WR', include_playable_types: 'SVOD,FREE' },
@@ -31,7 +27,7 @@
31
27
  }, {
32
28
  heading: t('List: Cinema'),
33
29
  params: { from_playlist_sid: 'li42WS', region: null, no_region_filter: true },
34
- properties: { aside: true, size: 'large' },
30
+ properties: { aside: true },
35
31
  }]
36
32
 
37
33
  async function getListTitles(params: Record<string, any> = {}): Promise<TitleData[]> {
@@ -39,13 +35,13 @@
39
35
  }
40
36
  </script>
41
37
 
42
- <svelte:window {onscroll} bind:innerWidth={windowWidth} />
38
+ <svelte:window {onscroll} />
43
39
 
44
40
  <div data-testid="explore-home"></div>
45
41
 
46
42
  {#each rails as { heading, params, properties }}
47
43
  <div class="rail">
48
- <TitlesRail {heading} titles={getListTitles(params)} size={size || 'small'} {...properties} bind:expandedTitle bind:expandedRailKey />
44
+ <TitlesRail {heading} titles={getListTitles(params)} size="flexible" {...properties} bind:expandedTitle bind:expandedRailKey />
49
45
  </div>
50
46
  {/each}
51
47
 
@@ -15,7 +15,7 @@
15
15
  interface Props {
16
16
  titles: Promise<TitleData[]> | TitleData[]
17
17
  heading?: string,
18
- size?: 'small' | 'large' | 'huge'
18
+ size?: 'small' | 'large' | 'flexible'
19
19
  aside?: boolean,
20
20
  expandable?: boolean,
21
21
  expandedTitle?: TitleData | null,
@@ -40,9 +40,10 @@
40
40
 
41
41
  let element: HTMLElement | null = $state(null)
42
42
  let slider: ReturnType<typeof TinySlider> | null = $state(null)
43
+ let recentlyExpanded = false
43
44
 
44
45
  onMount(() => {
45
- if (expandable) expandFirstAvailableTrailer()
46
+ if (expandable) expandWhenFirstInView()
46
47
  })
47
48
 
48
49
  function openTitle(event: MouseEvent | null, titles: TitleData[], index: number): void {
@@ -51,7 +52,17 @@
51
52
  const title = titles[index]
52
53
 
53
54
  if (expandable && !isExpanded(title)) {
55
+ const activeElement = element!.querySelectorAll('.title')[index]!
56
+ const parentOffset = element!.getBoundingClientRect().right
57
+ const elementOffsetInParent = parentOffset - activeElement.getBoundingClientRect().right
58
+
59
+ recentlyExpanded = true
60
+ setTimeout(() => recentlyExpanded = false, 500)
61
+
62
+ if (elementOffsetInParent < activeElement.clientWidth * 2) slider?.setIndex(index - 2)
63
+
54
64
  expandTitleIntoTrailer(title)
65
+
55
66
  return
56
67
  }
57
68
 
@@ -64,18 +75,15 @@
64
75
  onclick(title)
65
76
  }
66
77
 
67
- async function expandFirstAvailableTrailer(delay = 500): Promise<void> {
78
+ async function expandFirstAvailableTrailer(): Promise<void> {
68
79
  const response = await titles
69
-
70
80
  const title = await getFirstTitleWithAvailableTrailer(response)
71
- if (!title) return
72
81
 
73
- setTimeout(() => {
74
- if (expandedTitle) return
75
- if (expandedRailKey && expandedRailKey !== key) return
82
+ if (!title) return
83
+ if (expandedTitle) return
84
+ if (expandedRailKey && expandedRailKey !== key) return
76
85
 
77
- expandTitleIntoTrailer(title)
78
- }, delay)
86
+ expandTitleIntoTrailer(title)
79
87
  }
80
88
 
81
89
  function expandTitleIntoTrailer(title: TitleData): void {
@@ -87,19 +95,26 @@
87
95
  if (expandedRailKey === key) return
88
96
 
89
97
  const elements = Array.from(document.querySelectorAll('[data-role="expandable-rail"]')) as HTMLElement[]
90
- const elementsInView = elements.filter(element => element.getBoundingClientRect().top > 0)
98
+ const elementsInView = elements.filter(element => element.getBoundingClientRect().top >= 0)
91
99
  const elementsSortedByDistance = elementsInView.sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top)
92
100
 
93
101
  if (elementsSortedByDistance[0] !== element) return
94
102
 
95
103
  expandedTitle = null
96
104
  expandedRailKey = null
97
- expandFirstAvailableTrailer(0)
105
+ expandFirstAvailableTrailer()
98
106
  }
99
107
 
100
108
  function isExpanded(title: TitleData): boolean {
101
109
  return expandedRailKey === key && title.sid === expandedTitle?.sid
102
110
  }
111
+
112
+ function onchange(titles: TitleData[], index: number): void {
113
+ if (!expandable) return
114
+ if (recentlyExpanded) return
115
+
116
+ setTimeout(() => openTitle(null, titles, index), 250)
117
+ }
103
118
  </script>
104
119
 
105
120
  <svelte:window onscroll={expandWhenFirstInView} />
@@ -133,7 +148,7 @@
133
148
  <Rail
134
149
  bind:slider
135
150
  {heading}
136
- onchange={(index) => { if (expandable) setTimeout(() => openTitle(null, titles, index), 250) }}
151
+ onchange={(index) => onchange(titles, index)}
137
152
  onresize={() => slider?.reposition()}>
138
153
  {#each titles as title, index}
139
154
  {@const expanded = isExpanded(title)}
@@ -192,8 +207,8 @@
192
207
  --width: #{theme(rail-size-large, margin(7.5))};
193
208
  }
194
209
 
195
- &.huge {
196
- --width: #{theme(rail-size-huge, margin(8.5))};
210
+ &.flexible {
211
+ --width: #{theme(rail-size-flexible, clamp(margin(6), explore-width(18), margin(11.5)))};
197
212
  }
198
213
  }
199
214
 
@@ -219,6 +234,7 @@
219
234
  width: calc(var(--width) * 2);
220
235
  height: var(--image-height);
221
236
  overflow: hidden;
237
+ font-size: clamp(theme(font-size-small), explore-width(1.5), theme(font-size-base));
222
238
 
223
239
  &.expanded {
224
240
  width: calc(var(--expanded-width) + var(--width));
@@ -311,6 +327,7 @@
311
327
  .with-aside & {
312
328
  width: 100%;
313
329
  padding-top: 0;
330
+ color: theme(text-color) !important;
314
331
  font-weight: theme(rail-aside-heading-font-weight, font-bold);
315
332
  line-clamp: 1;
316
333
  -webkit-line-clamp: 1;
@@ -324,7 +341,7 @@
324
341
  mask-image: linear-gradient(to top, transparent 0.5lh, white 1.5lh);
325
342
  overflow: hidden;
326
343
  color: theme(rail-text-color, text-color-alt) !important;
327
- font-size: theme(rail-aside-font-size, 11px);
344
+ font-size: 0.9em;
328
345
  line-height: 1.2;
329
346
  }
330
347
 
@@ -64,7 +64,7 @@ describe('ExploreLayout.svelte', () => {
64
64
  })
65
65
 
66
66
  await waitFor(() => {
67
- expect(container.querySelector('.explore')?.getAttribute('style')).toBe('height: 500px;')
67
+ expect(container.querySelector('.explore')?.getAttribute('style')).toContain('height: 500px;')
68
68
  })
69
69
  })
70
70
 
@@ -69,7 +69,7 @@ describe('TitlesRail.svelte', () => {
69
69
 
70
70
  expect(getByTestId('title').classList).not.toContain('expanded')
71
71
 
72
- await new Promise(res => setTimeout(res, 1000))
72
+ await new Promise(res => setTimeout(res, 200))
73
73
 
74
74
  expect(getByTestId('title').classList).toContain('expanded')
75
75
  })
@@ -79,7 +79,7 @@ describe('TitlesRail.svelte', () => {
79
79
 
80
80
  expect(getByTestId('title').classList).not.toContain('expanded')
81
81
 
82
- await new Promise(res => setTimeout(res, 1000))
82
+ await new Promise(res => setTimeout(res, 200))
83
83
 
84
84
  expect(getByTestId('title').classList).not.toContain('expanded')
85
85
  })
@@ -89,32 +89,24 @@ describe('TitlesRail.svelte', () => {
89
89
 
90
90
  expect(getByTestId('title').classList).not.toContain('expanded')
91
91
 
92
- await new Promise(res => setTimeout(res, 1000))
93
-
94
92
  expect(getByTestId('title').classList).not.toContain('expanded')
95
93
  })
96
94
 
97
95
  it('Should not expand if a different rail key is given', async () => {
98
96
  const { getAllByTestId } = render(TitlesRail, { titles: [title], expandable: true, expandedRailKey: 'Not this rail' })
99
97
 
100
- await new Promise(res => setTimeout(res, 1000))
101
-
102
98
  expect(getAllByTestId('title')[0].classList).not.toContain('expanded')
103
99
  })
104
100
 
105
101
  it('Should not expand if a title is given that is not present in this rail', async () => {
106
102
  const { getAllByTestId } = render(TitlesRail, { titles: [title], expandable: true, expandedTitle: { ...title, sid: 'not' } })
107
103
 
108
- await new Promise(res => setTimeout(res, 1000))
109
-
110
104
  expect(getAllByTestId('title')[0].classList).not.toContain('expanded')
111
105
  })
112
106
 
113
107
  it('Should not expand if a title is given that is in this rail but expandedRailKey is not this rail', async () => {
114
108
  const { getAllByTestId } = render(TitlesRail, { titles: [title], expandable: true, expandedTitle: title, expandedRailKey: 'Not this rail' })
115
109
 
116
- await new Promise(res => setTimeout(res, 1000))
117
-
118
110
  expect(getAllByTestId('title')[0].classList).not.toContain('expanded')
119
111
  })
120
112