@playpilot/tpi 2.0.5 → 3.0.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 +2 -2
- package/eslint.config.js +20 -0
- package/package.json +5 -2
- package/src/lib/{api.js → api.ts} +26 -38
- package/src/lib/{array.js → array.ts} +1 -3
- package/src/lib/{auth.js → auth.ts} +8 -10
- package/src/lib/{fakeData.js → fakeData.ts} +7 -6
- package/src/lib/{hash.js → hash.ts} +2 -3
- package/src/lib/{html.js → html.ts} +2 -4
- package/src/lib/{linkInjection.js → linkInjection.ts} +41 -85
- package/src/lib/{localization.js → localization.ts} +10 -12
- package/src/lib/{meta.js → meta.ts} +8 -18
- package/src/lib/{playlink.js → playlink.ts} +4 -5
- package/src/lib/{search.js → search.ts} +2 -3
- package/src/lib/{text.js → text.ts} +7 -19
- package/src/lib/{tracking.js → tracking.ts} +6 -4
- package/src/lib/types/global.d.ts +13 -0
- package/src/lib/types/injection.d.ts +41 -0
- package/src/lib/types/language.d.ts +1 -0
- package/src/lib/types/participant.d.ts +13 -0
- package/src/lib/types/playlink.d.ts +11 -0
- package/src/lib/types/position.d.ts +6 -0
- package/src/lib/types/title.d.ts +23 -0
- package/src/lib/types/user.d.ts +7 -0
- package/src/lib/{url.js → url.ts} +1 -2
- package/src/routes/+layout.svelte +3 -3
- package/src/routes/+page.svelte +12 -17
- package/src/routes/components/AfterArticlePlaylinks.svelte +14 -13
- package/src/routes/components/ContextMenu.svelte +11 -7
- package/src/routes/components/Description.svelte +9 -4
- package/src/routes/components/Editorial/AIIndicator.svelte +10 -7
- package/src/routes/components/Editorial/Alert.svelte +8 -3
- package/src/routes/components/Editorial/DragHandle.svelte +18 -26
- package/src/routes/components/Editorial/Editor.svelte +24 -23
- package/src/routes/components/Editorial/EditorItem.svelte +20 -18
- package/src/routes/components/Editorial/ManualInjection.svelte +16 -17
- package/src/routes/components/Editorial/PlaylinkTypeSelect.svelte +8 -4
- package/src/routes/components/Editorial/Search/TitleSearch.svelte +13 -17
- package/src/routes/components/Editorial/Switch.svelte +14 -4
- package/src/routes/components/Editorial/TextInput.svelte +10 -3
- package/src/routes/components/Genres.svelte +8 -5
- package/src/routes/components/Icons/IconAlign.svelte +8 -3
- package/src/routes/components/Icons/IconChevron.svelte +6 -3
- package/src/routes/components/Icons/IconEnlarge.svelte +6 -3
- package/src/routes/components/Modal.svelte +11 -10
- package/src/routes/components/Participants.svelte +8 -3
- package/src/routes/components/Playlinks.svelte +11 -7
- package/src/routes/components/Popover.svelte +11 -10
- package/src/routes/components/RoundButton.svelte +11 -9
- package/src/routes/components/SkeletonText.svelte +8 -3
- package/src/routes/components/Title.svelte +9 -3
- package/src/routes/components/TitleModal.svelte +9 -4
- package/src/routes/components/TitlePopover.svelte +7 -3
- package/src/tests/routes/components/Genres.test.js +1 -1
- package/src/lib/index.js +0 -1
- package/src/typedefs.js +0 -95
- /package/src/lib/{constants.js → constants.ts} +0 -0
- /package/src/lib/{genres.json → data/genres.json} +0 -0
- /package/src/lib/data/{translations.js → translations.ts} +0 -0
- /package/src/lib/enums/{Language.js → Language.ts} +0 -0
- /package/src/lib/enums/{TrackingEvent.js → TrackingEvent.ts} +0 -0
- /package/{jsconfig.json → tsconfig.json} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { fade, fly, slide } from 'svelte/transition'
|
|
3
3
|
import { flip } from 'svelte/animate'
|
|
4
4
|
import EditorItem from './EditorItem.svelte'
|
|
@@ -10,16 +10,22 @@
|
|
|
10
10
|
import { untrack } from 'svelte'
|
|
11
11
|
import AIIndicator from './AIIndicator.svelte'
|
|
12
12
|
import { isEquivalentInjection, isValidInjection } from '$lib/linkInjection'
|
|
13
|
+
import type { Position } from '$lib/types/position'
|
|
14
|
+
import type { LinkInjection } from '$lib/types/injection'
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
linkInjections: LinkInjection[],
|
|
18
|
+
htmlString?: string,
|
|
19
|
+
loading?: boolean,
|
|
20
|
+
aiRunning?: boolean
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
let { linkInjections = $bindable(), htmlString = '', loading = false, aiRunning = false } = $props()
|
|
23
|
+
let { linkInjections = $bindable(), htmlString = '', loading = false, aiRunning = false }: Props = $props()
|
|
16
24
|
|
|
17
25
|
const editorPositionKey = 'editor-position'
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
let
|
|
21
|
-
/** @type {Position} */
|
|
22
|
-
let position = $state(JSON.parse(localStorage.getItem(editorPositionKey) || '{ "x": 0, "y": 0 }'))
|
|
27
|
+
let editorElement: HTMLElement | null = $state(null)
|
|
28
|
+
let position: Position = $state(JSON.parse(localStorage.getItem(editorPositionKey) || '{ "x": 0, "y": 0 }'))
|
|
23
29
|
let manualInjectionActive = $state(false)
|
|
24
30
|
let saving = $state(false)
|
|
25
31
|
let hasError = $state(false)
|
|
@@ -29,14 +35,14 @@
|
|
|
29
35
|
|
|
30
36
|
const linkInjectionsString = $derived(JSON.stringify(linkInjections))
|
|
31
37
|
const hasChanged = $derived(initialStateString !== linkInjectionsString)
|
|
32
|
-
const filteredInjections = $derived(linkInjections.filter(i => i.title_details && !i.removed && !i.duplicate))
|
|
38
|
+
const filteredInjections = $derived(linkInjections.filter((i) => i.title_details && !i.removed && !i.duplicate))
|
|
33
39
|
|
|
34
40
|
$effect(() => {
|
|
35
41
|
if (!loading) untrack(() => initialStateString = linkInjectionsString)
|
|
36
42
|
})
|
|
37
43
|
|
|
38
44
|
/** Save the given injections in their current state and rewrite object to returned value */
|
|
39
|
-
async function save() {
|
|
45
|
+
async function save(): Promise<void> {
|
|
40
46
|
try {
|
|
41
47
|
saving = true
|
|
42
48
|
hasError = false
|
|
@@ -50,18 +56,12 @@
|
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
* @param {Position} position
|
|
55
|
-
*/
|
|
56
|
-
function savePosition(position) {
|
|
59
|
+
function savePosition(position: Position): void {
|
|
57
60
|
localStorage.setItem(editorPositionKey, JSON.stringify(position))
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
*/
|
|
63
|
-
function removeInjection(key) {
|
|
64
|
-
const index = linkInjections.findIndex(i => i.key === key)
|
|
63
|
+
function removeInjection(key: string): void {
|
|
64
|
+
const index = linkInjections.findIndex((i) => i.key === key)
|
|
65
65
|
|
|
66
66
|
linkInjections[index].manual = true
|
|
67
67
|
linkInjections[index].removed = true
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
/**
|
|
73
73
|
* @param {HTMLElement} itemElement
|
|
74
74
|
*/
|
|
75
|
-
function scrollToItem(itemElement) {
|
|
75
|
+
function scrollToItem(itemElement: HTMLElement): void {
|
|
76
76
|
if (!editorElement) return
|
|
77
77
|
|
|
78
78
|
const itemTop = itemElement.offsetTop
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
/**
|
|
94
94
|
* @param {Event} event
|
|
95
95
|
*/
|
|
96
|
-
function onscroll(event) {
|
|
97
|
-
const target =
|
|
96
|
+
function onscroll(event: Event): void {
|
|
97
|
+
const target = event.target as HTMLElement
|
|
98
98
|
scrollDistance = target.scrollTop
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -103,12 +103,13 @@
|
|
|
103
103
|
* We only pass on links that do not already exist.
|
|
104
104
|
* @param {LinkInjection[]} injections
|
|
105
105
|
*/
|
|
106
|
-
export function requestNewAIInjections(injections) {
|
|
106
|
+
export function requestNewAIInjections(injections: LinkInjection[]): void {
|
|
107
107
|
const newInjections = injections.filter(injection => {
|
|
108
108
|
if (!isValidInjection(injection)) return
|
|
109
109
|
return !linkInjections.some((i) => isEquivalentInjection(injection, i))
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
+
// @ts-ignore
|
|
112
113
|
aIIndicator.notifyUserOfNewState(newInjections)
|
|
113
114
|
}
|
|
114
115
|
</script>
|
|
@@ -150,7 +151,7 @@
|
|
|
150
151
|
<div class="items">
|
|
151
152
|
{#each filteredInjections as linkInjection (linkInjection.key)}
|
|
152
153
|
<!-- We want to bind to the original object, not the derived object, so we get the index of the injection in the original object by it's key -->
|
|
153
|
-
{@const index = linkInjections.findIndex(i => i.key === linkInjection.key)}
|
|
154
|
+
{@const index = linkInjections.findIndex((i) => i.key === linkInjection.key)}
|
|
154
155
|
|
|
155
156
|
<div animate:flip={{ duration: 300 }}>
|
|
156
157
|
<EditorItem bind:linkInjection={linkInjections[index]} onremove={() => removeInjection(linkInjection.key)} onhighlight={scrollToItem} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { slide } from 'svelte/transition'
|
|
3
3
|
import IconChevron from '../Icons/IconChevron.svelte'
|
|
4
4
|
import IconIMDb from '../Icons/IconIMDb.svelte'
|
|
@@ -10,20 +10,26 @@
|
|
|
10
10
|
import { onMount } from 'svelte'
|
|
11
11
|
import { track } from '$lib/tracking'
|
|
12
12
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
13
|
+
import type { LinkInjection } from '$lib/types/injection'
|
|
14
|
+
import type { TitleData } from '$lib/types/title'
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
linkInjection: LinkInjection,
|
|
18
|
+
onremove?: () => void,
|
|
19
|
+
// eslint-disable-next-line no-unused-vars
|
|
20
|
+
onhighlight?: (element: HTMLElement) => void
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
const { linkInjection = $bindable(), onremove = () => null, onhighlight = () => null } = $props()
|
|
23
|
+
const { linkInjection = $bindable(), onremove = () => null, onhighlight = () => null }: Props = $props()
|
|
16
24
|
|
|
17
25
|
const { key, sentence, title_details, failed } = $derived(linkInjection || {})
|
|
18
26
|
|
|
19
|
-
/** @type {TitleData} */
|
|
20
27
|
// @ts-ignore Definitely not null
|
|
21
|
-
const title
|
|
28
|
+
const title: TitleData = $derived(title_details)
|
|
22
29
|
|
|
23
30
|
let expanded = $state(false)
|
|
24
31
|
let highlighted = $state(false)
|
|
25
|
-
|
|
26
|
-
let element = $state(null)
|
|
32
|
+
let element: HTMLElement | null = $state(null)
|
|
27
33
|
|
|
28
34
|
onMount(() => {
|
|
29
35
|
if (failed) track(TrackingEvent.InjectionFailed, title, { phrase: linkInjection.title, sentence })
|
|
@@ -31,9 +37,8 @@
|
|
|
31
37
|
|
|
32
38
|
/**
|
|
33
39
|
* Highlight links beloning to this item in the article itself.
|
|
34
|
-
* @param {boolean} state
|
|
35
40
|
*/
|
|
36
|
-
function toggleOnPageResultHighlight(state = true) {
|
|
41
|
+
function toggleOnPageResultHighlight(state: boolean = true): void {
|
|
37
42
|
const matchingElements = getMatchingElements()
|
|
38
43
|
matchingElements.forEach(element => {
|
|
39
44
|
element.classList.toggle('injection-highlight', state)
|
|
@@ -42,14 +47,13 @@
|
|
|
42
47
|
|
|
43
48
|
/**
|
|
44
49
|
* Highlight this editor when hovering links in the article itself.
|
|
45
|
-
* @param {MouseEvent} event
|
|
46
50
|
*/
|
|
47
|
-
function setInEditorHighlight(event) {
|
|
48
|
-
let target =
|
|
51
|
+
function setInEditorHighlight(event: MouseEvent): void {
|
|
52
|
+
let target = event.target as HTMLElement | null
|
|
49
53
|
let injectionKey = target?.dataset.playpilotInjectionKey
|
|
50
54
|
|
|
51
55
|
if (target && !injectionKey) {
|
|
52
|
-
target =
|
|
56
|
+
target = target.closest(`[data-playpilot-injection-key="${key}"]`) as HTMLElement | null
|
|
53
57
|
injectionKey = target?.dataset.playpilotInjectionKey
|
|
54
58
|
}
|
|
55
59
|
|
|
@@ -58,11 +62,10 @@
|
|
|
58
62
|
if (element && highlighted) onhighlight(element)
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
function scrollLinkIntoView(event) {
|
|
65
|
+
function scrollLinkIntoView(event: MouseEvent): void {
|
|
63
66
|
requestAnimationFrame(() => toggleOnPageResultHighlight()) // Reset highlight in case the playlink type changed
|
|
64
67
|
|
|
65
|
-
const target =
|
|
68
|
+
const target = event.target as HTMLElement
|
|
66
69
|
if (['BUTTON', 'INPUT'].includes(target.nodeName)) return
|
|
67
70
|
if (target.closest('button') || target.closest('input')) return
|
|
68
71
|
|
|
@@ -70,8 +73,7 @@
|
|
|
70
73
|
if (matchingElement) matchingElement.scrollIntoView({ behavior: 'smooth' })
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
function getMatchingElements() {
|
|
76
|
+
function getMatchingElements(): Element[] {
|
|
75
77
|
return Array.from(document.querySelectorAll(`[data-playpilot-injection-key="${key}"]`))
|
|
76
78
|
}
|
|
77
79
|
</script>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte'
|
|
3
3
|
import IconBack from '../Icons/IconBack.svelte'
|
|
4
4
|
import RoundButton from '../RoundButton.svelte'
|
|
@@ -9,14 +9,21 @@
|
|
|
9
9
|
import { generateInjectionKey } from '$lib/api'
|
|
10
10
|
import { decodeHtmlEntities } from '$lib/html'
|
|
11
11
|
import { getLinkInjectionsParentElement } from '$lib/linkInjection'
|
|
12
|
+
import type { LinkInjection } from '$lib/types/injection'
|
|
13
|
+
import type { TitleData } from '$lib/types/title'
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
htmlString: string
|
|
17
|
+
// eslint-disable-next-line no-unused-vars
|
|
18
|
+
onsave: (linkInjection: LinkInjection) => void
|
|
19
|
+
onclose?: () => void
|
|
20
|
+
}
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
let { htmlString = '', onsave, onclose = () => null } = $props()
|
|
22
|
+
let { htmlString = '', onsave, onclose = () => null }: Props = $props()
|
|
15
23
|
|
|
16
24
|
let currentSelection = $state('')
|
|
17
25
|
let selectionSentence = $state('')
|
|
18
|
-
|
|
19
|
-
let selectedTitle = $state(null)
|
|
26
|
+
let selectedTitle: TitleData | null = $state(null)
|
|
20
27
|
let error = $state('')
|
|
21
28
|
let query = $state('')
|
|
22
29
|
|
|
@@ -25,9 +32,8 @@
|
|
|
25
32
|
/**
|
|
26
33
|
* Find the user selected content on the page, if any.
|
|
27
34
|
* Results in a visual error if the selected content was not within the given HTML.
|
|
28
|
-
* @returns {void}
|
|
29
35
|
*/
|
|
30
|
-
function updateSelection() {
|
|
36
|
+
function updateSelection(): void {
|
|
31
37
|
const selection = window.getSelection()
|
|
32
38
|
if (!selection?.anchorNode) return
|
|
33
39
|
|
|
@@ -51,11 +57,8 @@
|
|
|
51
57
|
|
|
52
58
|
/**
|
|
53
59
|
* Find the sentence that the given selected phrase is in. This is limited by the node that the text is in.
|
|
54
|
-
* @param {Selection} selection
|
|
55
|
-
* @param {string} selectionText
|
|
56
|
-
* @returns {string}
|
|
57
60
|
*/
|
|
58
|
-
function findSentenceForSelection(selection, selectionText) {
|
|
61
|
+
function findSentenceForSelection(selection: Selection, selectionText: string): string {
|
|
59
62
|
const range = selection.getRangeAt(0)
|
|
60
63
|
|
|
61
64
|
// Get the node the text is in. If the content of the node is very short we use the parent node instead.
|
|
@@ -81,18 +84,14 @@
|
|
|
81
84
|
return fullText.slice(sentenceStart, sentenceEnd).trim()
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
* @returns {void}
|
|
86
|
-
*/
|
|
87
|
-
function save() {
|
|
87
|
+
function save(): void {
|
|
88
88
|
if (!currentSelection) return
|
|
89
89
|
if (!selectedTitle) return
|
|
90
90
|
|
|
91
91
|
const typePath = selectedTitle.type === 'movie' ? 'movie' : 'show'
|
|
92
92
|
const url = playPilotBaseUrl + `/${typePath}/${selectedTitle.slug}`
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
const linkInjection = {
|
|
94
|
+
const linkInjection: LinkInjection = {
|
|
96
95
|
sid: selectedTitle.sid,
|
|
97
96
|
title: currentSelection,
|
|
98
97
|
sentence: selectionSentence,
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { slide } from 'svelte/transition'
|
|
3
3
|
import IconAlign from '../Icons/IconAlign.svelte'
|
|
4
4
|
import Switch from './Switch.svelte'
|
|
5
|
+
import type { LinkInjection } from '$lib/types/injection'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
interface Props {
|
|
8
|
+
linkInjection: LinkInjection
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { linkInjection }: Props = $props()
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
const isAfterArticleButton = $derived(linkInjection.after_article_style === 'modal_button')
|
|
10
14
|
</script>
|
|
11
15
|
|
|
12
16
|
<div class="switches">
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { playPilotBaseUrl } from '$lib/constants'
|
|
3
3
|
import { searchTitles } from '$lib/search'
|
|
4
|
+
import type { TitleData } from '$lib/types/title'
|
|
4
5
|
import IconIMDb from '../../Icons/IconIMDb.svelte'
|
|
5
6
|
import IconNewTab from '../../Icons/IconNewTab.svelte'
|
|
6
7
|
import TextInput from '../TextInput.svelte'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
interface Props {
|
|
10
|
+
// eslint-disable-next-line no-unused-vars
|
|
11
|
+
onselect?: (title: TitleData) => void,
|
|
12
|
+
query: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { onselect = () => null, query = $bindable() }: Props = $props()
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
let
|
|
13
|
-
/** @type {TitleData | null} */
|
|
14
|
-
let selectedResult = $state(null)
|
|
17
|
+
let results: TitleData[] = $state([])
|
|
18
|
+
let selectedResult: TitleData | null = $state(null)
|
|
15
19
|
let loading = $state(false)
|
|
16
20
|
|
|
17
21
|
$effect(() => {
|
|
18
22
|
if (query) search(query)
|
|
19
23
|
})
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
* @param {string} query
|
|
23
|
-
* @returns {Promise<void>}
|
|
24
|
-
*/
|
|
25
|
-
async function search(query) {
|
|
25
|
+
async function search(query: string): Promise<void> {
|
|
26
26
|
loading = true
|
|
27
27
|
selectedResult = null
|
|
28
28
|
|
|
@@ -33,11 +33,7 @@
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
* @param {TitleData} title
|
|
38
|
-
* @returns {void}
|
|
39
|
-
*/
|
|
40
|
-
function select(title) {
|
|
36
|
+
function select(title: TitleData): void {
|
|
41
37
|
query = title.title
|
|
42
38
|
selectedResult = title
|
|
43
39
|
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
let { active = $bindable(), fullwidth = false, label = '', onclick = () => null, children } = $props()
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
interface Props {
|
|
5
|
+
active: boolean,
|
|
6
|
+
fullwidth?: boolean,
|
|
7
|
+
label?: string,
|
|
8
|
+
// eslint-disable-next-line no-unused-vars
|
|
9
|
+
onclick?: (active: boolean) => void,
|
|
10
|
+
children: Snippet
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { active = $bindable(), fullwidth = false, label = '', onclick = () => null, children }: Props = $props()
|
|
14
|
+
|
|
15
|
+
function toggle(): void {
|
|
6
16
|
active = !active
|
|
7
17
|
onclick(active)
|
|
8
18
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
value: string,
|
|
4
|
+
label?: string,
|
|
5
|
+
name?: string,
|
|
6
|
+
readonly?: boolean,
|
|
7
|
+
oninput?: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let { value = $bindable(), label = '', name = '', readonly = false, oninput = () => null }: Props = $props()
|
|
4
11
|
</script>
|
|
5
12
|
|
|
6
13
|
<input type="text" bind:value {name} aria-label={label} placeholder={label} {readonly} {oninput} />
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import genreData from '$lib/genres.json'
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import genreData from '$lib/data/genres.json'
|
|
3
3
|
import { t } from '$lib/localization'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
interface Props {
|
|
6
|
+
genres: string[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { genres }: Props = $props()
|
|
7
10
|
|
|
8
11
|
let expanded = $state(false)
|
|
9
|
-
|
|
12
|
+
const shownGenres = $derived(expanded ? genres : [genres[0]])
|
|
10
13
|
</script>
|
|
11
14
|
|
|
12
15
|
{#each shownGenres as genre}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Alignment } from '$lib/types/position'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
align: Alignment
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { align = 'bottom' }: Props = $props()
|
|
4
9
|
</script>
|
|
5
10
|
|
|
6
11
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
expanded: boolean
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { expanded = false }: Props = $props()
|
|
4
7
|
</script>
|
|
5
8
|
|
|
6
9
|
<svg width="8" height="4" viewBox="0 0 8 4" fill="none" class:expanded>
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
expanded: boolean
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { expanded = false }: Props = $props()
|
|
4
7
|
</script>
|
|
5
8
|
|
|
6
9
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { fade, fly, scale } from 'svelte/transition'
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fade, fly, scale, type TransitionConfig } from 'svelte/transition'
|
|
3
3
|
import IconClose from './Icons/IconClose.svelte'
|
|
4
4
|
import RoundButton from './RoundButton.svelte'
|
|
5
|
-
import { onMount, setContext } from 'svelte'
|
|
5
|
+
import { onMount, setContext, type Snippet } from 'svelte'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
interface Props {
|
|
8
|
+
children: Snippet
|
|
9
|
+
onclose?: () => void
|
|
10
|
+
onscroll?: () => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { children, onclose = () => null, onscroll = () => null }: Props = $props()
|
|
9
14
|
|
|
10
15
|
setContext('scope', 'modal')
|
|
11
16
|
|
|
@@ -16,11 +21,7 @@
|
|
|
16
21
|
return () => document.body.style.overflowY = baseOverflowStyle || ''
|
|
17
22
|
})
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
* @param {Element} node
|
|
21
|
-
* @returns {import('svelte/transition').TransitionConfig}
|
|
22
|
-
*/
|
|
23
|
-
function scaleOrFly(node) {
|
|
24
|
+
function scaleOrFly(node: Element): TransitionConfig {
|
|
24
25
|
const shouldFly = window.innerWidth < 600
|
|
25
26
|
|
|
26
27
|
if (shouldFly) return fly(node, { duration: 250, y: window.innerHeight })
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Participant } from '$lib/types/participant'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
participants: Participant[]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { participants }: Props = $props()
|
|
4
9
|
</script>
|
|
5
10
|
|
|
6
11
|
<h2>Cast</h2>
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
3
3
|
import { t } from '$lib/localization'
|
|
4
4
|
import { mergePlaylinks } from '$lib/playlink'
|
|
5
5
|
import { track } from '$lib/tracking'
|
|
6
|
+
import type { PlaylinkData } from '$lib/types/playlink'
|
|
7
|
+
import type { TitleData } from '$lib/types/title'
|
|
6
8
|
import IconContinue from './Icons/IconContinue.svelte'
|
|
7
9
|
import { getContext } from 'svelte'
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
interface Props {
|
|
12
|
+
playlinks: PlaylinkData[]
|
|
13
|
+
title: TitleData
|
|
14
|
+
list?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { playlinks, title, list = false }: Props = $props()
|
|
11
18
|
|
|
12
19
|
const isModal = getContext('scope') === 'modal'
|
|
13
20
|
|
|
@@ -22,10 +29,7 @@
|
|
|
22
29
|
TVOD: t('Rent Or Buy'),
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
* @param {string} playlink Name of the clicked playlink
|
|
27
|
-
*/
|
|
28
|
-
function onclick(playlink) {
|
|
32
|
+
function onclick(playlink: string): void {
|
|
29
33
|
track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.TitlePopoverPlaylinkClick, title, { playlink })
|
|
30
34
|
}
|
|
31
35
|
</script>
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { onMount, setContext, tick } from 'svelte'
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, setContext, tick, type Snippet } from 'svelte'
|
|
3
3
|
import { fly } from 'svelte/transition'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
interface Props {
|
|
6
|
+
children: Snippet
|
|
7
|
+
maxHeight?: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let { children, maxHeight = $bindable() }: Props = $props()
|
|
7
11
|
|
|
8
12
|
setContext('scope', 'popover')
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
let element = $state(null)
|
|
14
|
+
let element: HTMLElement | null = $state(null)
|
|
12
15
|
let flip = $state(false)
|
|
13
16
|
|
|
14
17
|
onMount(async () => {
|
|
@@ -23,9 +26,8 @@
|
|
|
23
26
|
* Flip the element vertically if it doesn't fit on screen above the link.
|
|
24
27
|
* Move it to the left if it doesn't fit from the right side of the screen.
|
|
25
28
|
* Vertically it is always above or below, but horizontally is a gradual.
|
|
26
|
-
* @return {void}
|
|
27
29
|
*/
|
|
28
|
-
function positionElement() {
|
|
30
|
+
function positionElement(): void {
|
|
29
31
|
if (!element) return
|
|
30
32
|
|
|
31
33
|
const { top, right, bottom, height } = element.getBoundingClientRect()
|
|
@@ -40,9 +42,8 @@
|
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* Set the max height of the dialog so that it always fits on screen, even after flipping.
|
|
43
|
-
* @return {void}
|
|
44
45
|
*/
|
|
45
|
-
function setMaxHeight() {
|
|
46
|
+
function setMaxHeight(): void {
|
|
46
47
|
if (!element) return
|
|
47
48
|
|
|
48
49
|
const { top, height } = element.getBoundingClientRect()
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
size?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
onclick?: () => void;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { children, size = 'margin(2)', onclick = () => null, ...rest }: Props = $props()
|
|
10
12
|
</script>
|
|
11
13
|
|
|
12
14
|
<svelte:element this={rest.href ? 'a' : 'button'} role={rest.href ? 'link' : 'button'} {onclick} class="button" style:--size={size} {...rest}>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
lines?: number
|
|
4
|
+
max?: number
|
|
5
|
+
min?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { lines = 4, max = 100, min = (max / 100) * 70 }: Props = $props()
|
|
4
9
|
</script>
|
|
5
10
|
|
|
6
11
|
<!-- eslint-disable-next-line no-unused-vars -->
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import Genres from './Genres.svelte'
|
|
3
3
|
import Playlinks from './Playlinks.svelte'
|
|
4
4
|
import Description from './Description.svelte'
|
|
5
5
|
import Participants from './Participants.svelte'
|
|
6
6
|
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
7
7
|
import { t } from '$lib/localization'
|
|
8
|
+
import type { TitleData } from '$lib/types/title'
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
interface Props {
|
|
11
|
+
title: TitleData
|
|
12
|
+
small?: boolean
|
|
13
|
+
compact?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { title, small = false, compact = false }: Props = $props()
|
|
11
17
|
|
|
12
18
|
let imageLoaded = $state(false)
|
|
13
19
|
</script>
|