UNPKG

postcss-logical-properties-polyfill

Version:

PostCSS plugin that polyfill W3C's CSS proposal to support logical properties and values

369 lines (368 loc) 15.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformToNonLogical = exports.isSupportedProp = void 0; const postcss_1 = __importStar(require("postcss")); function getDirectionParams(decl, options) { if (decl.prop.includes('block')) { return ['block', options.blockStart, options.blockEnd]; } else { return ['inline', options.inlineStart, options.inlineEnd]; } } const replaceStartEndValue = (decl, { valueStart, valueEnd }) => { const value = decl.value.toLowerCase(); if (value === 'start' || value === 'inline-start') { return decl.clone({ value: valueStart }); } if (value === 'end' || value === 'inline-end') { return decl.clone({ value: valueEnd }); } }; const replaceResize = (decl, { resizeBlock, resizeInline }) => { const value = decl.value.toLowerCase(); if (value === 'block') { return decl.clone({ value: resizeBlock }); } if (value === 'inline') { return decl.clone({ value: resizeInline }); } }; const replaceDimension = (decl, { blockSize, inlineSize }) => { if (decl.prop.includes('block')) { return decl.clone({ prop: decl.prop.replace('block-size', blockSize) }); } else { return decl.clone({ prop: decl.prop.replace('inline-size', inlineSize) }); } }; const replaceDirectionalBoxShorthand = (decl, options) => { var _a; const [direction, start, end] = getDirectionParams(decl, options); const parts = postcss_1.list.space(decl.value); return [ decl.clone({ prop: decl.prop.replace(direction, start), value: parts[0] }), decl.clone({ prop: decl.prop.replace(direction, end), value: (_a = parts[1]) !== null && _a !== void 0 ? _a : parts[0] }), ]; }; const replaceBox = (decl, options) => { const [direction, start, end] = getDirectionParams(decl, options); if (decl.prop.toLowerCase().includes('start')) { return decl.clone({ prop: decl.prop.replace(`${direction}-start`, start) }); } else { return decl.clone({ prop: decl.prop.replace(`${direction}-end`, end) }); } }; const replaceSimpleRL = (decl, options) => { const [direction, start, end] = getDirectionParams(decl, options); return [ decl.clone({ prop: decl.prop.replace(direction, start) }), decl.clone({ prop: decl.prop.replace(direction, end) }), ]; }; const replaceInlinePositioning = (decl, { inlineStart, inlineEnd }) => { if (decl.prop.toLowerCase().includes('start')) { return decl.clone({ prop: inlineStart }); } else { return decl.clone({ prop: inlineEnd }); } }; const replaceBoxShorthand = (decl, options) => { var _a, _b, _c, _d; const parts = postcss_1.list.space(decl.value); if (parts[0] !== 'logical') { return; } return [ decl.clone({ prop: `${decl.prop}-${options.blockStart}`, value: parts[1] }), decl.clone({ prop: `${decl.prop}-${options.inlineEnd}`, value: (_a = parts[2]) !== null && _a !== void 0 ? _a : parts[1] }), decl.clone({ prop: `${decl.prop}-${options.blockEnd}`, value: (_b = parts[3]) !== null && _b !== void 0 ? _b : parts[1] }), decl.clone({ prop: `${decl.prop}-${options.inlineStart}`, value: (_d = (_c = parts[4]) !== null && _c !== void 0 ? _c : parts[2]) !== null && _d !== void 0 ? _d : parts[1] }), ]; }; const replaceInsetShorthand = (decl, options) => { var _a, _b, _c, _d, _e, _f, _g, _h; const parts = postcss_1.list.space(decl.value); if (parts[0] === 'logical') { return [ decl.clone({ prop: options.blockStart, value: parts[1] }), decl.clone({ prop: options.inlineStart, value: (_a = parts[2]) !== null && _a !== void 0 ? _a : parts[1] }), decl.clone({ prop: options.blockEnd, value: (_b = parts[3]) !== null && _b !== void 0 ? _b : parts[1] }), decl.clone({ prop: options.inlineEnd, value: (_d = (_c = parts[4]) !== null && _c !== void 0 ? _c : parts[2]) !== null && _d !== void 0 ? _d : parts[1] }), ]; } return [ decl.clone({ prop: 'top', value: parts[0] }), decl.clone({ prop: 'left', value: (_e = parts[1]) !== null && _e !== void 0 ? _e : parts[0] }), decl.clone({ prop: 'bottom', value: (_f = parts[2]) !== null && _f !== void 0 ? _f : parts[0] }), decl.clone({ prop: 'right', value: (_h = (_g = parts[3]) !== null && _g !== void 0 ? _g : parts[1]) !== null && _h !== void 0 ? _h : parts[0] }), ]; }; const replaceBorderShorthand = (decl, options) => { var _a, _b, _c, _d; const parts = postcss_1.list.space(decl.value); if (parts[0] !== 'logical') { return; } const nameParts = decl.prop.split('-'); return [ decl.clone({ prop: `${nameParts[0]}-${options.blockStart}-${nameParts[1]}`, value: parts[1] }), decl.clone({ prop: `${nameParts[0]}-${options.inlineStart}-${nameParts[1]}`, value: (_a = parts[2]) !== null && _a !== void 0 ? _a : parts[1] }), decl.clone({ prop: `${nameParts[0]}-${options.blockEnd}-${nameParts[1]}`, value: (_b = parts[3]) !== null && _b !== void 0 ? _b : parts[1] }), decl.clone({ prop: `${nameParts[0]}-${options.inlineEnd}-${nameParts[1]}`, value: (_d = (_c = parts[4]) !== null && _c !== void 0 ? _c : parts[2]) !== null && _d !== void 0 ? _d : parts[1], }), ]; }; const replacePositioningShorthand = (decl, options) => { var _a; const [, start, end] = getDirectionParams(decl, options); const parts = postcss_1.list.space(decl.value); return [decl.clone({ prop: start, value: parts[0] }), decl.clone({ prop: end, value: (_a = parts[1]) !== null && _a !== void 0 ? _a : parts[0] })]; }; const replaceBorderRadius = (decl, options) => { return decl.clone({ prop: decl.prop .replace('start-start', options.borderStartStart) .replace('start-end', options.borderStartEnd) .replace('end-start', options.borderEndStart) .replace('end-end', options.borderEndEnd), }); }; const replaceTransition = (decl, options) => { const rawItems = postcss_1.list.comma(decl.value); // There might be "transition-property" or shorthand "transition" // In any case, property name is always comes first, so each element at [0] will contain a property name const parsedItems = rawItems.map(postcss_1.list.space); const indexesToModify = parsedItems.reduce((indexes, [prop], i) => { if (isSupportedProp(prop)) { indexes.push(i); } return indexes; }, []); if (indexesToModify.length === 0) { return; } let hasChangedProps = false; let countOfNewProps = 0; indexesToModify.forEach((i) => { const [prop, ...restParams] = parsedItems[i + countOfNewProps]; const tempDecl = postcss_1.default.decl({ prop, value: 'initial', }); const newDecls = shouldFindTransformer(prop)(tempDecl, options); if (!newDecls) { return; } (Array.isArray(newDecls) ? newDecls : [newDecls]).forEach((newDecl, declarationIndex) => { hasChangedProps = true; if (declarationIndex === 0) { parsedItems[i + countOfNewProps][0] = newDecl.prop; } else { parsedItems.splice(i + countOfNewProps, 0, [newDecl.prop, ...restParams]); countOfNewProps++; } }); }); if (!hasChangedProps) { return; } return decl.clone({ value: parsedItems.map((item) => item.join(' ')).join(', '), }); }; const transformationMap = { // https://www.w3.org/TR/css-logical-1/#float-clear float: replaceStartEndValue, clear: replaceStartEndValue, // https://www.w3.org/TR/css-logical-1/#text-align 'text-align': replaceStartEndValue, // https://www.w3.org/TR/css-logical-1/#resize resize: replaceResize, // https://www.w3.org/TR/css-logical-1/#dimension-properties 'block-size': replaceDimension, 'inline-size': replaceDimension, 'min-block-size': replaceDimension, 'min-inline-size': replaceDimension, 'max-block-size': replaceDimension, 'max-inline-size': replaceDimension, // https://www.w3.org/TR/css-logical-1/#margin-properties margin: replaceBoxShorthand, 'margin-block': replaceDirectionalBoxShorthand, 'margin-block-start': replaceBox, 'margin-block-end': replaceBox, 'margin-inline': replaceDirectionalBoxShorthand, 'margin-inline-start': replaceBox, 'margin-inline-end': replaceBox, // https://www.w3.org/TR/css-logical-1/#inset-properties inset: replaceInsetShorthand, 'inset-block': replacePositioningShorthand, 'inset-block-start': replaceInlinePositioning, 'inset-block-end': replaceInlinePositioning, 'inset-inline': replacePositioningShorthand, 'inset-inline-start': replaceInlinePositioning, 'inset-inline-end': replaceInlinePositioning, // https://www.w3.org/TR/css-logical-1/#padding-properties padding: replaceBoxShorthand, 'padding-inline': replaceDirectionalBoxShorthand, 'padding-inline-start': replaceBox, 'padding-inline-end': replaceBox, 'padding-block': replaceDirectionalBoxShorthand, 'padding-block-start': replaceBox, 'padding-block-end': replaceBox, // https://www.w3.org/TR/css-logical-1/#border-width 'border-width': replaceBorderShorthand, 'border-block-width': replaceDirectionalBoxShorthand, 'border-block-start-width': replaceBox, 'border-block-end-width': replaceBox, 'border-inline-width': replaceDirectionalBoxShorthand, 'border-inline-start-width': replaceBox, 'border-inline-end-width': replaceBox, // https://www.w3.org/TR/css-logical-1/#border-style 'border-style': replaceBorderShorthand, 'border-block-style': replaceDirectionalBoxShorthand, 'border-block-start-style': replaceBox, 'border-block-end-style': replaceBox, 'border-inline-style': replaceDirectionalBoxShorthand, 'border-inline-start-style': replaceBox, 'border-inline-end-style': replaceBox, // https://www.w3.org/TR/css-logical-1/#border-color 'border-color': replaceBorderShorthand, 'border-block-color': replaceDirectionalBoxShorthand, 'border-block-start-color': replaceBox, 'border-block-end-color': replaceBox, 'border-inline-color': replaceDirectionalBoxShorthand, 'border-inline-start-color': replaceBox, 'border-inline-end-color': replaceBox, // https://www.w3.org/TR/css-logical-1/#border-shorthands 'border-block': replaceSimpleRL, 'border-block-start': replaceBox, 'border-block-end': replaceBox, 'border-inline': replaceSimpleRL, 'border-inline-start': replaceBox, 'border-inline-end': replaceBox, // https://www.w3.org/TR/css-logical-1/#border-radius-shorthands 'border-start-start-radius': replaceBorderRadius, 'border-start-end-radius': replaceBorderRadius, 'border-end-start-radius': replaceBorderRadius, 'border-end-end-radius': replaceBorderRadius, transition: replaceTransition, 'transition-property': replaceTransition, }; function shouldFindTransformer(prop) { const transformer = transformationMap[prop.toLowerCase()]; if (!transformer) { throw new Error(`Unknown declaration property received: "${prop}"`); } return transformer; } function shouldGetTransformerOptions(writingMode, direction) { let options; switch (writingMode) { case 'horizontal-tb': options = { blockSize: 'height', inlineSize: 'width', inlineStart: 'left', inlineEnd: 'right', blockStart: 'top', blockEnd: 'bottom', borderStartStart: 'top-left', borderStartEnd: 'top-right', borderEndStart: 'bottom-left', borderEndEnd: 'bottom-right', }; break; case 'vertical-rl': case 'sideways-rl': options = { blockSize: 'width', inlineSize: 'height', inlineStart: 'top', inlineEnd: 'bottom', blockStart: 'right', blockEnd: 'left', borderStartStart: 'top-right', borderStartEnd: 'bottom-right', borderEndStart: 'top-left', borderEndEnd: 'bottom-left', }; break; case 'vertical-lr': options = { blockSize: 'width', inlineSize: 'height', inlineStart: 'top', inlineEnd: 'bottom', blockStart: 'left', blockEnd: 'right', borderStartStart: 'top-left', borderStartEnd: 'bottom-left', borderEndStart: 'top-right', borderEndEnd: 'bottom-right', }; break; case 'sideways-lr': options = { blockSize: 'width', inlineSize: 'height', inlineStart: 'bottom', inlineEnd: 'top', blockStart: 'left', blockEnd: 'right', borderStartStart: 'bottom-left', borderStartEnd: 'top-left', borderEndStart: 'bottom-right', borderEndEnd: 'top-right', }; break; default: throw new Error(`Unknown writing-mode received: "${writingMode}"`); } if (direction === 'rtl') { [options.inlineStart, options.inlineEnd] = [options.inlineEnd, options.inlineStart]; [options.borderStartStart, options.borderStartEnd] = [options.borderStartEnd, options.borderStartStart]; [options.borderEndStart, options.borderEndEnd] = [options.borderEndEnd, options.borderEndStart]; } return { valueStart: direction === 'ltr' ? 'left' : 'right', valueEnd: direction === 'ltr' ? 'right' : 'left', resizeBlock: writingMode === 'horizontal-tb' ? 'vertical' : 'horizontal', resizeInline: writingMode === 'horizontal-tb' ? 'horizontal' : 'vertical', ...options, }; } function isSupportedProp(prop) { return prop.toLowerCase() in transformationMap; } exports.isSupportedProp = isSupportedProp; function transformToNonLogical(decl, writingMode, direction) { const transformer = shouldFindTransformer(decl.prop); const options = shouldGetTransformerOptions(writingMode, direction); return transformer(decl, options); } exports.transformToNonLogical = transformToNonLogical;