viewport-extra
Version:
Enable setting minimum and maximum viewport width
122 lines (109 loc) • 6.56 kB
JavaScript
/*! Viewport Extra v3.0.0 | (c) dsktschy | MIT License */
var ViewportExtra = (function (exports) {
'use strict';
const ensureViewportMetaElement = (doc) => {
const viewportMetaElement = doc.querySelector('meta[name="viewport"]');
if (viewportMetaElement)
return viewportMetaElement;
const htmlMetaElement = doc.createElement("meta");
htmlMetaElement.setAttribute("name", "viewport");
doc.head.appendChild(htmlMetaElement);
return htmlMetaElement;
};
const getDocumentClientWidth = (doc) => doc.documentElement.clientWidth;
const kebabizeCamelCaseString = (str) => str
.replace(/\s+/g, "")
.replace(/[A-Z]+/g, (s) => `-${s[0]}`)
.toLowerCase();
const mathTrunc = /* eslint-disable-next-line compat/compat */
Math.trunc
;
const truncateDecimalNumber = (num, decimalPlaces) =>
// biome-ignore lint/suspicious/noGlobalIsFinite: isFinite is safe to use here
isFinite(decimalPlaces)
? mathTrunc(num * 10 ** decimalPlaces) / 10 ** decimalPlaces
: num;
const defaultContent = {
width: "device-width",
initialScale: 1,
minimumWidth: 0,
maximumWidth: Infinity,
};
const createContent = (partialContent = {}) => ({
...defaultContent,
...partialContent,
});
const mergeOptionalPartialContent = (precedingOptionalPartialContent, followingOptionalPartialContent) => precedingOptionalPartialContent
? {
...precedingOptionalPartialContent,
...(followingOptionalPartialContent ?? {}),
}
: followingOptionalPartialContent;
const createContentAttribute$1 = (content = { ...defaultContent },
// biome-ignore lint/style/noInferrableTypes: number type cannot be inferred from initialization
documentClientWidth = 0, decimalPlaces = 0) => {
const { width, initialScale } = content;
const { minimumWidth: _minimumWidth, maximumWidth: _maximumWidth, minWidth, maxWidth, ...contentWithoutExtraProperties } = content;
const minimumWidth = minWidth ?? _minimumWidth;
const maximumWidth = maxWidth ?? _maximumWidth;
if (minimumWidth <= maximumWidth && width === "device-width") {
if (documentClientWidth < minimumWidth) {
contentWithoutExtraProperties.width = minimumWidth;
contentWithoutExtraProperties.initialScale =
(documentClientWidth / minimumWidth) * initialScale;
}
else if (documentClientWidth > maximumWidth) {
contentWithoutExtraProperties.width = maximumWidth;
contentWithoutExtraProperties.initialScale =
(documentClientWidth / maximumWidth) * initialScale;
}
}
return Object.keys(contentWithoutExtraProperties)
.map((key) => `${kebabizeCamelCaseString(key)}=${typeof contentWithoutExtraProperties[key] === "number"
? truncateDecimalNumber(contentWithoutExtraProperties[key], decimalPlaces)
: contentWithoutExtraProperties[key]}`)
.sort()
.join(",");
};
const defaultMedia = "";
const createMedia = (optionalMedia) => optionalMedia ?? defaultMedia;
const mergeOptionalMedia = (precedingOptionalMedia, followingOptionalMedia) => followingOptionalMedia ?? precedingOptionalMedia;
const createMediaSpecificParameters = (partialMediaSpecificParameters = {}) => ({
content: createContent(partialMediaSpecificParameters.content),
media: createMedia(partialMediaSpecificParameters.media),
});
const mergePartialMediaSpecificParameters = (precedingPartialMediaSpecificParameters, followingPartialMediaSpecificParameters) => {
const partialMediaSpecificParameters = {};
const optionalPartialContent = mergeOptionalPartialContent(precedingPartialMediaSpecificParameters.content, followingPartialMediaSpecificParameters.content);
const optionalMedia = mergeOptionalMedia(precedingPartialMediaSpecificParameters.media, followingPartialMediaSpecificParameters.media);
if (optionalPartialContent)
partialMediaSpecificParameters.content = optionalPartialContent;
if (typeof optionalMedia !== "undefined")
partialMediaSpecificParameters.media = optionalMedia;
return partialMediaSpecificParameters;
};
const createContentAttribute = (optionalMediaSpecificParameters, optionalDocumentClientWidth, optionalDecimalPlaces) => optionalMediaSpecificParameters &&
typeof optionalDocumentClientWidth !== "undefined" &&
typeof optionalDecimalPlaces !== "undefined"
? createContentAttribute$1(optionalMediaSpecificParameters.content, optionalDocumentClientWidth, optionalDecimalPlaces)
: createContentAttribute$1();
const createPartialMediaSpecificParametersMerger = (isMatchingCurrentViewport) => (precedingPartialMediaSpecificParameters, followingPartialMediaSpecificParameters) => isMatchingCurrentViewport(followingPartialMediaSpecificParameters.media)
? mergePartialMediaSpecificParameters(precedingPartialMediaSpecificParameters, followingPartialMediaSpecificParameters)
: precedingPartialMediaSpecificParameters;
const setContentAttribute = (htmlMetaElement, contentAttribute) => htmlMetaElement.setAttribute("content", contentAttribute);
const applyMediaSpecificParameters = (htmlMetaElement, getDocumentClientWidth, getMediaSpecificParameters) => {
setContentAttribute(htmlMetaElement, createContentAttribute());
setContentAttribute(htmlMetaElement, createContentAttribute(getMediaSpecificParameters(), getDocumentClientWidth(), Infinity));
};
const createMatchMediaPredicate = (mm) => (media) => typeof media !== "undefined" ? mm(media).matches : true;
const setMediaSpecificParametersList = (partialMediaSpecificParametersList) => {
if (typeof window === "undefined")
return;
applyMediaSpecificParameters(ensureViewportMetaElement(document), () => getDocumentClientWidth(document), () => createMediaSpecificParameters(partialMediaSpecificParametersList.reduce(createPartialMediaSpecificParametersMerger(createMatchMediaPredicate(matchMedia)),
// Value that does not need to check matching current viewport
createMediaSpecificParameters())));
};
exports.apply = setMediaSpecificParametersList;
return exports;
})({});
//# sourceMappingURL=viewport-extra.js.map