@nuxt/scripts 0.6.5 → 0.7.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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/dist/client/200.html +9 -9
  3. package/dist/client/404.html +9 -9
  4. package/dist/client/_nuxt/{D3k5CjNA.js → BW5_3H_7.js} +1 -1
  5. package/dist/client/_nuxt/{D9Ze639F.js → CObySb1Z.js} +1 -1
  6. package/dist/client/_nuxt/Cdyv1BV6.js +31 -0
  7. package/dist/client/_nuxt/{D6UD5WyS.js → DPxoUSuY.js} +1 -1
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/b4af023e-aaec-4265-8ade-ce133bb4c4c3.json +1 -0
  10. package/dist/client/_nuxt/entry.FVeyw1Qn.css +1 -0
  11. package/dist/client/_nuxt/error-404.D8Cdd2Pl.css +1 -0
  12. package/dist/client/_nuxt/error-500.BENoupyF.css +1 -0
  13. package/dist/client/index.html +9 -9
  14. package/dist/module.d.mts +2 -10
  15. package/dist/module.d.ts +2 -10
  16. package/dist/module.json +2 -2
  17. package/dist/module.mjs +13 -4
  18. package/dist/runtime/components/ScriptCrisp.vue +10 -7
  19. package/dist/runtime/components/ScriptGoogleAdsense.vue +7 -7
  20. package/dist/runtime/components/ScriptGoogleMaps.vue +224 -66
  21. package/dist/runtime/components/ScriptIntercom.vue +10 -7
  22. package/dist/runtime/components/ScriptLemonSqueezy.vue +1 -1
  23. package/dist/runtime/components/ScriptStripePricingTable.vue +6 -6
  24. package/dist/runtime/components/ScriptVimeoPlayer.vue +62 -38
  25. package/dist/runtime/components/ScriptYouTubePlayer.vue +9 -9
  26. package/dist/runtime/composables/useScript.d.ts +6 -1
  27. package/dist/runtime/composables/useScript.js +8 -8
  28. package/dist/runtime/composables/useScriptEventPage.d.ts +2 -2
  29. package/dist/runtime/composables/useScriptEventPage.js +6 -5
  30. package/dist/runtime/registry/clarity.d.ts +1 -12
  31. package/dist/runtime/registry/cloudflare-web-analytics.d.ts +1 -3
  32. package/dist/runtime/registry/crisp.d.ts +1 -3
  33. package/dist/runtime/registry/fathom-analytics.d.ts +1 -3
  34. package/dist/runtime/registry/google-adsense.d.ts +1 -3
  35. package/dist/runtime/registry/google-analytics.d.ts +2 -10
  36. package/dist/runtime/registry/google-analytics.js +6 -3
  37. package/dist/runtime/registry/google-maps.d.ts +2 -4
  38. package/dist/runtime/registry/google-maps.js +1 -3
  39. package/dist/runtime/registry/google-tag-manager.d.ts +2 -10
  40. package/dist/runtime/registry/hotjar.d.ts +1 -3
  41. package/dist/runtime/registry/intercom.d.ts +1 -3
  42. package/dist/runtime/registry/lemon-squeezy.d.ts +1 -3
  43. package/dist/runtime/registry/matomo-analytics.d.ts +1 -3
  44. package/dist/runtime/registry/matomo-analytics.js +6 -2
  45. package/dist/runtime/registry/meta-pixel.d.ts +2 -3
  46. package/dist/runtime/registry/meta-pixel.js +5 -1
  47. package/dist/runtime/registry/npm.d.ts +1 -3
  48. package/dist/runtime/registry/plausible-analytics.d.ts +1 -3
  49. package/dist/runtime/registry/segment.d.ts +2 -5
  50. package/dist/runtime/registry/stripe.d.ts +1 -3
  51. package/dist/runtime/registry/vimeo-player.d.ts +1 -3
  52. package/dist/runtime/registry/vimeo-player.js +1 -1
  53. package/dist/runtime/registry/x-pixel.d.ts +1 -3
  54. package/dist/runtime/registry/x-pixel.js +6 -2
  55. package/dist/runtime/registry/youtube-player.d.ts +1 -3
  56. package/dist/runtime/registry/youtube-player.js +1 -1
  57. package/dist/runtime/types.d.ts +5 -5
  58. package/dist/runtime/types.js +1 -1
  59. package/dist/runtime/utils.d.ts +2 -4
  60. package/package.json +41 -37
  61. package/dist/client/_nuxt/BA8oXX6l.js +0 -31
  62. package/dist/client/_nuxt/builds/meta/a5c6ec68-62ae-43af-96d0-bdd9f64f38b1.json +0 -1
  63. package/dist/client/_nuxt/entry.Cts5wDvr.css +0 -1
  64. package/dist/client/_nuxt/error-404.-RjlvToe.css +0 -1
  65. package/dist/client/_nuxt/error-500.Bz7LXgZy.css +0 -1
