rn-css 1.7.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import { CompleteStyle } from './types';
2
+ declare const rnToCSS: (rn: Partial<CompleteStyle>) => string;
3
+ export default rnToCSS;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const rnToCSS = (rn) => Object.entries(rn)
4
+ .map(([key, value]) => `${camelToKebab(key)}: ${convertValue(value)};`)
5
+ .join('\n');
6
+ const camelToKebab = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
7
+ const convertValue = (value) => isNaN(value) ? value : (value + 'px');
8
+ exports.default = rnToCSS;
@@ -1,44 +1,44 @@
1
1
  import React, { MouseEvent } from 'react';
2
2
  import { FlatListProps, LayoutChangeEvent, SectionListProps, StyleProp, VirtualizedListProps } from 'react-native';
3
- import type { Units } from './types';
3
+ import type { AnyStyle, CompleteStyle, Units } from './types';
4
4
  export declare const defaultUnits: Units;
5
5
  export declare const RemContext: React.Context<number>;
6
6
  export declare const FontSizeContext: React.Context<number>;
7
7
  export declare const SharedValue: React.Context<unknown>;
8
8
  export interface DefaultTheme {
9
9
  }
10
- declare type Primitive = number | string | null | undefined | boolean;
10
+ declare type Primitive = number | string | null | undefined | boolean | CompleteStyle;
11
11
  declare type Functs<T> = (arg: T & {
12
12
  rnCSS?: string;
13
13
  shared: unknown;
14
14
  theme: DefaultTheme;
15
15
  }) => Primitive;
16
- declare type OptionalProps = {
16
+ declare type OptionalProps<T extends AnyStyle = AnyStyle> = {
17
17
  rnCSS?: `${string};`;
18
18
  onMouseEnter?: (event: MouseEvent) => void;
19
19
  onMouseLeave?: (event: MouseEvent) => void;
20
20
  onLayout?: (event: LayoutChangeEvent) => void;
21
21
  children?: React.ReactNode;
22
- style?: StyleProp<any>;
22
+ style?: StyleProp<T>;
23
23
  };
24
- declare const styled: <Props>(Component: React.ComponentType<Props>) => {
25
- <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & Props>)[]): React.ForwardRefExoticComponent<Props & S & OptionalProps & {
24
+ declare const styled: <Props, StyleType extends AnyStyle = AnyStyle>(Component: React.ComponentType<Props>) => {
25
+ <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & Props>)[]): React.ForwardRefExoticComponent<Props & S & OptionalProps<StyleType> & {
26
26
  ref?: React.Ref<any> | undefined;
27
27
  }>;
28
- attrs<S_1>(opts: Partial<S_1 & Props> | ((props: S_1 & Props) => Partial<S_1 & Props>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & Props>)[]) => React.ForwardRefExoticComponent<(Props | S_1) & OptionalProps & {
28
+ attrs<S_1>(opts: Partial<S_1 & Props> | ((props: S_1 & Props) => Partial<S_1 & Props>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & Props>)[]) => React.ForwardRefExoticComponent<(Props | S_1) & OptionalProps<StyleType> & {
29
29
  ref?: React.Ref<any> | undefined;
30
30
  }>;
31
31
  };
32
32
  export default styled;
33
33
  export declare const styledFlatList: {
34
- <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S>)[]): <Type>(props: S & FlatListProps<Type> & OptionalProps) => JSX.Element;
35
- attrs<S_1>(opts: Partial<S_1 & FlatListProps<any>> | ((props: S_1 & FlatListProps<any>) => Partial<S_1 & FlatListProps<any>>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & FlatListProps<any>>)[]) => <Props>(componentProps: S_1 & FlatListProps<Props> & OptionalProps) => JSX.Element;
34
+ <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S>)[]): <Type>(props: S & FlatListProps<Type> & OptionalProps<AnyStyle>) => JSX.Element;
35
+ attrs<S_1>(opts: Partial<S_1 & FlatListProps<any>> | ((props: S_1 & FlatListProps<any>) => Partial<S_1 & FlatListProps<any>>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & FlatListProps<any>>)[]) => <Props>(componentProps: S_1 & FlatListProps<Props> & OptionalProps<AnyStyle>) => JSX.Element;
36
36
  };
