@playpilot/tpi 5.12.0-beta.similar.1 → 5.12.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/dist/link-injections.js +10 -11
- package/events.md +1 -0
- package/package.json +1 -2
- package/src/lib/enums/SplitTest.ts +1 -2
- package/src/lib/enums/TrackingEvent.ts +2 -1
- package/src/lib/fakeData.ts +0 -70
- package/src/lib/linkInjection.ts +55 -12
- package/src/lib/playlink.ts +1 -4
- package/src/lib/splitTest.ts +9 -2
- package/src/lib/types/title.d.ts +0 -2
- package/src/routes/+page.svelte +0 -1
- package/src/routes/components/Icons/IconClose.svelte +1 -1
- package/src/routes/components/Icons/IconScrollIndicator.svelte +3 -0
- package/src/routes/components/Modal.svelte +7 -41
- package/src/routes/components/Playlinks.svelte +3 -6
- package/src/routes/components/Popover.svelte +111 -12
- package/src/routes/components/Title.svelte +18 -33
- package/src/routes/components/TitleModal.svelte +3 -2
- package/src/routes/components/TitlePopover.svelte +2 -5
- package/src/tests/lib/linkInjection.test.js +35 -9
- package/src/tests/lib/playlink.test.js +10 -25
- package/src/tests/lib/splitTest.test.js +31 -3
- package/src/tests/routes/components/Modal.test.js +19 -51
- package/src/tests/routes/components/Title.test.js +0 -8
- package/src/tests/setup.js +10 -0
- package/src/lib/modal.ts +0 -81
- 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 -172
- package/src/routes/components/Participant.svelte +0 -88
- package/src/routes/components/ParticipantModal.svelte +0 -30
- package/src/routes/components/PlaylinkIcon.svelte +0 -41
- package/src/routes/components/Rails/ParticipantsRail.svelte +0 -56
- package/src/routes/components/Rails/Rail.svelte +0 -91
- package/src/routes/components/Rails/SimilarRail.svelte +0 -16
- package/src/routes/components/Rails/TitlesRail.svelte +0 -95
- package/src/routes/components/Tabs.svelte +0 -47
- package/src/routes/components/TitlePoster.svelte +0 -30
- package/src/tests/lib/modal.test.js +0 -148
- package/src/tests/routes/components/ListTitle.test.js +0 -82
- package/src/tests/routes/components/PlaylinkIcon.test.js +0 -27
- package/src/tests/routes/components/Rails/ParticipantsRail.test.js +0 -41
- package/src/tests/routes/components/Rails/TitleRail.test.js +0 -38
- package/src/tests/routes/components/TitlePoster.test.js +0 -20
package/events.md
CHANGED
|
@@ -23,6 +23,7 @@ Event | Action | Info | Payload
|
|
|
23
23
|
--- | --- | --- | ---
|
|
24
24
|
`ali_article_page_view` | _Fires any time an article is visited_ | This event will fire right after all data is fetched and will fire regardless of if there are injections or not | It will fire even on pages where injections are disabled. | -
|
|
25
25
|
`ali_links_injected` | _Fires as long as any injections are injected into the article_ | Includes an object with the number of injections for this article with `manual` and `ai` as two separate numbers. | `manual` (number of manual injection), `ai` (number of ai injections)
|
|
26
|
+
`ali_injection_visible` | _Fires as soon as a link is visible on the screen_ | Fires once for each link in an article. | |
|
|
26
27
|
|
|
27
28
|
### Modal
|
|
28
29
|
Event | Action | Info | Payload
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playpilot/tpi",
|
|
3
|
-
"version": "5.12.
|
|
3
|
+
"version": "5.12.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"svelte": "^5.0.0",
|
|
33
33
|
"svelte-check": "^4.0.0",
|
|
34
34
|
"svelte-preprocess": "^6.0.3",
|
|
35
|
-
"svelte-tiny-slider": "^2.2.0",
|
|
36
35
|
"typescript": "^5.0.0",
|
|
37
36
|
"typescript-eslint": "^8.32.1",
|
|
38
37
|
"vite": "^5.0.3",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export const TrackingEvent = Object.freeze({
|
|
3
3
|
ArticlePageView: 'ali_article_page_view',
|
|
4
4
|
ArticleInjected: 'ali_links_injected',
|
|
5
|
+
InjectionVisible: 'ali_injection_visible',
|
|
5
6
|
|
|
6
7
|
TitleModalView: 'ali_title_modal_view',
|
|
7
8
|
TitleModalClose: 'ali_title_modal_close',
|
|
@@ -33,5 +34,5 @@ export const TrackingEvent = Object.freeze({
|
|
|
33
34
|
DisplayedAdPlaylickClick: 'ali_display_ad_playlink_click',
|
|
34
35
|
|
|
35
36
|
SplitTestView: 'ali_split_test_view',
|
|
36
|
-
SplitTestAction: 'ali_split_test_action'
|
|
37
|
+
SplitTestAction: 'ali_split_test_action',
|
|
37
38
|
})
|
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,14 +9,19 @@ 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 {
|
|
12
|
+
import { isEditorialModeEnabled } from './auth'
|
|
13
|
+
import { track } from './tracking'
|
|
14
|
+
import { TrackingEvent } from './enums/TrackingEvent'
|
|
12
15
|
|
|
13
16
|
const keyDataAttribute = 'data-playpilot-injection-key'
|
|
14
17
|
const keySelector = `[${keyDataAttribute}]`
|
|
15
18
|
|
|
19
|
+
const linksIntersectionObserver = typeof window !== 'undefined' ? new IntersectionObserver(trackLinkIntersection) : null
|
|
20
|
+
|
|
16
21
|
let currentlyHoveredInjection: EventTarget | null = null
|
|
17
22
|
let activePopoverInsertedComponent: object | null = null
|
|
18
23
|
let afterArticlePlaylinkInsertedComponent: object | null = null
|
|
24
|
+
let activeModalInsertedComponent: object | null = null
|
|
19
25
|
|
|
20
26
|
/**
|
|
21
27
|
* Return a list of all valid text containing elements that may get injected into.
|
|
@@ -347,7 +353,7 @@ function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
|
347
353
|
|
|
348
354
|
playFallbackViewTransition(() => {
|
|
349
355
|
destroyLinkPopover(false)
|
|
350
|
-
|
|
356
|
+
openLinkModal(event, injection)
|
|
351
357
|
}, !prefersReducedMotion.current && window.innerWidth >= mobileBreakpoint && !window.matchMedia("(pointer: coarse)").matches)
|
|
352
358
|
})
|
|
353
359
|
|
|
@@ -365,7 +371,7 @@ function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
|
365
371
|
|
|
366
372
|
const createdInjectionElements = Array.from(document.querySelectorAll(keySelector)) as HTMLElement[]
|
|
367
373
|
|
|
368
|
-
// Open and close popover on mouseenter/mouseleave
|
|
374
|
+
// Open and close popover on mouseenter/mouseleave and add link to intersection observer
|
|
369
375
|
createdInjectionElements.forEach((injectionElement) => {
|
|
370
376
|
const key = injectionElement.dataset.playpilotInjectionKey
|
|
371
377
|
const injection = injections.find(injection => key === injection.key)
|
|
@@ -376,9 +382,50 @@ function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
|
376
382
|
if (!activePopoverInsertedComponent) openLinkPopover(event, injection)
|
|
377
383
|
})
|
|
378
384
|
injectionElement.addEventListener('mouseleave', () => currentlyHoveredInjection = null)
|
|
385
|
+
|
|
386
|
+
// Add link to observer, this is used to track when a link enters the viewport
|
|
387
|
+
linksIntersectionObserver!.observe(injectionElement)
|
|
388
|
+
})
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Track each link as it enters the viewport. The link is removed from the list as soon as it has been tracked once.
|
|
393
|
+
* Does not fire at all in editorial mode.
|
|
394
|
+
*/
|
|
395
|
+
export function trackLinkIntersection(entries: IntersectionObserverEntry[]): void {
|
|
396
|
+
if (isEditorialModeEnabled()) return
|
|
397
|
+
|
|
398
|
+
entries.forEach(entry => {
|
|
399
|
+
if (!entry.isIntersecting) return
|
|
400
|
+
|
|
401
|
+
track(TrackingEvent.InjectionVisible)
|
|
402
|
+
linksIntersectionObserver!.unobserve(entry.target)
|
|
379
403
|
})
|
|
380
404
|
}
|
|
381
405
|
|
|
406
|
+
/**
|
|
407
|
+
* Open modal for the corresponding injection by mounting the component and saving it to a variable.
|
|
408
|
+
* Ignore clicks that used modifier keys or that were not left click.
|
|
409
|
+
*/
|
|
410
|
+
function openLinkModal(event: MouseEvent, injection: LinkInjection): void {
|
|
411
|
+
if (isHoldingSpecialKey(event)) return
|
|
412
|
+
if (activeModalInsertedComponent) return
|
|
413
|
+
|
|
414
|
+
event.preventDefault()
|
|
415
|
+
|
|
416
|
+
activeModalInsertedComponent = mount(TitleModal, { target: getPlayPilotWrapperElement(), props: { title: injection.title_details!, onclose: destroyLinkModal } })
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Unmount the modal, removing it from the dom
|
|
421
|
+
*/
|
|
422
|
+
function destroyLinkModal(outro: boolean = true): void {
|
|
423
|
+
if (!activeModalInsertedComponent) return
|
|
424
|
+
|
|
425
|
+
unmount(activeModalInsertedComponent, { outro })
|
|
426
|
+
activeModalInsertedComponent = null
|
|
427
|
+
}
|
|
428
|
+
|
|
382
429
|
/**
|
|
383
430
|
* When a link is hovered, it is shown as a popover. The component is mounted when a mouse enters the link,
|
|
384
431
|
* and removed when clicked or on mouseleave.
|
|
@@ -436,13 +483,7 @@ export function insertAfterArticlePlaylinks(elements: HTMLElement[], injections:
|
|
|
436
483
|
target.dataset.playpilotAfterArticlePlaylinks = 'true'
|
|
437
484
|
insertElement.insertAdjacentElement(insertPosition, target)
|
|
438
485
|
|
|
439
|
-
afterArticlePlaylinkInsertedComponent = mount(AfterArticlePlaylinks, {
|
|
440
|
-
target,
|
|
441
|
-
props: {
|
|
442
|
-
linkInjections: injections,
|
|
443
|
-
onclickmodal: (event, injection) => openModal({ event, injection, data: injection.title_details })
|
|
444
|
-
}
|
|
445
|
-
})
|
|
486
|
+
afterArticlePlaylinkInsertedComponent = mount(AfterArticlePlaylinks, { target, props: { linkInjections: injections, onclickmodal: (event, injection) => openLinkModal(event, injection) } })
|
|
446
487
|
}
|
|
447
488
|
|
|
448
489
|
function clearAfterArticlePlaylinks(): void {
|
|
@@ -461,8 +502,10 @@ export function clearLinkInjections(): void {
|
|
|
461
502
|
elements.forEach((element) => clearLinkInjection(element.getAttribute(keyDataAttribute) || ''))
|
|
462
503
|
|
|
463
504
|
clearAfterArticlePlaylinks()
|
|
464
|
-
|
|
505
|
+
destroyLinkModal(false)
|
|
465
506
|
destroyLinkPopover(false)
|
|
507
|
+
|
|
508
|
+
linksIntersectionObserver?.disconnect()
|
|
466
509
|
}
|
|
467
510
|
|
|
468
511
|
/**
|
|
@@ -568,6 +611,6 @@ export function isEquivalentInjection(injection1: LinkInjection, injection2: Lin
|
|
|
568
611
|
return injection1.title === injection2.title && cleanPhrase(injection1.sentence) === cleanPhrase(injection2.sentence)
|
|
569
612
|
}
|
|
570
613
|
|
|
571
|
-
|
|
614
|
+
function getPlayPilotWrapperElement(): Element {
|
|
572
615
|
return document.querySelector('[data-playpilot-link-injections]') || document.body
|
|
573
616
|
}
|
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/splitTest.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { track } from "./tracking"
|
|
|
4
4
|
type SplitTest = {
|
|
5
5
|
key: string
|
|
6
6
|
numberOfVariants: number
|
|
7
|
+
variantNames?: string[]
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -28,18 +29,24 @@ export function getSplitTestVariantIndex(test: SplitTest): number {
|
|
|
28
29
|
return Math.floor(identifier * test.numberOfVariants)
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
export function getSplitTestVariantName(test: SplitTest): string {
|
|
33
|
+
const variantIndex = getSplitTestVariantIndex(test)
|
|
34
|
+
|
|
35
|
+
return test.variantNames?.[variantIndex] || variantIndex.toString()
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
export function isInSplitTestVariant(test: SplitTest, variant = 1): boolean {
|
|
32
39
|
return getSplitTestVariantIndex(test) === variant
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
export function trackSplitTestView(test: SplitTest): void {
|
|
36
|
-
const variant =
|
|
43
|
+
const variant = getSplitTestVariantName(test)
|
|
37
44
|
|
|
38
45
|
track(TrackingEvent.SplitTestView, null, { key: test.key, variant })
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
export function trackSplitTestAction(test: SplitTest, action: string): void {
|
|
42
|
-
const variant =
|
|
49
|
+
const variant = getSplitTestVariantName(test)
|
|
43
50
|
|
|
44
51
|
track(TrackingEvent.SplitTestAction, null, { key: test.key, variant, action })
|
|
45
52
|
}
|
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([])
|
|
@@ -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>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg viewBox="0 -960 960 960" width="24px" height="24px" fill="currentColor">
|
|
2
|
+
<path d="m480-320 160-160-56-56-64 64v-168h-80v168l-64-64-56 56 160 160Zm0 240q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
|
|
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()} aria-label="Back">
|
|
88
|
-
<IconArrow direction="left" />
|
|
89
|
-
</RoundButton>
|
|
90
|
-
</div>
|
|
91
|
-
{/if}
|
|
92
|
-
|
|
93
|
-
<div class="close {closeButtonStyle}">
|
|
94
|
-
<RoundButton onclick={() => destroyAllModals()} aria-label="Close">
|
|
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 {
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import { heading } from '$lib/actions/heading'
|
|
9
9
|
import { getContext } from 'svelte'
|
|
10
10
|
import Playlink from './Playlink.svelte'
|
|
11
|
-
import Display from './Ads/Display.svelte'
|
|
12
11
|
import { campaignToPlaylink, getFirstAdOfType } from '$lib/ads'
|
|
13
12
|
|
|
14
13
|
interface Props {
|
|
@@ -28,7 +27,9 @@
|
|
|
28
27
|
// otherwise break the layout in ways that don't make sense to fix.
|
|
29
28
|
const list = $derived(outerWidth < 500 || !!displayAd)
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
// Remove any playlinks without logos, these are likely sub providers.
|
|
31
|
+
const filteredPlaylinks = $derived(playlinks.filter(playlink => !!playlink.logo_url))
|
|
32
|
+
const mergedPlaylink = $derived(mergePlaylinks(filteredPlaylinks))
|
|
32
33
|
|
|
33
34
|
function onclick(playlink: string): void {
|
|
34
35
|
track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.TitlePopoverPlaylinkClick, title, { playlink })
|
|
@@ -58,10 +59,6 @@
|
|
|
58
59
|
<div class="empty" data-testid="playlinks-empty">
|
|
59
60
|
{t('Title Unavailable')}
|
|
60
61
|
</div>
|
|
61
|
-
|
|
62
|
-
{#if displayAd}
|
|
63
|
-
<Display campaign={displayAd} compact={!isModal} />
|
|
64
|
-
{/if}
|
|
65
62
|
{/if}
|
|
66
63
|
</div>
|
|
67
64
|
|