@playpilot/tpi 8.36.3 → 8.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.36.3",
3
+ "version": "8.37.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
package/src/lib/modal.ts CHANGED
@@ -20,6 +20,8 @@ type Modal = {
20
20
  type: ModalType
21
21
  data: TitleData | TitleData[] | ParticipantData | null
22
22
  scrollPosition?: number
23
+ initialIndex?: number
24
+ props?: Record<string, any>
23
25
  }
24
26
 
25
27
  const modals: Modal[] = []
@@ -105,14 +107,14 @@ export function getAllModals(): Modal[] {
105
107
  }
106
108
 
107
109
  function saveCurrentModalScrollPosition(): void {
108
- // The scrollable element differs on mobile and desktop.
109
- // On desktop you scroll the entire parent but on mobile you scroll the dialog itself.
110
- const selector = `[data-playpilot-link-injections] ${window.innerWidth > mobileBreakpoint ? '.modal' : '.dialog'}`
111
- const element = document.querySelector(selector)
110
+ // Some modals have their own scroll target which can be set with the corresponding data attribute
111
+ const selector = window.innerWidth > mobileBreakpoint ? '.modal' : '.dialog'
112
+ const element = getPlayPilotWrapperElement().querySelector('[data-modal-scroll-target]') || getPlayPilotWrapperElement().querySelector(selector)
112
113
 
113
114
  if (!element?.scrollTop) return
114
115
 
115
116
  modals[modals.length - 1].scrollPosition = element.scrollTop
117
+ modals[modals.length - 1].props = { initialIndex: parseInt(getPlayPilotWrapperElement().querySelector<HTMLElement>('[data-active-index]')?.dataset.activeIndex || '0') }
116
118
  }
117
119
 
118
120
  export function openModalForInjectedLink(event: MouseEvent, injections: LinkInjection[]): void {
@@ -38,14 +38,13 @@
38
38
  data,
39
39
  props: {
40
40
  onclose: () => navigate('home'),
41
- pushState: false,
42
41
  },
43
42
  })
44
43
  }
45
44
  </script>
46
45
 
47
46
  {#await openModalViaRoute()}
48
- <Modal blur pushState={false}>
47
+ <Modal blur>
49
48
  {#snippet dialog()}
50
49
  <div class="loading">
51
50
  <div class="bar"></div>
@@ -55,7 +54,7 @@
55
54
  {/snippet}
56
55
  </Modal>
57
56
  {:catch}
58
- <Modal blur onclose={() => navigate('home')} pushState={false}>
57
+ <Modal blur onclose={() => navigate('home')}>
59
58
  <div class="error">
60
59
  {t('An Error Occurred')}
61
60
  </div>
@@ -10,6 +10,7 @@
10
10
  import { destroyAllModals, getPreviousModal, goBackToPreviousModal } from '$lib/modal'
11
11
  import { focustrap } from '$lib/actions/focustrap'
12
12
  import { getScrollRoot } from '$lib/scroll'
13
+ import { getPlayPilotWrapperElement } from '$lib/injection'
13
14
 
14
15
  interface Props {
15
16
  children?: Snippet
@@ -21,7 +22,6 @@
21
22
  blur?: boolean
22
23
  closeButtonStyle?: 'shadow' | 'flat'
23
24
  initialScrollPosition?: number
24
- pushState?: boolean
25
25
  onscroll?: () => void
26
26
  onclose?: () => void
27
27
  }
@@ -36,7 +36,6 @@
36
36
  blur = false,
37
37
  closeButtonStyle = 'shadow',
38
38
  initialScrollPosition = 0,
39
- pushState = true,
40
39
  onscroll = () => null,
41
40
  onclose = () => null,
42
41
  }: Props = $props()
@@ -64,11 +63,7 @@
64
63
 
65
64
  hasPreviousModal = !!getPreviousModal()
66
65
 
67
- // Add modal state to the browser history. This allows us to close to modal when using the back button.
68
- // Only do this for the very first modal opened in the stack. The back button always fully closes all modals.
69
- if (pushState && !hasPreviousModal) window.history.pushState({ modal: true }, '', historyHash)
70
-
71
- requestAnimationFrame(setInitialScrollPosition)
66
+ requestAnimationFrame(() => requestAnimationFrame(setInitialScrollPosition))
72
67
 
73
68
  return () => {
74
69
  if (!document.querySelector('.modal')) scrollElement.classList.remove('playpilot-modal-open')
@@ -81,7 +76,8 @@
81
76
  })
82
77
 
83
78
  function setInitialScrollPosition(): void {
84
- const scrollableElement = window.innerWidth > mobileBreakpoint ? modalElement : dialogElement
79
+ /** @see utils/modal.ts > saveCurrentModalScrollPosition */
80
+ const scrollableElement = getPlayPilotWrapperElement().querySelector('[data-modal-scroll-target]') || (window.innerWidth > mobileBreakpoint ? modalElement : dialogElement)
85
81
  scrollableElement?.scrollTo(0, initialScrollPosition)
86
82
  }
