vuex-viewport
Version:
Vuex extension that allows making window size as computed property.
140 lines (121 loc) • 3.13 kB
text/typescript
import { Store } from 'vuex';
import debounce from 'lodash/debounce';
interface Breakpoint {
boundary: number;
mediaName: string;
}
interface ModuleState {
width: number;
height: number;
}
interface PluginOptions {
delay?: number;
maxDelay?: number;
breakpoints?: {
[mediaName: string]: number;
};
}
if (Number.isFinite === undefined) {
Number.isFinite = (value) => {
return (typeof value === 'number' && isFinite(value));
};
}
// bootstrap-inspired breakpoints.
let breakpoints: Breakpoint[] = [
{
boundary: 992,
mediaName: 'desktop',
},
{
boundary: 768,
mediaName: 'tablet',
},
];
const storeModule = {
namespaced: true,
state(): ModuleState {
return {
width: 0,
height: 0,
};
},
getters: {
mediaName(state: ModuleState) {
const section = breakpoints.filter((breakpoint) => {
return breakpoint.boundary <= state.width;
})[0];
return (section ? section.mediaName : 'mobile');
},
},
mutations: {
measure(state: ModuleState) {
if (typeof window !== 'undefined') {
state.width = window.innerWidth;
state.height = window.innerHeight;
}
},
},
actions: {
},
};
const viewport = storeModule;
const createPlugin = (options: PluginOptions = {
delay: 200,
maxDelay: 1000,
breakpoints: {
tablet: 768,
desktop: 992,
},
}) => {
const wait = (Number.isFinite(options.delay) ? options.delay : 200);
const maxWait = (Number.isFinite(options.maxDelay) ? options.maxDelay : 1000);
const points = options.breakpoints || {};
const customBreakpoints: Breakpoint[] = [];
Object.entries(points).forEach(([ mediaName, boundary ]) => {
if (typeof boundary === 'number') {
if (!(boundary >= 0 && boundary < Infinity)) {
throw new RangeError('Breakpoint should be a non-negative finite value.');
} else {
customBreakpoints.push({
boundary,
mediaName,
});
}
} else {
throw new TypeError('Breakpoint should be a numeric value.');
}
});
if (customBreakpoints.length > 0) {
customBreakpoints.sort((former, latter) => {
return (latter.boundary - former.boundary);
});
breakpoints = customBreakpoints;
}
return (store: Store<any>) => {
const instantMeasure = () => {
store.commit('viewport/measure');
};
const debouncedMeasure = debounce(instantMeasure, wait, {maxWait});
if (typeof window !== 'undefined') {
instantMeasure();
window.addEventListener('load', instantMeasure);
window.addEventListener('resize', debouncedMeasure);
}
};
};
const viewportPlugin = createPlugin;
export {
ModuleState,
storeModule,
createPlugin,
// Following exports are deprecated. It will remove later.
viewport,
viewportPlugin,
};
export default {
storeModule,
createPlugin,
// Following exports are deprecated. It will remove later.
viewport,
viewportPlugin,
};