vue-component-media-queries
Version:
MatchMedia component library for vue
87 lines (85 loc) • 2.14 kB
text/typescript
import Vue, { PropType, VNode } from 'vue';
import { MediaQueriesProvision } from './MediaQueryProvider'
import { renderWrappedNodes } from "./utils";
type SlotProps = { matches: boolean } | MediaQueriesProvision
type Data = {
matches: boolean,
matcher: null | MediaQueryList,
mediaQueries?: null | MediaQueriesProvision,
$nuxt?: any
}
export const MatchMedia = Vue.extend({
name: 'MatchMedia',
inject: {
mediaQueries: {
default: null
},
},
props: {
query: {
type: String as PropType<string>
},
fallback: {
type: Boolean as PropType<boolean>
},
wrapperTag: {
type: String as PropType<string>,
default: 'span'
},
ssr: {
type: Boolean as PropType<boolean>,
}
},
data(): Data {
return {
matcher: null,
matches: this.fallback,
};
},
beforeMount() {
if (!this.ssr && !this.$nuxt) {
this.bootstrap();
}
},
mounted() {
if (this.ssr || this.$nuxt) {
this.bootstrap();
}
},
beforeDestroy() {
if (this.matcher) {
this.matcher.removeListener(this.onMedia);
this.matcher = null;
}
},
methods: {
bootstrap() {
if (this.query) {
const matcher = this.matcher = window.matchMedia(this.query);
matcher.addListener(this.onMedia);
this.matches = matcher.matches;
}
},
onMedia(event: MediaQueryListEvent) {
this.$emit('change', event);
this.matches = event.matches;
},
},
computed: {
slotProps(): SlotProps {
if (this.query) return { matches: this.matches };
if (!this.mediaQueries) {
console.error(
`
[vue-component-media-queries]:
<MatchMedia /> should be a descendant of a <MediaQueryProvider /> component tree or have a 'query' prop.
`
);
}
return this.mediaQueries as MediaQueriesProvision;
},
},
render(h): VNode {
return renderWrappedNodes(h, this.$scopedSlots.default!(this.slotProps)!, this.wrapperTag);
},
});