87
83
 
@@ -106,7 +102,6 @@
106
102
 
107
103
  <svelte:window
108
104
  onkeydown={({ key }) => { if (key === 'Escape') close() }}
109
- onhashchange={close}
110
105
  bind:innerWidth={windowWidth} />
111
106
 
112
107
  {#snippet closeButton()}
@@ -166,7 +161,7 @@
166
161
  transition:scaleOrFly|global>
167
162
  {#if hasPreviousModal}
168
163
  <div class="close back {closeButtonStyle}">
169
- <RoundButton onclick={() => goBackToPreviousModal()} aria-label="Back">
164
+ <RoundButton onclick={goBackToPreviousModal} aria-label="Back">
170
165
  <IconArrow direction="left" />
171
166
  </RoundButton>
172
167
  </div>
@@ -20,7 +20,6 @@
20
20
 
21
21
  const { items, initialIndex = 0, onchange = () => null, onclose = () => null, each, ...restProps }: Props = $props()
22
22
 
23
-
24
23
  let slider: TinySlider
25
24
  let initialized = $state(false)
26
25
  let transitionDuration = $state(prefersReducedMotion.current ? 0 : 300)
@@ -74,11 +73,13 @@
74
73
  <TinySlider threshold={40} moveThreshold={40} transitionDuration={initialized ? transitionDuration : 0} bind:this={slider}>
75
74
  {#snippet children({ currentIndex })}
76
75
  {#each items as item, index}
76
+ {@const active = currentIndex === index}
77
+
77
78
  <!-- svelte-ignore a11y_click_events_have_key_events -->
78
79
  <!-- svelte-ignore a11y_no_static_element_interactions -->
79
- <div class="item" class:active={index === currentIndex} onclick={() => setSliderIndex(index)}>
80
- {#if Math.abs(index - currentIndex) === 1 || currentIndex === index}
81
- <div class="content" transition:fade={{ duration: initialized ? transitionDuration : 0 }}>
80
+ <div class="item" class:active onclick={() => setSliderIndex(index)} data-active-index={active ? index : null}>
81
+ {#if Math.abs(index - currentIndex) === 1 || active}
82
+ <div class="content" data-modal-scroll-target={active ? '' : null} transition:fade={{ duration: initialized ? transitionDuration : 0 }}>
82
83
  {@render each(item, currentIndex)}
83
84
  </div>
84
85
  {/if}
@@ -77,7 +77,12 @@
77
77
  {#if participants?.length}
78
78
  <Rail {heading}>
79
79
  {#each orderParticipantsByPageTextOccurrence(participants).slice(0, 15) as participant}
80
- <button class="participant" data-testid="participant" onclick={event => onclick(event, participant)}>
80
+ <button
81
+ class="participant"
82
+ data-testid="participant"
83
+ onclick={event => onclick(event, participant)}
84
+ onmouseenter={() => fetchParticipantBySid(participant.sid)}
85
+ ontouchstart={() => fetchParticipantBySid(participant.sid)}>
81
86
  <ParticipantImage {participant} />
82
87
 
83
88
  <div class="name">{participant.name}</div>
@@ -1,3 +1,4 @@
1
+ import fs from 'fs'
1
2
  import path from 'path'
2
3
  import * as dotenv from 'dotenv'
3
4
  import { defineConfig } from 'vitest/config'
@@ -8,11 +9,13 @@ dotenv.config({ path: '.env' })
8
9
 
9
10
  export default defineConfig(({ mode }) => {
10
11
  const isEditiorial = mode === 'editorial'
12
+ const fileName = isEditiorial ? 'editorial.mount.js' : 'mount.js'
11
13
 
12
14
  return {
13
15
  plugins: [
14
16
  svelte(),
15
17
  injectEnvVariables,
18
+ wrapJsToBeSelfContained(fileName),
16
19
  ],
17
20
 
18
21
  build: {
@@ -21,7 +24,7 @@ export default defineConfig(({ mode }) => {
21
24
  input: './src/mount.ts',
22
25
  output: {
23
26
  name: 'PlayPilotMount',
24
- entryFileNames: isEditiorial ? 'editorial.mount.js' : 'mount.js',
27
+ entryFileNames: fileName,
25
28
  assetFileNames: (assetInfo) => assetInfo.name,
26
29
  },
27
30
  },
@@ -56,3 +59,15 @@ const injectEnvVariables = {
56
59
  }
57
60
  },
58
61
  }
62
+
63
+ const wrapJsToBeSelfContained = (fileName) => ({
64
+ name: 'wrap-to-be-self-contained',
65
+ writeBundle() {
66
+ const distPath = path.resolve(__dirname, './dist')
67
+ const targetPath = path.join(distPath, fileName)
68
+ const originalContent = fs.readFileSync(targetPath, 'utf8')
69
+ const wrappedContent = `(() => {\n${originalContent}\n})();\n`
70
+
71
+ fs.writeFileSync(targetPath, wrappedContent)
72
+ },
73
+ })