native-base
Version:
Essential cross-platform UI components for React Native
656 lines (625 loc) • 13.6 kB
text/typescript
import { StyleSheet } from 'react-native';
import get from 'lodash.get';
import { transparentize } from './tools';
import type { ITheme } from '.';
import type { UseResponsiveQueryParams } from '../utils/useResponsiveQuery';
import { getStyledFromProps } from '../utils/getStyledFromProps';
import { convertStringNumberToNumber } from '../utils/convertStringNumberToNumber';
const isNumber = (n: any) => typeof n === 'number' && !isNaN(n);
export const getColor = (rawValue: any, scale: any, theme: any) => {
const alphaMatched =
typeof rawValue === 'string' ? rawValue?.match(/:alpha\.\d\d?\d?/) : false;
if (alphaMatched) {
const colorMatched = rawValue?.match(/^.*?(?=:alpha)/);
const color = colorMatched ? colorMatched[0] : colorMatched;
const alphaValue = alphaMatched[0].split('.')[1];
const alphaFromToken = get(theme.opacity, alphaValue, alphaValue);
const alpha = alphaFromToken ? parseFloat(alphaFromToken) : 1;
const newColor = transparentize(color, alpha)(theme);
return newColor;
} else {
return get(scale, rawValue, rawValue);
}
};
// To handle negative margins
const getMargin = (n: any, scale: any) => {
n = convertStringNumberToNumber('margin', n);
if (!isNumber(n)) {
return get(scale, n, n);
}
const isNegative = n < 0;
const absolute = Math.abs(n);
const value = get(scale, absolute, absolute);
if (!isNumber(value)) {
return isNegative ? '-' + value : value;
}
return value * (isNegative ? -1 : 1);
};
export const layout = {
width: {
property: 'width',
scale: 'sizes',
},
w: {
property: 'width',
scale: 'sizes',
},
height: {
property: 'height',
scale: 'sizes',
},
h: {
property: 'height',
scale: 'sizes',
},
minWidth: {
property: 'minWidth',
scale: 'sizes',
},
minW: {
property: 'minWidth',
scale: 'sizes',
},
minHeight: {
property: 'minHeight',
scale: 'sizes',
},
minH: {
property: 'minHeight',
scale: 'sizes',
},
maxWidth: {
property: 'maxWidth',
scale: 'sizes',
},
maxW: {
property: 'maxWidth',
scale: 'sizes',
},
maxHeight: {
property: 'maxHeight',
scale: 'sizes',
},
maxH: {
property: 'maxHeight',
scale: 'sizes',
},
size: {
properties: ['width', 'height'],
scale: 'sizes',
},
boxSize: {
properties: ['width', 'height'],
scale: 'sizes',
},
overflow: true,
overflowX: true,
overflowY: true,
display: true,
verticalAlign: true,
textAlign: true,
} as const;
export const flexbox = {
alignItems: true,
alignContent: true,
justifyItems: true,
justifyContent: true,
flexWrap: true,
flexDirection: true,
flexDir: {
property: 'flexDirection',
scale: 'flexDirection',
},
// item
flex: true,
flexGrow: true,
flexShrink: true,
flexBasis: true,
justifySelf: true,
alignSelf: true,
order: true,
} as const;
export const position = {
position: true,
zIndex: {
property: 'zIndex',
},
top: {
property: 'top',
scale: 'space',
},
right: {
property: 'right',
scale: 'space',
},
bottom: {
property: 'bottom',
scale: 'space',
},
left: {
property: 'left',
scale: 'space',
},
} as const;
export const color = {
color: {
property: 'color',
scale: 'colors',
transformer: getColor,
},
tintColor: {
property: 'tintColor',
scale: 'colors',
transformer: getColor,
},
backgroundColor: {
property: 'backgroundColor',
scale: 'colors',
transformer: getColor,
},
opacity: {
property: 'opacity',
scale: 'opacity',
},
bg: {
property: 'backgroundColor',
scale: 'colors',
transformer: getColor,
},
bgColor: {
property: 'backgroundColor',
scale: 'colors',
transformer: getColor,
},
background: {
property: 'backgroundColor',
scale: 'colors',
transformer: getColor,
},
textDecorationColor: {
property: 'textDecorationColor',
scale: 'colors',
transformer: getColor,
},
} as const;
export const border = {
borderWidth: {
property: 'borderWidth',
scale: 'borderWidths',
},
borderStyle: {
property: 'borderStyle',
scale: 'borderStyles',
},
borderColor: {
property: 'borderColor',
scale: 'colors',
transformer: getColor,
},
borderRadius: {
property: 'borderRadius',
scale: 'radii',
},
borderTop: {
property: 'borderTop',
scale: 'borders',
},
borderTopRadius: {
properties: ['borderTopLeftRadius', 'borderTopRightRadius'],
scale: 'radii',
},
borderLeftRadius: {
properties: ['borderTopLeftRadius', 'borderBottomLeftRadius'],
scale: 'radii',
},
borderRightRadius: {
properties: ['borderTopRightRadius', 'borderBottomRightRadius'],
scale: 'radii',
},
borderTopLeftRadius: {
property: 'borderTopLeftRadius',
scale: 'radii',
},
borderTopRightRadius: {
property: 'borderTopRightRadius',
scale: 'radii',
},
borderRight: {
property: 'borderRight',
scale: 'borders',
},
borderBottom: {
property: 'borderBottom',
scale: 'borders',
},
borderBottomLeftRadius: {
property: 'borderBottomLeftRadius',
scale: 'radii',
},
borderBottomRightRadius: {
property: 'borderBottomRightRadius',
scale: 'radii',
},
borderLeft: {
property: 'borderLeft',
scale: 'borders',
},
borderX: {
properties: ['borderLeft', 'borderRight'],
scale: 'borders',
},
borderY: {
properties: ['borderTop', 'borderBottom'],
scale: 'borders',
},
borderTopWidth: {
property: 'borderTopWidth',
scale: 'borderWidths',
},
borderTopColor: {
property: 'borderTopColor',
scale: 'colors',
transformer: getColor,
},
borderTopStyle: {
property: 'borderTopStyle',
scale: 'borderStyles',
},
borderBottomWidth: {
property: 'borderBottomWidth',
scale: 'borderWidths',
},
borderBottomColor: {
property: 'borderBottomColor',
scale: 'colors',
transformer: getColor,
},
borderBottomStyle: {
property: 'borderBottomStyle',
scale: 'borderStyles',
},
borderLeftWidth: {
property: 'borderLeftWidth',
scale: 'borderWidths',
},
borderLeftColor: {
property: 'borderLeftColor',
scale: 'colors',
transformer: getColor,
},
borderLeftStyle: {
property: 'borderLeftStyle',
scale: 'borderStyles',
},
borderRightWidth: {
property: 'borderRightWidth',
scale: 'borderWidths',
},
borderRightColor: {
property: 'borderRightColor',
scale: 'colors',
transformer: getColor,
},
borderRightStyle: {
property: 'borderRightStyle',
scale: 'borderStyles',
},
rounded: {
property: 'borderRadius',
scale: 'radii',
},
roundedTopLeft: {
property: 'borderTopLeftRadius',
scale: 'radii',
},
roundedTopRight: {
property: 'borderTopRightRadius',
scale: 'radii',
},
roundedBottomLeft: {
property: 'borderBottomLeftRadius',
scale: 'radii',
},
roundedBottomRight: {
property: 'borderBottomRightRadius',
scale: 'radii',
},
roundedTop: {
properties: ['borderTopLeftRadius', 'borderTopRightRadius'],
scale: 'radii',
},
borderBottomRadius: {
properties: ['borderBottomLeftRadius', 'borderBottomRightRadius'],
scale: 'radii',
},
roundedBottom: {
properties: ['borderBottomLeftRadius', 'borderBottomRightRadius'],
scale: 'radii',
},
roundedLeft: {
properties: ['borderTopLeftRadius', 'borderBottomLeftRadius'],
scale: 'radii',
},
roundedRight: {
properties: ['borderTopRightRadius', 'borderBottomRightRadius'],
scale: 'radii',
},
} as const;
export const background = {
backgroundSize: true,
backgroundPosition: true,
backgroundRepeat: true,
backgroundAttachment: true,
backgroundBlendMode: true,
bgImage: {
property: 'backgroundImage',
},
bgImg: {
property: 'backgroundImage',
},
bgBlendMode: {
property: 'backgroundBlendMode',
},
bgSize: {
property: 'backgroundSize',
},
bgPosition: {
property: 'backgroundPosition',
},
bgPos: {
property: 'backgroundPosition',
},
bgRepeat: {
property: 'backgroundRepeat',
},
bgAttachment: {
property: 'backgroundAttachment',
},
} as const;
export const space = {
margin: {
property: 'margin',
scale: 'space',
transformer: getMargin,
},
m: {
property: 'margin',
scale: 'space',
transformer: getMargin,
},
marginTop: {
property: 'marginTop',
scale: 'space',
transformer: getMargin,
},
mt: {
property: 'marginTop',
scale: 'space',
transformer: getMargin,
},
marginRight: {
property: 'marginRight',
scale: 'space',
transformer: getMargin,
},
mr: {
property: 'marginRight',
scale: 'space',
transformer: getMargin,
},
marginBottom: {
property: 'marginBottom',
scale: 'space',
transformer: getMargin,
},
mb: {
property: 'marginBottom',
scale: 'space',
transformer: getMargin,
},
marginLeft: {
property: 'marginLeft',
scale: 'space',
transformer: getMargin,
},
ml: {
property: 'marginLeft',
scale: 'space',
transformer: getMargin,
},
marginX: {
properties: ['marginLeft', 'marginRight'],
scale: 'space',
transformer: getMargin,
},
mx: {
properties: ['marginLeft', 'marginRight'],
scale: 'space',
transformer: getMargin,
},
marginY: {
properties: ['marginTop', 'marginBottom'],
scale: 'space',
transformer: getMargin,
},
my: {
properties: ['marginTop', 'marginBottom'],
scale: 'space',
transformer: getMargin,
},
padding: {
property: 'padding',
scale: 'space',
},
p: {
property: 'padding',
scale: 'space',
},
paddingTop: {
property: 'paddingTop',
scale: 'space',
},
pt: {
property: 'paddingTop',
scale: 'space',
},
paddingRight: {
property: 'paddingRight',
scale: 'space',
},
pr: {
property: 'paddingRight',
scale: 'space',
},
paddingBottom: {
property: 'paddingBottom',
scale: 'space',
},
pb: {
property: 'paddingBottom',
scale: 'space',
},
paddingLeft: {
property: 'paddingLeft',
scale: 'space',
},
pl: {
property: 'paddingLeft',
scale: 'space',
},
paddingX: {
properties: ['paddingLeft', 'paddingRight'],
scale: 'space',
},
px: {
properties: ['paddingLeft', 'paddingRight'],
scale: 'space',
},
paddingY: {
properties: ['paddingTop', 'paddingBottom'],
scale: 'space',
},
py: {
properties: ['paddingTop', 'paddingBottom'],
scale: 'space',
},
gap: {
property: 'gap',
scale: 'space',
},
} as const;
export const typography = {
fontFamily: {
property: 'fontFamily',
scale: 'fonts',
// transformer: (val: any, scale: any) => {
// const value = get(scale, val);
// return value ? value.toString() : undefined;
// },
},
fontSize: {
property: 'fontSize',
scale: 'fontSizes',
},
fontWeight: {
property: 'fontWeight',
scale: 'fontWeights',
transformer: (val: any, scale: any) => {
return val ? get(scale, val, val).toString() : val;
},
},
lineHeight: {
property: 'lineHeight',
scale: 'lineHeights',
},
letterSpacing: {
property: 'letterSpacing',
scale: 'letterSpacings',
},
textAlign: true,
fontStyle: true,
wordBreak: true,
overflowWrap: true,
textOverflow: true,
textTransform: true,
whiteSpace: true,
textDecoration: { property: 'textDecorationLine' },
txtDecor: { property: 'textDecorationLine' },
textDecorationLine: true,
} as const;
const extraProps = {
outline: true,
outlineWidth: true,
outlineColor: true,
outlineStyle: true,
shadow: {
scale: 'shadows',
},
cursor: true,
overflow: true,
userSelect: { property: 'userSelect' },
} as const;
export const propConfig = {
...color,
...space,
...layout,
...flexbox,
...border,
...position,
...typography,
...background,
...extraProps,
} as const;
export const getStyleAndFilteredProps = ({
style,
theme,
debug,
currentBreakpoint,
getResponsiveStyles,
styledSystemProps,
}: any) => {
let dataSet: any = {};
const orderedBreakPoints = Object.entries(
theme.breakpoints as ITheme['breakpoints']
).sort((a, b) => a[1] - b[1]);
let { styleFromProps, responsiveStyles }: any = getStyledFromProps(
styledSystemProps,
theme,
currentBreakpoint,
propConfig
);
if (responsiveStyles) {
const query: UseResponsiveQueryParams = { query: [] };
orderedBreakPoints.forEach((o) => {
const key = o[0];
if (key === 'base') {
if (responsiveStyles) query.initial = responsiveStyles.base;
} else {
if (responsiveStyles)
if (key in responsiveStyles) {
query?.query?.push({
minWidth: o[1],
style: responsiveStyles[key],
});
}
}
});
const { dataSet: newDataSet, styles } = getResponsiveStyles(query);
dataSet = { ...dataSet, ...newDataSet };
styleFromProps = { ...styleFromProps, ...StyleSheet.flatten(styles) };
}
if (process.env.NODE_ENV === 'development' && debug) {
/* eslint-disable-next-line */
console.log('style ', debug + ' :: ', {
styleFromProps,
style,
styledSystemProps,
});
}
return {
styleSheet: StyleSheet.create({ box: styleFromProps }),
styleFromProps,
dataSet,
};
};
export type StyledPropConfig = typeof propConfig;