UNPKG

v-pip

Version:

Vue 3 wrapper for the native Picture-in-Picture (PiP) Web API — floating video, document PiP, and TypeScript-first composables.

101 lines (100 loc) • 3.12 kB
import { createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, toDisplayString, useTemplateRef } from "vue"; // src/components/VPip.vue const _hoisted_1 = [ "src", "poster", "height", "width" ]; const _hoisted_2 = ["type"]; const _sfc_main = /*@__PURE__*/ defineComponent({ __name: "VPip", props: { videoOptions: { default: () => ({ wrapper: "", src: "", poster: "", class: "", height: "100%", width: "100%" }) }, buttonOptions: { default: () => ({ wrapper: "", type: "button", class: "", label: "Toggle picture-in-picture" }) }, wrapper: { default: "" } }, emits: [ "video-in-pip", "requesting-pip-failure", "exiting-pip-failure" ], setup(__props, { expose: __expose, emit: __emit }) { const props = __props; const emit = __emit; const video = useTemplateRef("video"); const isPipSupported = ref(false); const enteredPip = () => { emit("video-in-pip", true); }; const leftPip = () => { emit("video-in-pip", false); }; onMounted(() => { isPipSupported.value = "pictureInPictureEnabled" in document && document.pictureInPictureEnabled; video.value?.addEventListener("enterpictureinpicture", enteredPip); video.value?.addEventListener("leavepictureinpicture", leftPip); }); onBeforeUnmount(() => { video.value?.removeEventListener("enterpictureinpicture", enteredPip); video.value?.removeEventListener("leavepictureinpicture", leftPip); }); const togglePip = async () => { if (document.pictureInPictureElement) { try { await document.exitPictureInPicture(); } catch (error) { emit("exiting-pip-failure", error); } return; } if (!video.value) return; try { await video.value.requestPictureInPicture(); } catch (error) { emit("requesting-pip-failure", error); } }; __expose({ togglePip }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass(props.wrapper) }, [createElementVNode("div", { class: normalizeClass(props.videoOptions.wrapper) }, [createElementVNode("video", { ref_key: "video", ref: video, src: props.videoOptions.src, poster: props.videoOptions.poster, class: normalizeClass(props.videoOptions.class), height: props.videoOptions.height, width: props.videoOptions.width, controls: "", playsinline: "" }, null, 10, _hoisted_1)], 2), createElementVNode("div", { class: normalizeClass(props.buttonOptions.wrapper) }, [isPipSupported.value ? (openBlock(), createElementBlock("button", { key: 0, type: props.buttonOptions.type ?? "button", class: normalizeClass(props.buttonOptions.class), onClick: togglePip }, toDisplayString(props.buttonOptions.label), 11, _hoisted_2)) : createCommentVNode("v-if", true)], 2)], 2); }; } }); // src/install.ts let installed = false; const install = (app) => { if (!installed) { app.component("VPip", _sfc_main); installed = true; } }; export { _sfc_main as VPip, install as default }; //# sourceMappingURL=index.js.map