rn-custom-style-sheet
Version:
React Native component to select a specific value from a range of values.
206 lines (197 loc) • 6.89 kB
JavaScript
;
import { between, down, not, only, up } from '../../../BreakPoint';
import { toPx } from '../../../Core';
import { makeQuery } from '../../Hooks';
const RE_MEDIA_QUERY =
// eslint-disable-next-line no-useless-escape
/^(?:(only|not)?\s*([_a-z][_a-z0-9-]*)|(\([^\)]+\)))(?:\s*and\s*(.*))?$/i;
const RE_MQ_EXPRESSION =
// eslint-disable-next-line no-useless-escape
/^\(\s*([_a-z-][_a-z0-9-]*)\s*(?:\:\s*([^\)]+))?\s*\)$/;
const RE_MQ_FEATURE = /^(?:(min|max|up|down|between|only|not)-)?(.+)/;
const RE_LENGTH_UNIT = /(em|rem|px|cm|mm|in|pt|pc)?\s*$/;
const RE_RESOLUTION_UNIT = /(dpi|dpcm|dppx)?\s*$/;
const RE_COMMENTS = /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi;
function matchQuery(mediaQuery, values, guideLineBreakpoint) {
return parseQuery(mediaQuery).some(function (query) {
const inverse = query.inverse;
//const only: boolean = query.only || false;
// Either the parsed or specified `type` is "all", or the types must be
// equal for a match.
const typeMatch = query.type === 'all' || values.type === query.type;
// Quit early when `type` doesn't match, but take "not" into account.
//only !== (values.only ?? false)
if (typeMatch && inverse || !(typeMatch || inverse)) {
return false;
}
const expressionsMatch = query.expressions.every(function (expression) {
const feature = expression.feature;
const modifier = expression.modifier;
let expValue = expression.value;
//@ts-ignore
let value = values[feature];
// Missing or falsy values don't match.
if (!value && value !== 0 && feature !== 'breakpoint') {
return false;
}
switch (feature) {
case 'orientation':
case 'scan':
return value.toLowerCase() === expValue.toLowerCase();
case 'width':
case 'height':
case 'device-width':
case 'device-height':
expValue = convertPx(expValue);
value = convertPx(value);
break;
case 'resolution':
expValue = toDpi(expValue);
value = toDpi(value);
break;
case 'aspect-ratio':
case 'device-aspect-ratio':
case /* Deprecated */'device-pixel-ratio':
expValue = toDecimal(expValue);
value = toDecimal(value);
break;
case 'pixel-ratio':
expValue = toDecimal(expValue);
value = toDecimal(value);
break;
case 'grid':
case 'color':
case 'color-index':
case 'monochrome':
expValue = parseInt(expValue, 10) || 1;
value = parseInt(value, 10) || 0;
break;
case 'breakpoint':
{
switch (modifier) {
case 'up':
return matchQuery(makeQuery({
query: up(expValue, guideLineBreakpoint)
}), values, guideLineBreakpoint);
case 'down':
return matchQuery(makeQuery({
query: down(expValue, guideLineBreakpoint)
}), values, guideLineBreakpoint);
case 'between':
const range = expValue.replaceAll('[', '').replaceAll(']', '').split(',').map(item => item.trim());
return matchQuery(makeQuery({
query: between(range[0], range[1], guideLineBreakpoint)
}), values, guideLineBreakpoint);
case 'only':
return matchQuery(makeQuery({
query: only(expValue, guideLineBreakpoint)
}), values, guideLineBreakpoint);
case 'not':
return matchQuery(makeQuery({
query: not(expValue, guideLineBreakpoint)
}), values, guideLineBreakpoint);
default:
return value === expValue;
}
}
default: //any
}
switch (modifier) {
case 'min':
return value >= expValue;
case 'max':
return value <= expValue;
default:
return value === expValue;
}
});
return expressionsMatch && !inverse || !expressionsMatch && inverse;
});
}
function parseQuery(mediaQuery) {
return mediaQuery.split('#').map(function (query) {
// Remove comments first
query = query.replace(RE_COMMENTS, '');
query = query.trim();
const captures = query.match(RE_MEDIA_QUERY);
// Media Query must be valid.
if (!captures) {
throw new SyntaxError('Invalid CSS media query : "' + query + '"');
}
const modifier = (captures[1] ?? '').trim();
const type = (captures[2] ?? '').trim();
const expressions = ((captures[3] ?? '') + (captures[4] ?? '')).trim();
const parsed = {
only: false,
inverse: false,
type: '',
expressions: []
};
parsed.only = !!modifier && modifier.toLowerCase() === 'only';
parsed.inverse = !!modifier && modifier.toLowerCase() === 'not';
parsed.type = type ? type.toLowerCase() : 'all';
// Check for media query expressions.
if (!expressions) {
parsed.expressions = [];
return parsed;
}
// Split expressions into a list.
const expressionList =
// eslint-disable-next-line no-useless-escape
expressions.match(/\([^\)]+\)/g);
// Media Query must be valid.
if (!expressionList) {
throw new SyntaxError('Invalid CSS media query :- "' + expressions + '"');
}
parsed.expressions = expressionList.map(function (expression) {
const capturesInner = expression.match(RE_MQ_EXPRESSION);
// Media Query must be valid.
if (!capturesInner) {
throw new SyntaxError('Invalid CSS media query := "' + expression + '"');
}
const feature = capturesInner?.[1]?.toLowerCase()?.match(RE_MQ_FEATURE);
return {
modifier: feature?.[1] ?? '',
feature: feature?.[2] ?? '',
value: capturesInner?.[2] ?? ''
};
});
return parsed;
});
}
function toDecimal(ratio) {
let decimal = Number(ratio);
let numbers;
if (!decimal) {
numbers = ratio.match(/^(\d+)\s*\/\s*(\d+)$/);
decimal = numbers[1] / numbers[2];
}
return decimal;
}
function toDpi(resolution) {
const value = parseFloat(resolution);
const units = String(resolution).match(RE_RESOLUTION_UNIT)?.[1] ?? '';
switch (units) {
case 'dpcm':
return value / 2.54;
case 'dppx':
return value * 96;
default:
return value;
}
}
function convertPx(length) {
const value = parseFloat(length);
const units = String(length).match(RE_LENGTH_UNIT)?.[1] ?? '';
return toPx(value, units);
}
function matchMedia(query, values, guideLineBreakpoint) {
const isMatches = matchQuery(query, values, guideLineBreakpoint);
const media = query;
return {
isMatches,
media
};
}
export default matchMedia;
//# sourceMappingURL=MatchMediaQuery.js.map