@@ -1,10 +1,11 @@
1
1
  <script lang="ts" setup>
2
2
  /// <reference types="google.maps" />
3
- import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
3
+ import { computed, onBeforeUnmount, onMounted, ref, watch, toRaw } from 'vue'
4
4
  import type { HTMLAttributes, ImgHTMLAttributes, Ref, ReservedProps } from 'vue'
5
5
  import { withQuery } from 'ufo'
6
6
  import type { QueryObject } from 'ufo'
7
7
  import { defu } from 'defu'
8
+ import { hash } from 'ohash'
8
9
  import type { ElementScriptTrigger } from '../types'
9
10
  import { scriptRuntimeConfig } from '../utils'
10
11
  import { useScriptTriggerElement } from '../composables/useScriptTriggerElement'
@@ -44,15 +45,19 @@ const props = withDefaults(defineProps<{
44
45
  /**
45
46
  * Defines the Google Maps API key. Must have access to the Static Maps API as well.
46
47
  */
47
- apiKey: string
48
+ apiKey?: string
48
49
  /**
49
- * Defines map marker location.
50
+ * A latitude / longitude of where to focus the map.
50
51
  */
51
- query?: string
52
+ center?: google.maps.LatLng | google.maps.LatLngLiteral | `${string},${string}`
53
+ /**
54
+ * Should a marker be displayed on the map where the centre is.
55
+ */
56
+ centerMarker?: boolean
52
57
  /**
53
58
  * Options for the map.
54
59
  */
55
- options?: google.maps.MapOptions
60
+ mapOptions?: google.maps.MapOptions
56
61
  /**
57
62
  * Defines the width of the map.
58
63
  */
@@ -75,30 +80,39 @@ const props = withDefaults(defineProps<{
75
80
  * Customize the root element attributes.
76
81
  */
77
82
  rootAttrs?: HTMLAttributes & ReservedProps & Record<string, unknown>
83
+ /**
84
+ * Extra Markers to add to the map.
85
+ */
86
+ markers?: (`${string},${string}` | google.maps.marker.AdvancedMarkerElementOptions)[]
78
87
  }>(), {
79
88
  // @ts-expect-error untyped
80
89
  trigger: ['mouseenter', 'mouseover', 'mousedown'],
81
- width: 600,
90
+ width: 640,
82
91
  height: 400,
92
+ centerMarker: true,
83
93
  })
84
94
 
85
95
  const emits = defineEmits<{
86
96
  // our emit
87
- ready: [e: Ref<google.maps.Map | undefined>]
97
+ ready: [e: typeof googleMaps]
88
98
  error: []
89
99
  }>()
90
100
 
91
101
  const apiKey = props.apiKey || scriptRuntimeConfig('googleMaps')?.apiKey
92
102
 
93
- if (!apiKey)
103
+ const mapsApi = ref<typeof google.maps | undefined>()
104
+
105
+ if (import.meta.dev && !apiKey)
94
106
  throw new Error('GoogleMaps requires an API key. Please provide `apiKey` on the <ScriptGoogleMaps> or globally via `runtimeConfig.public.scripts.googleMaps.apiKey`.')
95
- if (!props.query && !props.options?.center)
96
- throw new Error('GoogleMaps requires either a query or center prop to be set.')
107
+
108
+ // TODO allow a null center may need to be resolved via an API function
97
109
 
98
110
  const rootEl = ref<HTMLElement>()
99
111
  const mapEl = ref<HTMLElement>()
100
112
 
101
- const { $script } = useScriptGoogleMaps({
113
+ const centerOverride = ref()
114
+
115
+ const { load, status, onLoaded } = useScriptGoogleMaps({
102
116
  apiKey: props.apiKey,
103
117
  scriptOptions: {
104
118
  trigger: useScriptTriggerElement({ trigger: props.trigger, el: rootEl }),
@@ -106,16 +120,65 @@ const { $script } = useScriptGoogleMaps({
106
120
  })
107
121
 
108
122
  const options = computed(() => {
109
- return defu(props.options, {
123
+ return defu({ center: centerOverride.value }, props.mapOptions, {
124
+ center: props.center,
110
125
  zoom: 15,
111
126
  mapId: 'map',
112
127
  })
113
128
  })
114
129
  const ready = ref(false)
115
130
 
116
- function queryToLocation(service: google.maps.places.PlacesService, query: string) {
117
- return new Promise<google.maps.LatLng>((resolve, reject) => {
118
- service.findPlaceFromQuery({
131
+ const map: Ref<google.maps.Map | undefined> = ref()
132
+ const mapMarkers: Ref<Map<string, google.maps.marker.AdvancedMarkerElement>> = ref(new Map())
133
+
134
+ function isLocationQuery(s: string | any) {
135
+ return typeof s === 'string' && (s.split(',').length > 2 || s.includes('+'))
136
+ }
137
+
138
+ async function createAdvancedMapMarker(_options: google.maps.marker.AdvancedMarkerElementOptions | `${string},${string}`) {
139
+ const lib = await importLibrary('marker')
140
+ const options = typeof _options === 'string'
141
+ ? {
142
+ position: {
143
+ lat: Number.parseFloat(_options.split(',')[0]),
144
+ lng: Number.parseFloat(_options.split(',')[1]),
145
+ },
146
+ }
147
+ : _options
148
+ const mapMarkerOptions = defu(toRaw(options), {
149
+ map: toRaw(map.value!),
150
+ // @ts-expect-error unified API for maps and markers
151
+ position: options.location,
152
+ })
153
+ const marker = new lib.AdvancedMarkerElement(mapMarkerOptions)
154
+ // create new marker
155
+ mapMarkers.value.set(hash(_options), marker)
156
+ return marker
157
+ }
158
+
159
+ const queryToLatLngCache = new Map<string, google.maps.LatLng>()
160
+
161
+ async function resolveQueryToLatLang(query: string) {
162
+ if (query && typeof query === 'object')
163
+ return Promise.resolve(query)
164
+ if (queryToLatLngCache.has(query)) {
165
+ return Promise.resolve(queryToLatLngCache.get(query))
166
+ }
167
+ // only if the query is a string we need to do a lookup
168
+ // eslint-disable-next-line no-async-promise-executor
169
+ return new Promise<google.maps.LatLng>(async (resolve, reject) => {
170
+ if (!mapsApi.value) {
171
+ await load()
172
+ // await new promise, watch until mapsApi is set
173
+ await new Promise<void>((resolve) => {
174
+ const _ = watch(mapsApi, () => {
175
+ _()
176
+ resolve()
177
+ })
178
+ })
179
+ }
180
+ const placesService = new mapsApi.value!.places.PlacesService(map.value!)
181
+ placesService.findPlaceFromQuery({
119
182
  query,
120
183
  fields: ['name', 'geometry'],
121
184
  }, (results, status) => {
@@ -123,58 +186,134 @@ function queryToLocation(service: google.maps.places.PlacesService, query: strin
123
186
  return resolve(results[0].geometry.location)
124
187
  return reject(new Error(`No location found for ${query}`))
125
188
  })
189
+ }).then((res) => {
190
+ queryToLatLngCache.set(query, res)
191
+ return res
126
192
  })
127
193
  }
128
194
 
129
- const map: Ref<google.maps.Map | undefined> = ref()
130
- const markers: Ref<google.maps.marker.AdvancedMarkerElement[]> = ref([])
131
- defineExpose({
195
+ const libraries = new Map<string, any>()
196
+
197
+ function importLibrary(key: 'marker'): Promise<google.maps.MarkerLibrary>
198
+ function importLibrary(key: 'places'): Promise<google.maps.PlacesLibrary>
199
+ function importLibrary(key: 'geometry'): Promise<google.maps.GeometryLibrary>
200
+ function importLibrary(key: 'drawing'): Promise<google.maps.DrawingLibrary>
201
+ function importLibrary(key: 'visualization'): Promise<google.maps.VisualizationLibrary>
202
+ function importLibrary(key: string): Promise<any>
203
+ function importLibrary<T>(key: string): Promise<T> {
204
+ if (libraries.has(key))
205
+ return libraries.get(key)
206
+ const p = mapsApi.value?.importLibrary(key) || new Promise((resolve) => {
207
+ const stop = watch(mapsApi, (api) => {
208
+ if (api) {
209
+ const p = api.importLibrary(key)
210
+ resolve(p)
211
+ stop()
212
+ }
213
+ }, { immediate: true })
214
+ })
215
+ libraries.set(key, p)
216
+ return p as any as Promise<T>
217
+ }
218
+
219
+ const googleMaps = {
220
+ googleMaps: mapsApi,
132
221
  map,
133
- markers,
134
- })
222
+ createAdvancedMapMarker,
223
+ resolveQueryToLatLang,
224
+ importLibrary,
225
+ } as const
226
+
227
+ defineExpose(googleMaps)
135
228
 
136
229
  onMounted(() => {
137
230
  watch(ready, (v) => {
138
- v && emits('ready', map)
231
+ if (v) {
232
+ emits('ready', googleMaps)
233
+ }
139
234
  })
140
- watch($script.status, () => {
141
- if ($script.status.value === 'error') {
235
+ watch(status, (v) => {
236
+ if (v === 'error') {
142
237
  emits('error')
143
238
  }
144
239
  })
145
- // create the map
146
- $script.then(async (instance) => {
147
- const maps = await instance.maps as any as typeof google.maps // some weird type issue here
148
- const _map = new maps.Map(mapEl.value!, options.value)
149
- const placesService = new maps.places.PlacesService(_map)
150
-
151
- watch(options, () => _map.setOptions(options.value))
152
- watch(() => props.query, async (query) => {
153
- // always clear old markers
154
- markers.value.forEach(marker => marker.remove())
155
- markers.value = []
156
- if (query) {
157
- const marker = await maps.importLibrary('marker') as google.maps.MarkerLibrary
158
- const location = await queryToLocation(placesService, query).catch((err) => {
159
- console.warn(err)
160
- return {
161
- lat: 0,
162
- lng: 0,
163
- }
164
- })
165
- _map.setCenter(location)
166
- markers.value.push(new marker.AdvancedMarkerElement({
167
- map: _map,
168
- position: location,
169
- }))
170
- ready.value = true
240
+ watch(options, () => {
241
+ map.value?.setOptions(options.value)
242
+ })
243
+ watch([() => props.markers, map], () => {
244
+ if (!map.value) {
245
+ return
246
+ }
247
+ // mapMarkers is a map where we hash the next array entry as the map key
248
+ // we need to do a diff to see what we remove or add
249
+ const nextMap = new Map((props.markers || []).map(m => [hash(m), m]))
250
+ // compare idsToMatch in nextMap, if we're missing an id, we need to remove it
251
+ const toRemove = new Set([
252
+ ...mapMarkers.value.keys(),
253
+ ].filter(k => !nextMap.has(k)))
254
+ // compare to existing
255
+ const toAdd = new Set([...nextMap.keys()].filter(k => !mapMarkers.value.has(k)))
256
+ // do a diff of next and prev
257
+ const centerHash = hash({ position: options.value.center })
258
+ toRemove.forEach((key) => {
259
+ if (key === centerHash) {
260
+ return
171
261
  }
172
- }, {
173
- immediate: !!props.query,
262
+ // @ts-expect-error broken type
263
+ mapMarkers.value.get(key)?.setMap(null)
264
+ mapMarkers.value.delete(key)
174
265
  })
175
- if (!props.query)
176
- ready.value = true
177
- map.value = _map
266
+ for (const k of toAdd) {
267
+ // @ts-expect-error broken
268
+ createAdvancedMapMarker(nextMap.get(k))
269
+ }
270
+ }, {
271
+ immediate: true,
272
+ deep: true,
273
+ })
274
+ watch([() => props.center, ready], async (next, prev) => {
275
+ if (!map.value) {
276
+ return
277
+ }
278
+ let center = toRaw(next[0])
279
+ if (center) {
280
+ if (isLocationQuery(center) && ready.value) {
281
+ // need to resolve center from query
282
+ // @ts-expect-error broken
283
+ center = await resolveQueryToLatLang(center as string)
284
+ }
285
+ map.value!.setCenter(center as google.maps.LatLng)
286
+ if (props.centerMarker) {
287
+ if (prev[0]) {
288
+ const prevCenterHash = hash({ position: prev[0] })
289
+ // @ts-expect-error broken upstream type
290
+ mapMarkers.value.get(prevCenterHash)?.setMap(null)
291
+ mapMarkers.value.delete(prevCenterHash)
292
+ }
293
+ // @ts-expect-error untyped
294
+ createAdvancedMapMarker({ position: center })
295
+ }
296
+ }
297
+ }, {
298
+ immediate: true,
299
+ deep: true,
300
+ })
301
+ onLoaded(async (instance) => {
302
+ mapsApi.value = await instance.maps as any as typeof google.maps // some weird type issue here
303
+ // may need to transform the center before we can init the map
304
+ const center = options.value.center as string
305
+ const _options: google.maps.MapOptions = {
306
+ ...options.value,
307
+ // @ts-expect-error broken
308
+ center: !center || isLocationQuery(center) ? undefined : center,
309
+ }
310
+ map.value = new mapsApi.value!.Map(mapEl.value!, _options)
311
+ if (center && isLocationQuery(center)) {
312
+ // need to resolve center
313
+ centerOverride.value = await resolveQueryToLatLang(center)
314
+ map.value?.setCenter(centerOverride.value)
315
+ }
316
+ ready.value = true
178
317
  })
179
318
  })
180
319
 
@@ -190,15 +329,34 @@ if (import.meta.server) {
190
329
  }
191
330
 
192
331
  const placeholder = computed(() => {
332
+ let center = options.value.center
333
+ if (center && typeof center === 'object') {
334
+ center = `${center.lat},${center.lng}`
335
+ }
336
+ // @ts-expect-error lazy type
193
337
  const placeholderOptions: PlaceholderOptions = defu(props.placeholderOptions, {
194
338
  // only map option values
195
339
  zoom: options.value.zoom,
196
- center: options.value.center?.toString() || '',
340
+ center,
197
341
  }, {
198
342
  size: `${props.width}x${props.height}`,
199
343
  key: apiKey,
200
344
  scale: 2, // we assume a high DPI to avoid hydration issues
201
- markers: `color:red|${props.query}`,
345
+ markers: [
346
+ ...(props.markers || []),
347
+ center,
348
+ ]
349
+ .filter(Boolean)
350
+ .map((m) => {
351
+ if (typeof m === 'object' && m.location) {
352
+ m = m.location
353
+ }
354
+ if (typeof m === 'object' && m.lat) {
355
+ return `${m.lat},${m.lng}`
356
+ }
357
+ return m
358
+ })
359
+ .join('|'),
202
360
  })
203
361
  return withQuery('https://maps.googleapis.com/maps/api/staticmap', placeholderOptions as QueryObject)
204
362
  })
@@ -206,7 +364,7 @@ const placeholder = computed(() => {
206
364
  const placeholderAttrs = computed(() => {
207
365
  return defu(props.placeholderAttrs, {
208
366
  src: placeholder.value,
209
- alt: props.query || '',
367
+ alt: 'Google Maps Static Map',
210
368
  loading: props.aboveTheFold ? 'eager' : 'lazy',
211
369
  style: {
212
370
  cursor: 'pointer',
@@ -219,10 +377,10 @@ const placeholderAttrs = computed(() => {
219
377
 
220
378
  const rootAttrs = computed(() => {
221
379
  return defu(props.rootAttrs, {
222
- 'aria-busy': $script.status.value === 'loading',
223
- 'aria-label': $script.status.value === 'awaitingLoad'
380
+ 'aria-busy': status.value === 'loading',
381
+ 'aria-label': status.value === 'awaitingLoad'
224
382
  ? 'Google Maps Static Map'
225
- : $script.status.value === 'loading'
383
+ : status.value === 'loading'
226
384
  ? 'Google Maps Map Embed Loading'
227
385
  : 'Google Maps Embed',
228
386
  'aria-live': 'polite',
@@ -241,8 +399,8 @@ const rootAttrs = computed(() => {
241
399
  const ScriptLoadingIndicator = resolveComponent('ScriptLoadingIndicator')
242
400
 
243
401
  onBeforeUnmount(() => {
244
- markers.value.forEach(marker => marker.remove())
245
- markers.value = []
402
+ mapMarkers.value.forEach(marker => marker.remove())
403
+ mapMarkers.value.clear()
246
404
  map.value?.unbindAll()
247
405
  map.value = undefined
248
406
  mapEl.value?.firstChild?.remove()
@@ -251,15 +409,15 @@ onBeforeUnmount(() => {
251
409
 
252
410
  <template>
253
411
  <div ref="rootEl" v-bind="rootAttrs">
254
- <div v-show="ready" ref="mapEl" class="script-google-maps__map" :style="{ width: '100%', height: '100%', maxWidth: '100%' }" />
412
+ <div v-show="ready" ref="mapEl" :style="{ width: '100%', height: '100%', maxWidth: '100%' }" />
255
413
  <slot v-if="!ready" :placeholder="placeholder" name="placeholder">
256
414
  <img v-bind="placeholderAttrs">
257
415
  </slot>
258
- <slot v-if="$script.status.value === 'loading'" name="loading">
416
+ <slot v-if="status !== 'awaitingLoad' && !ready" name="loading">
259
417
  <ScriptLoadingIndicator color="black" />
260
418
  </slot>
261
- <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
262
- <slot v-else-if="$script.status.value === 'error'" name="error" />
419
+ <slot v-if="status === 'awaitingLoad'" name="awaitingLoad" />
420
+ <slot v-else-if="status === 'error'" name="error" />
263
421
  <slot />
264
422
  </div>
265
423
  </template>
@@ -46,9 +46,12 @@ const intercom = useScriptIntercom({
46
46
  trigger,
47
47
  },
48
48
  })
49
- if (props.trigger === 'click')
50
- intercom.Intercom('show')
51
- const { $script } = intercom
49
+ const { status, onLoaded } = intercom
50
+ if (props.trigger === 'click') {
51
+ onLoaded((instance) => {
52
+ instance.Intercom('show')
53
+ })
54
+ }
52
55
 
53
56
  defineExpose({
54
57
  intercom,
@@ -56,7 +59,7 @@ defineExpose({
56
59
 
57
60
  let observer: MutationObserver
58
61
  onMounted(() => {
59
- watch($script.status, (status) => {
62
+ watch(status, (status) => {
60
63
  if (status === 'loading') {
61
64
  observer = new MutationObserver(() => {
62
65
  if (document.getElementById('intercom-frame')) {
@@ -86,8 +89,8 @@ onBeforeUnmount(() => {
86
89
  }"
87
90
  >
88
91
  <slot :ready="isReady" />
89
- <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
90
- <slot v-else-if="$script.status.value === 'loading' || !isReady" name="loading" />
91
- <slot v-else-if="$script.status.value === 'error'" name="error" />
92
+ <slot v-if="status === 'awaitingLoad'" name="awaitingLoad" />
93
+ <slot v-else-if="status === 'loading' || !isReady" name="loading" />
94
+ <slot v-else-if="status === 'error'" name="error" />
92
95
  </div>
93
96
  </template>
@@ -26,7 +26,7 @@ onMounted(() => {
26
26
  rootEl.value?.querySelectorAll('a[href]').forEach((a) => {
27
27
  a.classList.add('lemonsqueezy-button')
28
28
  })
29
- instance.$script.then(({ Setup, Refresh }) => {
29
+ instance.onLoaded(({ Setup, Refresh }) => {
30
30
  Setup({
31
31
  eventHandler(event) {
32
32
  emits('lemonSqueezyEvent', event)
@@ -26,11 +26,11 @@ const containerEl = ref<HTMLDivElement | undefined>()
26
26
  const instance = useScript(`https://js.stripe.com/v3/pricing-table.js`, {
27
27
  trigger: useScriptTriggerElement({ trigger: props.trigger, el: rootEl }),
28
28
  })
29
- const { $script } = instance
29
+ const { onLoaded, status } = instance
30
30
 
31
31
  const pricingTable = ref<HTMLElement | undefined>()
32
32
  onMounted(() => {
33
- $script.then(() => {
33
+ onLoaded(() => {
34
34
  const StripePricingTable = window.customElements.get('stripe-pricing-table')!
35
35
  const stripePricingTable = new StripePricingTable()
36
36
  stripePricingTable.setAttribute('publishable-key', props.publishableKey)
@@ -45,7 +45,7 @@ onMounted(() => {
45
45
  rootEl.value!.appendChild(stripePricingTable)
46
46
  emit('ready', instance)
47
47
  })
48
- watch($script.status, (status) => {
48
+ watch(status, (status) => {
49
49
  if (status === 'error') {
50
50
  emit('error')
51
51
  }
@@ -60,9 +60,9 @@ onBeforeUnmount(() => {
60
60
  <template>
61
61
  <div ref="rootEl">
62
62
  <div ref="containerEl" />
63
- <slot v-if="$script.status.value === 'loading'" name="loading" />
64
- <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
65
- <slot v-else-if="$script.status.value === 'error'" name="error" />
63
+ <slot v-if="status === 'loading'" name="loading" />
64
+ <slot v-if="status === 'awaitingLoad'" name="awaitingLoad" />
65
+ <slot v-else-if="status === 'error'" name="error" />
66
66
  <slot />
67
67
  </div>
68
68
  </template>
@@ -8,12 +8,7 @@ import { useScriptTriggerElement } from '../composables/useScriptTriggerElement'
8
8
  import { useScriptVimeoPlayer } from '../registry/vimeo-player'
9
9
  import { useAsyncData, useHead } from '#imports'
10
10
 
11
- const props = withDefaults(defineProps<{
12
- // custom
13
- trigger?: ElementScriptTrigger
14
- placeholderAttrs?: ImgHTMLAttributes
15
- rootAttrs?: HTMLAttributes
16
- aboveTheFold?: boolean
11
+ interface VimeoOptions {
17
12
  // copied from @types/vimeo__player
18
13
  id?: number | undefined
19
14
  url?: string | undefined
@@ -25,7 +20,7 @@ const props = withDefaults(defineProps<{
25
20
  controls?: boolean | undefined
26
21
  dnt?: boolean | undefined
27
22
  height?: number | undefined
28
- // eslint-disable-next-line vue/prop-name-casing
23
+
29
24
  interactive_params?: string | undefined
30
25
  keyboard?: boolean | undefined
31
26
  loop?: boolean | undefined
@@ -42,12 +37,19 @@ const props = withDefaults(defineProps<{
42
37
  title?: boolean | undefined
43
38
  transparent?: boolean | undefined
44
39
  width?: number | undefined
40
+ }
41
+
42
+ const props = withDefaults(defineProps<{
43
+ // custom
44
+ trigger?: ElementScriptTrigger
45
+ placeholderAttrs?: ImgHTMLAttributes
46
+ rootAttrs?: HTMLAttributes
47
+ aboveTheFold?: boolean
48
+ vimeoOptions?: VimeoOptions
49
+ id?: number | undefined
50
+ url?: string | undefined
45
51
  }>(), {
46
52
  trigger: 'mousedown',
47
- width: 640,
48
- height: 480,
49
- loop: false,
50
- controls: true,
51
53
  })
52
54
 
53
55
  const emits = defineEmits<TEmits>()
@@ -122,7 +124,7 @@ if (props.trigger === 'mousedown') {
122
124
  })
123
125
  }
124
126
  const ready = ref(false)
125
- const { $script } = useScriptVimeoPlayer({
127
+ const { onLoaded, status } = useScriptVimeoPlayer({
126
128
  scriptOptions: {
127
129
  trigger,
128
130
  },
@@ -140,12 +142,16 @@ if (import.meta.server) {
140
142
  })
141
143
  }
142
144
 
145
+ const id = computed(() => {
146
+ return props.vimeoOptions?.id || props.id
147
+ })
148
+
143
149
  const { data: payload } = useAsyncData(
144
- `vimeo-embed:${props.id}`,
150
+ `vimeo-embed:${id.value}`,
145
151
  // TODO ideally we cache this
146
- () => $fetch(`https://vimeo.com/api/v2/video/${props.id}.json`).then(res => (res as any)[0]),
152
+ () => $fetch(`https://vimeo.com/api/v2/video/${id.value}.json`).then(res => (res as any)[0]),
147
153
  {
148
- watch: [() => props.id],
154
+ watch: [id],
149
155
  },
150
156
  )
151
157
 
@@ -169,10 +175,26 @@ defineExpose({
169
175
  setPlaybackRate: (rate: number) => player?.setPlaybackRate(rate),
170
176
  })
171
177
 
178
+ const width = computed(() => {
179
+ return props.vimeoOptions?.width || elVimeo.value?.parentNode?.offsetWidth || 640
180
+ })
181
+
182
+ const height = computed(() => {
183
+ return props.vimeoOptions?.height || elVimeo.value?.parentNode?.offsetHeight || 480
184
+ })
185
+
172
186
  onMounted(() => {
173
- $script.then(async ({ Vimeo }) => {
174
- // filter props for false values
175
- player = new Vimeo.Player(elVimeo.value, props)
187
+ onLoaded(async ({ Vimeo }) => {
188
+ const vimeoOptions = props.vimeoOptions || {}
189
+ if (!vimeoOptions.id && props.id) {
190
+ vimeoOptions.id = props.id
191
+ }
192
+ if (!vimeoOptions.url && props.url) {
193
+ vimeoOptions.url = props.url
194
+ }
195
+ vimeoOptions.width = width.value
196
+ vimeoOptions.height = height.value
197
+ player = new Vimeo.Player(elVimeo.value, vimeoOptions)
176
198
  if (clickTriggered) {
177
199
  player!.play()
178
200
  clickTriggered = false
@@ -186,33 +208,35 @@ onMounted(() => {
186
208
  })
187
209
  }
188
210
  })
211
+ })
189
212
 
190
- watch(() => props.id, (v) => {
191
- v && player?.loadVideo(Number(v))
192
- })
193
- watch($script.status, (status) => {
194
- if (status === 'error') {
195
- // @ts-expect-error untyped
196
- emits('error')
197
- }
198
- })
213
+ watch(() => props.id, (v) => {
214
+ if (v) {
215
+ player?.loadVideo(Number(v))
216
+ }
217
+ })
218
+ watch(status, (status) => {
219
+ if (status === 'error') {
220
+ // @ts-expect-error untyped
221
+ emits('error')
222
+ }
199
223
  })
200
224
 
201
225
  const rootAttrs = computed(() => {
202
226
  return defu(props.rootAttrs, {
203
- 'aria-busy': $script.status.value === 'loading',
204
- 'aria-label': $script.status.value === 'awaitingLoad'
227
+ 'aria-busy': status.value === 'loading',
228
+ 'aria-label': status.value === 'awaitingLoad'
205
229
  ? 'Vimeo Player - Placeholder'
206
- : $script.status.value === 'loading'
230
+ : status.value === 'loading'
207
231
  ? 'Vimeo Player - Loading'
208
232
  : 'Vimeo Player - Loaded',
209
233
  'aria-live': 'polite',
210
234
  'role': 'application',
211
235
  'style': {
212
236
  maxWidth: '100%',
213
- width: `${props.width}px`,
214
- height: `'auto'`,
215
- aspectRatio: `${props.width}/${props.height}`,
237
+ width: `${width.value}px`,
238
+ height: 'auto',
239
+ aspectRatio: `16/9`,
216
240
  position: 'relative',
217
241
  backgroundColor: 'black',
218
242
  },
@@ -240,19 +264,19 @@ onBeforeUnmount(() => player?.unload())
240
264
 
241
265
  <template>
242
266
  <div ref="rootEl" v-bind="rootAttrs">
243
- <div v-show="ready" ref="elVimeo" class="vimeo-player" style="width: 100%; height: 100%; max-width: 100%;" />
267
+ <div v-show="ready" ref="elVimeo" class="vimeo-player" />
244
268
  <slot v-if="!ready" v-bind="payload" :placeholder="placeholder" name="placeholder">
245
269
  <img v-if="placeholder" v-bind="placeholderAttrs">
246
270
  </slot>
247
- <slot v-if="$script.status.value === 'loading'" name="loading">
271
+ <slot v-if="status === 'loading'" name="loading">
248
272
  <ScriptLoadingIndicator color="white" />
249
273
  </slot>
250
- <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
251
- <slot v-else-if="$script.status.value === 'error'" name="error" />
274
+ <slot v-if="status === 'awaitingLoad'" name="awaitingLoad" />
275
+ <slot v-else-if="status === 'error'" name="error" />
252
276
  <slot />
253
277
  </div>
254
278
  </template>
255
279
 
256
280
  <style>
257
- .vimeo-player iframe{max-width:100%!important}
281
+ .vimeo-player iframe{aspect-ratio:16/9;height:auto;max-width:100%!important;width:100%}
258
282
  </style>