rn-css 1.8.13 → 1.9.0
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.
- package/README.md +16 -0
- package/dist/app.json +4 -0
- package/dist/cssToRN/convert.js +26 -6
- package/dist/index.d.ts +3 -1439
- package/dist/index.js +166 -56
- package/dist/src/convertStyle.d.ts +5 -0
- package/dist/src/convertStyle.js +56 -0
- package/dist/src/convertUnits.d.ts +5 -0
- package/dist/src/convertUnits.js +71 -0
- package/dist/src/cssToRN/convert.d.ts +55 -0
- package/dist/src/cssToRN/convert.js +418 -0
- package/dist/src/cssToRN/index.d.ts +8 -0
- package/dist/src/cssToRN/index.js +134 -0
- package/dist/src/cssToRN/maths.d.ts +4 -0
- package/dist/src/cssToRN/maths.js +86 -0
- package/dist/src/cssToRN/mediaQueries.d.ts +7 -0
- package/dist/src/cssToRN/mediaQueries.js +161 -0
- package/dist/src/features.d.ts +34 -0
- package/dist/src/features.js +102 -0
- package/dist/src/generateHash.d.ts +2 -0
- package/dist/src/generateHash.js +8 -0
- package/dist/src/index.d.ts +1805 -0
- package/dist/src/index.js +60 -0
- package/dist/src/polyfill.d.ts +3 -0
- package/dist/src/polyfill.js +23 -0
- package/dist/src/rnToCss.d.ts +3 -0
- package/dist/src/rnToCss.js +8 -0
- package/dist/src/styleComponent.d.ts +45 -0
- package/dist/src/styleComponent.js +163 -0
- package/dist/src/types.d.ts +85 -0
- package/dist/src/types.js +2 -0
- package/dist/src/useTheme.d.ts +18 -0
- package/dist/src/useTheme.js +33 -0
- package/package.json +1 -1
- package/src/cssToRN/convert.ts +25 -6
- package/src/cssToRN/index.ts +7 -1
- package/src/features.tsx +15 -1
- package/src/styleComponent.tsx +15 -8
- package/src/types.ts +1 -0
- package/CHANGELOG.md +0 -25
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
22
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.RemContext = exports.FontSizeContext = exports.SharedValue = exports.cssToRNStyle = void 0;
|
|
26
|
+
const RN = __importStar(require("react-native"));
|
|
27
|
+
require("./polyfill");
|
|
28
|
+
const styleComponent_1 = __importStar(require("./styleComponent"));
|
|
29
|
+
var cssToRN_1 = require("./cssToRN");
|
|
30
|
+
Object.defineProperty(exports, "cssToRNStyle", { enumerable: true, get: function () { return cssToRN_1.cssToRNStyle; } });
|
|
31
|
+
var styleComponent_2 = require("./styleComponent");
|
|
32
|
+
Object.defineProperty(exports, "SharedValue", { enumerable: true, get: function () { return styleComponent_2.SharedValue; } });
|
|
33
|
+
Object.defineProperty(exports, "FontSizeContext", { enumerable: true, get: function () { return styleComponent_2.FontSizeContext; } });
|
|
34
|
+
Object.defineProperty(exports, "RemContext", { enumerable: true, get: function () { return styleComponent_2.RemContext; } });
|
|
35
|
+
__exportStar(require("./useTheme"), exports);
|
|
36
|
+
const styled = (Component) => (0, styleComponent_1.default)(Component);
|
|
37
|
+
styled.ActivityIndicator = styled(RN.ActivityIndicator);
|
|
38
|
+
styled.DrawerLayoutAndroid = styled(RN.DrawerLayoutAndroid);
|
|
39
|
+
styled.Image = styled(RN.Image);
|
|
40
|
+
styled.ImageBackground = styled(RN.ImageBackground);
|
|
41
|
+
styled.KeyboardAvoidingView = styled(RN.KeyboardAvoidingView);
|
|
42
|
+
styled.Modal = styled(RN.Modal);
|
|
43
|
+
styled.NavigatorIOS = styled(RN.NavigatorIOS);
|
|
44
|
+
styled.ScrollView = styled(RN.ScrollView);
|
|
45
|
+
styled.SnapshotViewIOS = styled(RN.SnapshotViewIOS);
|
|
46
|
+
styled.Switch = styled(RN.Switch);
|
|
47
|
+
styled.RecyclerViewBackedScrollView = styled(RN.RecyclerViewBackedScrollView);
|
|
48
|
+
styled.RefreshControl = styled(RN.RefreshControl);
|
|
49
|
+
styled.SafeAreaView = styled(RN.SafeAreaView);
|
|
50
|
+
styled.Text = styled(RN.Text);
|
|
51
|
+
styled.TextInput = styled(RN.TextInput);
|
|
52
|
+
styled.TouchableHighlight = styled(RN.TouchableHighlight);
|
|
53
|
+
styled.TouchableNativeFeedback = styled(RN.TouchableNativeFeedback);
|
|
54
|
+
styled.TouchableOpacity = styled(RN.TouchableOpacity);
|
|
55
|
+
styled.TouchableWithoutFeedback = styled(RN.TouchableWithoutFeedback);
|
|
56
|
+
styled.View = styled(RN.View);
|
|
57
|
+
styled.FlatList = styleComponent_1.styledFlatList;
|
|
58
|
+
styled.SectionList = styleComponent_1.styledSectionList;
|
|
59
|
+
styled.VirtualizedList = styleComponent_1.styledVirtualizedList;
|
|
60
|
+
exports.default = styled;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** polyfill for Node < 12 */
|
|
3
|
+
// eslint-disable-next-line no-extend-native
|
|
4
|
+
if (!Array.prototype.flat)
|
|
5
|
+
Array.prototype.flat = function (depth) { return flat(this, depth); };
|
|
6
|
+
// eslint-disable-next-line no-extend-native
|
|
7
|
+
if (!String.prototype.matchAll)
|
|
8
|
+
String.prototype.matchAll = function (regex) { return matchAll(this, regex); };
|
|
9
|
+
/** polyfill for Node < 12 */
|
|
10
|
+
function flat(array, depth = 1) {
|
|
11
|
+
if (!depth || depth < 1 || !Array.isArray(array))
|
|
12
|
+
return array;
|
|
13
|
+
return array.reduce((result, current) => result.concat(flat(current, depth - 1)), []);
|
|
14
|
+
}
|
|
15
|
+
function matchAll(str, regex) {
|
|
16
|
+
const matches = [];
|
|
17
|
+
let groups;
|
|
18
|
+
// eslint-disable-next-line no-cond-assign
|
|
19
|
+
while (groups = regex.exec(str)) {
|
|
20
|
+
matches.push(groups);
|
|
21
|
+
}
|
|
22
|
+
return matches[Symbol.iterator]();
|
|
23
|
+
}
|
|
@@ -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;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { MouseEvent } from 'react';
|
|
2
|
+
import { FlatListProps, LayoutChangeEvent, NativeSyntheticEvent, SectionListProps, StyleProp, TargetedEvent, VirtualizedListProps } from 'react-native';
|
|
3
|
+
import type { CompleteStyle, Units } from './types';
|
|
4
|
+
export declare const defaultUnits: Units;
|
|
5
|
+
export declare const RemContext: React.Context<number>;
|
|
6
|
+
export declare const FontSizeContext: React.Context<number>;
|
|
7
|
+
export declare const SharedValue: React.Context<unknown>;
|
|
8
|
+
export interface DefaultTheme {
|
|
9
|
+
}
|
|
10
|
+
declare type Primitive = number | string | null | undefined | boolean | CompleteStyle;
|
|
11
|
+
declare type Functs<T> = (arg: T & {
|
|
12
|
+
rnCSS?: string;
|
|
13
|
+
shared: unknown;
|
|
14
|
+
theme: DefaultTheme;
|
|
15
|
+
}) => Primitive;
|
|
16
|
+
declare type OptionalProps = {
|
|
17
|
+
rnCSS?: `${string};`;
|
|
18
|
+
onFocus?: (event: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
19
|
+
onBlur?: (event: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
20
|
+
onMouseEnter?: (event: MouseEvent) => void;
|
|
21
|
+
onMouseLeave?: (event: MouseEvent) => void;
|
|
22
|
+
onLayout?: (event: LayoutChangeEvent) => void;
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
};
|
|
25
|
+
declare const styled: <StyleType, InitialProps extends {
|
|
26
|
+
style?: StyleProp<StyleType>;
|
|
27
|
+
}, Props extends InitialProps & OptionalProps = InitialProps & OptionalProps>(Component: React.ComponentType<InitialProps>) => {
|
|
28
|
+
<S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & Props>)[]): React.ForwardRefExoticComponent<Props & S & {
|
|
29
|
+
ref?: React.Ref<any> | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
attrs<Part, Result extends Partial<Props & Part> = Partial<Props & Part>>(opts: Result | ((props: Props & Part) => Result)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<Props & Part>)[]) => React.ForwardRefExoticComponent<React.PropsWithoutRef<Omit<Props, keyof Result> & Part & Partial<Pick<Props, Extract<keyof Props, keyof Result>>>> & React.RefAttributes<any>>;
|
|
32
|
+
};
|
|
33
|
+
export default styled;
|
|
34
|
+
export declare const styledFlatList: {
|
|
35
|
+
<S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & FlatListProps<any> & OptionalProps>)[]): <Type>(props: S & OptionalProps & FlatListProps<Type>) => JSX.Element;
|
|
36
|
+
attrs<S_1, Result extends Partial<S_1 & FlatListProps<any> & OptionalProps> = {}>(opts: Result | ((props: S_1 & OptionalProps & FlatListProps<any>) => Result)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1>)[]) => <Props>(componentProps: Omit<FlatListProps<Props> & OptionalProps, keyof Result> & S_1 & Partial<Result>) => JSX.Element;
|
|
37
|
+
};
|
|
38
|
+
export declare const styledSectionList: {
|
|
39
|
+
<S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & SectionListProps<any, import("react-native").DefaultSectionT> & OptionalProps>)[]): <Type>(props: S & OptionalProps & SectionListProps<Type, import("react-native").DefaultSectionT>) => JSX.Element;
|
|
40
|
+
attrs<S_1, Result extends Partial<S_1 & SectionListProps<any, import("react-native").DefaultSectionT> & OptionalProps> = {}>(opts: Result | ((props: S_1 & OptionalProps & SectionListProps<any, import("react-native").DefaultSectionT>) => Result)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1>)[]) => <Props>(componentProps: Omit<SectionListProps<Props, import("react-native").DefaultSectionT> & OptionalProps, keyof Result> & S_1 & Partial<Result>) => JSX.Element;
|
|
41
|
+
};
|
|
42
|
+
export declare const styledVirtualizedList: {
|
|
43
|
+
<S>(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & VirtualizedListProps<any> & OptionalProps>)[]): <Type>(props: S & OptionalProps & VirtualizedListProps<Type>) => JSX.Element;
|
|
44
|
+
attrs<S_1, Result extends Partial<S_1 & VirtualizedListProps<any> & OptionalProps> = {}>(opts: Result | ((props: S_1 & OptionalProps & VirtualizedListProps<any>) => Result)): (chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S_1>)[]) => <Props>(componentProps: Omit<VirtualizedListProps<Props> & OptionalProps, keyof Result> & S_1 & Partial<Result>) => JSX.Element;
|
|
45
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.styledVirtualizedList = exports.styledSectionList = exports.styledFlatList = exports.SharedValue = exports.FontSizeContext = exports.RemContext = exports.defaultUnits = void 0;
|
|
7
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
8
|
+
/* eslint-disable react/prop-types */
|
|
9
|
+
/* eslint-disable react/display-name */
|
|
10
|
+
const react_1 = __importDefault(require("react"));
|
|
11
|
+
const react_native_1 = require("react-native");
|
|
12
|
+
const convertStyle_1 = __importDefault(require("./convertStyle"));
|
|
13
|
+
const cssToRN_1 = __importDefault(require("./cssToRN"));
|
|
14
|
+
const features_1 = require("./features");
|
|
15
|
+
const generateHash_1 = __importDefault(require("./generateHash"));
|
|
16
|
+
const rnToCss_1 = __importDefault(require("./rnToCss"));
|
|
17
|
+
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 };
|
|
18
|
+
exports.RemContext = react_1.default.createContext(exports.defaultUnits.rem);
|
|
19
|
+
exports.FontSizeContext = react_1.default.createContext(exports.defaultUnits.em);
|
|
20
|
+
// We use this to share value within the component (Theme, Translation, whatever)
|
|
21
|
+
exports.SharedValue = react_1.default.createContext(undefined);
|
|
22
|
+
/** Converts the tagged template string into a css string */
|
|
23
|
+
function buildCSSString(chunks, functs, props, shared) {
|
|
24
|
+
let computedString = chunks
|
|
25
|
+
// Evaluate the chunks from the tagged template
|
|
26
|
+
.map((chunk, i) => ([chunk, (functs[i] instanceof Function) ? functs[i]({ shared, theme: shared, ...props }) : functs[i]]))
|
|
27
|
+
.flat()
|
|
28
|
+
// Convert the objects to string if the result is not a primitive
|
|
29
|
+
.map(chunk => typeof chunk === 'object' ? (0, rnToCss_1.default)(chunk) : chunk)
|
|
30
|
+
.join('');
|
|
31
|
+
if (props.rnCSS)
|
|
32
|
+
computedString += props.rnCSS.replace(/=/gm, ':') + ';';
|
|
33
|
+
return computedString;
|
|
34
|
+
}
|
|
35
|
+
const styleMap = {};
|
|
36
|
+
function getStyle(hash, style) {
|
|
37
|
+
const styleInfo = styleMap[hash];
|
|
38
|
+
if (styleInfo) {
|
|
39
|
+
styleInfo.usage++;
|
|
40
|
+
return styleInfo.style;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const sheet = react_native_1.StyleSheet.create({ [hash]: style });
|
|
44
|
+
return (styleMap[hash] = { style: sheet[hash], usage: 1 }).style;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function removeStyle(hash) {
|
|
48
|
+
styleMap[hash].usage--;
|
|
49
|
+
if (styleMap[hash].usage <= 0)
|
|
50
|
+
delete styleMap[hash];
|
|
51
|
+
}
|
|
52
|
+
const styled = (Component) => {
|
|
53
|
+
const styledComponent = (chunks, ...functs) => {
|
|
54
|
+
const ForwardRefComponent = react_1.default.forwardRef((props, ref) => {
|
|
55
|
+
const rem = react_1.default.useContext(exports.RemContext);
|
|
56
|
+
const shared = react_1.default.useContext(exports.SharedValue);
|
|
57
|
+
// Build the css string with the context
|
|
58
|
+
const css = react_1.default.useMemo(() => buildCSSString(chunks, functs, props, shared), [props, shared]);
|
|
59
|
+
// Store the style in RN format
|
|
60
|
+
const rnStyle = react_1.default.useMemo(() => (0, cssToRN_1.default)(css), [css]);
|
|
61
|
+
const { needsLayout, needsHover, needsFocus } = react_1.default.useMemo(() => ({
|
|
62
|
+
// needsFontSize: !!css.match(/\b(\d+)(\.\d+)?em\b/)
|
|
63
|
+
// needsScreenSize: !!css.match(/\b(\d+)(\.\d*)?v([hw]|min|max)\b/) || !!rnStyle.media,
|
|
64
|
+
needsLayout: !!css.match(/\d%/),
|
|
65
|
+
needsHover: !!rnStyle.hover,
|
|
66
|
+
needsFocus: !!rnStyle.active
|
|
67
|
+
}), [css, rnStyle.active, rnStyle.hover]);
|
|
68
|
+
// Handle hover
|
|
69
|
+
const { onMouseEnter, onMouseLeave, hover } = (0, features_1.useHover)(props.onMouseEnter, props.onMouseLeave, needsHover);
|
|
70
|
+
// Handle active
|
|
71
|
+
const { onFocus, onBlur, active } = (0, features_1.useActive)(props.onFocus, props.onBlur, needsFocus);
|
|
72
|
+
const tempStyle = react_1.default.useMemo(() => {
|
|
73
|
+
const style = { ...rnStyle };
|
|
74
|
+
delete style.media;
|
|
75
|
+
delete style.hover;
|
|
76
|
+
delete style.active;
|
|
77
|
+
if (hover)
|
|
78
|
+
Object.assign(style, rnStyle.hover);
|
|
79
|
+
if (active)
|
|
80
|
+
Object.assign(style, rnStyle.active);
|
|
81
|
+
return style;
|
|
82
|
+
}, [active, hover, rnStyle]);
|
|
83
|
+
// Calculate current em unit for media-queries
|
|
84
|
+
const parentEm = react_1.default.useContext(exports.FontSizeContext);
|
|
85
|
+
const { em: tempEm } = (0, features_1.useFontSize)(tempStyle.fontSize, rem, parentEm);
|
|
86
|
+
// Handle layout data needed for % units
|
|
87
|
+
const { width, height, onLayout } = (0, features_1.useLayout)(props.onLayout, needsLayout);
|
|
88
|
+
// Handle screen size needed for vw and wh units
|
|
89
|
+
const screenUnits = (0, features_1.useScreenSize)();
|
|
90
|
+
/** The units before re-updating the em */
|
|
91
|
+
const baseUnits = react_1.default.useMemo(() => ({
|
|
92
|
+
...exports.defaultUnits,
|
|
93
|
+
rem,
|
|
94
|
+
em: tempEm,
|
|
95
|
+
width,
|
|
96
|
+
height,
|
|
97
|
+
...screenUnits
|
|
98
|
+
}), [height, rem, screenUnits, tempEm, width]);
|
|
99
|
+
// apply media queries
|
|
100
|
+
const mediaQuery = (0, features_1.useMediaQuery)(rnStyle.media, baseUnits);
|
|
101
|
+
// Handle em units
|
|
102
|
+
const fontSize = (mediaQuery && mediaQuery.fontSize) || tempStyle.fontSize;
|
|
103
|
+
const { em } = (0, features_1.useFontSize)(fontSize, baseUnits.rem, parentEm);
|
|
104
|
+
const finalStyle = react_1.default.useMemo(() => {
|
|
105
|
+
const style = { ...tempStyle, ...mediaQuery };
|
|
106
|
+
if (style.fontSize)
|
|
107
|
+
style.fontSize = em + 'px';
|
|
108
|
+
if (style.zIndex === undefined && react_native_1.Platform.OS === 'web')
|
|
109
|
+
style.zIndex = 'auto';
|
|
110
|
+
return style;
|
|
111
|
+
}, [em, mediaQuery, tempStyle]);
|
|
112
|
+
const units = react_1.default.useMemo(() => ({ ...baseUnits, em }), [baseUnits, em]);
|
|
113
|
+
const { style: styleConvertedFromCSS, hash } = react_1.default.useMemo(() => {
|
|
114
|
+
const style = (0, convertStyle_1.default)(finalStyle, units);
|
|
115
|
+
delete style.textOverflow;
|
|
116
|
+
const hash = (0, generateHash_1.default)(JSON.stringify(style));
|
|
117
|
+
return { style: getStyle(hash, style), hash };
|
|
118
|
+
}, [finalStyle, units]);
|
|
119
|
+
const newProps = react_1.default.useMemo(() => {
|
|
120
|
+
const newProps = { style: [styleConvertedFromCSS, props.style], onMouseEnter, onMouseLeave, onLayout, onFocus, onBlur };
|
|
121
|
+
if (finalStyle.textOverflow === 'ellipsis') {
|
|
122
|
+
Object.assign(newProps, { numberOfLines: 1 });
|
|
123
|
+
}
|
|
124
|
+
return newProps;
|
|
125
|
+
}, [finalStyle.textOverflow, onBlur, onFocus, onLayout, onMouseEnter, onMouseLeave, props.style, styleConvertedFromCSS]);
|
|
126
|
+
react_1.default.useEffect(() => () => removeStyle(hash), [hash]);
|
|
127
|
+
// em !== parentEm alone is a bit dangerous as the component would rerender when the font size change
|
|
128
|
+
if (em !== parentEm || finalStyle.fontSize !== undefined) {
|
|
129
|
+
return react_1.default.createElement(exports.FontSizeContext.Provider, { value: em },
|
|
130
|
+
react_1.default.createElement(Component, { ref: ref, ...props, ...newProps }));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return react_1.default.createElement(Component, { ref: ref, ...props, ...newProps });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return ForwardRefComponent;
|
|
137
|
+
};
|
|
138
|
+
// provide styled(Comp).attrs({} | () => {}) feature
|
|
139
|
+
styledComponent.attrs = (opts) => (chunks, ...functs) => {
|
|
140
|
+
const ComponentWithAttrs = styledComponent(chunks, ...functs);
|
|
141
|
+
// We need to limit the props control to only Result https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABBATgUwIZTQUQB7ZgAmaRAwnALYAOAPAAopzUDOAfABQU0BciH1Jqz6NmLAJSIAvG0QYwAT0kBvAFCJkCFlET5CJYt2rT+gsSKETpstRo3ooIFEiMDL49YgC+nvWmL+5FTUAHRYUCgsJrQASmgsIAA2OmgEgVH0GCiwGIkMlmyc4ZF8cQnJkjKItnYQWjqMaHVgwDAA5k5YpEYmbua6eBCJICT5YgA0iGVJUGyVNp52iA5OLsEcyiFbZqyTW2FQESxeHks+SyvOiI3NrR0oXUE0nufLaI5XfgGGwao+qqBILAEIgen1hNVENhtHxtCgYGA2pNtApEmhYREEW1vCpPJckDsWH9VM1tNd0Ld2j0pMh0F0viQntQuMFxAcjhsofEoHwAORwADWvJxqhuCDurmUiBRaL50KgwpOQA
|
|
142
|
+
const ForwardRefComponent = react_1.default.forwardRef((props, ref) => {
|
|
143
|
+
const attrs = (opts instanceof Function) ? opts(props) : opts;
|
|
144
|
+
return react_1.default.createElement(ComponentWithAttrs, { ref: ref, ...props, ...attrs });
|
|
145
|
+
});
|
|
146
|
+
// TODO : Find a way to remove from the Props the properties affected by opts
|
|
147
|
+
return ForwardRefComponent;
|
|
148
|
+
};
|
|
149
|
+
return styledComponent;
|
|
150
|
+
};
|
|
151
|
+
exports.default = styled;
|
|
152
|
+
const styledFlatList = (chunks, ...functs) => (props) => invoke(styled(react_native_1.FlatList)(chunks, ...functs), props);
|
|
153
|
+
exports.styledFlatList = styledFlatList;
|
|
154
|
+
exports.styledFlatList.attrs = (opts) => (chunks, ...functs) => (componentProps) => invoke(styled(react_native_1.FlatList).attrs(opts)(chunks, ...functs), componentProps);
|
|
155
|
+
const styledSectionList = (chunks, ...functs) => (props) => invoke(styled(react_native_1.SectionList)(chunks, ...functs), props);
|
|
156
|
+
exports.styledSectionList = styledSectionList;
|
|
157
|
+
exports.styledSectionList.attrs = (opts) => (chunks, ...functs) => (componentProps) => invoke(styled(react_native_1.SectionList).attrs(opts)(chunks, ...functs), componentProps);
|
|
158
|
+
const styledVirtualizedList = (chunks, ...functs) => (props) => invoke(styled(react_native_1.VirtualizedList)(chunks, ...functs), props);
|
|
159
|
+
exports.styledVirtualizedList = styledVirtualizedList;
|
|
160
|
+
exports.styledVirtualizedList.attrs = (opts) => (chunks, ...functs) => (componentProps) => invoke(styled(react_native_1.VirtualizedList).attrs(opts)(chunks, ...functs), componentProps);
|
|
161
|
+
function invoke(Component, props) {
|
|
162
|
+
return react_1.default.createElement(Component, { ...props });
|
|
163
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ViewStyle, TextStyle, ImageStyle } from 'react-native';
|
|
2
|
+
export declare type Units = {
|
|
3
|
+
'%'?: number;
|
|
4
|
+
vw?: number;
|
|
5
|
+
vh?: number;
|
|
6
|
+
vmin?: number;
|
|
7
|
+
vmax?: number;
|
|
8
|
+
em: number;
|
|
9
|
+
rem: number;
|
|
10
|
+
px: number;
|
|
11
|
+
pt: number;
|
|
12
|
+
pc: number;
|
|
13
|
+
in: number;
|
|
14
|
+
cm: number;
|
|
15
|
+
mm: number;
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
};
|
|
19
|
+
export declare type Context = {
|
|
20
|
+
type: 'all' | 'sprint' | 'speech' | 'screen';
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
aspectRatio: number;
|
|
24
|
+
orientation: 'portrait' | 'landscape';
|
|
25
|
+
resolution: number;
|
|
26
|
+
scan: 'interlace' | 'progressive';
|
|
27
|
+
grid: 0 | 1;
|
|
28
|
+
update: 'none' | 'slow' | 'fast';
|
|
29
|
+
overflowBlock: 'none' | 'scroll' | 'paged';
|
|
30
|
+
overflowInline: 'none' | 'scroll';
|
|
31
|
+
environmentBlending: 'opaque' | 'additive' | 'subtractive';
|
|
32
|
+
color: number;
|
|
33
|
+
colorGamut: 'srgb' | 'p3' | 'rec2020';
|
|
34
|
+
colorIndex: number;
|
|
35
|
+
dynamicRange: 'standard' | 'high';
|
|
36
|
+
monochrome: number;
|
|
37
|
+
invertedColors: 'none' | 'inverted';
|
|
38
|
+
pointer: 'none' | 'coarse' | 'fine';
|
|
39
|
+
hover: 'none' | 'hover';
|
|
40
|
+
anyPointer: 'none' | 'coarse' | 'fine';
|
|
41
|
+
anyHover: 'none' | 'hover';
|
|
42
|
+
prefersReducedMotion: 'no-preference' | 'reduce';
|
|
43
|
+
prefersReducedTransparency: 'no-preference' | 'reduce';
|
|
44
|
+
prefersReducedData: 'no-preference' | 'reduce';
|
|
45
|
+
prefersContrast: 'no-preference' | 'high' | 'low' | 'forced';
|
|
46
|
+
prefersColorScheme: 'light' | 'dark';
|
|
47
|
+
forcedColor: 'none' | 'active';
|
|
48
|
+
scripting: 'none' | 'initial-only' | 'enabled';
|
|
49
|
+
deviceWidth: number;
|
|
50
|
+
deviceHeight: number;
|
|
51
|
+
deviceAspectRatio: number;
|
|
52
|
+
units: Units;
|
|
53
|
+
};
|
|
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>> & {
|
|
57
|
+
shadowOffset?: {
|
|
58
|
+
width: string;
|
|
59
|
+
height: string;
|
|
60
|
+
};
|
|
61
|
+
textShadowOffset?: {
|
|
62
|
+
width: string;
|
|
63
|
+
height: string;
|
|
64
|
+
};
|
|
65
|
+
textOverflow?: 'ellipsis';
|
|
66
|
+
transform?: Transform[];
|
|
67
|
+
};
|
|
68
|
+
export declare type Style = PartialStyle & {
|
|
69
|
+
hover?: PartialStyle;
|
|
70
|
+
active?: PartialStyle;
|
|
71
|
+
media?: MediaQuery[];
|
|
72
|
+
};
|
|
73
|
+
export declare type MediaQuery = (context: Context) => false | PartialStyle;
|
|
74
|
+
export declare type Transform = {
|
|
75
|
+
scaleX?: string;
|
|
76
|
+
scaleY?: string;
|
|
77
|
+
translateX?: string;
|
|
78
|
+
translateY?: string;
|
|
79
|
+
skewX?: string;
|
|
80
|
+
skewY?: string;
|
|
81
|
+
perspective?: string;
|
|
82
|
+
rotateX?: string;
|
|
83
|
+
rotateY?: string;
|
|
84
|
+
rotateZ?: string;
|
|
85
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DefaultTheme } from './styleComponent';
|
|
3
|
+
/** Provider for the theme. */
|
|
4
|
+
export declare const ThemeProvider: ({ theme, children }: {
|
|
5
|
+
theme: DefaultTheme;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}) => JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the Theme
|
|
10
|
+
* @returns The Theme object
|
|
11
|
+
*/
|
|
12
|
+
export declare const useTheme: () => unknown;
|
|
13
|
+
/**
|
|
14
|
+
* Adds the theme prop to a non rn-css component
|
|
15
|
+
* @param Component A non rn-css component
|
|
16
|
+
* @returns A component with the theme prop
|
|
17
|
+
*/
|
|
18
|
+
export declare const withTheme: <T>(Component: React.ComponentType<T>) => React.ForwardRefExoticComponent<React.PropsWithoutRef<T> & React.RefAttributes<React.Component<{}, {}, any>>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withTheme = exports.useTheme = exports.ThemeProvider = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const styleComponent_1 = require("./styleComponent");
|
|
9
|
+
/** Provider for the theme. */
|
|
10
|
+
const ThemeProvider = ({ theme, children }) => {
|
|
11
|
+
return react_1.default.createElement(styleComponent_1.SharedValue.Provider, { value: theme }, children);
|
|
12
|
+
};
|
|
13
|
+
exports.ThemeProvider = ThemeProvider;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the Theme
|
|
16
|
+
* @returns The Theme object
|
|
17
|
+
*/
|
|
18
|
+
const useTheme = () => react_1.default.useContext(styleComponent_1.SharedValue);
|
|
19
|
+
exports.useTheme = useTheme;
|
|
20
|
+
/**
|
|
21
|
+
* Adds the theme prop to a non rn-css component
|
|
22
|
+
* @param Component A non rn-css component
|
|
23
|
+
* @returns A component with the theme prop
|
|
24
|
+
*/
|
|
25
|
+
const withTheme = (Component) => {
|
|
26
|
+
const ThemedComponent = react_1.default.forwardRef((props, ref) => {
|
|
27
|
+
const theme = (0, exports.useTheme)();
|
|
28
|
+
return react_1.default.createElement(Component, { ref: ref, theme: theme, ...props });
|
|
29
|
+
});
|
|
30
|
+
ThemedComponent.displayName = 'ThemedComponent';
|
|
31
|
+
return ThemedComponent;
|
|
32
|
+
};
|
|
33
|
+
exports.withTheme = withTheme;
|
package/package.json
CHANGED
package/src/cssToRN/convert.ts
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import type { Style, Transform } from '../types'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Check if the value is a number. Numbers start with a digit, a decimal point or calc(, max( ou min(.
|
|
5
|
+
* Optionally accept "auto" value (for margins)
|
|
6
|
+
* @param value The value to check
|
|
7
|
+
* @param acceptAuto true if auto is an accepted value
|
|
8
|
+
* @returns true if the value is a number
|
|
9
|
+
*/
|
|
10
|
+
function isNumber (value: string, acceptAuto?: boolean) {
|
|
11
|
+
if (acceptAuto && value === 'auto') return true
|
|
5
12
|
return value.match(/^[+-]?(\.\d|\d|calc\(|max\(|min\()/mg)
|
|
6
13
|
}
|
|
7
14
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Check if the value is a number. Numbers start with a digit, a decimal point or calc(, max( ou min(.
|
|
17
|
+
* Optionally accept "auto" value (for margins)
|
|
18
|
+
* @param value The value to check
|
|
19
|
+
* @param acceptAuto true if auto is an accepted value
|
|
20
|
+
* @returns true if the value is a number
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Split the value into numbers values and non numbers values
|
|
24
|
+
* @param value The value to check
|
|
25
|
+
* @param acceptAuto true if auto is an accepted value
|
|
26
|
+
* @returns An object containing the number and non number values as arrays.
|
|
27
|
+
*/
|
|
28
|
+
function findNumbers (value: string, acceptAuto?: boolean) {
|
|
10
29
|
const result = {
|
|
11
30
|
nonNumbers: [] as string[],
|
|
12
31
|
numbers: [] as string[]
|
|
@@ -17,7 +36,7 @@ function findNumbers (value: string) {
|
|
|
17
36
|
if (val.startsWith('"') || val.startsWith("'")) group = val.charAt(0)
|
|
18
37
|
if (group && val.endsWith(group)) group = ''
|
|
19
38
|
if (group) result.nonNumbers.push(val)
|
|
20
|
-
else result[isNumber(val) ? 'numbers' : 'nonNumbers'].push(val)
|
|
39
|
+
else result[isNumber(val, acceptAuto) ? 'numbers' : 'nonNumbers'].push(val)
|
|
21
40
|
})
|
|
22
41
|
return result
|
|
23
42
|
}
|
|
@@ -188,7 +207,7 @@ export function font (value: string) {
|
|
|
188
207
|
/** Parses a css value for the side of an element (border-width, margin, padding) */
|
|
189
208
|
export function sideValue <T extends 'padding' | 'margin' | 'border'> (prefixKey: T, value: string, postFix: T extends 'border' ? 'Width' | 'Style' | 'Color' | '' : '' = ''): { [x: string]: string} {
|
|
190
209
|
if (value === 'none') return sideValue(prefixKey, '0', postFix)
|
|
191
|
-
const [top = value, right = top, bottom = top, left = right] = findNumbers(value).numbers
|
|
210
|
+
const [top = value, right = top, bottom = top, left = right] = findNumbers(value, prefixKey === 'margin').numbers
|
|
192
211
|
return {
|
|
193
212
|
[prefixKey + 'Top' + postFix]: top,
|
|
194
213
|
[prefixKey + 'Left' + postFix]: left,
|
package/src/cssToRN/index.ts
CHANGED
|
@@ -29,7 +29,13 @@ function cssToStyle (css: string) {
|
|
|
29
29
|
result.hover = cssChunkToStyle(hoverInstructions)
|
|
30
30
|
return ''
|
|
31
31
|
})
|
|
32
|
-
|
|
32
|
+
// Find active (we don't support active within media queries) (We use [\s\S] instead of . because dotall flag (s) is not supported by react-native-windows)
|
|
33
|
+
const cssWithoutActive = cssWithoutHover.replace(/&:active\s*{([\s\S]*?)}/gmi, res => {
|
|
34
|
+
const activeInstructions = res.substring(0, res.length - 1).replace(/&:active\s*{/mi, '')// We remove the `&:active {` and `}`
|
|
35
|
+
result.active = cssChunkToStyle(activeInstructions)
|
|
36
|
+
return ''
|
|
37
|
+
})
|
|
38
|
+
Object.assign(result, cssChunkToStyle(cssWithoutActive))
|
|
33
39
|
return result
|
|
34
40
|
}
|
|
35
41
|
|
package/src/features.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable react/display-name */
|
|
2
2
|
import React, { MouseEvent } from 'react'
|
|
3
3
|
import type { Style, Units, MediaQuery, PartialStyle } from './types'
|
|
4
|
-
import { useWindowDimensions, LayoutChangeEvent } from 'react-native'
|
|
4
|
+
import { useWindowDimensions, LayoutChangeEvent, NativeSyntheticEvent, TargetedEvent } from 'react-native'
|
|
5
5
|
import { parseValue } from './convertUnits'
|
|
6
6
|
import { createContext } from './cssToRN/mediaQueries'
|
|
7
7
|
|
|
@@ -27,6 +27,20 @@ export const useHover = (onMouseEnter: undefined | ((event: MouseEvent) => void)
|
|
|
27
27
|
return { hover, onMouseEnter: hoverStart || onMouseEnter, onMouseLeave: hoverStop || onMouseLeave }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** Hook that will apply the style reserved for active state if needed */
|
|
31
|
+
export const useActive = (onFocus: undefined | ((event: NativeSyntheticEvent<TargetedEvent>) => void), onBlur: undefined | ((event: NativeSyntheticEvent<TargetedEvent>) => void | undefined), needsFocus: boolean) => {
|
|
32
|
+
const [active, setActive] = React.useState(false)
|
|
33
|
+
const focusStart = React.useMemo(() => needsFocus ? (event: NativeSyntheticEvent<TargetedEvent>) => {
|
|
34
|
+
if (onFocus) onFocus(event)
|
|
35
|
+
setActive(true)
|
|
36
|
+
} : undefined, [needsFocus, onFocus])
|
|
37
|
+
const focusStop = React.useMemo(() => needsFocus ? (event: NativeSyntheticEvent<TargetedEvent>) => {
|
|
38
|
+
if (onBlur) onBlur(event)
|
|
39
|
+
setActive(false)
|
|
40
|
+
} : undefined, [needsFocus, onBlur])
|
|
41
|
+
return { active, onFocus: focusStart || onFocus, onBlur: focusStop || onBlur }
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
/** Hook that will apply the style provided in the media queries */
|
|
31
45
|
export const useMediaQuery = (media: undefined | MediaQuery[], units: Units): Style | undefined => {
|
|
32
46
|
const mediaStyle = React.useMemo(() => {
|
package/src/styleComponent.tsx
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
/* eslint-disable react/prop-types */
|
|
3
3
|
/* eslint-disable react/display-name */
|
|
4
4
|
import React, { MouseEvent } from 'react'
|
|
5
|
-
import { FlatList, FlatListProps, LayoutChangeEvent, Platform, SectionList, SectionListProps, StyleProp, StyleSheet, ViewStyle, VirtualizedList, VirtualizedListProps } from 'react-native'
|
|
5
|
+
import { FlatList, FlatListProps, LayoutChangeEvent, NativeSyntheticEvent, Platform, SectionList, SectionListProps, StyleProp, StyleSheet, TargetedEvent, ViewStyle, VirtualizedList, VirtualizedListProps } from 'react-native'
|
|
6
6
|
import convertStyle from './convertStyle'
|
|
7
7
|
import cssToStyle from './cssToRN'
|
|
8
|
-
import { useFontSize, useHover, useLayout, useScreenSize, useMediaQuery } from './features'
|
|
8
|
+
import { useFontSize, useHover, useLayout, useScreenSize, useMediaQuery, useActive } from './features'
|
|
9
9
|
import type { AnyStyle, CompleteStyle, Style, Units } from './types'
|
|
10
10
|
import generateHash from './generateHash'
|
|
11
11
|
import rnToCSS from './rnToCss'
|
|
@@ -24,6 +24,8 @@ type Primitive = number | string | null | undefined | boolean | CompleteStyle
|
|
|
24
24
|
type Functs<T> = (arg: T & { rnCSS?: string; shared: unknown, theme: DefaultTheme }) => Primitive
|
|
25
25
|
type OptionalProps = {
|
|
26
26
|
rnCSS?: `${string};`;
|
|
27
|
+
onFocus?: (event: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
28
|
+
onBlur?: (event: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
27
29
|
onMouseEnter?: (event: MouseEvent) => void;
|
|
28
30
|
onMouseLeave?: (event: MouseEvent) => void;
|
|
29
31
|
onLayout?: (event: LayoutChangeEvent) => void
|
|
@@ -70,22 +72,27 @@ const styled = <StyleType, InitialProps extends { style?: StyleProp<StyleType> }
|
|
|
70
72
|
// Store the style in RN format
|
|
71
73
|
const rnStyle = React.useMemo(() => cssToStyle(css), [css])
|
|
72
74
|
|
|
73
|
-
const { needsLayout, needsHover } = React.useMemo(() => ({
|
|
75
|
+
const { needsLayout, needsHover, needsFocus } = React.useMemo(() => ({
|
|
74
76
|
// needsFontSize: !!css.match(/\b(\d+)(\.\d+)?em\b/)
|
|
75
77
|
// needsScreenSize: !!css.match(/\b(\d+)(\.\d*)?v([hw]|min|max)\b/) || !!rnStyle.media,
|
|
76
78
|
needsLayout: !!css.match(/\d%/),
|
|
77
|
-
needsHover: !!rnStyle.hover
|
|
78
|
-
|
|
79
|
+
needsHover: !!rnStyle.hover,
|
|
80
|
+
needsFocus: !!rnStyle.active
|
|
81
|
+
}), [css, rnStyle.active, rnStyle.hover])
|
|
79
82
|
|
|
80
83
|
// Handle hover
|
|
81
84
|
const { onMouseEnter, onMouseLeave, hover } = useHover(props.onMouseEnter, props.onMouseLeave, needsHover)
|
|
85
|
+
// Handle active
|
|
86
|
+
const { onFocus, onBlur, active } = useActive(props.onFocus, props.onBlur, needsFocus)
|
|
82
87
|
const tempStyle = React.useMemo<Style>(() => {
|
|
83
88
|
const style = { ...rnStyle }
|
|
84
89
|
delete style.media
|
|
85
90
|
delete style.hover
|
|
91
|
+
delete style.active
|
|
86
92
|
if (hover) Object.assign(style, rnStyle.hover)
|
|
93
|
+
if (active) Object.assign(style, rnStyle.active)
|
|
87
94
|
return style
|
|
88
|
-
}, [hover, rnStyle])
|
|
95
|
+
}, [active, hover, rnStyle])
|
|
89
96
|
|
|
90
97
|
// Calculate current em unit for media-queries
|
|
91
98
|
const parentEm = React.useContext(FontSizeContext)
|
|
@@ -127,12 +134,12 @@ const styled = <StyleType, InitialProps extends { style?: StyleProp<StyleType> }
|
|
|
127
134
|
return { style: getStyle<CompleteStyle>(hash, style), hash }
|
|
128
135
|
}, [finalStyle, units])
|
|
129
136
|
const newProps = React.useMemo(() => {
|
|
130
|
-
const newProps = { style: [styleConvertedFromCSS as StyleType, props.style], onMouseEnter, onMouseLeave, onLayout }
|
|
137
|
+
const newProps = { style: [styleConvertedFromCSS as StyleType, props.style], onMouseEnter, onMouseLeave, onLayout, onFocus, onBlur }
|
|
131
138
|
if (finalStyle.textOverflow === 'ellipsis') {
|
|
132
139
|
Object.assign(newProps, { numberOfLines: 1 })
|
|
133
140
|
}
|
|
134
141
|
return newProps
|
|
135
|
-
}, [finalStyle.textOverflow, onLayout, onMouseEnter, onMouseLeave, props.style, styleConvertedFromCSS])
|
|
142
|
+
}, [finalStyle.textOverflow, onBlur, onFocus, onLayout, onMouseEnter, onMouseLeave, props.style, styleConvertedFromCSS])
|
|
136
143
|
|
|
137
144
|
React.useEffect(() => () => removeStyle(hash), [hash])
|
|
138
145
|
|