@playpilot/tpi 8.26.2 → 8.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.26.2",
3
+ "version": "8.27.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -3,9 +3,11 @@
3
3
  import TinySlider from 'svelte-tiny-slider'
4
4
  import { fade } from 'svelte/transition'
5
5
  import { t } from '$lib/localization'
6
+ import { getFirstAdOfType } from '$lib/api/ads'
6
7
  import Modal from './Modal.svelte'
7
8
  import IconArrow from '../Icons/IconArrow.svelte'
8
9
  import ModalCloseOverlay from './ModalCloseOverlay.svelte'
10
+ import Display from '../Ads/Display.svelte'
9
11
 
10
12
  interface Props {
11
13
  items: Record<string, any>[]
@@ -17,12 +19,17 @@
17
19
 
18
20
  const { items, initialIndex = 0, onchange = () => null, onclose = () => null, each, ...restProps }: Props = $props()
19
21
 
22
+ const displayAd = getFirstAdOfType('card')
20
23
  const transitionDuration = 300
21
24
 
22
25
  let slider: TinySlider
23
26
  let initialized = $state(false)
27
+ let availableHeight: number = $state(window.innerHeight)
28
+ let element: HTMLElement | null = $state(null)
24
29
 
25
30
  onMount(() => {
31
+ setAvailableHeight()
32
+
26
33
  requestAnimationFrame(() => {
27
34
  if (initialIndex !== 0) slider?.setIndex(initialIndex)
28
35
 
@@ -38,11 +45,26 @@
38
45
  onchange(index)
39
46
  slider.setIndex(index)
40
47
  }
48
+
49
+ function setAvailableHeight(): void {
50
+ const modal = document.querySelector('.modal')!
51
+ availableHeight = window.innerHeight - (element!.getBoundingClientRect().top + modal.scrollTop + parseInt(window.getComputedStyle(modal).paddingBottom))
52
+ }
41
53
  </script>
42
54
 
43
- <Modal blur {onclose} {...restProps}>
55
+ <svelte:window onresize={setAvailableHeight} />
56
+
57
+ {#snippet prepend()}
58
+ {#if displayAd}
59
+ <div class="ad">
60
+ <Display campaign={displayAd} />
61
+ </div>
62
+ {/if}
63
+ {/snippet}
64
+
65
+ <Modal blur {onclose} {prepend} {...restProps}>
44
66
  {#snippet dialog()}
45
- <div class="rail-modal" class:initialized style:--transition-duration="{transitionDuration}ms">
67
+ <div class="rail-modal" class:initialized style:--transition-duration="{transitionDuration}ms" style:--available-height="{availableHeight}px" bind:this={element}>
46
68
  <TinySlider threshold={40} moveThreshold={40} transitionDuration={initialized ? transitionDuration : 0} bind:this={slider}>
47
69
  {#snippet children({ currentIndex })}
48
70
  {#each items as item, index}
@@ -143,20 +165,16 @@
143
165
  border-radius: theme(rail-modal-item-border-radius, border-radius-huge) theme(rail-modal-item-border-radius, border-radius-huge) 0 0;
144
166
  background: theme(rail-modal-item-background, light);
145
167
  box-shadow: none;
146
- height: calc(90vh - env(safe-area-inset-top));
168
+ height: calc(var(--available-height, 90vh) - env(safe-area-inset-top));
147
169
  overflow: auto;
148
170
  overscroll-behavior: contain;
149
171
  transition: box-shadow var(--transition-duration);
150
172
 
151
173
  @include styled-scrollbar;
152
174
 
153
- @supports (height: 1dvh) {
154
- height: calc(90dvh - env(safe-area-inset-top));
155
- }
156
-
157
175
  @include desktop {
158
176
  height: auto;
159
- max-height: 85vh;
177
+ max-height: var(--available-height, 85vh);
160
178
  border-radius: theme(rail-modal-item-border-radius, border-radius-huge);
161
179
  }
162
180
 
@@ -212,4 +230,10 @@
212
230
  .close {
213
231
  --modal-close-overlay-left: calc((100vw - #{$size}) / 2);
214
232
  }
233
+
234
+ .ad {
235
+ padding-top: margin(2.5);
236
+ max-width: $size;
237
+ margin: 0 auto;
238
+ }
215
239
  </style>
@@ -5,6 +5,7 @@
5
5
  import { t } from '$lib/localization'
6
6
  import { track } from '$lib/tracking'
7
7
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
8
+ import { getFirstAdOfType } from '$lib/api/ads'
8
9
  import TinySlider from 'svelte-tiny-slider'
9
10
  import Modal from './Modal.svelte'
10
11
  import TitleBackground from '../TitleBackground.svelte'
@@ -14,6 +15,7 @@
14
15
  import PlaylinksDisclaimer from '../Playlinks/PlaylinksDisclaimer.svelte'
15
16
  import ModalCloseOverlay from './ModalCloseOverlay.svelte'
16
17
  import IconArrow from '../Icons/IconArrow.svelte'
18
+ import Display from '../Ads/Display.svelte'
17
19
 
18
20
  interface Props {
19
21
  titles: TitleData[],
@@ -22,10 +24,15 @@
22
24
 
23
25
  const { titles, onclose = () => null }: Props = $props()
24
26
 
27
+ const displayAd = getFirstAdOfType('card')
28
+
25
29
  let interval: ReturnType<typeof setInterval> | null = null
30
+ let availableHeight: number = $state(window.innerHeight)
31
+ let element: HTMLElement | null = $state(null)
26
32
 
27
33
  onMount(() => {
28
34
  pauseAndPlay(0)
35
+ setAvailableHeight()
29
36
 
30
37
  return () => {
31
38
  if (interval) clearInterval(interval)
@@ -51,12 +58,25 @@
51
58
  window.dispatchEvent(new CustomEvent('playembed', { detail: currentTitle?.embeddable_url } ))
52
59
  }, 500)
53
60
  }
61
+
62
+ function setAvailableHeight(): void {
63
+ const modal = document.querySelector('.modal')!
64
+ availableHeight = window.innerHeight - (element!.getBoundingClientRect().top + modal.scrollTop + parseInt(window.getComputedStyle(modal).paddingBottom))
65
+ }
54
66
  </script>
55
67
 
56
- <div class="reel">
57
- <Modal>
68
+ <svelte:window onresize={setAvailableHeight} />
69
+
70
+ {#snippet prepend()}
71
+ {#if displayAd}
72
+ <Display campaign={displayAd} />
73
+ {/if}
74
+ {/snippet}
75
+
76
+ <div class="reel" style:--available-height="{availableHeight}px">
77
+ <Modal {prepend}>
58
78
  {#snippet dialog()}
59
- <div class="slider">
79
+ <div class="slider" bind:this={element}>
60
80
  <TinySlider vertical threshold={50} moveThreshold={10} {onchange}>
61
81
  {#snippet children({ currentIndex, setIndex })}
62
82
  {#each titles as title, index}
@@ -107,7 +127,7 @@
107
127
 
108
128
  <style lang="scss">
109
129
  $modal-height: calc(100vh - margin(4.5));
110
- $item-height: calc($modal-height - margin(5));
130
+ $item-height: calc(var(--available-height, $modal-height) - margin(5));
111
131
 
112
132
  .reel {
113
133
  --playpilot-detail-background-height: #{$item-height};
@@ -130,10 +150,19 @@
130
150
  :global(.modal) {
131
151
  padding: 0;
132
152
  }
153
+
154
+ :global(.prepend) {
155
+ padding-top: margin(4.5);
156
+ width: 100%;
157
+ margin: 0;
158
+ }
159
+
160
+ :global(.display) {
161
+ border-radius: 0;
162
+ }
133
163
  }
134
164
 
135
165
  .slider {
136
- margin-top: calc(100vh - $modal-height);
137
166
  height: $modal-height;
138
167
  width: 100%;
139
168
  }
@@ -1,160 +1,160 @@
1
- <script lang="ts">
2
- import { onMount } from 'svelte'
3
- import { heading } from '$lib/actions/heading'
4
- import { fetchTitlesForParticipant } from '$lib/api/participants'
5
- import { openModal } from '$lib/modal'
6
- import type { ParticipantData } from '$lib/types/participant'
7
- import type { TitleData } from '$lib/types/title'
8
- import ListTitle from './ListTitle.svelte'
9
- import { t } from '$lib/localization'
10
- import { isRetargetingAllowed } from '$lib/retargeting'
11
- import { trackViaPixel } from '@playpilot/retargeting-tracking'
12
- import { MetaEvent } from '$lib/enums/TrackingEvent'
13
-
14
- interface Props {
15
- participant: ParticipantData
16
- small?: boolean
17
- }
18
-
19
- const { participant, small = false }: Props = $props()
20
-
21
- const { name, birth_date, death_date } = $derived(participant)
22
-
23
- const pageSize = 30
24
-
25
- let titles: TitleData[] = $state([])
26
- let page = $state(1)
27
- let hasMorePages = $state(true)
28
- let loading = $state(true)
29
-
30
- onMount(loadMore)
31
-
32
- if (isRetargetingAllowed()) trackViaPixel(MetaEvent.ParticipantView, { participant: participant.name })
33
-
34
- function formatDate(dateString: string): string {
35
- const date = new Date(dateString)
36
- return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
37
- }
38
-
39
- async function loadMore(): Promise<void> {
40
- loading = true
41
-
42
- try {
43
- const results = await fetchTitlesForParticipant(participant, { page })
44
-
45
- titles = [...titles, ...results]
46
- hasMorePages = results?.length >= pageSize
47
- } catch {
48
- hasMorePages = false
49
- } finally {
50
- loading = false
51
- page += 1
52
- }
53
- }
54
- </script>
55
-
56
- <div class="header" class:small>
57
- <div class="heading" use:heading={2} id="heading">{name}</div>
58
-
59
- {#if birth_date}
60
- <p class="dates">
61
- {t('Born On')} <strong>{formatDate(birth_date)}</strong>{#if death_date}, {t('Died On')} <strong>{formatDate(death_date)}</strong>{/if}
62
- </p>
63
- {/if}
64
- </div>
65
-
66
- <div class="content">
67
- {#if !small}
68
- <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
69
- {/if}
70
-
71
- <div class="list">
72
- {#each titles as title}
73
- <ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
74
- {/each}
75
-
76
- {#if loading}
77
- {#each { length: 6 }}
78
- <div class="skeleton" data-testid="skeleton"></div>
79
- {/each}
80
- {:else if hasMorePages}
81
- <button class="more" onclick={loadMore}>{t('Show More')}</button>
82
- {/if}
83
- </div>
84
- </div>
85
-
86
- <style lang="scss">
87
- .header {
88
- padding: margin(4) margin(1) margin(2);
89
- font-family: theme(detail-font-family, font-family);
90
- font-weight: theme(detail-font-weight, normal);
91
- font-size: theme(detail-font-size, font-size-base);
92
- line-height: theme(participant-description-line-height, normal);
93
- color: theme(detail-text-color, text-color);
94
-
95
- &.small {
96
- padding: margin(1);
97
- }
98
- }
99
-
100
- .heading {
101
- margin: 0;
102
- font-family: theme(detail-title-font-family, font-family);
103
- font-weight: theme(detail-title-font-weight, lighter);
104
- font-size: theme(detail-title-font-size, margin(1.5));
105
- line-height: normal;
106
- font-style: theme(detail-title-font-style, normal);
107
-
108
- &.subheading {
109
- margin: 0 0 margin(0.5);
110
- font-size: theme(detail-title-small-font-size, margin(1.25));
111
- }
112
- }
113
-
114
- .dates {
115
- margin: margin(0.5) 0 0;
116
- }
117
-
118
- .content {
119
- padding: 0 margin(1) margin(1);
120
- color: theme(detail-text-color, text-color);
121
- font-family: theme(detail-font-family, font-family);
122
- font-weight: theme(detail-font-weight, normal);
123
- font-size: theme(detail-font-size, font-size-base);
124
- line-height: normal;
125
- font-style: normal;
126
-
127
- ::view-transition-old(content),
128
- ::view-transition-new(content) {
129
- height: 100%;
130
- }
131
- }
132
-
133
- .list {
134
- display: flex;
135
- flex-direction: column;
136
- gap: margin(0.5);
137
- }
138
-
139
- .skeleton {
140
- min-height: margin(7.25);
141
- border-radius: theme(playlink-border-radius, border-radius);
142
- background: theme(detail-background-light, lighter);
143
- }
144
-
145
- .more {
146
- cursor: pointer;
147
- appearance: none;
148
- padding: margin(0.5) margin(1);
149
- border: 0;
150
- border-radius: theme(participants-load-more-border-radius, border-radius);
151
- background: theme(participants-load-more-background, content);
152
- color: inherit;
153
- font-size: inherit;
154
- font-family: inherit;
155
-
156
- &:hover {
157
- filter: brightness(1.2);
158
- }
159
- }
160
- </style>
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte'
3
+ import { heading } from '$lib/actions/heading'
4
+ import { fetchTitlesForParticipant } from '$lib/api/participants'
5
+ import { openModal } from '$lib/modal'
6
+ import type { ParticipantData } from '$lib/types/participant'
7
+ import type { TitleData } from '$lib/types/title'
8
+ import ListTitle from './ListTitle.svelte'
9
+ import { t } from '$lib/localization'
10
+ import { isRetargetingAllowed } from '$lib/retargeting'
11
+ import { trackViaPixel } from '@playpilot/retargeting-tracking'
12
+ import { MetaEvent } from '$lib/enums/TrackingEvent'
13
+
14
+ interface Props {
15
+ participant: ParticipantData
16
+ small?: boolean
17
+ }
18
+
19
+ const { participant, small = false }: Props = $props()
20
+
21
+ const { name, birth_date, death_date } = $derived(participant)
22
+
23
+ const pageSize = 30
24
+
25
+ let titles: TitleData[] = $state([])
26
+ let page = $state(1)
27
+ let hasMorePages = $state(true)
28
+ let loading = $state(true)
29
+
30
+ onMount(loadMore)
31
+
32
+ if (isRetargetingAllowed()) trackViaPixel(MetaEvent.ParticipantView, { participant: participant.name })
33
+
34
+ function formatDate(dateString: string): string {
35
+ const date = new Date(dateString)
36
+ return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
37
+ }
38
+
39
+ async function loadMore(): Promise<void> {
40
+ loading = true
41
+
42
+ try {
43
+ const results = await fetchTitlesForParticipant(participant, { page })
44
+
45
+ titles = [...titles, ...results]
46
+ hasMorePages = results?.length >= pageSize
47
+ } catch {
48
+ hasMorePages = false
49
+ } finally {
50
+ loading = false
51
+ page += 1
52
+ }
53
+ }
54
+ </script>
55
+
56
+ <div class="header" class:small>
57
+ <div class="heading" use:heading={2} id="heading">{name}</div>
58
+
59
+ {#if birth_date}
60
+ <p class="dates">
61
+ {t('Born On')} <strong>{formatDate(birth_date)}</strong>{#if death_date}, {t('Died On')} <strong>{formatDate(death_date)}</strong>{/if}
62
+ </p>
63
+ {/if}
64
+ </div>
65
+
66
+ <div class="content">
67
+ {#if !small}
68
+ <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
69
+ {/if}
70
+
71
+ <div class="list">
72
+ {#each titles as title}
73
+ <ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
74
+ {/each}
75
+
76
+ {#if loading}
77
+ {#each { length: 6 }}
78
+ <div class="skeleton" data-testid="skeleton"></div>
79
+ {/each}
80
+ {:else if hasMorePages}
81
+ <button class="more" onclick={loadMore}>{t('Show More')}</button>
82
+ {/if}
83
+ </div>
84
+ </div>
85
+
86
+ <style lang="scss">
87
+ .header {
88
+ padding: margin(4) margin(1) margin(2);
89
+ font-family: theme(detail-font-family, font-family);
90
+ font-weight: theme(detail-font-weight, normal);
91
+ font-size: theme(detail-font-size, font-size-base);
92
+ line-height: theme(participant-description-line-height, normal);
93
+ color: theme(detail-text-color, text-color);
94
+
95
+ &.small {
96
+ padding: margin(1);
97
+ }
98
+ }
99
+
100
+ .heading {
101
+ margin: 0;
102
+ font-family: theme(detail-title-font-family, font-family);
103
+ font-weight: theme(detail-title-font-weight, lighter);
104
+ font-size: theme(detail-title-font-size, margin(1.5));
105
+ line-height: normal;
106
+ font-style: theme(detail-title-font-style, normal);
107
+
108
+ &.subheading {
109
+ margin: 0 0 margin(0.5);
110
+ font-size: theme(detail-title-small-font-size, margin(1.25));
111
+ }
112
+ }
113
+
114
+ .dates {
115
+ margin: margin(0.5) 0 0;
116
+ }
117
+
118
+ .content {
119
+ padding: 0 margin(1) margin(1);
120
+ color: theme(detail-text-color, text-color);
121
+ font-family: theme(detail-font-family, font-family);
122
+ font-weight: theme(detail-font-weight, normal);
123
+ font-size: theme(detail-font-size, font-size-base);
124
+ line-height: normal;
125
+ font-style: normal;
126
+
127
+ ::view-transition-old(content),
128
+ ::view-transition-new(content) {
129
+ height: 100%;
130
+ }
131
+ }
132
+
133
+ .list {
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: margin(0.5);
137
+ }
138
+
139
+ .skeleton {
140
+ min-height: margin(7.25);
141
+ border-radius: theme(playlink-border-radius, border-radius);
142
+ background: theme(detail-background-light, lighter);
143
+ }
144
+
145
+ .more {
146
+ cursor: pointer;
147
+ appearance: none;
148
+ padding: margin(0.5) margin(1);
149
+ border: 0;
150
+ border-radius: theme(participants-load-more-border-radius, border-radius);
151
+ background: theme(participants-load-more-background, content);
152
+ color: inherit;
153
+ font-size: inherit;
154
+ font-family: inherit;
155
+
156
+ &:hover {
157
+ filter: brightness(1.2);
158
+ }
159
+ }
160
+ </style>
@@ -1,18 +0,0 @@
1
- name: Asana
2
-
3
- on:
4
- pull_request:
5
- types: [opened, synchronize, reopened, edited]
6
-
7
- jobs:
8
- create-asana-attachment-job:
9
- runs-on: ubuntu-latest
10
- name: Create pull request attachments on Asana tasks
11
- steps:
12
- - name: Create pull request attachments
13
- uses: Asana/create-app-attachment-github-action@latest
14
- id: postAttachment
15
- with:
16
- asana-secret: ${{ secrets.ASANA_SECRET }}
17
- - name: Log output status
18
- run: echo "Status is ${{ steps.postAttachment.outputs.status }}"