@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
|
@@ -1,63 +1,57 @@
|
|
|
1
1
|
import { render, fireEvent } from '@testing-library/svelte'
|
|
2
|
-
import {
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import Modal from '../../../routes/components/Modal.svelte'
|
|
5
5
|
import { createRawSnippet } from 'svelte'
|
|
6
|
-
import { destroyAllModals, getPreviousModal, goBackToPreviousModal } from '$lib/modal'
|
|
7
|
-
|
|
8
|
-
vi.mock('$lib/modal', () => ({
|
|
9
|
-
destroyAllModals: vi.fn(),
|
|
10
|
-
getPreviousModal: vi.fn(),
|
|
11
|
-
goBackToPreviousModal: vi.fn(),
|
|
12
|
-
}))
|
|
13
6
|
|
|
14
7
|
describe('Modal.svelte', () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
vi.resetAllMocks()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
8
|
const children = createRawSnippet(() => ({ render: () => '<div></div>' }))
|
|
20
9
|
|
|
21
|
-
it('Should fire given
|
|
22
|
-
const
|
|
10
|
+
it('Should fire given onclose function when backdrop is clicked', async () => {
|
|
11
|
+
const onclose = vi.fn()
|
|
12
|
+
const { container } = render(Modal, { children, onclose })
|
|
23
13
|
|
|
24
14
|
await fireEvent.click(/** @type {Node} */ (container.querySelector('.backdrop')))
|
|
25
15
|
|
|
26
|
-
expect(
|
|
16
|
+
expect(onclose).toHaveBeenCalled()
|
|
27
17
|
})
|
|
28
18
|
|
|
29
|
-
it('Should fire
|
|
30
|
-
const
|
|
19
|
+
it('Should fire given onclose function when close button is clicked', async () => {
|
|
20
|
+
const onclose = vi.fn()
|
|
21
|
+
const { container } = render(Modal, { children, onclose })
|
|
31
22
|
|
|
32
23
|
await fireEvent.click(/** @type {Node} */ (container.querySelector('button')))
|
|
33
24
|
|
|
34
|
-
expect(
|
|
25
|
+
expect(onclose).toHaveBeenCalled()
|
|
35
26
|
})
|
|
36
27
|
|
|
37
28
|
it('Should not fire given onclose function when dialog is clicked', async () => {
|
|
38
|
-
const
|
|
29
|
+
const onclose = vi.fn()
|
|
30
|
+
const { container } = render(Modal, { children, onclose })
|
|
39
31
|
|
|
40
32
|
await fireEvent.click(/** @type {Node} */ (container.querySelector('.dialog')))
|
|
41
33
|
|
|
42
|
-
expect(
|
|
34
|
+
expect(onclose).not.toHaveBeenCalled()
|
|
43
35
|
})
|
|
44
36
|
|
|
45
|
-
it('Should fire given
|
|
46
|
-
|
|
37
|
+
it('Should fire given onclose function when escape key is pressed', async () => {
|
|
38
|
+
const onclose = vi.fn()
|
|
39
|
+
render(Modal, { children, onclose })
|
|
47
40
|
|
|
48
41
|
fireEvent.keyDown(window, { key: 'Escape' })
|
|
49
42
|
|
|
50
|
-
expect(
|
|
43
|
+
expect(onclose).toHaveBeenCalled()
|
|
51
44
|
})
|
|
52
45
|
|
|
53
46
|
it('Should not fire given onclose function when key other than escape key is pressed', async () => {
|
|
54
|
-
|
|
47
|
+
const onclose = vi.fn()
|
|
48
|
+
render(Modal, { children, onclose })
|
|
55
49
|
|
|
56
50
|
fireEvent.keyDown(window, { key: 'a' })
|
|
57
51
|
fireEvent.keyDown(window, { key: 'Enter' })
|
|
58
52
|
fireEvent.keyDown(window, { key: 'Space' })
|
|
59
53
|
|
|
60
|
-
expect(
|
|
54
|
+
expect(onclose).not.toHaveBeenCalled()
|
|
61
55
|
})
|
|
62
56
|
|
|
63
57
|
it('Should fire given onscroll function when dialog is scrolled', async () => {
|
|
@@ -102,30 +96,4 @@ describe('Modal.svelte', () => {
|
|
|
102
96
|
|
|
103
97
|
expect(getByRole('dialog').classList).toContain('tall')
|
|
104
98
|
})
|
|
105
|
-
|
|
106
|
-
it('Should not show back button by default', () => {
|
|
107
|
-
const { queryByLabelText } = render(Modal, { children })
|
|
108
|
-
|
|
109
|
-
expect(queryByLabelText('Back')).not.toBeTruthy()
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('Should show back button if previous modal was opened', () => {
|
|
113
|
-
// @ts-ignore
|
|
114
|
-
vi.mocked(getPreviousModal).mockReturnValueOnce(true)
|
|
115
|
-
|
|
116
|
-
const { getByLabelText } = render(Modal, { children })
|
|
117
|
-
|
|
118
|
-
expect(getByLabelText('Back')).toBeTruthy()
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('Should fire goBackToPreviousModal when back button is clicked', async () => {
|
|
122
|
-
// @ts-ignore
|
|
123
|
-
vi.mocked(getPreviousModal).mockReturnValueOnce(true)
|
|
124
|
-
|
|
125
|
-
const { getByLabelText } = render(Modal, { children })
|
|
126
|
-
|
|
127
|
-
await fireEvent.click(getByLabelText('Back'))
|
|
128
|
-
|
|
129
|
-
expect(goBackToPreviousModal).toHaveBeenCalled()
|
|
130
|
-
})
|
|
131
99
|
})
|
|
@@ -68,12 +68,4 @@ describe('Title.svelte', () => {
|
|
|
68
68
|
|
|
69
69
|
expect(queryByText('Some description')).not.toBeTruthy()
|
|
70
70
|
})
|
|
71
|
-
|
|
72
|
-
it('Should render as compacter version when compact prop is true', () => {
|
|
73
|
-
const { queryByAltText, container } = render(Title, { title: { ...title, description: 'Some description' }, compact: true })
|
|
74
|
-
|
|
75
|
-
expect(queryByAltText(`Movie poster for '${title.title}'`)).not.toBeTruthy()
|
|
76
|
-
expect(container.querySelector('.compact')).toBeTruthy()
|
|
77
|
-
expect(container.querySelector('.faded')).toBeTruthy()
|
|
78
|
-
})
|
|
79
71
|
})
|
package/src/tests/setup.js
CHANGED
|
@@ -56,9 +56,19 @@ const mockSessionStorage = () => {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const mockIntersectionObserver = () => {
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
global.IntersectionObserver = vi.fn(() => ({
|
|
62
|
+
disconnect: vi.fn(),
|
|
63
|
+
observe: vi.fn(),
|
|
64
|
+
unobserve: vi.fn(),
|
|
65
|
+
}))
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
beforeAll(() => {
|
|
60
69
|
mockLocalStorage()
|
|
61
70
|
mockSessionStorage()
|
|
71
|
+
mockIntersectionObserver()
|
|
62
72
|
})
|
|
63
73
|
|
|
64
74
|
beforeEach(() => {
|
package/src/lib/modal.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { mount, unmount } from "svelte"
|
|
2
|
-
import { isHoldingSpecialKey } from "./event"
|
|
3
|
-
import TitleModal from "../routes/components/TitleModal.svelte"
|
|
4
|
-
import type { LinkInjection } from "./types/injection"
|
|
5
|
-
import { getPlayPilotWrapperElement } from "./linkInjection"
|
|
6
|
-
import ParticipantModal from "../routes/components/ParticipantModal.svelte"
|
|
7
|
-
import type { TitleData } from "./types/title"
|
|
8
|
-
import type { ParticipantData } from "./types/participant"
|
|
9
|
-
|
|
10
|
-
type ModalType = 'title' | 'participant'
|
|
11
|
-
|
|
12
|
-
type Modal = {
|
|
13
|
-
injection?: LinkInjection | null
|
|
14
|
-
component: object
|
|
15
|
-
type: ModalType
|
|
16
|
-
data: TitleData | ParticipantData | null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const modals: Modal[] = []
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Open modal for the corresponding injection by mounting the component and saving it to a variable.
|
|
23
|
-
* Ignore clicks that used modifier keys or that were not left click.
|
|
24
|
-
*/
|
|
25
|
-
export function openModal(
|
|
26
|
-
{ type = 'title', event = null, injection = null, data = null }:
|
|
27
|
-
{ type?: ModalType, event?: MouseEvent | null, injection?: LinkInjection | null, data?: TitleData | ParticipantData | null, push?: boolean } = {}): void {
|
|
28
|
-
if (event && isHoldingSpecialKey(event)) return
|
|
29
|
-
|
|
30
|
-
event?.preventDefault()
|
|
31
|
-
|
|
32
|
-
if (modals?.length) closeCurrentModal()
|
|
33
|
-
|
|
34
|
-
const target = getPlayPilotWrapperElement()
|
|
35
|
-
const component = type === 'title' ?
|
|
36
|
-
mount(TitleModal, { target, props: { title: data as TitleData } }) :
|
|
37
|
-
mount(ParticipantModal, { target, props: { participant: data as ParticipantData } })
|
|
38
|
-
|
|
39
|
-
modals.push({ type, injection, data, component })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Unmount the last modal is the list of modals but keep it in the list of active modals */
|
|
43
|
-
export function closeCurrentModal(outro: boolean = true): void {
|
|
44
|
-
if (!modals.length) return
|
|
45
|
-
|
|
46
|
-
unmount(modals[modals.length - 1].component, { outro })
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Unmount the last modal is the list of modals and remove it from the list */
|
|
50
|
-
export function destroyCurrentModal(outro: boolean = true): void {
|
|
51
|
-
closeCurrentModal(outro)
|
|
52
|
-
|
|
53
|
-
modals.pop()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function destroyAllModals(outro: boolean = true): void {
|
|
57
|
-
closeCurrentModal(outro)
|
|
58
|
-
|
|
59
|
-
while (modals.length > 0) modals.pop()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** @returns The modal before the currently active modal, if any */
|
|
63
|
-
export function getPreviousModal(): Modal | null {
|
|
64
|
-
return modals[modals.length - 2] || null
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function goBackToPreviousModal(): void {
|
|
68
|
-
if (modals.length < 2) return
|
|
69
|
-
|
|
70
|
-
destroyCurrentModal()
|
|
71
|
-
|
|
72
|
-
// Get the previous modal from the array by removing it, we're re-adding it when calling openModal
|
|
73
|
-
const previousModal = modals.pop()
|
|
74
|
-
if (!previousModal) return
|
|
75
|
-
|
|
76
|
-
openModal({ ...previousModal })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getAllModals(): Modal[] {
|
|
80
|
-
return modals
|
|
81
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export type ParticipantData = {
|
|
2
|
-
sid: string
|
|
3
|
-
name: string
|
|
4
|
-
birth_date: string
|
|
5
|
-
death_date: string | null
|
|
6
|
-
jobs: Job[]
|
|
7
|
-
image: string | null
|
|
8
|
-
image_uuid: string | null
|
|
9
|
-
gender: string
|
|
10
|
-
character?: string | null
|
|
11
|
-
bio?: string | null
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type Job = 'actor' | 'writer' | 'director'
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
interface Props {
|
|
3
|
-
direction?: 'left' | 'right'
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const { direction = 'right' }: Props = $props()
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<svg width="10" height="18" viewBox="0 0 10 18" class="{direction}">
|
|
10
|
-
<path d="M94.55,59.274a1.517,1.517,0,0,0,0-2.157,1.535,1.535,0,0,0-2.157,0L87,62.509l-5.4-5.392a1.525,1.525,0,0,0-2.157,2.157l6.48,6.48a1.535,1.535,0,0,0,2.157,0Z" transform="translate(-56.054 95.5) rotate(-90)" fill="currentColor" stroke-width="1"/>
|
|
11
|
-
</svg>
|
|
12
|
-
|
|
13
|
-
<style lang="scss">
|
|
14
|
-
.left {
|
|
15
|
-
transform: rotate(180deg);
|
|
16
|
-
}
|
|
17
|
-
</style>
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { playPilotBaseUrl } from '$lib/constants'
|
|
3
|
-
import { t } from '$lib/localization'
|
|
4
|
-
import { mergePlaylinks } from '$lib/playlink'
|
|
5
|
-
import type { TitleData } from '$lib/types/title'
|
|
6
|
-
import IconArrow from './Icons/IconArrow.svelte'
|
|
7
|
-
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
8
|
-
import PlaylinkIcon from './PlaylinkIcon.svelte'
|
|
9
|
-
import TitlePoster from './TitlePoster.svelte'
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
title: TitleData
|
|
13
|
-
// eslint-disable-next-line no-unused-vars
|
|
14
|
-
onclick?: (event: MouseEvent) => void
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const { title, onclick = () => null }: Props = $props()
|
|
18
|
-
|
|
19
|
-
const playlinks = $derived(mergePlaylinks(title.providers))
|
|
20
|
-
const limitedPlaylinks = $derived(playlinks.slice(0, 3))
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<a class="title" href="{playPilotBaseUrl}/{title.type}/{title.slug}" {onclick}>
|
|
24
|
-
<div class="poster">
|
|
25
|
-
<TitlePoster {title} width={28} height={42} />
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<div class="content">
|
|
29
|
-
<div class="heading">{title.title}</div>
|
|
30
|
-
|
|
31
|
-
<div class="meta">
|
|
32
|
-
<div class="imdb">
|
|
33
|
-
<IconIMDb />
|
|
34
|
-
{title.imdb_score}
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<div>{title.year}</div>
|
|
38
|
-
<div>{title.type}</div>
|
|
39
|
-
|
|
40
|
-
{#if title.length}
|
|
41
|
-
<div>{title.length} min</div>
|
|
42
|
-
{/if}
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<div class="playlinks">
|
|
46
|
-
{#each limitedPlaylinks as playlink}
|
|
47
|
-
<PlaylinkIcon {playlink} onclick={(event) => event.stopPropagation()} />
|
|
48
|
-
{/each}
|
|
49
|
-
|
|
50
|
-
{#if playlinks.length > limitedPlaylinks.length}
|
|
51
|
-
<span class="more">
|
|
52
|
-
+{playlinks.length - limitedPlaylinks.length}
|
|
53
|
-
</span>
|
|
54
|
-
{/if}
|
|
55
|
-
|
|
56
|
-
{#if !playlinks.length}
|
|
57
|
-
<div class="empty" data-testid="playlinks-empty">
|
|
58
|
-
{t('Title Unavailable')}
|
|
59
|
-
</div>
|
|
60
|
-
{/if}
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div class="action">
|
|
65
|
-
<IconArrow />
|
|
66
|
-
</div>
|
|
67
|
-
</a>
|
|
68
|
-
|
|
69
|
-
<style lang="scss">
|
|
70
|
-
.title {
|
|
71
|
-
appearance: none;
|
|
72
|
-
display: flex;
|
|
73
|
-
gap: margin(1);
|
|
74
|
-
width: 100%;
|
|
75
|
-
background: var(--playpilot-list-item-background, var(--playpilot-lighter));
|
|
76
|
-
padding: margin(0.5);
|
|
77
|
-
border: 0;
|
|
78
|
-
border-radius: var(--playpilot-list-item-border-radius, margin(0.5));
|
|
79
|
-
text-decoration: none;
|
|
80
|
-
font-style: normal !important;
|
|
81
|
-
|
|
82
|
-
&:hover {
|
|
83
|
-
background: var(--playpilot-list-item-hover-background, var(--playpilot-content));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
&:last-child {
|
|
87
|
-
border-bottom: 0;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.poster {
|
|
92
|
-
flex: 0 0 auto;
|
|
93
|
-
height: auto;
|
|
94
|
-
align-self: start;
|
|
95
|
-
width: margin(4);
|
|
96
|
-
aspect-ratio: 28/42;
|
|
97
|
-
border-radius: var(--playpilot-detail-image-border-radius, margin(0.5));
|
|
98
|
-
background: var(--playpilot-detail-image-background, var(--playpilot-content));
|
|
99
|
-
overflow: hidden;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.content {
|
|
103
|
-
display: flex;
|
|
104
|
-
flex-direction: column;
|
|
105
|
-
font-family: inherit;
|
|
106
|
-
text-align: left;
|
|
107
|
-
color: var(--playpilot-list-item-text-color, var(--playpilot-text-color));
|
|
108
|
-
font-size: var(--playpilot-detail-font-size, 14px);
|
|
109
|
-
line-height: normal;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.heading {
|
|
113
|
-
color: var(--playpilot-list-item-title-text-color, var(--playpilot-detail-title-text-color, var(--playpilot-text-color)));
|
|
114
|
-
font-weight: var(--playpilot-list-item-title-font-weight, var(--playpilot-detail-title-font-weight, 500));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.meta {
|
|
118
|
-
display: flex;
|
|
119
|
-
flex-wrap: wrap;
|
|
120
|
-
gap: 0 margin(0.5);
|
|
121
|
-
margin: margin(0.25) 0 margin(0.5);
|
|
122
|
-
font-size: var(--playpilot-detail-font-size-small, 12px);
|
|
123
|
-
font-weight: var(--playpilot-list-item-meta-font-weight, var(--playpilot-detail-title-font-weight, 500));
|
|
124
|
-
color: var(--playpilot-list-item-meta-text-color, var(--playpilot-text-color-alt));
|
|
125
|
-
|
|
126
|
-
> div {
|
|
127
|
-
text-transform: capitalize;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.imdb {
|
|
132
|
-
display: flex;
|
|
133
|
-
align-items: center;
|
|
134
|
-
gap: margin(0.25);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.playlinks {
|
|
138
|
-
display: flex;
|
|
139
|
-
flex-wrap: wrap;
|
|
140
|
-
gap: margin(0.25);
|
|
141
|
-
margin-top: auto;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.more {
|
|
145
|
-
display: flex;
|
|
146
|
-
align-items: center;
|
|
147
|
-
padding: margin(0.25);
|
|
148
|
-
color: var(--playpilot-list-item-more-text-color, var(--playpilot-text-color-alt));
|
|
149
|
-
font-size: var(--playpilot-detail-font-size-small, 12px);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.empty {
|
|
153
|
-
margin-top: margin(0.5);
|
|
154
|
-
opacity: 0.65;
|
|
155
|
-
font-style: italic;
|
|
156
|
-
font-size: 0.85em;
|
|
157
|
-
white-space: initial;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.action {
|
|
161
|
-
display: flex;
|
|
162
|
-
margin: 0 margin(0.5) 0 auto;
|
|
163
|
-
padding-left: margin(0.5);
|
|
164
|
-
align-self: center;
|
|
165
|
-
color: var(--playpilot-list-item-action-color, var(--playpilot-detail-text-color, var(--playpilot-text-color-alt)));
|
|
166
|
-
|
|
167
|
-
&:hover,
|
|
168
|
-
&:active {
|
|
169
|
-
color: var(--playpilot-list-item-action-hover-color, var(--playpilot-detail-text-color, var(--playpilot-text-color)));
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
</style>
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { heading } from '$lib/actions/heading'
|
|
3
|
-
import { openModal } from '$lib/modal'
|
|
4
|
-
import type { ParticipantData } from '$lib/types/participant'
|
|
5
|
-
import ListTitle from './ListTitle.svelte'
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
participant: ParticipantData
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const { participant }: Props = $props()
|
|
12
|
-
|
|
13
|
-
const { name, birth_date, death_date } = $derived(participant)
|
|
14
|
-
|
|
15
|
-
function formatDate(dateString: string): string {
|
|
16
|
-
const date = new Date(dateString)
|
|
17
|
-
return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
18
|
-
}
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<div class="header">
|
|
22
|
-
<div class="heading" use:heading={2} id="name">{name}</div>
|
|
23
|
-
|
|
24
|
-
{#if birth_date}
|
|
25
|
-
<p class="dates">
|
|
26
|
-
Born on <strong>{formatDate(birth_date)}</strong>{#if death_date}, died on <strong>{formatDate(death_date)}</strong>{/if}
|
|
27
|
-
</p>
|
|
28
|
-
{/if}
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<div class="content">
|
|
32
|
-
<div class="heading small" use:heading={3} id="credits">Credits</div>
|
|
33
|
-
|
|
34
|
-
<div class="list">
|
|
35
|
-
{#each window.PlayPilotLinkInjections?.evaluated_link_injections?.map(i => i.title_details) || [] as title}
|
|
36
|
-
{#if title}
|
|
37
|
-
<ListTitle {title} onclick={(event) => openModal({ event, data: title })} />
|
|
38
|
-
{/if}
|
|
39
|
-
{/each}
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<style lang="scss">
|
|
44
|
-
.header {
|
|
45
|
-
padding: margin(4) margin(1) margin(2);
|
|
46
|
-
background: linear-gradient(to bottom, var(--playpilot-detail-background-light, var(--playpilot-lighter)), transparent);
|
|
47
|
-
border-radius: var(--playpilot-detail-border-radius, margin(1) margin(1) 0 0);
|
|
48
|
-
font-family: var(--playpilot-detail-font-family, var(--playpilot-font-family));
|
|
49
|
-
font-weight: var(--playpilot-detail-font-weight, normal);
|
|
50
|
-
font-size: var(--playpilot-detail-font-size, 14px);
|
|
51
|
-
line-height: var(--playpilot-participant-description-line-height, normal);
|
|
52
|
-
color: var(--playpilot-detail-text-color, var(--playpilot-text-color));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.heading {
|
|
56
|
-
margin: 0;
|
|
57
|
-
font-family: var(--playpilot-detail-title-font-family, var(--playpilot-font-family));
|
|
58
|
-
font-weight: var(--playpilot-detail-title-font-weight, lighter);
|
|
59
|
-
font-size: var(--playpilot-detail-title-font-size, margin(1.5));
|
|
60
|
-
line-height: normal;
|
|
61
|
-
font-style: var(--playpilot-detail-title-font-style, normal);
|
|
62
|
-
|
|
63
|
-
&.small {
|
|
64
|
-
margin: 0 0 margin(0.5);
|
|
65
|
-
font-size: var(--playpilot-detail-title-small-font-size, margin(1.25));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.dates {
|
|
70
|
-
margin: margin(0.5) 0 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.content {
|
|
74
|
-
padding: 0 margin(1) margin(1);
|
|
75
|
-
color: var(--playpilot-detail-text-color, var(--playpilot-text-color));
|
|
76
|
-
font-family: var(--playpilot-detail-font-family, var(--playpilot-font-family));
|
|
77
|
-
font-weight: var(--playpilot-detail-font-weight, normal);
|
|
78
|
-
font-size: var(--playpilot-detail-font-size, 14px);
|
|
79
|
-
line-height: normal;
|
|
80
|
-
font-style: normal;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.list {
|
|
84
|
-
display: flex;
|
|
85
|
-
flex-direction: column;
|
|
86
|
-
gap: margin(0.5);
|
|
87
|
-
}
|
|
88
|
-
</style>
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
import Modal from './Modal.svelte'
|
|
4
|
-
import Participant from './Participant.svelte'
|
|
5
|
-
import type { ParticipantData } from '$lib/types/participant'
|
|
6
|
-
|
|
7
|
-
interface Props {
|
|
8
|
-
participant: ParticipantData
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const { participant = {
|
|
12
|
-
sid: 'pr5C5W',
|
|
13
|
-
name: 'James Franco',
|
|
14
|
-
birth_date: '1978-04-19',
|
|
15
|
-
death_date: null,
|
|
16
|
-
jobs: ['actor'],
|
|
17
|
-
image: 'https://hips.hearstapps.com/hmg-prod/images/gettyimages-161098947-square.jpg',
|
|
18
|
-
image_uuid: '08ed2fac357011eb87470aff12c0f5c9',
|
|
19
|
-
gender: 'Male',
|
|
20
|
-
bio: 'Aenean feugiat nec odio at venenatis. Integer porta neque metus, a sollicitudin dolor dapibus et. In sollicitudin nulla eget ultricies porttitor. Nulla facilisi. Sed turpis orci, facilisis efficitur neque in, ultrices ultricies purus. Mauris nec augue a nisi imperdiet semper ut nec tellus. Donec at tristique odio. Etiam luctus eget metus non mattis. Integer imperdiet in elit eu varius. Donec ornare, nibh vitae accumsan consequat, lacus nulla elementum sapien, a scelerisque tellus augue ac erat. Aenean finibus fringilla magna, ac laoreet nisl convallis vel. Proin laoreet ex ac augue maximus, nec gravida tortor pharetra.\nCurabitur maximus dui sed risus placerat pharetra vitae ut orci. Proin sodales enim a elit euismod, a varius sem suscipit. Vivamus eu magna cursus, fringilla est in, mollis nunc. Mauris fringilla eleifend nibh, eget auctor lectus bibendum non. Praesent sed elit ipsum. Donec nunc dolor, sagittis hendrerit gravida et, lacinia sed metus. Morbi tempus mi massa. In hac habitasse platea dictumst. Suspendisse aliquet tincidunt lectus ut elementum.',
|
|
21
|
-
} }: Props = $props()
|
|
22
|
-
|
|
23
|
-
let windowWidth = $state(0)
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<svelte:window bind:innerWidth={windowWidth} />
|
|
27
|
-
|
|
28
|
-
<Modal closeButtonStyle="flat">
|
|
29
|
-
<Participant {participant} />
|
|
30
|
-
</Modal>
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { removeImageUrlPrefix } from '$lib/image'
|
|
3
|
-
import type { PlaylinkData } from '$lib/types/playlink'
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
playlink: PlaylinkData
|
|
7
|
-
size?: number
|
|
8
|
-
// eslint-disable-next-line no-unused-vars
|
|
9
|
-
onclick?: (event: MouseEvent) => void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const { playlink, size = 40, onclick = () => null }: Props = $props()
|
|
13
|
-
|
|
14
|
-
const { name, url, logo_url } = $derived(playlink)
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<a href={url} target="_blank" class="playlink" data-playlink={name} rel="sponsored" aria-label={name} {onclick}>
|
|
18
|
-
<img src={removeImageUrlPrefix(logo_url)} alt="" height={size} width={size} />
|
|
19
|
-
</a>
|
|
20
|
-
|
|
21
|
-
<style lang="scss">
|
|
22
|
-
.playlink {
|
|
23
|
-
display: inline-block;
|
|
24
|
-
background: var(--playpilot-playlink-background, var(--playpilot-light));
|
|
25
|
-
border-radius: var(--playpilot-playlink-border-radius, margin(0.5));
|
|
26
|
-
overflow: hidden;
|
|
27
|
-
|
|
28
|
-
&:hover,
|
|
29
|
-
&:active {
|
|
30
|
-
filter: var(--playpilot-playlink-hover-filter, brightness(1.1));
|
|
31
|
-
background: var(--playpilot-playlink-hover-background, var(--playpilot-playlink-background, var(--playpilot-lighter))) !important;
|
|
32
|
-
text-decoration: none !important;
|
|
33
|
-
outline: 2px solid var(--playpilot-detail-text-color, var(--playpilot-text-color));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
img {
|
|
37
|
-
display: block;
|
|
38
|
-
margin: 0;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
</style>
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { openModal } from '$lib/modal'
|
|
3
|
-
import type { ParticipantData } from '$lib/types/participant'
|
|
4
|
-
import Rail from './Rail.svelte'
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
participants: ParticipantData[]
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const { participants }: Props = $props()
|
|
11
|
-
</script>
|
|
12
|
-
|
|
13
|
-
<Rail heading="Cast">
|
|
14
|
-
{#each participants.slice(0, 15) as participant}
|
|
15
|
-
<button class="participant" onclick={event => openModal({ event, type: 'participant', data: participant })}>
|
|
16
|
-
<span class="truncate">{participant.name}</span>
|
|
17
|
-
|
|
18
|
-
<div class="character truncate">{participant.character}</div>
|
|
19
|
-
</button>
|
|
20
|
-
{/each}
|
|
21
|
-
</Rail>
|
|
22
|
-
|
|
23
|
-
<style lang="scss">
|
|
24
|
-
.participant {
|
|
25
|
-
display: block;
|
|
26
|
-
flex: 0 0 10rem;
|
|
27
|
-
width: 10rem;
|
|
28
|
-
padding: margin(0.5);
|
|
29
|
-
border: 0;
|
|
30
|
-
border-radius: var(--playpilot-cast-border-radius, var(--playpilot-playlink-border-radius, margin(0.5)));
|
|
31
|
-
background: var(--playpilot-cast-background, var(--playpilot-playlink-background, var(--playpilot-lighter)));
|
|
32
|
-
cursor: pointer;
|
|
33
|
-
font-family: inherit;
|
|
34
|
-
text-align: left;
|
|
35
|
-
color: inherit;
|
|
36
|
-
font-size: var(--playpilot-cast-font-size, var(--playpilot-playlinks-font-size, margin(0.75)));
|
|
37
|
-
white-space: nowrap;
|
|
38
|
-
|
|
39
|
-
&:hover,
|
|
40
|
-
&:active {
|
|
41
|
-
filter: var(--playpilot-cast-hover-filter, var(--playpilot-playlink-hover-filter, brightness(1.1)));
|
|
42
|
-
text-decoration: none !important;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.character {
|
|
47
|
-
color: var(--playpilot-cast-character-text-color, var(--playpilot-text-color-alt));
|
|
48
|
-
font-style: italic;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.truncate {
|
|
52
|
-
overflow: hidden;
|
|
53
|
-
text-overflow: ellipsis;
|
|
54
|
-
white-space: nowrap;
|
|
55
|
-
}
|
|
56
|
-
</style>
|