@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
210 lines • 5.88 kB
JavaScript
import { isTrue, toKebabCase } from "./component-helper.js";
import { warn } from "./helpers.js";
export const defaultBreakpoints = {
small: '40em',
medium: '60em',
large: '72em',
'x-large': '80em',
'xx-large': '90em'
};
export function onMediaQueryChange(property, callback, {
runOnInit = false
} = {}) {
let query = property;
let when = null;
let not = null;
if (property && typeof property === 'object') {
query = null;
when = property.when || property;
not = property.not;
}
const mediaQueryList = makeMediaQueryList({
query,
when,
not
});
if (runOnInit) {
if (typeof callback === 'function') {
callback(mediaQueryList?.matches, mediaQueryList);
}
}
return createMediaQueryListener(mediaQueryList, callback);
}
export const isMatchMediaSupported = () => typeof window !== 'undefined' && typeof window.matchMedia !== 'undefined';
export function makeMediaQueryList({
query,
when,
not = null
} = {}, breakpoints = null, options) {
const isSupported = isMatchMediaSupported();
if (options?.disabled || !isSupported) {
return null;
}
if (when) {
query = buildQuery({
query,
when,
not
}, breakpoints, options);
}
const mediaQueryString = convertToMediaQuery(query, breakpoints, options);
const mediaQueryList = window.matchMedia(mediaQueryString);
if (options?.log) {
warn('MediaQuery:', mediaQueryString);
}
return mediaQueryList;
}
export function createMediaQueryListener(mediaQueryList, callback) {
if (!mediaQueryList) {
return () => null;
}
const listener = event => {
if (typeof callback === 'function') {
callback(event?.matches, event);
}
};
if (mediaQueryList?.addEventListener) {
mediaQueryList.addEventListener('change', listener);
}
return () => {
if (mediaQueryList?.removeEventListener) {
mediaQueryList.removeEventListener('change', listener);
}
};
}
export function buildQuery({
query = null,
when = null,
not = null
} = {}, breakpoints, options) {
if (when) {
if (typeof when === 'string') {
when = when.split(/[ ,]/g);
}
let listOfQueries = [];
if (Array.isArray(when)) {
listOfQueries = listOfQueries.concat(combineQueries(when, breakpoints, options));
} else if (typeof when === 'object') {
const queryItem = convertToMediaQuery(when, breakpoints, options);
if (queryItem) {
listOfQueries.push(queryItem);
}
}
if (listOfQueries.length > 0) {
query = [listOfQueries.join(' '), query || ''].filter(Boolean).join(' and ').replace(/ +/g, ' ').replace(/ ,/g, ',');
}
}
if (isTrue(not)) {
query = reverseQuery(String(query));
}
return query || 'not';
}
function reverseQuery(query) {
if (query.startsWith('not')) {
return query.replace(/^not +/, '');
}
if (!/^(screen|all|print|speech)/.test(query)) {
query = `all and ${query}`;
}
return `not ${query}`;
}
function combineQueries(queries, breakpoints = null, options) {
return queries.reduce((listOfQueries, when, i, arr) => {
if (breakpoints) {
breakpoints = mergeBreakpoints(breakpoints);
}
const query = convertToMediaQuery(when, breakpoints, options);
if (query) {
if (query !== 'and' && arr[i - 1] !== 'and') {
listOfQueries.push(', ');
}
listOfQueries.push(query);
}
return listOfQueries;
}, []).filter((query, i) => {
return !(i === 0 && query.startsWith(', '));
});
}
function mergeBreakpoints(breakpoints) {
return Object.entries({
...defaultBreakpoints,
...breakpoints
}).sort((a, b) => a[1] > b[1] ? 1 : -1).reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
}
export function convertToMediaQuery(query, breakpoints = null, options) {
if (typeof query === 'string') {
return query;
}
if (Array.isArray(query)) {
return query.reduce((acc, q, index) => {
acc += objToMediaQuery(q, breakpoints, options);
if (index < query.length - 1) {
if (q !== 'and' && query[index + 1] !== 'and') {
acc += ',';
}
acc += ' ';
}
return acc;
}, '');
}
return objToMediaQuery(query, breakpoints, options);
}
function objToMediaQuery(obj, breakpoints = null, options) {
if (typeof obj === 'string') {
return obj;
}
let hasNot = false;
let query = Object.keys(obj).reduce((acc, feature) => {
let value = obj[feature];
feature = toKebabCase(feature);
if (feature === 'not') {
hasNot = true;
return acc;
}
if (feature === 'monochrome') {
feature = `(${feature})`;
}
if (feature === 'min' || feature === 'max') {
feature = `${feature}-width`;
}
if (typeof value === 'number' && /[height|width]$/.test(feature)) {
value = value + 'em';
}
if (value === true) {
acc.push(feature);
} else if (value === false) {
acc.push('not ' + feature);
} else {
value = getValueByFeature(value, breakpoints);
if (typeof value !== 'undefined') {
acc.push(`(${feature}: ${value})`);
}
}
return acc;
}, []);
if (Array.isArray(query)) {
query = query.length > 0 ? query.join(' and ') : query.join('');
}
if (hasNot) {
query = reverseQuery(query);
}
if (options?.correctRange && /\(min-width: [0-9]+em\)/.test(query)) {
const size = parseFloat(query.match(/\(min-width: ([0-9]+)em\)/)[1]) || 0;
if (size > 0) {
const correctedSize = (size * 16 + 0.1) / 16;
query = query.replace(/(min-width: [0-9]+em)/, `min-width: ${correctedSize}em`);
}
}
return query;
}
function getValueByFeature(value, types = null) {
types = types || defaultBreakpoints;
if (Object.hasOwn(types, value)) {
value = types[value];
}
return value;
}
//# sourceMappingURL=MediaQueryUtils.js.map