Skip to content

Commit 7626cea

Browse files
feat: add useScriptVimeo + Vimeo component (#8)
Co-authored-by: Harlan Wilton <[email protected]>
1 parent c03df8d commit 7626cea

File tree

6 files changed

+256
-0
lines changed

6 files changed

+256
-0
lines changed

playground/pages/index.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ const thirdParties = [
3434
name: 'Segment',
3535
path: '/third-parties/segment',
3636
},
37+
{
38+
name: 'Vimeo',
39+
path: '/third-parties/vimeo',
40+
},
41+
{
42+
name: 'Vimeo component',
43+
path: '/third-parties/vimeo-component',
44+
},
3745
]
3846
3947
const features = [
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts" setup>
2+
import { useHead } from '#imports'
3+
import { VimeoEmbed } from '#components'
4+
5+
useHead({
6+
title: 'Vimeo component',
7+
})
8+
9+
function handlePlay() {
10+
console.log('Playing')
11+
}
12+
</script>
13+
14+
<template>
15+
<VimeoEmbed video-id="76979871" width="640" height="400" :loop="true" @play="handlePlay" />
16+
</template>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script lang="ts" setup>
2+
import { onMounted, useHead, useScriptVimeo } from '#imports'
3+
4+
useHead({
5+
title: 'Vimeo',
6+
})
7+
8+
onMounted(() => {
9+
const { Player } = useScriptVimeo()
10+
Player('player', {
11+
id: 76979871,
12+
width: 400,
13+
height: 400
14+
})
15+
})
16+
</script>
17+
18+
<template>
19+
<div id="player"></div>
20+
</template>

src/module.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
addBuildPlugin,
3+
addComponentsDir,
34
addImports,
45
addImportsDir,
56
addPlugin,
@@ -106,6 +107,10 @@ export default defineNuxtModule<ModuleOptions>({
106107
resolve('./runtime/composables'),
107108
])
108109

110+
addComponentsDir({
111+
path: resolve('./runtime/components')
112+
})
113+
109114
nuxt.hooks.hook('modules:done', async () => {
110115
let registry: RegistryScripts = [
111116
{
@@ -148,6 +153,11 @@ export default defineNuxtModule<ModuleOptions>({
148153
})
149154
},
150155
},
156+
{
157+
name: 'useScriptVimeo',
158+
from: resolve('./runtime/registry/vimeo'),
159+
key: 'vimeo',
160+
},
151161
{
152162
name: 'useScriptIntercom',
153163
from: resolve('./runtime/registry/intercom'),

src/runtime/components/VimeoEmbed.vue

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<template>
2+
<div ref="root" :id="id">
3+
<slot />
4+
</div>
5+
</template>
6+
7+
<script lang="ts" setup>
8+
import { useIntersectionObserver } from "@vueuse/core";
9+
import { ref, onBeforeUnmount, useId, useScriptVimeo } from "#imports";
10+
11+
const props = withDefaults(
12+
defineProps<{
13+
videoId: string;
14+
lazy?: boolean;
15+
rootMargin?: string;
16+
width?: string | number;
17+
height?: string | number;
18+
options?: Object;
19+
loop?: boolean;
20+
autoplay?: boolean;
21+
controls?: boolean;
22+
}>(),
23+
{
24+
lazy: true,
25+
rootMargin: "50px 50px 50px 50px",
26+
width: "640",
27+
height: "360",
28+
options: () => ({}),
29+
loop: false,
30+
autoplay: false,
31+
controls: true,
32+
}
33+
);
34+
35+
// TODO: put events in <script> - after build it doesn't work for some reason
36+
// error: "both scripts must have same language type" even if they're both written in ts
37+
const events: string[] = [
38+
"play",
39+
"playing",
40+
"pause",
41+
"ended",
42+
"timeupdate",
43+
"progress",
44+
"seeking",
45+
"seeked",
46+
"texttrackchange",
47+
"chapterchange",
48+
"cuechange",
49+
"cuepoint",
50+
"volumechange",
51+
"playbackratechange",
52+
"bufferstart",
53+
"bufferend",
54+
"error",
55+
"loaded",
56+
"durationchange",
57+
"fullscreenchange",
58+
"qualitychange",
59+
"camerachange",
60+
"resize",
61+
];
62+
63+
const emit = defineEmits([
64+
"play",
65+
"playing",
66+
"pause",
67+
"ended",
68+
"timeupdate",
69+
"progress",
70+
"seeking",
71+
"seeked",
72+
"texttrackchange",
73+
"chapterchange",
74+
"cuechange",
75+
"cuepoint",
76+
"volumechange",
77+
"playbackratechange",
78+
"bufferstart",
79+
"bufferend",
80+
"error",
81+
"loaded",
82+
"durationchange",
83+
"fullscreenchange",
84+
"qualitychange",
85+
"camerachange",
86+
"resize",
87+
]);
88+
89+
const _id = useId();
90+
const id = _id.replace("-", "").replace("_", "");
91+
92+
const status: Ref<string | null> = ref(null);
93+
94+
const root = ref(null);
95+
96+
const { Player, $script } = useScriptVimeo({
97+
trigger: props.lazy ? "manual" : undefined,
98+
});
99+
100+
let player: any;
101+
102+
if (!props.lazy) $script.then(init);
103+
else {
104+
const { stop: stopIntersectionObserver } = useIntersectionObserver(
105+
root,
106+
([{ isIntersecting }]) => {
107+
if (isIntersecting && !$script.loaded) {
108+
$script.load().then(() => {
109+
init();
110+
stopIntersectionObserver();
111+
});
112+
}
113+
},
114+
{ rootMargin: props.rootMargin }
115+
);
116+
}
117+
118+
onBeforeUnmount(() => player?.unload());
119+
120+
function init() {
121+
player = Player(id, {
122+
id: props.videoId,
123+
width: props.width,
124+
height: props.height,
125+
loop: props.loop,
126+
autoplay: props.autoplay,
127+
controls: props.controls,
128+
...props.options,
129+
});
130+
131+
for (const event of events) {
132+
player?.on(event, (e: any) => {
133+
emit(event as keyof typeof emit, e, player);
134+
status.value = event;
135+
});
136+
}
137+
}
138+
139+
function play() {
140+
player?.play();
141+
}
142+
143+
function pause() {
144+
player?.pause();
145+
}
146+
147+
function mute() {
148+
player?.setVolume(0);
149+
}
150+
151+
function unmute(volume: number = 1) {
152+
player?.setVolume(volume);
153+
}
154+
155+
defineExpose({
156+
play,
157+
pause,
158+
mute,
159+
unmute,
160+
status,
161+
});
162+
</script>

src/runtime/registry/vimeo.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { type Input, object, string } from 'valibot'
2+
import { useScript } from '#imports'
3+
import type { NuxtUseScriptOptions } from '#nuxt-scripts'
4+
5+
export interface VimeoScriptApi {
6+
Player: any
7+
}
8+
9+
export const VimeoScriptOptions = object({})
10+
11+
export type VimeoPlayer = ((...params: any[]) => void) | undefined
12+
13+
export type VimeoScriptInput = Input<typeof VimeoScriptOptions>
14+
15+
declare global {
16+
interface Window {
17+
Vimeo: VimeoScriptApi
18+
}
19+
}
20+
21+
export function useScriptVimeo<T>(options?: VimeoScriptInput, _scriptOptions?: Omit<NuxtUseScriptOptions<T>, 'beforeInit' | 'use'>) {
22+
const scriptOptions: NuxtUseScriptOptions<T> = _scriptOptions || {}
23+
24+
return useScript<VimeoScriptApi>({
25+
key: 'vimeo',
26+
src: 'https://player.vimeo.com/api/player.js',
27+
...options,
28+
}, {
29+
...scriptOptions,
30+
use: () => {
31+
let Player: VimeoPlayer
32+
if (import.meta.client) {
33+
Player = function (...params: any[]) {
34+
return new window.Vimeo.Player(...params)
35+
}
36+
}
37+
return { Player }
38+
},
39+
})
40+
}

0 commit comments

Comments
 (0)