37
37
  export declare const styledSectionList: {
38
- <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S>)[]): <Type>(props: S & SectionListProps<Type, import("react-native").DefaultSectionT> & OptionalProps) => JSX.Element;
39
- attrs<S_1>(opts: Partial<S_1 & SectionListProps<any, import("react-native").DefaultSectionT>> | ((props: S_1 & SectionListProps<any, import("react-native").DefaultSectionT>) => Partial<S_1 & SectionListProps<any, import("react-native").DefaultSectionT>>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & SectionListProps<any, import("react-native").DefaultSectionT>>)[]) => <Props>(componentProps: S_1 & SectionListProps<Props, import("react-native").DefaultSectionT> & OptionalProps) => JSX.Element;
38
+ <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S>)[]): <Type>(props: S & SectionListProps<Type, import("react-native").DefaultSectionT> & OptionalProps<AnyStyle>) => JSX.Element;
39
+ attrs<S_1>(opts: Partial<S_1 & SectionListProps<any, import("react-native").DefaultSectionT>> | ((props: S_1 & SectionListProps<any, import("react-native").DefaultSectionT>) => Partial<S_1 & SectionListProps<any, import("react-native").DefaultSectionT>>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & SectionListProps<any, import("react-native").DefaultSectionT>>)[]) => <Props>(componentProps: S_1 & SectionListProps<Props, import("react-native").DefaultSectionT> & OptionalProps<AnyStyle>) => JSX.Element;
40
40
  };
41
41
  export declare const styledVirtualizedList: {
42
- <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S>)[]): <Type>(props: S & VirtualizedListProps<Type> & OptionalProps) => JSX.Element;
43
- attrs<S_1>(opts: Partial<S_1 & VirtualizedListProps<any>> | ((props: S_1 & VirtualizedListProps<any>) => Partial<S_1 & VirtualizedListProps<any>>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & VirtualizedListProps<any>>)[]) => <Props>(componentProps: S_1 & VirtualizedListProps<Props> & OptionalProps) => JSX.Element;
42
+ <S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S>)[]): <Type>(props: S & VirtualizedListProps<Type> & OptionalProps<AnyStyle>) => JSX.Element;
43
+ attrs<S_1>(opts: Partial<S_1 & VirtualizedListProps<any>> | ((props: S_1 & VirtualizedListProps<any>) => Partial<S_1 & VirtualizedListProps<any>>)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1 & VirtualizedListProps<any>>)[]) => <Props>(componentProps: S_1 & VirtualizedListProps<Props> & OptionalProps<AnyStyle>) => JSX.Element;
44
44
  };
@@ -12,13 +12,21 @@ const convertStyle_1 = __importDefault(require("./convertStyle"));
12
12
  const cssToRN_1 = __importDefault(require("./cssToRN"));
13
13
  const features_1 = require("./features");
14
14
  const generateHash_1 = __importDefault(require("./generateHash"));
15
+ const rnToCss_1 = __importDefault(require("./rnToCss"));
15
16
  exports.defaultUnits = { em: 16, vw: 1, vh: 1, vmin: 1, vmax: 1, rem: 16, px: 1, pt: 72 / 96, in: 96, pc: 9, cm: 96 / 2.54, mm: 96 / 25.4 };
16
17
  exports.RemContext = react_1.default.createContext(exports.defaultUnits.rem);
17
18
  exports.FontSizeContext = react_1.default.createContext(exports.defaultUnits.em);
18
19
  // We use this to share value within the component (Theme, Translation, whatever)
19
20
  exports.SharedValue = react_1.default.createContext(undefined);
21
+ /** Converts the tagged template string into a css string */
20
22
  function buildCSSString(chunks, functs, props, shared) {
21
- let computedString = chunks.map((chunk, i) => ([chunk, (functs[i] instanceof Function) ? functs[i]({ shared, theme: shared, ...props }) : functs[i]])).flat().join('');
23
+ let computedString = chunks
24
+ // Evaluate the chunks from the tagged template
25
+ .map((chunk, i) => ([chunk, (functs[i] instanceof Function) ? functs[i]({ shared, theme: shared, ...props }) : functs[i]]))
26
+ .flat()
27
+ // Convert the objects to string if the result is not a primitive
28
+ .map(chunk => typeof chunk === 'object' ? (0, rnToCss_1.default)(chunk) : chunk)
29
+ .join('');
22
30
  if (props.rnCSS)
23
31
  computedString += props.rnCSS.replace(/=/gm, ':') + ';';
24
32
  return computedString;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ViewStyle, TextStyle } from 'react-native';
