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
JavaScript
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