| 1 | const feature_unit = {
|
|---|
| 2 | 'width': 'px',
|
|---|
| 3 | 'height': 'px',
|
|---|
| 4 | 'device-width': 'px',
|
|---|
| 5 | 'device-height': 'px',
|
|---|
| 6 | 'aspect-ratio': '',
|
|---|
| 7 | 'device-aspect-ratio': '',
|
|---|
| 8 | 'color': '',
|
|---|
| 9 | 'color-index': '',
|
|---|
| 10 | 'monochrome': '',
|
|---|
| 11 | 'resolution': 'dpi'
|
|---|
| 12 | };
|
|---|
| 13 |
|
|---|
| 14 | // Supported min-/max- attributes
|
|---|
| 15 | const feature_name = Object.keys(feature_unit);
|
|---|
| 16 |
|
|---|
| 17 | const step = .001; // smallest even number that won’t break complex queries (1in = 96px)
|
|---|
| 18 |
|
|---|
| 19 | const power = {
|
|---|
| 20 | '>': 1,
|
|---|
| 21 | '<': -1
|
|---|
| 22 | };
|
|---|
| 23 |
|
|---|
| 24 | const minmax = {
|
|---|
| 25 | '>': 'min',
|
|---|
| 26 | '<': 'max'
|
|---|
| 27 | };
|
|---|
| 28 |
|
|---|
| 29 | function create_query(name, gtlt, eq, value) {
|
|---|
| 30 | return value.replace(/([-\d\.]+)(.*)/, function (_match, number, unit) {
|
|---|
| 31 | const initialNumber = parseFloat(number);
|
|---|
| 32 |
|
|---|
| 33 | if (parseFloat(number) || eq) {
|
|---|
| 34 | // if eq is true, then number remains same
|
|---|
| 35 | if (!eq) {
|
|---|
| 36 | // change integer pixels value only on integer pixel
|
|---|
| 37 | if (unit === 'px' && initialNumber === parseInt(number, 10)) {
|
|---|
| 38 | number = initialNumber + power[gtlt];
|
|---|
| 39 | } else {
|
|---|
| 40 | number = Number(Math.round(parseFloat(number) + step * power[gtlt] + 'e6')+'e-6');
|
|---|
| 41 | }
|
|---|
| 42 | }
|
|---|
| 43 | } else {
|
|---|
| 44 | number = power[gtlt] + feature_unit[name];
|
|---|
| 45 | }
|
|---|
| 46 |
|
|---|
| 47 | return '(' + minmax[gtlt] + '-' + name + ': ' + number + unit + ')';
|
|---|
| 48 | });
|
|---|
| 49 | }
|
|---|
| 50 |
|
|---|
| 51 | function transform(rule) {
|
|---|
| 52 | /**
|
|---|
| 53 | * 转换 <mf-name> <|>= <mf-value>
|
|---|
| 54 | * $1 $2 $3
|
|---|
| 55 | * (width >= 300px) => (min-width: 300px)
|
|---|
| 56 | * (width <= 900px) => (max-width: 900px)
|
|---|
| 57 | */
|
|---|
| 58 |
|
|---|
| 59 | if (!rule.params.includes('<') && !rule.params.includes('>')) {
|
|---|
| 60 | return
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | // The value doesn't support negative values
|
|---|
| 64 | // But -0 is always equivalent to 0 in CSS, and so is also accepted as a valid <mq-boolean> value.
|
|---|
| 65 |
|
|---|
| 66 | rule.params = rule.params.replace(/\(\s*([a-z-]+?)\s*([<>])(=?)\s*((?:-?\d*\.?(?:\s*\/?\s*)?\d+[a-z]*)?)\s*\)/gi, function($0, $1, $2, $3, $4) {
|
|---|
| 67 | if (feature_name.indexOf($1) > -1) {
|
|---|
| 68 | return create_query($1, $2, $3, $4);
|
|---|
| 69 | }
|
|---|
| 70 | // If it is not the specified attribute, don't replace
|
|---|
| 71 | return $0;
|
|---|
| 72 | })
|
|---|
| 73 |
|
|---|
| 74 | /**
|
|---|
| 75 | * 转换 <mf-value> <|<= <mf-name> <|<= <mf-value>
|
|---|
| 76 | * 转换 <mf-value> >|>= <mf-name> >|>= <mf-value>
|
|---|
| 77 | * $1 $2$3 $4 $5$6 $7
|
|---|
| 78 | * (500px <= width <= 1200px) => (min-width: 500px) and (max-width: 1200px)
|
|---|
| 79 | * (500px < width <= 1200px) => (min-width: 501px) and (max-width: 1200px)
|
|---|
| 80 | * (900px >= width >= 300px) => (min-width: 300px) and (max-width: 900px)
|
|---|
| 81 | */
|
|---|
| 82 |
|
|---|
| 83 | rule.params = rule.params.replace(/\(\s*((?:-?\d*\.?(?:\s*\/?\s*)?\d+[a-z]*)?)\s*(<|>)(=?)\s*([a-z-]+)\s*(<|>)(=?)\s*((?:-?\d*\.?(?:\s*\/?\s*)?\d+[a-z]*)?)\s*\)/gi, function($0, $1, $2, $3, $4, $5, $6, $7) {
|
|---|
| 84 |
|
|---|
| 85 | if (feature_name.indexOf($4) > -1) {
|
|---|
| 86 | if ($2 === '<' && $5 === '<' || $2 === '>' && $5 === '>') {
|
|---|
| 87 | const min = ($2 === '<') ? $1 : $7;
|
|---|
| 88 | const max = ($2 === '<') ? $7 : $1;
|
|---|
| 89 |
|
|---|
| 90 | // output differently depended on expression direction
|
|---|
| 91 | // <mf-value> <|<= <mf-name> <|<= <mf-value>
|
|---|
| 92 | // or
|
|---|
| 93 | // <mf-value> >|>= <mf-name> >|>= <mf-value>
|
|---|
| 94 | let equals_for_min = $3;
|
|---|
| 95 | let equals_for_max = $6;
|
|---|
| 96 |
|
|---|
| 97 | if ($2 === '>') {
|
|---|
| 98 | equals_for_min = $6;
|
|---|
| 99 | equals_for_max = $3;
|
|---|
| 100 | }
|
|---|
| 101 |
|
|---|
| 102 | return create_query($4, '>', equals_for_min, min) + ' and ' + create_query($4, '<', equals_for_max, max);
|
|---|
| 103 | }
|
|---|
| 104 | }
|
|---|
| 105 | // If it is not the specified attribute, don't replace
|
|---|
| 106 | return $0;
|
|---|
| 107 | });
|
|---|
| 108 | }
|
|---|
| 109 |
|
|---|
| 110 | module.exports = () => ({
|
|---|
| 111 | postcssPlugin: 'postcss-media-minmax',
|
|---|
| 112 | AtRule: {
|
|---|
| 113 | media: (atRule) => {
|
|---|
| 114 | transform(atRule);
|
|---|
| 115 | },
|
|---|
| 116 | 'custom-media': (atRule) => {
|
|---|
| 117 | transform(atRule);
|
|---|
| 118 | },
|
|---|
| 119 | },
|
|---|
| 120 | });
|
|---|
| 121 |
|
|---|
| 122 | module.exports.postcss = true
|
|---|