react-native-styled-px2dp
Version:
Write CSS px in react-native using styled-components and adapt multi resolution screens automatically.
104 lines (85 loc) • 3.88 kB
text/typescript
const { Dimensions } = require('react-native')
const styled = require('styled-components/native').default;
import { ReactNativeStyledInterface, DefaultTheme} from "styled-components/native"
type IOrientation = 'portrait'|'landscape'
// current deisgn width and height which are affected by orientation
// they will switch value with each other after orientation changed
let currentDesignWidth, currentDesignHeight
// device orientation, 'protrait' by default
let orientation: IOrientation = 'portrait'
// always re-get current screen width(becuz of oritation changes)
const currentScreenWidth = () => Dimensions.get('screen').width
const relativeCaculator = (px: number) => {
return Number(
((px / currentDesignWidth) * currentScreenWidth()).toFixed(2)
)
}
const stringToRelativePX = (cssStr: string) => {
// calling cssStr.replace on non-string will trigger error
if(typeof cssStr !== 'string') return cssStr
return cssStr.replace(/([\d|.]+)px/gm, (matched, pxNumber) => {
// you have to write px in styled components
// and css-to-react-native(a dependency of Styled Components) will translate it to RN unit, which is dp
return relativeCaculator(pxNumber) + "px";
});
}
const interpolationToRelativePX = (interpolation) => {
if (typeof interpolation === 'string') {
return stringToRelativePX(interpolation)
} else { // deal with non-string interpolations like functions
if (typeof interpolation === 'function') {
// wrap the original function with stringToRelativePX and rewrite it
const originFunction = interpolation
const wrappedInterpolation = (...args) => stringToRelativePX(originFunction(...args))
return wrappedInterpolation
}
// just return if it is non-function interpolation
return interpolation;
}
}
//for extending styles like styled(SomeComponent)`blahblah`
const extendingStyled = tag => (strings, ...interpolations) => {
const transformedStrings = strings.map(stringToRelativePX)
const transformedInterpolations = interpolations.map(interpolationToRelativePX)
return styled(tag)(transformedStrings, ...transformedInterpolations)
}
//for using primitives directly like styled.View`blahblah`
const flexibleStyled = new Proxy(extendingStyled, {
get: (target, prop) =>
// return Tagged Template Literal to pretend styled component
(strings, ...interpolations) => {
const transformedStrings = strings.map(stringToRelativePX)
const transformedInterpolations = interpolations.map(interpolationToRelativePX)
return styled[prop](transformedStrings, ...transformedInterpolations)
}
})
const updateOrientation = (newOrientation: IOrientation) => {
if(newOrientation !== orientation) {
let temp = currentDesignWidth
currentDesignWidth = currentDesignHeight
currentDesignHeight = temp
orientation = newOrientation
}
}
interface IFlexibleInitProps {
designWidth: number,
designHeight?: number,
orientation?: IOrientation
}
const getFlexibleStyled = (props: IFlexibleInitProps) => {
currentDesignWidth = props.designWidth
if(props.designHeight) {
currentDesignHeight = props.designHeight
}
if(props.orientation) {
orientation = props.orientation
// throws warns if it is not a typical 'portrait' or 'landscape'
if(orientation === 'portrait' && props.designWidth > props.designHeight) {
console.warn("You are setting orientation to 'portrait' while you passed a designWidth greater than designHeight")
} else if (orientation === 'landscape' && props.designWidth < props.designHeight) {
console.warn("You are setting orientation to 'landscape' while you passed a designWidth less than designHeight")
}
}
return { styled: flexibleStyled as ReactNativeStyledInterface<DefaultTheme>, px2dp: relativeCaculator, updateOrientation}
}
export default getFlexibleStyled