@platform/css
Version:
Helpers for working with inline CSS.
269 lines (268 loc) • 8.24 kB
JavaScript
import { jss, R, valueUtil } from '../common';
import { toEdges } from './util';
export * from './util';
export const MEDIA_QUERY_RETINA = `@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)`;
export const image = (image1x, image2x, options = { width: 10, height: 10 }) => {
if (!image1x) {
throw new Error('Must have at least a 1x image.');
}
const { width, height } = options;
const result = {
width,
height,
backgroundImage: `url(${image1x})`,
backgroundSize: `${width}px ${height}px`,
backgroundRepeat: 'no-repeat',
};
if (image2x) {
result[MEDIA_QUERY_RETINA] = {
backgroundImage: `url(${image2x})`,
};
}
return result;
};
const mergeAndReplace = (key, value, target) => {
Object.assign(target, value);
delete target[key];
return target;
};
const formatImage = (key, value, target) => {
let [image1x, image2x, width, height] = value;
if (typeof image2x === 'number') {
height = width;
width = image2x;
image2x = undefined;
}
const options = {
width: width,
height: height,
};
const style = image(image1x, image2x, options);
mergeAndReplace(key, style, target);
};
export const toPositionEdges = (key, value = undefined) => {
const edges = toEdges(value);
if (R.isEmpty(edges)) {
return undefined;
}
const { left, top, right, bottom } = edges;
if (top === undefined && right === undefined && bottom === undefined && left === undefined) {
return undefined;
}
return {
position: key.toLowerCase(),
top,
right,
bottom,
left,
};
};
export const formatPositionEdges = (key, target) => {
const styles = toPositionEdges(key, target[key]);
mergeAndReplace(key, styles, target);
};
const formatAbsoluteCenter = (key, value, target) => {
if (value === true) {
value = 'xy';
}
if (value === false || value === undefined || value === null) {
return;
}
const styles = {
position: 'absolute',
left: target.left,
top: target.top,
transform: '',
};
const stringValue = value
.toString()
.trim()
.toLowerCase();
if (stringValue.includes('x')) {
styles.left = '50%';
}
if (stringValue.includes('y')) {
styles.top = '50%';
}
let transform;
switch (value) {
case 'yx':
case 'xy':
transform = 'translate(-50%, -50%)';
break;
case 'x':
transform = 'translateX(-50%)';
break;
case 'y':
transform = 'translateY(-50%)';
break;
default:
throw new Error(`AbsoluteCenter value '${value}' not supported.`);
}
styles.transform = `${target.transform || ''} ${transform}`.trim();
mergeAndReplace(key, styles, target);
};
function formatSpacingPlane(plane, prefix, key, value, target) {
const styles = {};
const edges = toEdges(value);
if (edges && plane.includes('x')) {
styles[`${prefix}Left`] = edges.left;
styles[`${prefix}Right`] = edges.right;
}
if (edges && plane.includes('y')) {
styles[`${prefix}Top`] = edges.top;
styles[`${prefix}Bottom`] = edges.bottom;
}
mergeAndReplace(key, styles, target);
}
function formatScroll(key, value, target) {
if (value === true) {
const styles = {
overflowX: 'hidden',
overflowY: 'scroll',
WebkitOverflowScrolling: 'touch',
};
mergeAndReplace(key, styles, target);
}
if (value === false) {
const styles = {
overflow: 'hidden',
};
mergeAndReplace(key, styles, target);
}
}
const AlignMap = {
center: 'center',
left: 'flex-start',
top: 'flex-start',
start: 'flex-start',
right: 'flex-end',
bottom: 'flex-end',
end: 'flex-end',
full: 'stretch',
stretch: 'stretch',
baseline: 'baseline',
};
function convertCrossAlignToFlex(token) {
return AlignMap[token] || undefined;
}
const MainAlignMap = {
center: 'center',
left: 'flex-start',
top: 'flex-start',
start: 'flex-start',
right: 'flex-end',
bottom: 'flex-end',
end: 'flex-end',
spaceBetween: 'space-between',
spaceAround: 'space-around',
spaceEvenly: 'space-evenly',
};
function convertMainAlignToFlex(token) {
return MainAlignMap[token] || undefined;
}
function formatFlexPosition(key, value, target) {
let direction;
let mainAlignment;
let crossAlignment;
const tokens = value.split('-').map(token => token.trim());
tokens.map(token => {
const tokenIsOneOf = (options) => options.includes(token);
if (direction == null && tokenIsOneOf(['horizontal', 'vertical'])) {
direction = token === 'vertical' ? 'column' : 'row';
return;
}
if (tokenIsOneOf(['center', 'start', 'end', 'left', 'right', 'top', 'bottom', 'full', 'baseline'])) {
if (crossAlignment == null) {
if (direction == null && tokenIsOneOf(['left', 'right'])) {
direction = 'column';
}
if (direction == null && tokenIsOneOf(['top', 'bottom'])) {
direction = 'row';
}
crossAlignment = convertCrossAlignToFlex(token);
return;
}
mainAlignment = convertMainAlignToFlex(token);
return;
}
if (tokenIsOneOf(['spaceAround', 'spaceBetween', 'spaceEvenly'])) {
mainAlignment = convertMainAlignToFlex(token);
return;
}
});
const styles = {
display: 'flex',
flexDirection: direction,
alignItems: crossAlignment,
justifyContent: mainAlignment,
};
mergeAndReplace(key, styles, target);
}
export const transform = (style = {}) => {
if (style == null) {
return {};
}
if (style === false) {
return {};
}
if (typeof style !== 'object') {
return style;
}
Object.keys(style).forEach(key => {
const value = style[key];
if (value === false || value === null || value === undefined) {
delete style[key];
}
else if (valueUtil.isPlainObject(value)) {
style[key] = transform(value);
}
else {
switch (key) {
case 'Image':
formatImage(key, value, style);
break;
case 'Absolute':
formatPositionEdges(key, style);
break;
case 'Fixed':
formatPositionEdges(key, style);
break;
case 'AbsoluteCenter':
formatAbsoluteCenter(key, value, style);
break;
case 'Margin':
formatSpacingPlane('xy', 'margin', key, value, style);
break;
case 'MarginX':
formatSpacingPlane('x', 'margin', key, value, style);
break;
case 'MarginY':
formatSpacingPlane('y', 'margin', key, value, style);
break;
case 'Padding':
formatSpacingPlane('xy', 'padding', key, value, style);
break;
case 'PaddingX':
formatSpacingPlane('x', 'padding', key, value, style);
break;
case 'PaddingY':
formatSpacingPlane('y', 'padding', key, value, style);
break;
case 'Flex':
formatFlexPosition(key, value, style);
break;
case 'Scroll':
formatScroll(key, value, style);
break;
default:
}
}
});
return style;
};
const formatCss = (...styles) => {
return jss.css(...styles.map(transform));
};
formatCss.image = image;
export const format = formatCss;