UNPKG

react-native-dynamic-style

Version:

lightweight library for react native to manage component/screens with animation

272 lines (256 loc) 8.72 kB
import { useMemo } from 'react'; import { ImageStyle, StyleSheet, TextStyle, ViewStyle, useWindowDimensions, } from 'react-native'; type DeepRecord = Record<string, Record<string, any>>; type FunctionType = (...args: any) => any; type FunctionStyleParams = { width: number; height: number; isExtraSmallAndUp: boolean; isExtraSmall: boolean; isSmallAndUp: boolean; isSmall: boolean; isSmallAndDown: boolean; isMediumAndUp: boolean; isMedium: boolean; isMediumAndDown: boolean; isLargeAndUp: boolean; isLarge: boolean; isLargeAndDown: boolean; isExtraLargeAndUp: boolean; isExtraLarge: boolean; isExtraLargeAndDown: boolean; isExtraExtraLarge: boolean; isExtraExtraLargeAndDown: boolean; }; type FunctionStyle<T> = (params: FunctionStyleParams) => T | undefined; type BreakpointStyle< XS = any, S = any, M = any, L = any, XL = any, XXL = any, > = { xs?: XS; s?: S; m?: M; l?: L; xl?: XL; xxl?: XXL }; type ContainNumber = number | string | undefined; type DynamicNumberStyle<T> = T extends undefined ? undefined : T extends number ? number | [number, number] | FunctionStyle<T> : T | FunctionStyle<T>; type DynamicStyle<T> = { [P in keyof T]: T[P] extends ContainNumber ? | DynamicNumberStyle<T[P]> | BreakpointStyle<T[P], T[P], T[P], T[P], T[P], T[P]> : | T[P] | BreakpointStyle<T[P], T[P], T[P], T[P], T[P], T[P]> | FunctionStyle<T[P]>; }; type DynamicNamedStyles<T> = { [P in keyof T]: | DynamicStyle<ViewStyle> | BreakpointStyle< ViewStyle, ViewStyle, ViewStyle, ViewStyle, ViewStyle, ViewStyle > | DynamicStyle<TextStyle> | BreakpointStyle< TextStyle, TextStyle, TextStyle, TextStyle, TextStyle, TextStyle > | DynamicStyle<ImageStyle> | BreakpointStyle< ImageStyle, ImageStyle, ImageStyle, ImageStyle, ImageStyle, ImageStyle >; }; type NamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle }; type StaticBreakpointStyle<T extends BreakpointStyle> = | (T extends { xs?: any } ? T['xs'] : undefined) | (T extends { s?: any } ? T['s'] : undefined) | (T extends { m?: any } ? T['m'] : undefined) | (T extends { l?: any } ? T['l'] : undefined) | (T extends { xl?: any } ? T['xl'] : undefined) | (T extends { xxl?: any } ? T['xxl'] : undefined); type StaticNamedStyles<T> = { [P in keyof T]: T[P] extends BreakpointStyle ? | StaticBreakpointStyle<T[P]> | Omit<T[P], 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl'> : { [S in keyof T[P]]: T[P][S] extends [number, number] ? number : T[P][S] extends FunctionType ? ReturnType<T[P][S]> : T[P][S] extends BreakpointStyle ? StaticBreakpointStyle<T[P][S]> : T[P][S]; }; }; const DynamicStyleSheet = { create: <T extends DynamicNamedStyles<T>>( styles: T | DynamicNamedStyles<T>, ): T => { return styles as T; }, getRelativeValue: ( minValue: number, minRelativeValue: number, maxValue: number, maxRelativeValue: number, value: number, ) => { if (value <= minValue) { return minRelativeValue; } else if (value >= maxValue) { return maxRelativeValue; } else { if (minRelativeValue < maxRelativeValue) { return ( minRelativeValue + (value - minValue) * ((maxRelativeValue - minRelativeValue) / (maxValue - minValue)) ); } else { return ( minRelativeValue - (value - minValue) * ((minRelativeValue - maxRelativeValue) / (maxValue - minValue)) ); } } }, getBreakpointStyle: ( style: Record<string, any>, breakpointsParams: FunctionStyleParams, ) => { let breakpointStyle: any; if ('xs' in style && breakpointsParams.isExtraSmallAndUp) { breakpointStyle = style.xs; } if ('s' in style && breakpointsParams.isSmallAndUp) { breakpointStyle = style.s; } if ('m' in style && breakpointsParams.isMediumAndUp) { breakpointStyle = style.m; } if ('l' in style && breakpointsParams.isLargeAndUp) { breakpointStyle = style.l; } if ('xl' in style && breakpointsParams.isExtraLargeAndUp) { breakpointStyle = style.xl; } if ('xxl' in style && breakpointsParams.isExtraExtraLarge) { breakpointStyle = style.xxl; } return breakpointStyle; }, }; const useBreakpoints = (): FunctionStyleParams => { const { width, height } = useWindowDimensions(); const isExtraSmallAndUp = width >= 0; const isExtraSmall = width < 576; const isSmallAndUp = width >= 576; const isSmall = width >= 576 && width < 768; const isSmallAndDown = width < 768; const isMediumAndUp = width >= 768; const isMedium = width >= 768 && width < 992; const isMediumAndDown = width < 992; const isLargeAndUp = width >= 992; const isLarge = width >= 992 && width < 1200; const isLargeAndDown = width < 1200; const isExtraLargeAndUp = width >= 1200; const isExtraLarge = width >= 1200 && width < 1400; const isExtraLargeAndDown = width < 1400; const isExtraExtraLarge = width >= 1400; const isExtraExtraLargeAndDown = width >= 1400 || width < 1400; return { width, height, isExtraSmallAndUp, isExtraSmall, isSmallAndUp, isSmall, isSmallAndDown, isMediumAndUp, isMedium, isMediumAndDown, isLargeAndUp, isLarge, isLargeAndDown, isExtraLargeAndUp, isExtraLarge, isExtraLargeAndDown, isExtraExtraLarge, isExtraExtraLargeAndDown, }; }; const useDynamicStyles = <T extends DynamicNamedStyles<T>>( dynamicStyles: T | DynamicNamedStyles<T>, params?: { minWidth: number; maxWidth: number }, ): StaticNamedStyles<T> => { const breakpointsParams = useBreakpoints(); return useMemo(() => { const styles: DeepRecord = {}; const dynamicStylesRecord = dynamicStyles as DeepRecord; for (const styleKey of Object.keys(dynamicStyles)) { const breakpointStyle = DynamicStyleSheet.getBreakpointStyle( dynamicStylesRecord[styleKey], breakpointsParams, ); styles[styleKey] = { ...breakpointStyle }; for (const styleProperty of Object.keys( dynamicStylesRecord[styleKey], )) { const styleValue = dynamicStylesRecord[styleKey][styleProperty]; if (Array.isArray(styleValue)) { if (styleValue.length >= 2 && params) { styles[styleKey][styleProperty] = DynamicStyleSheet.getRelativeValue( params.minWidth, styleValue[0], params.maxWidth, styleValue[1], breakpointsParams.width, ); } } else if (typeof styleValue === 'function') { styles[styleKey][styleProperty] = styleValue(breakpointsParams); } else if (typeof styleValue === 'object') { styles[styleKey][styleProperty] = DynamicStyleSheet.getBreakpointStyle( styleValue, breakpointsParams, ); } else { styles[styleKey][styleProperty] = styleValue; } } } return StyleSheet.create(styles as NamedStyles<T>); }, [dynamicStyles, params, breakpointsParams]); }; export default DynamicStyleSheet; export { useDynamicStyles, useBreakpoints };