Skip to content
This repository was archived by the owner on Feb 18, 2022. It is now read-only.

Commit 606e092

Browse files
committed
3.0.0
1 parent 581ba23 commit 606e092

19 files changed

+900
-399
lines changed

.tape.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ module.exports = {
7474
options: {
7575
dir: 'ltr'
7676
}
77+
},
78+
'transition': {
79+
message: 'supports logical "transition" properties'
80+
},
81+
'transition:ltr': {
82+
message: 'supports logical "transition" properties with { dir: "ltr" }',
83+
options: {
84+
dir: 'ltr'
85+
}
7786
}
7887
}
7988
};

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changes to PostCSS Logical Properties
22

3+
### 3.0.0 (September 20, 2018)
4+
5+
- Added: Support for logical properties within `transition` and
6+
`transition-property`.
7+
- Changed: Physical rule fallbacks are written as full selectors rather than
8+
as nesting selectors.
9+
310
### 2.0.0 (September 17, 2018)
411

512
- Updated: Support for PostCSS v7+

README.md

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,15 @@ physical, direction and dimension mappings in CSS, following the
2020
2121
/* becomes */
2222
23-
.banner {
24-
color: #222222;
25-
top: 0; left: 5px; bottom: 10px; right: 5px;
23+
.banner:dir(ltr) {
24+
padding-left: 20px; padding-right: 40px;
25+
}
2626
27-
&:dir(ltr) {
28-
padding-left: 20px; padding-right: 40px;
29-
}
30-
31-
&:dir(rtl) {
32-
padding-right: 20px; padding-left: 40px;
33-
}
27+
.banner:dir(rtl) {
28+
padding-right: 20px; padding-left: 40px;
29+
}
3430
31+
.banner {
3532
resize: vertical;
3633
transition: color 200ms;
3734
}
@@ -48,18 +45,17 @@ physical, direction and dimension mappings in CSS, following the
4845
4946
/* or, when used with { preserve: true } */
5047
48+
.banner:dir(ltr) {
49+
padding-left: 20px; padding-right: 40px;
50+
}
51+
52+
.banner:dir(rtl) {
53+
padding-right: 20px; padding-left: 40px;
54+
}
55+
5156
.banner {
5257
color: #222222;
5358
top: 0; left: 5px; bottom: 10px; right: 5px;
54-
55-
&:dir(ltr) {
56-
padding-left: 20px; padding-right: 40px;
57-
}
58-
59-
&:dir(rtl) {
60-
padding-right: 20px; padding-left: 40px;
61-
}
62-
6359
inset: logical 0 5px 10px;
6460
padding-inline: 20px 40px;
6561
resize: block;

index.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import transformSide from './lib/transform-side';
1010
import transformSize from './lib/transform-size';
1111
import transformSpacing from './lib/transform-spacing';
1212
import transformTextAlign from './lib/transform-text-align';
13+
import transformTransition from './lib/transform-transition';
1314
import matchSupportedProperties from './lib/match-supported-properties';
15+
import { splitBySlash, splitBySpace } from './lib/split';
1416

1517
// supported transforms
1618
const transforms = {
@@ -38,9 +40,14 @@ const transforms = {
3840
'float': transformFloat,
3941
'resize': transformResize,
4042
'size': transformSize,
41-
'text-align': transformTextAlign
43+
'text-align': transformTextAlign,
44+
'transition': transformTransition,
45+
'transition-property': transformTransition
4246
};
4347

48+
// properties that will be split by slash
49+
const splitBySlashPropRegExp = /^border(-block|-inline|-start|-end)?(-width|-style|-color)?$/i;
50+
4451
// plugin
4552
export default postcss.plugin('postcss-logical-properties', opts => {
4653
const preserve = Boolean(Object(opts).preserve);
@@ -52,17 +59,28 @@ export default postcss.plugin('postcss-logical-properties', opts => {
5259

5360
return root => {
5461
root.walkDecls(decl => {
55-
const values = postcss.list.split(decl.value, /^border(-block|-inline|-start|-end)?(-width|-style|-color)?$/i.test(decl.prop) ? '/' : ' ');
62+
const parent = decl.parent;
63+
const values = splitBySlashPropRegExp.test(decl.prop) ? splitBySlash(decl.value, true) : splitBySpace(decl.value, true);
5664
const prop = decl.prop.replace(matchSupportedProperties, '$2$5').toLowerCase();
5765

5866
if (prop in transforms) {
5967
const replacer = transforms[prop](decl, values, dir);
6068

6169
if (replacer) {
62-
if (preserve) {
63-
decl.before(replacer);
64-
} else {
65-
decl.replaceWith(replacer);
70+
[].concat(replacer).forEach(replacement => {
71+
if (replacement.type === 'rule') {
72+
parent.before(replacement);
73+
} else {
74+
decl.before(replacement);
75+
}
76+
});
77+
78+
if (!preserve) {
79+
decl.remove();
80+
81+
if (!parent.nodes.length) {
82+
parent.remove();
83+
}
6684
}
6785
}
6886
}

lib/clone-rule.js

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
1-
'use strict';
2-
31
import postcss from 'postcss';
42

53
export default (decl, dir) => {
6-
let node = decl.parent;
7-
8-
while (node && 'rule' !== node.type) {
9-
node = node.parent;
10-
}
11-
12-
if (node) {
13-
node = node.clone({
14-
raws: {}
15-
}).removeAll()
16-
} else {
17-
node = postcss.rule();
18-
}
4+
const rule = Object(decl.parent).type === 'rule' ? decl.parent.clone({
5+
raws: {}
6+
}).removeAll() : postcss.rule({ selector: '&' });
197

20-
node.selector = `&:dir(${dir})`;
8+
rule.selectors = rule.selectors.map(selector => `${selector}:dir(${dir})`);
219

22-
return node;
10+
return rule;
2311
};

lib/split.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
export function splitByComma(string, isTrimmed) {
2+
return splitByRegExp(string, /^,$/, isTrimmed);
3+
}
4+
5+
export function splitBySpace(string, isTrimmed) {
6+
return splitByRegExp(string, /^\s$/, isTrimmed);
7+
}
8+
9+
export function splitBySlash(string, isTrimmed) {
10+
return splitByRegExp(string, /^\/$/, isTrimmed);
11+
}
12+
13+
function splitByRegExp(string, re, isTrimmed) {
14+
const array = [];
15+
let buffer = '';
16+
let split = false;
17+
let func = 0;
18+
let i = -1;
19+
20+
while (++i < string.length) {
21+
const char = string[i];
22+
23+
if (char === '(') {
24+
func += 1;
25+
} else if (char === ')') {
26+
if (func > 0) {
27+
func -= 1;
28+
}
29+
} else if (func === 0) {
30+
if (re.test(char)) {
31+
split = true;
32+
}
33+
}
34+
35+
if (split) {
36+
if (!isTrimmed || buffer.trim()) {
37+
array.push(isTrimmed ? buffer.trim() : buffer);
38+
}
39+
40+
if (!isTrimmed) {
41+
array.push(char);
42+
}
43+
44+
buffer = '';
45+
split = false;
46+
} else {
47+
buffer += char
48+
}
49+
}
50+
51+
if (buffer !== '') {
52+
array.push(isTrimmed ? buffer.trim() : buffer);
53+
}
54+
55+
return array;
56+
}

lib/transform-transition.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import cloneRule from './clone-rule';
2+
import { splitByComma, splitBySpace } from './split';
3+
4+
export default (decl, notValues, dir) => {
5+
const ltrValues = [];
6+
const rtlValues = [];
7+
8+
splitByComma(decl.value).forEach(value => {
9+
let hasBeenSplit = false;
10+
11+
splitBySpace(value).forEach((word, index, words) => {
12+
if (word in valueMap) {
13+
hasBeenSplit = true;
14+
15+
valueMap[word].ltr.forEach(replacement => {
16+
const clone = words.slice();
17+
18+
clone.splice(index, 1, replacement);
19+
20+
if (ltrValues.length && !/^,$/.test(ltrValues[ltrValues.length - 1])) {
21+
ltrValues.push(',');
22+
}
23+
24+
ltrValues.push(clone.join(''));
25+
});
26+
27+
valueMap[word].rtl.forEach(replacement => {
28+
const clone = words.slice();
29+
30+
clone.splice(index, 1, replacement);
31+
32+
if (rtlValues.length && !/^,$/.test(rtlValues[rtlValues.length - 1])) {
33+
rtlValues.push(',');
34+
}
35+
36+
rtlValues.push(clone.join(''));
37+
});
38+
}
39+
});
40+
41+
if (!hasBeenSplit) {
42+
ltrValues.push(value);
43+
rtlValues.push(value);
44+
}
45+
});
46+
47+
const ltrDecl = decl.clone({ value: ltrValues.join('') });
48+
const rtlDecl = decl.clone({ value: rtlValues.join('') });
49+
50+
return ltrValues.length && 'ltr' === dir
51+
? ltrDecl
52+
: rtlValues.length && 'rtl' === dir
53+
? rtlDecl
54+
: ltrDecl.value !== rtlDecl.value
55+
? [
56+
cloneRule(decl, 'ltr').append(ltrDecl),
57+
cloneRule(decl, 'rtl').append(rtlDecl)
58+
]
59+
: null;
60+
};
61+
62+
const valueMap = {
63+
'border-block': { ltr: ['border-top', 'border-bottom'], rtl: ['border-top', 'border-bottom'] },
64+
'border-block-color': { ltr: ['border-top-color', 'border-bottom-color'], rtl: ['border-top-color', 'border-bottom-color'] },
65+
'border-block-end': { ltr: ['border-bottom'], rtl: ['border-bottom'] },
66+
'border-block-end-color': { ltr: ['border-bottom-color'], rtl: ['border-bottom-color'] },
67+
'border-block-end-style': { ltr: ['border-bottom-style'], rtl: ['border-bottom-style'] },
68+
'border-block-end-width': { ltr: ['border-bottom-width'], rtl: ['border-bottom-width'] },
69+
'border-block-start': { ltr: ['border-top'], rtl: ['border-top'] },
70+
'border-block-start-color': { ltr: ['border-top-color'], rtl: ['border-top-color'] },
71+
'border-block-start-style': { ltr: ['border-top-style'], rtl: ['border-top-style'] },
72+
'border-block-start-width': { ltr: ['border-top-width'], rtl: ['border-top-width'] },
73+
'border-block-style': { ltr: ['border-top-style', 'border-bottom-style'], rtl: ['border-top-style', 'border-bottom-style'] },
74+
'border-block-width': { ltr: ['border-top-width', 'border-bottom-width'], rtl: ['border-top-width', 'border-bottom-width'] },
75+
'border-end': { ltr: ['border-bottom', 'border-right'], rtl: ['border-bottom', 'border-left'] },
76+
'border-end-color': { ltr: ['border-bottom-color', 'border-right-color'], rtl: ['border-bottom-color', 'border-left-color'] },
77+
'border-end-style': { ltr: ['border-bottom-style', 'border-right-style'], rtl: ['border-bottom-style', 'border-left-style'] },
78+
'border-end-width': { ltr: ['border-bottom-width', 'border-right-width'], rtl: ['border-bottom-width', 'border-left-width'] },
79+
'border-inline': { ltr: ['border-left', 'border-right'], rtl: ['border-left', 'border-right'] },
80+
'border-inline-color': { ltr: ['border-left-color', 'border-right-color'], rtl: ['border-left-color', 'border-right-color'] },
81+
'border-inline-end': { ltr: ['border-right'], rtl: ['border-left'] },
82+
'border-inline-end-color': { ltr: ['border-right-color'], rtl: ['border-left-color'] },
83+
'border-inline-end-style': { ltr: ['border-right-style'], rtl: ['border-left-style'] },
84+
'border-inline-end-width': { ltr: ['border-right-width'], rtl: ['border-left-width'] },
85+
'border-inline-start': { ltr: ['border-left'], rtl: ['border-right'] },
86+
'border-inline-start-color': { ltr: ['border-left-color'], rtl: ['border-right-color'] },
87+
'border-inline-start-style': { ltr: ['border-left-style'], rtl: ['border-right-style'] },
88+
'border-inline-start-width': { ltr: ['border-left-width'], rtl: ['border-right-width'] },
89+
'border-inline-style': { ltr: ['border-left-style', 'border-right-style'], rtl: ['border-left-style', 'border-right-style'] },
90+
'border-inline-width': { ltr: ['border-left-width', 'border-right-width'], rtl: ['border-left-width', 'border-right-width'] },
91+
'border-start': { ltr: ['border-top', 'border-left'], rtl: ['border-top', 'border-right'] },
92+
'border-start-color': { ltr: ['border-top-color', 'border-left-color'], rtl: ['border-top-color', 'border-right-color'] },
93+
'border-start-style': { ltr: ['border-top-style', 'border-left-style'], rtl: ['border-top-style', 'border-right-style'] },
94+
'border-start-width': { ltr: ['border-top-width', 'border-left-width'], rtl: ['border-top-width', 'border-right-width'] },
95+
'block-size': { ltr: ['height'], rtl: ['height'] },
96+
'inline-size': { ltr: ['width'], rtl: ['width'] },
97+
'inset': { ltr: ['top', 'right', 'bottom', 'left'], rtl: ['top', 'right', 'bottom', 'left'] },
98+
'inset-block': { ltr: ['top', 'bottom'], rtl: ['top', 'bottom'] },
99+
'inset-block-start': { ltr: ['top'], rtl: ['top'] },
100+
'inset-block-end': { ltr: ['bottom'], rtl: ['bottom'] },
101+
'inset-end': { ltr: ['bottom', 'right'], rtl: ['bottom', 'left'] },
102+
'inset-inline': { ltr: ['left', 'right'], rtl: ['left', 'right'] },
103+
'inset-inline-start': { ltr: ['left'], rtl: ['right'] },
104+
'inset-inline-end': { ltr: ['right'], rtl: ['left'] },
105+
'inset-start': { ltr: ['top', 'left'], rtl: ['top', 'right'] },
106+
'margin-block': { ltr: ['margin-top', 'margin-bottom'], rtl: ['margin-top', 'margin-bottom'] },
107+
'margin-block-start': { ltr: ['margin-top'], rtl: ['margin-top'] },
108+
'margin-block-end': { ltr: ['margin-bottom'], rtl: ['margin-bottom'] },
109+
'margin-end': { ltr: ['margin-bottom', 'margin-right'], rtl: ['margin-bottom', 'margin-left'] },
110+
'margin-inline': { ltr: ['margin-left', 'margin-right'], rtl: ['margin-left', 'margin-right'] },
111+
'margin-inline-start': { ltr: ['margin-left'], rtl: ['margin-right'] },
112+
'margin-inline-end': { ltr: ['margin-right'], rtl: ['margin-left'] },
113+
'margin-start': { ltr: ['margin-top', 'margin-left'], rtl: ['margin-top', 'margin-right'] },
114+
'padding-block': { ltr: ['padding-top', 'padding-bottom'], rtl: ['padding-top', 'padding-bottom'] },
115+
'padding-block-start': { ltr: ['padding-top'], rtl: ['padding-top'] },
116+
'padding-block-end': { ltr: ['padding-bottom'], rtl: ['padding-bottom'] },
117+
'padding-end': { ltr: ['padding-bottom', 'padding-right'], rtl: ['padding-bottom', 'padding-left'] },
118+
'padding-inline': { ltr: ['padding-left', 'padding-right'], rtl: ['padding-left', 'padding-right'] },
119+
'padding-inline-start': { ltr: ['padding-left'], rtl: ['padding-right'] },
120+
'padding-inline-end': { ltr: ['padding-right'], rtl: ['padding-left'] },
121+
'padding-start': { ltr: ['padding-top', 'padding-left'], rtl: ['padding-top', 'padding-right'] },
122+
};

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "postcss-logical",
3-
"version": "2.0.0",
3+
"version": "3.0.0",
44
"description": "Use logical properties and values in CSS",
55
"author": "Jonathan Neal <jonathantneal@hotmail.com>",
66
"license": "CC0-1.0",
@@ -30,16 +30,16 @@
3030
"postcss": "^7.0.2"
3131
},
3232
"devDependencies": {
33-
"@babel/core": "^7.0.0",
34-
"@babel/preset-env": "^7.0.0",
33+
"@babel/core": "^7.1.0",
34+
"@babel/preset-env": "^7.1.0",
3535
"babel-eslint": "^9.0.0",
3636
"echint": "^4.0.1",
3737
"eslint": "^5.6.0",
3838
"eslint-config-dev": "^2.0.0",
3939
"postcss-tape": "^2.2.0",
4040
"pre-commit": "^1.2.2",
41-
"rollup": "^0.66.0",
42-
"rollup-plugin-babel": "^4.0.1"
41+
"rollup": "^0.66.1",
42+
"rollup-plugin-babel": "^4.0.3"
4343
},
4444
"eslintConfig": {
4545
"extends": "dev",

0 commit comments

Comments
 (0)