1
+ import { ViewStyle, TextStyle, ImageStyle } from 'react-native';
2
2
  export declare type Units = {
3
3
  '%'?: number;
4
4
  vw?: number;
@@ -51,7 +51,9 @@ export declare type Context = {
51
51
  deviceAspectRatio: number;
52
52
  units: Units;
53
53
  };
54
- export declare type PartialStyle = Partial<Record<keyof ViewStyle | keyof TextStyle, string>> & {
54
+ export declare type AnyStyle = ViewStyle | TextStyle | ImageStyle;
55
+ export declare type CompleteStyle = ViewStyle & TextStyle & ImageStyle;
56
+ export declare type PartialStyle = Partial<Record<keyof CompleteStyle, string>> & {
55
57
  shadowOffset?: {
56
58
  width: string;
57
59
  height: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-css",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "scripts": {
5
5
  "test": "jest",
6
6
  "prepare": "tsc",
@@ -1,12 +1,12 @@
1
1
  /* eslint-disable react/display-name */
2
- import { /* StyleSheet, */ TextStyle, ViewStyle } from 'react-native'
2
+ import { /* StyleSheet, */ TextStyle } from 'react-native'
3
3
  import { convertValue } from './convertUnits'
4
- import type { PartialStyle, Units } from './types'
4
+ import type { AnyStyle, CompleteStyle, PartialStyle, Units } from './types'
5
5
 
6
6
  /** Mix the calculated RN style within the object style */
7
- const convertStyle = (rnStyle: PartialStyle, units: Units) => {
7
+ const convertStyle = <T extends AnyStyle = AnyStyle>(rnStyle: PartialStyle, units: Units) => {
8
8
  /** This is the result of the convertions from css style into RN style */
9
- const convertedStyle: TextStyle & ViewStyle = {};
9
+ const convertedStyle: CompleteStyle = {};
10
10
  // If width and height are specified, we can use those values for the first render
11
11
  (['width', 'height'] as const).forEach(key => {
12
12
  if (!units[key] && rnStyle[key]) {
@@ -52,7 +52,7 @@ const convertStyle = (rnStyle: PartialStyle, units: Units) => {
52
52
  convertedStyle[key] = convertValue(key, value, units)
53
53
  }
54
54
  })
55
- return convertedStyle
55
+ return convertedStyle as T
56
56
  }
57
57
 
58
58
  export default convertStyle
@@ -11,7 +11,7 @@ export function parseValue (value: string): [number, string | undefined] {
11
11
 
12
12
  /** Convert a value using the provided unit transform table */
13
13
  export function convertValue (key: keyof PartialStyle | keyof Transform, value: string, units: Units): string | number {
14
- if (!(Object(value) instanceof String)) {
14
+ if (typeof value !== 'string') {
15
15
  console.error(`Failed to parse CSS instruction: ${key}=${value}. We expect a string, but ${value} was of type ${typeof value}.`)
16
16
  return 0
17
17
  }
package/src/index.tsx CHANGED
@@ -2,11 +2,12 @@ import React from 'react'
2
2
  import * as RN from 'react-native'
3
3
  import './polyfill'
4
4
  import styledComponent, { styledFlatList, styledSectionList, styledVirtualizedList } from './styleComponent'
5
+ import { AnyStyle } from './types'
5
6
  export { cssToRNStyle } from './cssToRN'
6
7
  export { SharedValue, FontSizeContext, RemContext, DefaultTheme } from './styleComponent'
7
8
  export * from './useTheme'
8
9
 
9
- const styled = <T, >(Component: React.ComponentType<T>) => styledComponent<T>(Component)
10
+ const styled = <T, >(Component: React.ComponentType<T>) => styledComponent<T, AnyStyle>(Component)
10
11
 
11
12
  styled.ActivityIndicator = styled(RN.ActivityIndicator)
12
13
  styled.Button = styled(RN.Button)
package/src/rnToCss.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { CompleteStyle } from './types'
2
+
3
+ const rnToCSS = (rn: Partial<CompleteStyle>) =>
4
+ Object.entries(rn)
5
+ .map(([key, value]) => `${camelToKebab(key)}: ${convertValue(value)};`)
6
+ .join('\n')
7
+
8
+ const camelToKebab = (str: string) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
9
+ const convertValue = (value: unknown) => isNaN(value as number) ? value : (value + 'px')
10
+
11
+ export default rnToCSS
@@ -1,12 +1,13 @@
1
1
  /* eslint-disable react/prop-types */
2
2
  /* eslint-disable react/display-name */
3
3
  import React, { MouseEvent } from 'react'
4
- import { FlatList, FlatListProps, LayoutChangeEvent, Platform, SectionList, SectionListProps, StyleProp, StyleSheet, TextStyle, ViewStyle, VirtualizedList, VirtualizedListProps } from 'react-native'
4
+ import { FlatList, FlatListProps, LayoutChangeEvent, Platform, SectionList, SectionListProps, StyleProp, StyleSheet, VirtualizedList, VirtualizedListProps } from 'react-native'
5
5
  import convertStyle from './convertStyle'
6
6
  import cssToStyle from './cssToRN'
7
7
  import { useFontSize, useHover, useLayout, useScreenSize, useMediaQuery } from './features'
8
- import type { Style, Units } from './types'
8
+ import type { AnyStyle, CompleteStyle, Style, Units } from './types'
9
9
  import generateHash from './generateHash'
10
+ import rnToCSS from './rnToCss'
10
11
 
11
12
  export const defaultUnits: Units = { em: 16, vw: 1, vh: 1, vmin: 1, vmax: 1, rem: 16, px: 1, pt: 72 / 96, in: 96, pc: 9, cm: 96 / 2.54, mm: 96 / 25.4 }
12
13
  export const RemContext = React.createContext<number>(defaultUnits.rem)
@@ -18,40 +19,49 @@ export const SharedValue = React.createContext<unknown>(undefined)
18
19
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
19
20
  export interface DefaultTheme {}
20
21
 
21
- type Primitive = number | string | null | undefined | boolean
22
+ type Primitive = number | string | null | undefined | boolean | CompleteStyle
22
23
  type Functs<T> = (arg: T & { rnCSS?: string; shared: unknown, theme: DefaultTheme }) => Primitive
23
- type OptionalProps = {
24
+ type OptionalProps<T extends AnyStyle = AnyStyle> = {
24
25
  rnCSS?: `${string};`;
25
26
  onMouseEnter?: (event: MouseEvent) => void;
26
27
  onMouseLeave?: (event: MouseEvent) => void;
27
28
  onLayout?: (event: LayoutChangeEvent) => void
28
29
  children?: React.ReactNode;
29
- style?: StyleProp<any>;
30
+ style?: StyleProp<T>;
30
31
  }
32
+
33
+ /** Converts the tagged template string into a css string */
31
34
  function buildCSSString<T extends { rnCSS?: string }> (chunks: TemplateStringsArray, functs: (Primitive | Functs<T>)[], props: T, shared: unknown) {
32
- let computedString = chunks.map((chunk, i) => ([chunk, (functs[i] instanceof Function) ? (functs[i] as Functs<T>)({ shared, theme: (shared as DefaultTheme), ...props }) : functs[i]])).flat().join('')
35
+ let computedString = chunks
36
+ // Evaluate the chunks from the tagged template
37
+ .map((chunk, i) => ([chunk, (functs[i] instanceof Function) ? (functs[i] as Functs<T>)({ shared, theme: (shared as DefaultTheme), ...props }) : functs[i]]))
38
+ .flat()
39
+ // Convert the objects to string if the result is not a primitive
40
+ .map(chunk => typeof chunk === 'object' ? rnToCSS(chunk as Partial<CompleteStyle>) : chunk)
41
+ .join('')
33
42
  if (props.rnCSS) computedString += props.rnCSS.replace(/=/gm, ':') + ';'
34
43
  return computedString
35
44
  }
36
- const styleMap: Record<string, { style: ViewStyle & TextStyle, usage: number }> = {}
37
- function getStyle (hash: string, style: ViewStyle & TextStyle) {
45
+
46
+ const styleMap: Record<string, { style: AnyStyle, usage: number }> = {}
47
+ function getStyle <T extends AnyStyle, > (hash: string, style: T) {
38
48
  const styleInfo = styleMap[hash]
39
49
  if (styleInfo) {
40
50
  styleInfo.usage++
41
- return styleInfo.style
51
+ return styleInfo.style as T
42
52
  }
43
53
  else {
44
54
  const sheet = StyleSheet.create({ [hash]: style })
45
- return (styleMap[hash] = { style: sheet[hash], usage: 1 }).style
55
+ return (styleMap[hash] = { style: sheet[hash], usage: 1 }).style as T
46
56
  }
47
57
  }
48
58
  function removeStyle (hash: string) {
49
59
  styleMap[hash].usage--
50
60
  if (styleMap[hash].usage <= 0) delete styleMap[hash]
51
61
  }
52
- const styled = <Props, >(Component: React.ComponentType<Props>) => {
62
+ const styled = <Props, StyleType extends AnyStyle = AnyStyle>(Component: React.ComponentType<Props>) => {
53
63
  const styledComponent = <S, >(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & Props>)[]) => {
54
- const ForwardRefComponent = React.forwardRef<React.ComponentType<S & Props & OptionalProps>, S & Props & OptionalProps>((props: S & Props & OptionalProps, ref) => {
64
+ const ForwardRefComponent = React.forwardRef<React.ComponentType<S & Props & OptionalProps<StyleType>>, S & Props & OptionalProps<StyleType>>((props: S & Props & OptionalProps<StyleType>, ref) => {
55
65
  const rem = React.useContext(RemContext)
56
66
  const shared = React.useContext(SharedValue)
57
67
  // Build the css string with the context
@@ -110,13 +120,13 @@ const styled = <Props, >(Component: React.ComponentType<Props>) => {
110
120
  const units = React.useMemo<Units>(() => ({ ...baseUnits, em }), [baseUnits, em])
111
121
 
112
122
  const { style: styleConvertedFromCSS, hash } = React.useMemo(() => {
113
- const style = convertStyle(finalStyle, units)
123
+ const style = convertStyle<StyleType>(finalStyle, units)
114
124
  delete (style as Style).textOverflow
115
125
  const hash = generateHash(JSON.stringify(style))
116
- return { style: getStyle(hash, style), hash }
126
+ return { style: getStyle<StyleType>(hash, style), hash }
117
127
  }, [finalStyle, units])
118
128
  const newProps = React.useMemo(() => {
119
- const newProps: OptionalProps = { style: [styleConvertedFromCSS, props.style], onMouseEnter, onMouseLeave, onLayout }
129
+ const newProps: OptionalProps<StyleType> = { style: [styleConvertedFromCSS, props.style], onMouseEnter, onMouseLeave, onLayout }
120
130
  if (finalStyle.textOverflow === 'ellipsis') {
121
131
  Object.assign(newProps, { numberOfLines: 1 })
122
132
  }
@@ -135,7 +145,7 @@ const styled = <Props, >(Component: React.ComponentType<Props>) => {
135
145
  return <Component ref={ref} {...props} {...newProps} />
136
146
  }
137
147
  })
138
- return ForwardRefComponent as React.ForwardRefExoticComponent<Props & S & OptionalProps & { ref?: React.Ref<any> }>
148
+ return ForwardRefComponent as React.ForwardRefExoticComponent<Props & S & OptionalProps<StyleType> & { ref?: React.Ref<any> }>
139
149
  }
140
150
 
141
151
  // provide styled(Comp).attrs({} | () => {}) feature
@@ -146,7 +156,7 @@ const styled = <Props, >(Component: React.ComponentType<Props>) => {
146
156
  return <ComponentWithAttrs ref={ref} {...props} {...attrs} />
147
157
  })
148
158
  // TODO : Find a way to remove from the Props the properties affected by opts
149
- return ForwardRefComponent as React.ForwardRefExoticComponent<(Props | S) & OptionalProps & { ref?: React.Ref<any> }>
159
+ return ForwardRefComponent as React.ForwardRefExoticComponent<(Props | S) & OptionalProps<StyleType> & { ref?: React.Ref<any> }>
150
160
  }
151
161
 
152
162
  return styledComponent
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ViewStyle, TextStyle } from 'react-native'
1
+ import { ViewStyle, TextStyle, ImageStyle } from 'react-native'
2
2
 
3
3
  export type Units = {
4
4
  '%'?: number;
@@ -54,7 +54,11 @@ export type Context = {
54
54
  units: Units
55
55
  }
56
56
 
57
- export type PartialStyle = Partial<Record<keyof ViewStyle | keyof TextStyle, string>> & {
57
+ export type AnyStyle = ViewStyle | TextStyle | ImageStyle
58
+
59
+ export type CompleteStyle = ViewStyle & TextStyle & ImageStyle
60
+
61
+ export type PartialStyle = Partial<Record<keyof CompleteStyle, string>> & {
58
62
  shadowOffset?: {
59
63
  width: string;
60
64
  height: string;