@playpilot/tpi 5.11.0-beta.participants.2 → 5.11.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/link-injections.js +10 -11
- package/package.json +1 -1
- package/src/lib/fakeData.ts +0 -70
- package/src/lib/linkInjection.ts +29 -11
- package/src/lib/playlink.ts +1 -4
- package/src/lib/types/title.d.ts +0 -2
- package/src/routes/+page.svelte +0 -1
- package/src/routes/components/Ads/Disclaimer.svelte +83 -0
- package/src/routes/components/Ads/Display.svelte +22 -7
- package/src/routes/components/Ads/TopScroll.svelte +2 -64
- package/src/routes/components/Icons/IconClose.svelte +1 -1
- package/src/routes/components/Modal.svelte +7 -41
- package/src/routes/components/Playlink.svelte +7 -1
- package/src/routes/components/Playlinks.svelte +4 -2
- package/src/routes/components/Title.svelte +2 -8
- package/src/routes/components/TitleModal.svelte +3 -2
- package/src/tests/routes/components/Ads/Disclaimer.test.js +20 -0
- package/src/tests/routes/components/Ads/Display.test.js +9 -2
- package/src/tests/routes/components/Ads/TopScroll.test.js +8 -10
- package/src/tests/routes/components/Playlink.test.js +14 -0
- package/src/lib/modal.ts +0 -77
- package/src/lib/types/participant.d.ts +0 -14
- package/src/routes/components/Icons/IconArrow.svelte +0 -17
- package/src/routes/components/ListTitle.svelte +0 -169
- package/src/routes/components/Participant.svelte +0 -88
- package/src/routes/components/ParticipantModal.svelte +0 -30
- package/src/routes/components/Participants.svelte +0 -79
- package/src/routes/components/PlaylinkIcon.svelte +0 -40
- package/src/routes/components/Tabs.svelte +0 -47
- package/src/tests/routes/components/Participants.test.js +0 -41
package/package.json
CHANGED
package/src/lib/fakeData.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { LinkInjection } from "./types/injection"
|
|
2
|
-
import type { ParticipantData } from "./types/participant"
|
|
3
2
|
import type { TitleData } from "./types/title"
|
|
4
3
|
|
|
5
4
|
export const title: TitleData = {
|
|
@@ -70,72 +69,3 @@ export const linkInjections: LinkInjection[] = [{
|
|
|
70
69
|
playpilot_url: 'https://playpilot.com/movie/example-4/',
|
|
71
70
|
key: 'some-key-4',
|
|
72
71
|
}]
|
|
73
|
-
|
|
74
|
-
export const participants: ParticipantData[] = [
|
|
75
|
-
{
|
|
76
|
-
sid: 'pr5C5W',
|
|
77
|
-
name: 'James Franco',
|
|
78
|
-
birth_date: '1978-04-19',
|
|
79
|
-
death_date: null,
|
|
80
|
-
jobs: ['actor'],
|
|
81
|
-
image: null,
|
|
82
|
-
image_uuid: null,
|
|
83
|
-
gender: 'Male',
|
|
84
|
-
character: 'Will Rodman (archive footage) (uncredited)',
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
sid: 'pr8bZm',
|
|
88
|
-
name: 'Thomas Rosales Jr.',
|
|
89
|
-
birth_date: '1948-02-03',
|
|
90
|
-
death_date: null,
|
|
91
|
-
jobs: ['actor'],
|
|
92
|
-
image: null,
|
|
93
|
-
image_uuid: null,
|
|
94
|
-
gender: 'Male',
|
|
95
|
-
character: 'Old Man',
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
sid: 'pr45Dp',
|
|
99
|
-
name: 'Barack Obama',
|
|
100
|
-
birth_date: '1961-08-04',
|
|
101
|
-
death_date: null,
|
|
102
|
-
jobs: ['actor'],
|
|
103
|
-
image: null,
|
|
104
|
-
image_uuid: null,
|
|
105
|
-
gender: 'Male',
|
|
106
|
-
character: 'Self (archive footage) (uncredited)',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
sid: 'pr6DnN',
|
|
110
|
-
name: 'Gary Oldman',
|
|
111
|
-
birth_date: '1958-03-21',
|
|
112
|
-
death_date: null,
|
|
113
|
-
jobs: ['actor'],
|
|
114
|
-
image: null,
|
|
115
|
-
image_uuid: null,
|
|
116
|
-
gender: 'Male',
|
|
117
|
-
character: 'Dreyfus',
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
sid: 'pr7GK8',
|
|
121
|
-
name: 'Michael Papajohn',
|
|
122
|
-
birth_date: '1964-11-07',
|
|
123
|
-
death_date: null,
|
|
124
|
-
jobs: ['actor'],
|
|
125
|
-
image: null,
|
|
126
|
-
image_uuid: null,
|
|
127
|
-
gender: 'Male',
|
|
128
|
-
character: 'Cannon-Gunner',
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
sid: 'pr88KG',
|
|
132
|
-
name: 'Judy Greer',
|
|
133
|
-
birth_date: '1975-07-20',
|
|
134
|
-
death_date: null,
|
|
135
|
-
jobs: ['actor'],
|
|
136
|
-
image: null,
|
|
137
|
-
image_uuid: null,
|
|
138
|
-
gender: 'Female',
|
|
139
|
-
character: 'Cornelia',
|
|
140
|
-
},
|
|
141
|
-
]
|
package/src/lib/linkInjection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mount, unmount } from 'svelte'
|
|
2
|
+
import TitleModal from '../routes/components/TitleModal.svelte'
|
|
2
3
|
import TitlePopover from '../routes/components/TitlePopover.svelte'
|
|
3
4
|
import AfterArticlePlaylinks from '../routes/components/AfterArticlePlaylinks.svelte'
|
|
4
5
|
import { cleanPhrase, findNumberOfMatchesInString, findShortestMatchBetweenPhrases, findTextNodeContaining, getIndexOfPhraseInElement, getNumberOfLeadingAndTrailingSpaces, isNodeInLink, replaceBetween, replaceStartingFrom } from './text'
|
|
@@ -8,7 +9,6 @@ import { playFallbackViewTransition } from './viewTransition'
|
|
|
8
9
|
import { prefersReducedMotion } from 'svelte/motion'
|
|
9
10
|
import { getNumberOfOccurrencesInArray } from './array'
|
|
10
11
|
import { mobileBreakpoint } from './constants'
|
|
11
|
-
import { destroyAllModals, openModal } from './modal'
|
|
12
12
|
|
|
13
13
|
const keyDataAttribute = 'data-playpilot-injection-key'
|
|
14
14
|
const keySelector = `[${keyDataAttribute}]`
|
|
@@ -16,6 +16,7 @@ const keySelector = `[${keyDataAttribute}]`
|
|
|
16
16
|
let currentlyHoveredInjection: EventTarget | null = null
|
|
17
17
|
let activePopoverInsertedComponent: object | null = null
|
|
18
18
|
let afterArticlePlaylinkInsertedComponent: object | null = null
|
|
19
|
+
let activeModalInsertedComponent: object | null = null
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Return a list of all valid text containing elements that may get injected into.
|
|
@@ -347,7 +348,7 @@ function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
|
347
348
|
|
|
348
349
|
playFallbackViewTransition(() => {
|
|
349
350
|
destroyLinkPopover(false)
|
|
350
|
-
|
|
351
|
+
openLinkModal(event, injection)
|
|
351
352
|
}, !prefersReducedMotion.current && window.innerWidth >= mobileBreakpoint && !window.matchMedia("(pointer: coarse)").matches)
|
|
352
353
|
})
|
|
353
354
|
|
|
@@ -379,6 +380,29 @@ function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
|
379
380
|
})
|
|
380
381
|
}
|
|
381
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Open modal for the corresponding injection by mounting the component and saving it to a variable.
|
|
385
|
+
* Ignore clicks that used modifier keys or that were not left click.
|
|
386
|
+
*/
|
|
387
|
+
function openLinkModal(event: MouseEvent, injection: LinkInjection): void {
|
|
388
|
+
if (isHoldingSpecialKey(event)) return
|
|
389
|
+
if (activeModalInsertedComponent) return
|
|
390
|
+
|
|
391
|
+
event.preventDefault()
|
|
392
|
+
|
|
393
|
+
activeModalInsertedComponent = mount(TitleModal, { target: getPlayPilotWrapperElement(), props: { title: injection.title_details!, onclose: destroyLinkModal } })
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Unmount the modal, removing it from the dom
|
|
398
|
+
*/
|
|
399
|
+
function destroyLinkModal(outro: boolean = true): void {
|
|
400
|
+
if (!activeModalInsertedComponent) return
|
|
401
|
+
|
|
402
|
+
unmount(activeModalInsertedComponent, { outro })
|
|
403
|
+
activeModalInsertedComponent = null
|
|
404
|
+
}
|
|
405
|
+
|
|
382
406
|
/**
|
|
383
407
|
* When a link is hovered, it is shown as a popover. The component is mounted when a mouse enters the link,
|
|
384
408
|
* and removed when clicked or on mouseleave.
|
|
@@ -436,13 +460,7 @@ export function insertAfterArticlePlaylinks(elements: HTMLElement[], injections:
|
|
|
436
460
|
target.dataset.playpilotAfterArticlePlaylinks = 'true'
|
|
437
461
|
insertElement.insertAdjacentElement(insertPosition, target)
|
|
438
462
|
|
|
439
|
-
afterArticlePlaylinkInsertedComponent = mount(AfterArticlePlaylinks, {
|
|
440
|
-
target,
|
|
441
|
-
props: {
|
|
442
|
-
linkInjections: injections,
|
|
443
|
-
onclickmodal: (event, injection) => openModal({ event, injection, data: injection.title_details })
|
|
444
|
-
}
|
|
445
|
-
})
|
|
463
|
+
afterArticlePlaylinkInsertedComponent = mount(AfterArticlePlaylinks, { target, props: { linkInjections: injections, onclickmodal: (event, injection) => openLinkModal(event, injection) } })
|
|
446
464
|
}
|
|
447
465
|
|
|
448
466
|
function clearAfterArticlePlaylinks(): void {
|
|
@@ -461,7 +479,7 @@ export function clearLinkInjections(): void {
|
|
|
461
479
|
elements.forEach((element) => clearLinkInjection(element.getAttribute(keyDataAttribute) || ''))
|
|
462
480
|
|
|
463
481
|
clearAfterArticlePlaylinks()
|
|
464
|
-
|
|
482
|
+
destroyLinkModal(false)
|
|
465
483
|
destroyLinkPopover(false)
|
|
466
484
|
}
|
|
467
485
|
|
|
@@ -568,6 +586,6 @@ export function isEquivalentInjection(injection1: LinkInjection, injection2: Lin
|
|
|
568
586
|
return injection1.title === injection2.title && cleanPhrase(injection1.sentence) === cleanPhrase(injection2.sentence)
|
|
569
587
|
}
|
|
570
588
|
|
|
571
|
-
|
|
589
|
+
function getPlayPilotWrapperElement(): Element {
|
|
572
590
|
return document.querySelector('[data-playpilot-link-injections]') || document.body
|
|
573
591
|
}
|
package/src/lib/playlink.ts
CHANGED
|
@@ -2,14 +2,11 @@ import type { PlaylinkData } from "./types/playlink"
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Merge playlinks of the same provider of BUY and RENT categories into a shared TVOD category.
|
|
5
|
-
* Also remove playlinks without logos, as these are likely sub providers.
|
|
6
5
|
*/
|
|
7
6
|
export function mergePlaylinks(playlinks: PlaylinkData[]): PlaylinkData[] {
|
|
8
|
-
const filtered = playlinks.filter(playlink => !!playlink.logo_url)
|
|
9
|
-
|
|
10
7
|
let merged: PlaylinkData[] = []
|
|
11
8
|
|
|
12
|
-
for (const playlink of
|
|
9
|
+
for (const playlink of playlinks) {
|
|
13
10
|
let newPlaylink = playlink
|
|
14
11
|
const existingPlaylink = merged.find(p => p.name === newPlaylink.name)
|
|
15
12
|
|
package/src/lib/types/title.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ParticipantData } from "./participant"
|
|
2
1
|
import type { PlaylinkData } from "./playlink"
|
|
3
2
|
|
|
4
3
|
export type TitleData = {
|
|
@@ -19,5 +18,4 @@ export type TitleData = {
|
|
|
19
18
|
original_title: string
|
|
20
19
|
length?: number
|
|
21
20
|
blurb?: string
|
|
22
|
-
participants?: ParticipantData[]
|
|
23
21
|
}
|
package/src/routes/+page.svelte
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import TrackingPixels from './components/TrackingPixels.svelte'
|
|
15
15
|
import Debugger from './components/Debugger.svelte'
|
|
16
16
|
import { fetchAds } from '$lib/ads'
|
|
17
|
-
import ParticipantModal from './components/ParticipantModal.svelte';
|
|
18
17
|
|
|
19
18
|
let parentElement: HTMLElement | null = $state(null)
|
|
20
19
|
let elements: HTMLElement[] = $state([])
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fade } from 'svelte/transition'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
disclaimer: string
|
|
6
|
+
small?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { disclaimer, small = false }: Props = $props()
|
|
10
|
+
|
|
11
|
+
let tooltipVisible = $state(false)
|
|
12
|
+
|
|
13
|
+
function toggleTooltip(event: MouseEvent): void {
|
|
14
|
+
event.preventDefault()
|
|
15
|
+
event.stopPropagation()
|
|
16
|
+
|
|
17
|
+
tooltipVisible = !tooltipVisible
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<button class="tooltip" onclick={toggleTooltip} data-testid="disclaimer">
|
|
22
|
+
<div class="disclaimer" class:small aria-label="Disclaimer">i</div>
|
|
23
|
+
|
|
24
|
+
{#if tooltipVisible}
|
|
25
|
+
<div class="tooltip-content" transition:fade={{ duration: 50 }} role="paragraph">
|
|
26
|
+
{disclaimer}
|
|
27
|
+
</div>
|
|
28
|
+
{/if}
|
|
29
|
+
</button>
|
|
30
|
+
|
|
31
|
+
<style lang="scss">
|
|
32
|
+
.disclaimer {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
border: var(--playpilot-disclaimer-border, 2px solid rgba(255, 255, 255, 0.5));
|
|
37
|
+
padding: margin(0.25);
|
|
38
|
+
height: margin(1.25);
|
|
39
|
+
width: margin(1.25);
|
|
40
|
+
border-radius: 99px;
|
|
41
|
+
color: var(--playpilot-disclaimer-text-color, rgba(255, 255, 255, 0.75));
|
|
42
|
+
font-weight: bold;
|
|
43
|
+
line-height: 1;
|
|
44
|
+
|
|
45
|
+
&:hover {
|
|
46
|
+
border-color: var(--playpilot-disclaimer-hover-border-color, white);
|
|
47
|
+
color: var(--playpilot-disclaimer-hover-text-color, white);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.small {
|
|
51
|
+
height: margin(1);
|
|
52
|
+
width: margin(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tooltip {
|
|
57
|
+
z-index: 2;
|
|
58
|
+
position: absolute;
|
|
59
|
+
right: 0;
|
|
60
|
+
bottom: 0;
|
|
61
|
+
padding: margin(0.25);
|
|
62
|
+
background: transparent;
|
|
63
|
+
border: 0;
|
|
64
|
+
color: var(--playpilot-disclaimer-tooltip-text-color, var(--playpilot-detail-text-color, white));
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.tooltip-content {
|
|
69
|
+
display: block;
|
|
70
|
+
z-index: 20;
|
|
71
|
+
position: absolute;
|
|
72
|
+
right: 100%;
|
|
73
|
+
bottom: margin(0.25);
|
|
74
|
+
width: calc(var(--width, 15rem) * 0.8);
|
|
75
|
+
padding: margin(0.5);
|
|
76
|
+
border-radius: margin(0.5);
|
|
77
|
+
background: var(--playpilot-disclaimer-tooltip-background, var(--playpilot-detail-background, var(--playpilot-content)));
|
|
78
|
+
box-shadow: var(--playpilot-shadow);
|
|
79
|
+
line-height: 1.1;
|
|
80
|
+
text-align: right;
|
|
81
|
+
white-space: normal;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { track } from '$lib/tracking'
|
|
5
5
|
import type { Campaign } from '$lib/types/campaign'
|
|
6
6
|
import Tooltip from '../Tooltip.svelte'
|
|
7
|
+
import Disclaimer from './Disclaimer.svelte'
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
campaign: Campaign
|
|
@@ -14,7 +15,9 @@
|
|
|
14
15
|
|
|
15
16
|
const { disclaimer, content, cta, campaign_type } = $derived(campaign)
|
|
16
17
|
const { header, subheader, header_logo: logo, image, video } = $derived(content)
|
|
17
|
-
const { header: buttonLabel, url: href } = $derived(cta)
|
|
18
|
+
const { header: buttonLabel, url: href, subheader: info } = $derived(cta)
|
|
19
|
+
|
|
20
|
+
let contentHeight = $state(0)
|
|
18
21
|
|
|
19
22
|
track(TrackingEvent.DisplayAdView, null, { campaign_name: campaign.campaign_name })
|
|
20
23
|
|
|
@@ -24,24 +27,24 @@
|
|
|
24
27
|
</script>
|
|
25
28
|
|
|
26
29
|
<a {href} target="_blank" class="display" class:compact rel="sponsored" {onclick} use:middlemouse={{ onclick }}>
|
|
27
|
-
{#if
|
|
28
|
-
<div class="
|
|
30
|
+
{#if info}
|
|
31
|
+
<div class="info">
|
|
29
32
|
{#if compact}
|
|
30
33
|
<Tooltip>
|
|
31
34
|
Ad
|
|
32
35
|
|
|
33
36
|
{#snippet content()}
|
|
34
|
-
{
|
|
37
|
+
{info}
|
|
35
38
|
{/snippet}
|
|
36
39
|
</Tooltip>
|
|
37
40
|
{:else}
|
|
38
|
-
{
|
|
41
|
+
{info}
|
|
39
42
|
{/if}
|
|
40
43
|
</div>
|
|
41
44
|
{/if}
|
|
42
45
|
|
|
43
46
|
{#if logo || header || subheader || buttonLabel}
|
|
44
|
-
<div class="cta">
|
|
47
|
+
<div class="cta" bind:clientHeight={contentHeight}>
|
|
45
48
|
{#if logo}
|
|
46
49
|
<img class="logo" src={logo} alt="" height="40" width="40" />
|
|
47
50
|
{/if}
|
|
@@ -70,6 +73,12 @@
|
|
|
70
73
|
<track kind="captions" />
|
|
71
74
|
</video>
|
|
72
75
|
{/if}
|
|
76
|
+
|
|
77
|
+
{#if disclaimer}
|
|
78
|
+
<div class="disclaimer" style:--offset="{contentHeight}px">
|
|
79
|
+
<Disclaimer {disclaimer} />
|
|
80
|
+
</div>
|
|
81
|
+
{/if}
|
|
73
82
|
</a>
|
|
74
83
|
|
|
75
84
|
<style lang="scss">
|
|
@@ -87,7 +96,7 @@
|
|
|
87
96
|
font-style: normal !important;
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
.
|
|
99
|
+
.info {
|
|
91
100
|
position: absolute;
|
|
92
101
|
top: 0;
|
|
93
102
|
left: 0;
|
|
@@ -160,4 +169,10 @@
|
|
|
160
169
|
width: 100%;
|
|
161
170
|
height: auto;
|
|
162
171
|
}
|
|
172
|
+
|
|
173
|
+
.disclaimer {
|
|
174
|
+
position: absolute;
|
|
175
|
+
bottom: calc(var(--offset) + margin(0.25));
|
|
176
|
+
right: margin(0.25);
|
|
177
|
+
}
|
|
163
178
|
</style>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { isInSplitTestVariant, trackSplitTestView } from '$lib/splitTest'
|
|
8
8
|
import { track } from '$lib/tracking'
|
|
9
9
|
import type { Campaign } from '$lib/types/campaign'
|
|
10
|
-
import
|
|
10
|
+
import Disclaimer from './Disclaimer.svelte'
|
|
11
11
|
|
|
12
12
|
interface Props {
|
|
13
13
|
campaign: Campaign
|
|
@@ -29,18 +29,10 @@
|
|
|
29
29
|
const contentImage = $derived(imageFromUUID(backgroundImageUUID, ImageDimensions.TopScrollContent))
|
|
30
30
|
|
|
31
31
|
let clientWidth = $state(0)
|
|
32
|
-
let tooltipVisible = $state(false)
|
|
33
32
|
|
|
34
33
|
function onclick(): void {
|
|
35
34
|
track(TrackingEvent.TopScrollClick, null, { campaign_name: campaign.campaign_name })
|
|
36
35
|
}
|
|
37
|
-
|
|
38
|
-
function toggleTooltip(event: MouseEvent): void {
|
|
39
|
-
event.preventDefault()
|
|
40
|
-
event.stopPropagation()
|
|
41
|
-
|
|
42
|
-
tooltipVisible = !tooltipVisible
|
|
43
|
-
}
|
|
44
36
|
</script>
|
|
45
37
|
|
|
46
38
|
<a
|
|
@@ -77,15 +69,7 @@
|
|
|
77
69
|
{/if}
|
|
78
70
|
|
|
79
71
|
{#if disclaimer}
|
|
80
|
-
<
|
|
81
|
-
<div class="disclaimer">i</div>
|
|
82
|
-
|
|
83
|
-
{#if tooltipVisible}
|
|
84
|
-
<div class="tooltip-content" transition:fade={{ duration: 50 }}>
|
|
85
|
-
{disclaimer}
|
|
86
|
-
</div>
|
|
87
|
-
{/if}
|
|
88
|
-
</button>
|
|
72
|
+
<Disclaimer {disclaimer} />
|
|
89
73
|
{/if}
|
|
90
74
|
{/if}
|
|
91
75
|
</a>
|
|
@@ -198,50 +182,4 @@
|
|
|
198
182
|
border-radius: $border-radius-size $border-radius-size 0 0;
|
|
199
183
|
}
|
|
200
184
|
}
|
|
201
|
-
|
|
202
|
-
.disclaimer {
|
|
203
|
-
display: flex;
|
|
204
|
-
align-items: center;
|
|
205
|
-
justify-content: center;
|
|
206
|
-
border: var(--playpilot-top-scroll-disclaimer-border, 2px solid rgba(255, 255, 255, 0.5));
|
|
207
|
-
padding: margin(0.25);
|
|
208
|
-
height: margin(1.25);
|
|
209
|
-
width: margin(1.25);
|
|
210
|
-
border-radius: 99px;
|
|
211
|
-
color: var(--playpilot-top-scroll-disclaimer-text-color, rgba(255, 255, 255, 0.75));
|
|
212
|
-
font-weight: bold;
|
|
213
|
-
line-height: 1;
|
|
214
|
-
|
|
215
|
-
&:hover {
|
|
216
|
-
border-color: var(--playpilot-top-scroll-disclaimer-hover-border-color, white);
|
|
217
|
-
color: var(--playpilot-top-scroll-disclaimer-hover-text-color, white);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.tooltip {
|
|
222
|
-
z-index: 2;
|
|
223
|
-
position: absolute;
|
|
224
|
-
right: 0;
|
|
225
|
-
bottom: 0;
|
|
226
|
-
padding: margin(0.25);
|
|
227
|
-
background: transparent;
|
|
228
|
-
border: 0;
|
|
229
|
-
color: var(--playpilot-top-scroll-text-color, white);
|
|
230
|
-
cursor: pointer;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.tooltip-content {
|
|
234
|
-
display: block;
|
|
235
|
-
z-index: 20;
|
|
236
|
-
position: absolute;
|
|
237
|
-
right: 100%;
|
|
238
|
-
bottom: margin(0.25);
|
|
239
|
-
width: calc(var(--width) * 0.8);
|
|
240
|
-
padding: margin(0.5);
|
|
241
|
-
border-radius: margin(1);
|
|
242
|
-
background: var(--playpilot-content);
|
|
243
|
-
box-shadow: var(--playpilot-shadow);
|
|
244
|
-
line-height: 1.1;
|
|
245
|
-
text-align: right;
|
|
246
|
-
}
|
|
247
185
|
</style>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
2
|
-
<path d="M14 2L2 14M2 2L14 14" stroke="currentColor" stroke-width="
|
|
2
|
+
<path d="M14 2L2 14M2 2L14 14" stroke="currentColor" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
3
|
</svg>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { fade, fly, scale, type TransitionConfig } from 'svelte/transition'
|
|
3
3
|
import IconClose from './Icons/IconClose.svelte'
|
|
4
|
-
import IconArrow from './Icons/IconArrow.svelte'
|
|
5
4
|
import RoundButton from './RoundButton.svelte'
|
|
6
5
|
import DragHandle from './DragHandle.svelte'
|
|
7
6
|
import { onMount, setContext, type Snippet } from 'svelte'
|
|
@@ -9,32 +8,23 @@
|
|
|
9
8
|
import { isInSplitTestVariant } from '$lib/splitTest'
|
|
10
9
|
import { SplitTest } from '$lib/enums/SplitTest'
|
|
11
10
|
import { mobileBreakpoint } from '$lib/constants'
|
|
12
|
-
import { destroyAllModals, getPreviousModal, goBackToPreviousModal } from '$lib/modal'
|
|
13
11
|
|
|
14
12
|
interface Props {
|
|
15
13
|
children: Snippet
|
|
16
14
|
bubble?: Snippet | null
|
|
17
15
|
prepend?: Snippet | null
|
|
18
16
|
tall?: boolean
|
|
19
|
-
|
|
17
|
+
onclose?: () => void
|
|
20
18
|
onscroll?: () => void
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
const {
|
|
24
|
-
children,
|
|
25
|
-
bubble,
|
|
26
|
-
prepend,
|
|
27
|
-
tall = false,
|
|
28
|
-
closeButtonStyle = 'shadow',
|
|
29
|
-
onscroll = () => null,
|
|
30
|
-
}: Props = $props()
|
|
21
|
+
const { children, bubble, prepend, tall = false, onclose = () => null, onscroll = () => null }: Props = $props()
|
|
31
22
|
|
|
32
23
|
const inlineBubble = isInSplitTestVariant(SplitTest.TopScrollFormat, 1)
|
|
33
24
|
|
|
34
25
|
let windowWidth = $state(0)
|
|
35
26
|
let dialogElement: HTMLElement | null = $state(null)
|
|
36
27
|
let dialogOffset: number = $state(0)
|
|
37
|
-
let hasPreviousModal = $state(false)
|
|
38
28
|
|
|
39
29
|
const isMobile = $derived(windowWidth < mobileBreakpoint)
|
|
40
30
|
|
|
@@ -47,8 +37,6 @@
|
|
|
47
37
|
const baseOverflowStyle = document.body.style.overflowY
|
|
48
38
|
document.body.style.overflowY = 'hidden'
|
|
49
39
|
|
|
50
|
-
hasPreviousModal = !!getPreviousModal()
|
|
51
|
-
|
|
52
40
|
return () => document.body.style.overflowY = baseOverflowStyle || ''
|
|
53
41
|
})
|
|
54
42
|
|
|
@@ -60,7 +48,7 @@
|
|
|
60
48
|
}
|
|
61
49
|
</script>
|
|
62
50
|
|
|
63
|
-
<svelte:window onkeydown={({ key }) => { if (key === 'Escape')
|
|
51
|
+
<svelte:window onkeydown={({ key }) => { if (key === 'Escape') onclose() }} bind:innerWidth={windowWidth} />
|
|
64
52
|
|
|
65
53
|
<div class="modal" style:--dialog-offset="{dialogOffset}px" transition:fade|global={{ duration: 150 }} class:has-bubble={!!bubble && inlineBubble} class:has-prepend={!!prepend}>
|
|
66
54
|
{#if prepend}
|
|
@@ -77,21 +65,13 @@
|
|
|
77
65
|
|
|
78
66
|
{#if isMobile}
|
|
79
67
|
<div class="drag-handle" transition:scaleOrFly|global>
|
|
80
|
-
<DragHandle target={dialogElement!} onpassed={() =>
|
|
68
|
+
<DragHandle target={dialogElement!} onpassed={() => onclose()} />
|
|
81
69
|
</div>
|
|
82
70
|
{/if}
|
|
83
71
|
|
|
84
72
|
<div class="dialog" class:tall {onscroll} bind:this={dialogElement} role="dialog" aria-labelledby="title" transition:scaleOrFly|global={{ y: window.innerHeight }} data-view-transition-new>
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
<RoundButton onclick={() => goBackToPreviousModal()}>
|
|
88
|
-
<IconArrow direction="left" />
|
|
89
|
-
</RoundButton>
|
|
90
|
-
</div>
|
|
91
|
-
{/if}
|
|
92
|
-
|
|
93
|
-
<div class="close {closeButtonStyle}">
|
|
94
|
-
<RoundButton onclick={() => destroyAllModals()}>
|
|
73
|
+
<div class="close">
|
|
74
|
+
<RoundButton onclick={() => onclose()}>
|
|
95
75
|
<IconClose />
|
|
96
76
|
</RoundButton>
|
|
97
77
|
</div>
|
|
@@ -101,7 +81,7 @@
|
|
|
101
81
|
|
|
102
82
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
103
83
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
104
|
-
<div class="backdrop" onclick={() =>
|
|
84
|
+
<div class="backdrop" onclick={() => onclose()}></div>
|
|
105
85
|
</div>
|
|
106
86
|
|
|
107
87
|
<style lang="scss">
|
|
@@ -213,20 +193,6 @@
|
|
|
213
193
|
&:hover {
|
|
214
194
|
filter: brightness(1.1);
|
|
215
195
|
}
|
|
216
|
-
|
|
217
|
-
&.flat {
|
|
218
|
-
--playpilot-button-background: transparent;
|
|
219
|
-
--playpilot-button-shadow: none;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
&.back {
|
|
223
|
-
right: auto;
|
|
224
|
-
left: margin(1);
|
|
225
|
-
|
|
226
|
-
&.flat {
|
|
227
|
-
left: margin(0.5);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
196
|
}
|
|
231
197
|
|
|
232
198
|
.prepend {
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import Disclaimer from './Ads/Disclaimer.svelte'
|
|
2
3
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
3
4
|
import { t } from '$lib/localization'
|
|
4
5
|
import type { PlaylinkData } from '$lib/types/playlink'
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
playlink: PlaylinkData
|
|
9
|
+
disclaimer?: string
|
|
8
10
|
hideCategory?: boolean
|
|
9
11
|
onclick?: () => void
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
const { playlink, hideCategory = false, onclick = () => null }: Props = $props()
|
|
14
|
+
const { playlink, disclaimer = '', hideCategory = false, onclick = () => null }: Props = $props()
|
|
13
15
|
|
|
14
16
|
const { name, url, logo_url, highlighted, cta_text, action_text, extra_info: { category } } = $derived(playlink)
|
|
15
17
|
|
|
@@ -38,6 +40,10 @@
|
|
|
38
40
|
<div class="action">
|
|
39
41
|
{action_text || t('Watch')}
|
|
40
42
|
</div>
|
|
43
|
+
|
|
44
|
+
{#if disclaimer}
|
|
45
|
+
<Disclaimer {disclaimer} small />
|
|
46
|
+
{/if}
|
|
41
47
|
</div>
|
|
42
48
|
</a>
|
|
43
49
|
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
// otherwise break the layout in ways that don't make sense to fix.
|
|
29
29
|
const list = $derived(outerWidth < 500 || !!displayAd)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
// Remove any playlinks without logos, these are likely sub providers.
|
|
32
|
+
const filteredPlaylinks = $derived(playlinks.filter(playlink => !!playlink.logo_url))
|
|
33
|
+
const mergedPlaylink = $derived(mergePlaylinks(filteredPlaylinks))
|
|
32
34
|
|
|
33
35
|
function onclick(playlink: string): void {
|
|
34
36
|
track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.TitlePopoverPlaylinkClick, title, { playlink })
|
|
@@ -50,7 +52,7 @@
|
|
|
50
52
|
|
|
51
53
|
<!-- A fake highlighted playlink as part of the display ad, to be shown after the first playlink -->
|
|
52
54
|
{#if displayAd && (index === 0)}
|
|
53
|
-
<Playlink playlink={campaignToPlaylink(displayAd)} onclick={() => track(TrackingEvent.DisplayedAdPlaylickClick, title, { campaign_name: displayAd.campaign_name })} hideCategory />
|
|
55
|
+
<Playlink playlink={campaignToPlaylink(displayAd)} onclick={() => track(TrackingEvent.DisplayedAdPlaylickClick, title, { campaign_name: displayAd.campaign_name })} hideCategory disclaimer={displayAd.disclaimer || ''} />
|
|
54
56
|
{/if}
|
|
55
57
|
{/each}
|
|
56
58
|
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
import Playlinks from './Playlinks.svelte'
|
|
4
4
|
import Description from './Description.svelte'
|
|
5
5
|
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
6
|
-
import Participants from './Participants.svelte'
|
|
7
6
|
import { t } from '$lib/localization'
|
|
8
7
|
import type { TitleData } from '$lib/types/title'
|
|
9
8
|
import { heading } from '$lib/actions/heading'
|
|
10
9
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
11
|
-
import { participants } from '$lib/fakeData'
|
|
12
10
|
|
|
13
11
|
interface Props {
|
|
14
12
|
title: TitleData
|
|
@@ -60,10 +58,6 @@
|
|
|
60
58
|
|
|
61
59
|
{#if !small}
|
|
62
60
|
<Description text={title.description} blurb={title.blurb} />
|
|
63
|
-
|
|
64
|
-
{#if true || title.participants?.length}
|
|
65
|
-
<Participants participants={participants} />
|
|
66
|
-
{/if}
|
|
67
61
|
{/if}
|
|
68
62
|
</div>
|
|
69
63
|
</div>
|
|
@@ -116,14 +110,14 @@
|
|
|
116
110
|
}
|
|
117
111
|
|
|
118
112
|
&.small {
|
|
119
|
-
font-size: var(--playpilot-detail-font-size
|
|
113
|
+
font-size: var(--playpilot-detail-font-size, 12px);
|
|
120
114
|
line-height: 1.45;
|
|
121
115
|
padding-bottom: margin(0.5);
|
|
122
116
|
}
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
.header {
|
|
126
|
-
padding: margin(
|
|
120
|
+
padding: margin(2) 0 margin(1);
|
|
127
121
|
background: transparent;
|
|
128
122
|
|
|
129
123
|
.small & {
|