@playpilot/tpi 8.26.2 → 8.27.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.26.2",
3
+ "version": "8.27.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -31,6 +31,7 @@
31
31
 
32
32
  <style lang="scss">
33
33
  .back {
34
+ z-index: 10;
34
35
  position: absolute;
35
36
  top: margin(1.5);
36
37
  left: var(--modal-close-overlay-left, margin(1));
@@ -57,6 +58,7 @@
57
58
  .close {
58
59
  --playpilot-button-background: transparent;
59
60
  --playpilot-button-text-color: white;
61
+ z-index: 10;
60
62
  position: absolute;
61
63
  top: margin(1);
62
64
  right: margin(1);
@@ -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 | null = $state(null)
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,31 @@
38
45
  onchange(index)
39
46
  slider.setIndex(index)
40
47
  }
48
+
49
+ function setAvailableHeight(): void {
50
+ if (!displayAd) {
51
+ availableHeight = null
52
+ return
53
+ }
54
+
55
+ const modal = document.querySelector('.modal')!
56
+ availableHeight = window.innerHeight - (element!.getBoundingClientRect().top + modal.scrollTop + parseInt(window.getComputedStyle(modal).paddingBottom))
57
+ }
41
58
  </script>
42
59
 
43
- <Modal blur {onclose} {...restProps}>
60
+ <svelte:window onresize={setAvailableHeight} />
61
+
62
+ {#snippet prepend()}
63
+ {#if displayAd}
64
+ <div class="ad">
65
+ <Display campaign={displayAd} />
66
+ </div>
67
+ {/if}
68
+ {/snippet}
69
+
70
+ <Modal blur {onclose} {prepend} {...restProps}>
44
71
  {#snippet dialog()}
45
- <div class="rail-modal" class:initialized style:--transition-duration="{transitionDuration}ms">
72
+ <div class="rail-modal" class:initialized style:--transition-duration="{transitionDuration}ms" style:--available-height={availableHeight && availableHeight + 'px'} bind:this={element}>
46
73
  <TinySlider threshold={40} moveThreshold={40} transitionDuration={initialized ? transitionDuration : 0} bind:this={slider}>
47
74
  {#snippet children({ currentIndex })}
48
75
  {#each items as item, index}
@@ -143,20 +170,16 @@
143
170
  border-radius: theme(rail-modal-item-border-radius, border-radius-huge) theme(rail-modal-item-border-radius, border-radius-huge) 0 0;
144
171
  background: theme(rail-modal-item-background, light);
145
172
  box-shadow: none;
146
- height: calc(90vh - env(safe-area-inset-top));
173
+ height: calc(var(--available-height, 90vh) - margin(1) - env(safe-area-inset-top));
147
174
  overflow: auto;
148
175
  overscroll-behavior: contain;
149
176
  transition: box-shadow var(--transition-duration);
150
177
 
151
178
  @include styled-scrollbar;
152
179
 
153
- @supports (height: 1dvh) {
154
- height: calc(90dvh - env(safe-area-inset-top));
155
- }
156
-
157
180
  @include desktop {
158
181
  height: auto;
159
- max-height: 85vh;
182
+ max-height: var(--available-height, 85vh);
160
183
  border-radius: theme(rail-modal-item-border-radius, border-radius-huge);
161
184
  }
162
185
 
@@ -212,4 +235,10 @@
212
235
  .close {
213
236
  --modal-close-overlay-left: calc((100vw - #{$size}) / 2);
214
237
  }
238
+
239
+ .ad {
240
+ padding-top: margin(2.5);
241
+ max-width: $size;
242
+ margin: 0 auto;
243
+ }
215
244
  </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 }}"