vue-screen
Version:
Reactive screen size and media query states for Vue. Integrates with most UI frameworks out of the box.
329 lines (312 loc) • 10.6 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var vue = require('vue');
var inBrowser = typeof window !== 'undefined';
var debounce = function (callback, wait) {
var timeout;
// eslint-disable-next-line func-names
return function () {
// eslint-disable-next-line @typescript-eslint/no-this-alias
var context = this;
// eslint-disable-next-line prefer-rest-params
var args = arguments;
// eslint-disable-next-line func-names
var later = function () {
timeout = null;
callback.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
var remToPx = function (rem) {
var fontSize;
if (inBrowser) {
fontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
}
else {
fontSize = 16;
}
return parseFloat(rem.toString()) * fontSize;
};
// GoogleBot default screen properties
var DEFAULT_WIDTH = 410;
var DEFAULT_HEIGHT = 730;
var DEFAULT_ORIENTATION = 'portrait';
var DEFAULT_TOUCH_SUPPORT = true;
var DEFAULT_DEBOUNCE_DELAY = 100;
var useScreen = function (config, debounceDelay) {
if (config === void 0) { config = {}; }
if (debounceDelay === void 0) { debounceDelay = DEFAULT_DEBOUNCE_DELAY; }
var width = config.width || DEFAULT_WIDTH;
var height = config.height || DEFAULT_HEIGHT;
var orientation = config.orientation || DEFAULT_ORIENTATION;
var touch = config.touch === undefined ? DEFAULT_TOUCH_SUPPORT : config.touch;
var screen = vue.reactive({
resolution: "".concat(width, "x").concat(height),
width: width,
height: height,
orientation: orientation,
portrait: orientation === 'portrait',
landscape: orientation !== 'portrait',
touch: touch,
});
/* istanbul ignore next */
var updateWindowProperties = function () {
screen.width = window.innerWidth;
screen.height = window.innerHeight;
screen.resolution = "".concat(screen.width, "x").concat(screen.height);
};
/* istanbul ignore next */
var updateOrientationPropperties = function (e) {
screen.portrait = e.matches;
screen.landscape = !e.matches;
screen.orientation = e.matches ? 'portrait' : 'landscape';
};
/* istanbul ignore if */
if (inBrowser) {
var resizeListener_1 = debounce(updateWindowProperties, debounceDelay);
window.addEventListener('resize', resizeListener_1);
updateWindowProperties();
// This does not react to resize events.
// You always need to reload the browser to add/remove touch support,
// even when using DevTools device simulation
screen.touch = 'ontouchstart' in window;
var query_1 = 'matchMedia' in window && window.matchMedia('(orientation: portrait)');
if (query_1) {
if ('addEventListener' in query_1) {
query_1.addEventListener('change', updateOrientationPropperties);
}
else {
// https://github.com/reegodev/vue-screen/issues/30
// query.addListener is not deprecated for iOS 12
// eslint-disable-next-line @typescript-eslint/no-explicit-any
query_1.addListener(updateOrientationPropperties);
}
updateOrientationPropperties(query_1);
}
// Do not leak memory by keeping event listeners active.
// This appears to work as expected, using useScreen() inside components
// triggers this hook when they are destroyed.
if (vue.getCurrentInstance()) {
vue.onUnmounted(function () {
window.removeEventListener('resize', resizeListener_1);
if (query_1) {
if ('removeEventListener' in query_1) {
query_1.removeEventListener('change', updateOrientationPropperties);
}
else {
// https://github.com/reegodev/vue-screen/issues/30
// query.removeListener is not deprecated for iOS 12
// eslint-disable-next-line @typescript-eslint/no-explicit-any
query_1.removeListener(updateOrientationPropperties);
}
}
});
}
}
return screen;
};
var tailwind = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
};
var bootstrap = {
xs: '0px',
sm: '768px',
md: '992px',
lg: '1200px',
};
var bootstrap4 = {
xs: '0px',
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
};
var bootstrap5 = {
xs: '0px',
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
xxl: '1400px',
};
var bulma = {
mobile: 0,
tablet: 769,
desktop: 1024,
widescreen: 1216,
fullhd: 1408,
};
var materialize = {
s: 0,
m: 601,
l: 993,
xl: 1201,
};
var foundation = {
small: 0,
medium: 640,
large: 1024,
};
var semanticUi = {
mobile: 0,
tablet: 768,
computer: 992,
large: 1201,
};
var grids = {
tailwind: tailwind,
bootstrap: bootstrap,
bootstrap3: bootstrap,
bootstrap4: bootstrap4,
bootstrap5: bootstrap5,
bulma: bulma,
materialize: materialize,
foundation: foundation,
semanticUi: semanticUi,
};
var DEFAULT_GRID_FRAMEWORK = 'tailwind';
var createGridObject = function (config) {
return Object.keys(config).reduce(function (accumulator, key) {
accumulator[key] = false;
return accumulator;
}, {
breakpoint: '',
});
};
var createConfigFromLiteral = function (literal) {
if (!grids[literal]) {
throw new Error("Invalid grid type \"".concat(literal, "\""));
}
return grids[literal];
};
var getCurrentBreakpoint = function (config, object) {
var current = Object.keys(config)
.filter(function (key) { return typeof config[key] !== "function"; })
.sort(function (a, b) {
var valueA = remToPx(config[a]);
var valueB = remToPx(config[b]);
return valueA - valueB;
})
.reverse()
.find(function (key) { return object[key]; });
return current || "";
};
/* istanbul ignore next */
var updateComputedProperties = function (config, object) {
Object.keys(config)
.filter(function (breakpoint) {
return typeof config[breakpoint] === 'function';
})
.forEach(function (breakpoint) {
var fn = config[breakpoint];
object[breakpoint] = fn.call(null, object);
});
object.breakpoint = getCurrentBreakpoint(config, object);
};
/* istanbul ignore next */
var createMediaQueries = function (config, object) {
var debouncedUpdateComputedProperties = debounce(updateComputedProperties, 100);
Object.keys(config)
.filter(function (breakpoint) {
return typeof config[breakpoint] !== 'function';
})
.forEach(function (breakpoint) {
var width = config[breakpoint];
if (typeof width === 'number') {
width = "".concat(width, "px");
}
else {
width = width.toString();
}
var onChange = function (event) {
object[breakpoint] = event.matches;
debouncedUpdateComputedProperties(config, object);
};
var query = 'matchMedia' in window && window.matchMedia("(min-width: ".concat(width, ")"));
if (query) {
if ('addEventListener' in query) {
query.addEventListener('change', onChange);
}
else {
// https://github.com/reegodev/vue-screen/issues/30
// query.addListener is not deprecated for iOS 12
// eslint-disable-next-line @typescript-eslint/no-explicit-any
query.addListener(onChange);
}
object[breakpoint] = query.matches;
}
// Do not leak memory by keeping event listeners active.
// This appears to work as expected, using useGrid() inside components
// triggers this hook when they are destroyed.
if (vue.getCurrentInstance()) {
vue.onUnmounted(function () {
if (query) {
if ('removeEventListener' in query) {
query.removeEventListener('change', onChange);
}
else {
// https://github.com/reegodev/vue-screen/issues/30
// query.removeListener is not deprecated for iOS 12
// eslint-disable-next-line @typescript-eslint/no-explicit-any
query.removeListener(onChange);
}
}
});
}
});
updateComputedProperties(config, object);
};
function useGrid(gridConfig) {
if (gridConfig === void 0) { gridConfig = DEFAULT_GRID_FRAMEWORK; }
var config;
if (typeof gridConfig === 'string') {
config = createConfigFromLiteral(gridConfig);
}
else {
config = Object.assign(gridConfig);
}
var gridObject = vue.reactive(createGridObject(config));
/* istanbul ignore if */
if (inBrowser) {
createMediaQueries(config, gridObject);
}
return gridObject;
}
var extendGrid = function (literalConfig, extraProperties) {
return Object.assign({}, createConfigFromLiteral(literalConfig), extraProperties);
};
var install = function (app, options) {
var screen;
var grid;
if (typeof options === 'string') {
screen = useScreen();
grid = useGrid(options);
}
else {
options = options || { grid: undefined, ssr: undefined, debounceDelay: undefined };
screen = useScreen(options.ssr, options.debounceDelay);
// ts cant figure out the type of arguments when union types are
// passed to an overloaded function, so we need to use "any" or do a typeof check
// on the arguments, which would ship 5 lines of js instead of one.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
grid = useGrid(options.grid);
}
app.config.globalProperties.$screen = screen;
app.config.globalProperties.$grid = grid;
app.provide('screen', screen);
app.provide('grid', grid);
};
var plugin = {
install: install
};
exports["default"] = plugin;
exports.extendGrid = extendGrid;
exports.grids = grids;
exports.useGrid = useGrid;
exports.useScreen = useScreen;