rn-css 1.5.4 → 1.6.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.
- package/dist/convertUnits.js +1 -1
- package/dist/features.d.ts +11 -14
- package/dist/features.js +47 -57
- package/dist/index.d.ts +1 -2
- package/dist/index.js +3 -3
- package/dist/styleComponent.d.ts +4 -0
- package/dist/styleComponent.js +59 -66
- package/package.json +6 -5
- package/src/convertUnits.ts +1 -1
- package/src/features.tsx +45 -55
- package/src/index.tsx +1 -2
- package/src/styleComponent.tsx +61 -64
package/dist/convertUnits.js
CHANGED
|
@@ -6,7 +6,7 @@ const react_native_1 = require("react-native");
|
|
|
6
6
|
/** Take a css value like 12em and return [12, 'em'] */
|
|
7
7
|
function parseValue(value) {
|
|
8
8
|
// Match a single unit
|
|
9
|
-
const unit = value.match(/([+-]?\b\d+(\.\d+)?)([a-z]+\b|%)
|
|
9
|
+
const unit = value.match(/([+-]?\b\d+(\.\d+)?)([a-z]+\b|%)?/i);
|
|
10
10
|
return [parseFloat(unit[1]), unit[3]];
|
|
11
11
|
}
|
|
12
12
|
exports.parseValue = parseValue;
|
package/dist/features.d.ts
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { MouseEvent } from 'react';
|
|
2
2
|
import type { Style, Units, MediaQuery } from './types';
|
|
3
3
|
import { LayoutChangeEvent } from 'react-native';
|
|
4
|
-
|
|
5
|
-
export declare const zIndexContext: React.Context<(_zIndex: number) => void>;
|
|
6
|
-
/** HOC that will apply the screen size to the styles defined with vmin, vmax, vw, vh units, and handle media queries constraints */
|
|
4
|
+
/** Hook that will apply the screen size to the styles defined with vmin, vmax, vw, vh units, and handle media queries constraints */
|
|
7
5
|
export declare const useScreenSize: () => {
|
|
8
6
|
vw: number;
|
|
9
7
|
vh: number;
|
|
10
8
|
vmin: number;
|
|
11
9
|
vmax: number;
|
|
12
10
|
};
|
|
13
|
-
/**
|
|
14
|
-
export declare const useHover: (
|
|
15
|
-
|
|
11
|
+
/** Hook that will apply the style reserved for hover state if needed */
|
|
12
|
+
export declare const useHover: (onMouseEnter: ((event: MouseEvent) => void) | undefined, onMouseLeave: ((event: MouseEvent) => void | undefined) | undefined, needsHover: boolean) => {
|
|
13
|
+
hover: boolean;
|
|
16
14
|
onMouseEnter: ((event: MouseEvent) => void) | undefined;
|
|
17
|
-
onMouseLeave: ((event: MouseEvent) => void) | undefined;
|
|
15
|
+
onMouseLeave: ((event: MouseEvent) => void | undefined) | undefined;
|
|
18
16
|
};
|
|
19
|
-
/**
|
|
17
|
+
/** Hook that will apply the style provided in the media queries */
|
|
20
18
|
export declare const useMediaQuery: (media: undefined | MediaQuery[], units: Units) => Style | undefined;
|
|
21
|
-
/**
|
|
22
|
-
export declare const useLayout: (onLayout
|
|
19
|
+
/** Hook that will measure the layout to handle styles that use % units */
|
|
20
|
+
export declare const useLayout: (onLayout: ((event: LayoutChangeEvent) => void) | undefined, needsLayout: boolean) => {
|
|
23
21
|
width: number;
|
|
24
22
|
height: number;
|
|
25
|
-
onLayout: (event: LayoutChangeEvent) => void;
|
|
23
|
+
onLayout: ((event: LayoutChangeEvent) => void) | undefined;
|
|
26
24
|
};
|
|
27
25
|
/** Apply the new fontSize to the component before we can calculate em units */
|
|
28
|
-
export declare const useFontSize: (
|
|
26
|
+
export declare const useFontSize: (fontSizeTarget: string | undefined, rem: number, em: number) => {
|
|
29
27
|
em: number;
|
|
30
28
|
};
|
|
31
|
-
export declare const useZIndex: (zIndexFromStyle: number) => number | "auto" | undefined;
|
package/dist/features.js
CHANGED
|
@@ -3,94 +3,84 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.useFontSize = exports.useLayout = exports.useMediaQuery = exports.useHover = exports.useScreenSize = void 0;
|
|
7
7
|
/* eslint-disable react/display-name */
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
9
|
const react_native_1 = require("react-native");
|
|
10
10
|
const convertUnits_1 = require("./convertUnits");
|
|
11
11
|
const mediaQueries_1 = require("./cssToRN/mediaQueries");
|
|
12
|
-
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
14
|
-
exports.zIndexContext = react_1.default.createContext((_zIndex) => { });
|
|
15
|
-
/** HOC that will apply the screen size to the styles defined with vmin, vmax, vw, vh units, and handle media queries constraints */
|
|
12
|
+
/** Hook that will apply the screen size to the styles defined with vmin, vmax, vw, vh units, and handle media queries constraints */
|
|
16
13
|
const useScreenSize = () => {
|
|
17
14
|
const { width, height } = (0, react_native_1.useWindowDimensions)();
|
|
18
|
-
return
|
|
15
|
+
return react_1.default.useMemo(() => ({
|
|
16
|
+
vw: width / 100, vh: height / 100, vmin: Math.min(width, height) / 100, vmax: Math.max(width, height) / 100
|
|
17
|
+
}), [height, width]);
|
|
19
18
|
};
|
|
20
19
|
exports.useScreenSize = useScreenSize;
|
|
21
|
-
/**
|
|
22
|
-
const useHover = (
|
|
20
|
+
/** Hook that will apply the style reserved for hover state if needed */
|
|
21
|
+
const useHover = (onMouseEnter, onMouseLeave, needsHover) => {
|
|
23
22
|
const [hover, setHover] = react_1.default.useState(false);
|
|
24
|
-
const hoverStart = react_1.default.
|
|
23
|
+
const hoverStart = react_1.default.useMemo(() => needsHover ? (event) => {
|
|
25
24
|
if (onMouseEnter)
|
|
26
25
|
onMouseEnter(event);
|
|
27
26
|
setHover(true);
|
|
28
|
-
}, [onMouseEnter]);
|
|
29
|
-
const hoverStop = react_1.default.
|
|
27
|
+
} : undefined, [needsHover, onMouseEnter]);
|
|
28
|
+
const hoverStop = react_1.default.useMemo(() => needsHover ? (event) => {
|
|
30
29
|
if (onMouseLeave)
|
|
31
30
|
onMouseLeave(event);
|
|
32
31
|
setHover(false);
|
|
33
|
-
}, [onMouseLeave]);
|
|
34
|
-
return {
|
|
32
|
+
} : undefined, [needsHover, onMouseLeave]);
|
|
33
|
+
return { hover, onMouseEnter: hoverStart || onMouseEnter, onMouseLeave: hoverStop || onMouseLeave };
|
|
35
34
|
};
|
|
36
35
|
exports.useHover = useHover;
|
|
37
|
-
/**
|
|
36
|
+
/** Hook that will apply the style provided in the media queries */
|
|
38
37
|
const useMediaQuery = (media, units) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
const mediaStyle = react_1.default.useMemo(() => {
|
|
39
|
+
if (media) {
|
|
40
|
+
const context = (0, mediaQueries_1.createContext)(units);
|
|
41
|
+
const mediaStyles = media.map(m => m(context)).filter(m => !!m);
|
|
42
|
+
if (!mediaStyles.length)
|
|
43
|
+
return;
|
|
44
|
+
const mq = {};
|
|
45
|
+
Object.assign(mq, ...mediaStyles);
|
|
46
|
+
return mq;
|
|
47
|
+
}
|
|
48
|
+
}, [media, units]);
|
|
49
|
+
return mediaStyle;
|
|
48
50
|
};
|
|
49
51
|
exports.useMediaQuery = useMediaQuery;
|
|
50
|
-
/**
|
|
51
|
-
const useLayout = (onLayout) => {
|
|
52
|
+
/** Hook that will measure the layout to handle styles that use % units */
|
|
53
|
+
const useLayout = (onLayout, needsLayout) => {
|
|
52
54
|
const [layout, setLayout] = react_1.default.useState({ width: 0, height: 0 });
|
|
53
55
|
// Prevent calling setState if the component is unmounted
|
|
54
56
|
const unmounted = react_1.default.useRef(false);
|
|
55
57
|
react_1.default.useEffect(() => () => { unmounted.current = true; }, []);
|
|
56
|
-
const updateLayout = react_1.default.
|
|
57
|
-
if (onLayout)
|
|
58
|
-
onLayout(event);
|
|
58
|
+
const updateLayout = react_1.default.useMemo(() => needsLayout ? (event) => {
|
|
59
59
|
if (unmounted.current)
|
|
60
60
|
return;
|
|
61
|
+
if (onLayout)
|
|
62
|
+
onLayout(event);
|
|
61
63
|
const { width, height } = event.nativeEvent.layout;
|
|
62
64
|
setLayout(layout => layout.width === width && layout.height === height ? layout : { width, height });
|
|
63
|
-
}, [onLayout]);
|
|
64
|
-
return { onLayout: updateLayout, ...layout };
|
|
65
|
+
} : undefined, [needsLayout, onLayout]);
|
|
66
|
+
return { onLayout: updateLayout || onLayout, ...layout };
|
|
65
67
|
};
|
|
66
68
|
exports.useLayout = useLayout;
|
|
67
69
|
/** Apply the new fontSize to the component before we can calculate em units */
|
|
68
|
-
const useFontSize = (
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
70
|
+
const useFontSize = (fontSizeTarget, rem, em) => {
|
|
71
|
+
const [fontSize, fontUnit] = react_1.default.useMemo(() => fontSizeTarget === undefined ? [fontSizeTarget] : (0, convertUnits_1.parseValue)(fontSizeTarget), [fontSizeTarget]);
|
|
72
|
+
const isRelative = fontUnit && ['rem', 'em', '%'].includes(fontUnit);
|
|
73
|
+
const newSize = react_1.default.useMemo(() => {
|
|
74
|
+
if (fontSize && isRelative) {
|
|
75
|
+
const newSize = fontUnit === 'em' ? em * fontSize
|
|
76
|
+
: fontUnit === 'rem' ? fontSize * rem
|
|
77
|
+
: fontUnit === '%' ? em * (1 + fontSize / 100)
|
|
78
|
+
: fontSize;
|
|
79
|
+
return newSize;
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
return fontSize || em;
|
|
83
|
+
}, [em, fontSize, fontUnit, isRelative, rem]);
|
|
84
|
+
return { em: newSize };
|
|
84
85
|
};
|
|
85
86
|
exports.useFontSize = useFontSize;
|
|
86
|
-
const useZIndex = (zIndexFromStyle) => {
|
|
87
|
-
const [zIndex, setZIndex] = react_1.default.useState();
|
|
88
|
-
const updateParentZIndex = react_1.default.useContext(exports.zIndexContext);
|
|
89
|
-
react_1.default.useEffect(() => {
|
|
90
|
-
setZIndex(zIndexFromStyle);
|
|
91
|
-
if (react_native_1.Platform.OS === 'ios' && zIndexFromStyle && zIndexFromStyle !== zIndex)
|
|
92
|
-
updateParentZIndex(zIndexFromStyle);
|
|
93
|
-
}, [zIndexFromStyle, updateParentZIndex]);
|
|
94
|
-
return react_native_1.Platform.OS === 'web' ? (zIndex || 'auto') : zIndex;
|
|
95
|
-
};
|
|
96
|
-
exports.useZIndex = useZIndex;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import * as RN from 'react-native';
|
|
3
3
|
export { cssToRNStyle } from './cssToRN';
|
|
4
|
-
export { FontSizeContext } from './
|
|
5
|
-
export { SharedValue } from './styleComponent';
|
|
4
|
+
export { SharedValue, FontSizeContext, RemContext } from './styleComponent';
|
|
6
5
|
export * from './useTheme';
|
|
7
6
|
declare const styled: {
|
|
8
7
|
<T>(Component: React.ComponentType<T>): {
|
package/dist/index.js
CHANGED
|
@@ -22,15 +22,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
22
22
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.
|
|
25
|
+
exports.RemContext = exports.FontSizeContext = exports.SharedValue = exports.cssToRNStyle = void 0;
|
|
26
26
|
const RN = __importStar(require("react-native"));
|
|
27
27
|
const styleComponent_1 = __importStar(require("./styleComponent"));
|
|
28
28
|
var cssToRN_1 = require("./cssToRN");
|
|
29
29
|
Object.defineProperty(exports, "cssToRNStyle", { enumerable: true, get: function () { return cssToRN_1.cssToRNStyle; } });
|
|
30
|
-
var features_1 = require("./features");
|
|
31
|
-
Object.defineProperty(exports, "FontSizeContext", { enumerable: true, get: function () { return features_1.FontSizeContext; } });
|
|
32
30
|
var styleComponent_2 = require("./styleComponent");
|
|
33
31
|
Object.defineProperty(exports, "SharedValue", { enumerable: true, get: function () { return styleComponent_2.SharedValue; } });
|
|
32
|
+
Object.defineProperty(exports, "FontSizeContext", { enumerable: true, get: function () { return styleComponent_2.FontSizeContext; } });
|
|
33
|
+
Object.defineProperty(exports, "RemContext", { enumerable: true, get: function () { return styleComponent_2.RemContext; } });
|
|
34
34
|
__exportStar(require("./useTheme"), exports);
|
|
35
35
|
const styled = (Component) => (0, styleComponent_1.default)(Component);
|
|
36
36
|
styled.ActivityIndicator = styled(RN.ActivityIndicator);
|
package/dist/styleComponent.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import React, { MouseEvent } from 'react';
|
|
2
2
|
import { FlatListProps, LayoutChangeEvent, SectionListProps, StyleProp, VirtualizedListProps } from 'react-native';
|
|
3
|
+
import type { 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>;
|
|
3
7
|
export declare const SharedValue: React.Context<unknown>;
|
|
4
8
|
declare type Primitive = number | string | null | undefined | boolean;
|
|
5
9
|
declare type Functs<T> = (arg: T & {
|
package/dist/styleComponent.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.styledVirtualizedList = exports.styledSectionList = exports.styledFlatList = exports.SharedValue = void 0;
|
|
6
|
+
exports.styledVirtualizedList = exports.styledSectionList = exports.styledFlatList = exports.SharedValue = exports.FontSizeContext = exports.RemContext = exports.defaultUnits = void 0;
|
|
7
7
|
/* eslint-disable react/prop-types */
|
|
8
8
|
/* eslint-disable react/display-name */
|
|
9
9
|
const react_1 = __importDefault(require("react"));
|
|
@@ -12,6 +12,9 @@ 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
|
+
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
|
+
exports.RemContext = react_1.default.createContext(exports.defaultUnits.rem);
|
|
17
|
+
exports.FontSizeContext = react_1.default.createContext(exports.defaultUnits.em);
|
|
15
18
|
// We use this to cache the computed styles
|
|
16
19
|
const styleMap = {};
|
|
17
20
|
// We use this to share value within the component (Theme, Translation, whatever)
|
|
@@ -25,15 +28,13 @@ function buildCSSString(chunks, functs, props, shared) {
|
|
|
25
28
|
const styled = (Component) => {
|
|
26
29
|
const styledComponent = (chunks, ...functs) => {
|
|
27
30
|
const ForwardRefComponent = react_1.default.forwardRef((props, ref) => {
|
|
28
|
-
const
|
|
31
|
+
const rem = react_1.default.useContext(exports.RemContext);
|
|
29
32
|
const shared = react_1.default.useContext(exports.SharedValue);
|
|
30
|
-
//
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
+
// Build the css string with the context
|
|
34
|
+
const css = react_1.default.useMemo(() => buildCSSString(chunks, functs, props, shared), [props, shared]);
|
|
35
|
+
// Store the style in RN format
|
|
36
|
+
const [rnStyle, setRNStyle] = react_1.default.useState({});
|
|
33
37
|
react_1.default.useEffect(() => {
|
|
34
|
-
// Build the css string with the context
|
|
35
|
-
const css = buildCSSString(chunks, functs, props, shared);
|
|
36
|
-
cssString.current = css;
|
|
37
38
|
// Try to load an existing style from the style map or save it for next time
|
|
38
39
|
const hash = (0, generateHash_1.default)(css);
|
|
39
40
|
const style = styleMap[hash];
|
|
@@ -53,75 +54,67 @@ const styled = (Component) => {
|
|
|
53
54
|
if (style.usages <= 0)
|
|
54
55
|
delete styleMap[hash];
|
|
55
56
|
};
|
|
56
|
-
}, [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// setNeedsFontSize(!!css.match(/\b(\d+)(\.\d+)?em\b/)) // Do we need em units
|
|
64
|
-
// setNeedsScreenSize(!!css.match(/\b(\d+)(\.\d+)?v([hw]|min|max)\b/)) // Do we need vx units
|
|
65
|
-
setNeedsLayout(!!css.match(/\d%/)); // Do we need % units
|
|
66
|
-
// setNeedsHover(!!css.match(/&:hover/)) // Do we need to track the mouse
|
|
67
|
-
}, [cssString.current]);
|
|
68
|
-
const finalStyle = { ...rnStyle };
|
|
69
|
-
delete finalStyle.media;
|
|
70
|
-
delete finalStyle.hover;
|
|
71
|
-
// Read all the data we might need
|
|
57
|
+
}, [css]);
|
|
58
|
+
const { needsLayout, needsHover } = react_1.default.useMemo(() => ({
|
|
59
|
+
// needsFontSize: !!css.match(/\b(\d+)(\.\d+)?em\b/)
|
|
60
|
+
// needsScreenSize: !!cssString.current.match(/\b(\d+)(\.\d+)?v([hw]|min|max)\b/) || !!rnStyle.media,
|
|
61
|
+
needsLayout: !!css.match(/\d%/),
|
|
62
|
+
needsHover: !!rnStyle.hover
|
|
63
|
+
}), [css, rnStyle.hover]);
|
|
72
64
|
// Handle hover
|
|
73
|
-
const { onMouseEnter, onMouseLeave,
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
const { onMouseEnter, onMouseLeave, hover } = (0, features_1.useHover)(props.onMouseEnter, props.onMouseLeave, needsHover);
|
|
66
|
+
const tempStyle = react_1.default.useMemo(() => {
|
|
67
|
+
const style = { ...rnStyle };
|
|
68
|
+
delete style.media;
|
|
69
|
+
delete style.hover;
|
|
70
|
+
if (hover)
|
|
71
|
+
Object.assign(style, rnStyle.hover);
|
|
72
|
+
return style;
|
|
73
|
+
}, [hover, rnStyle]);
|
|
76
74
|
// Calculate current em unit for media-queries
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
units.current = { ...units.current, em: tempEm };
|
|
75
|
+
const parentEm = react_1.default.useContext(exports.FontSizeContext);
|
|
76
|
+
const { em: tempEm } = (0, features_1.useFontSize)(tempStyle.fontSize, rem, parentEm);
|
|
80
77
|
// Handle layout data needed for % units
|
|
81
|
-
const { width, height, onLayout } = (0, features_1.useLayout)(props.onLayout);
|
|
82
|
-
if (needsLayout && (units.current.width !== width || units.current.height !== height)) {
|
|
83
|
-
units.current = { ...units.current, width, height };
|
|
84
|
-
}
|
|
78
|
+
const { width, height, onLayout } = (0, features_1.useLayout)(props.onLayout, needsLayout);
|
|
85
79
|
// Handle screen size needed for vw and wh units
|
|
86
80
|
const screenUnits = (0, features_1.useScreenSize)();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
/** The units before re-updating the em */
|
|
82
|
+
const baseUnits = react_1.default.useMemo(() => ({
|
|
83
|
+
...exports.defaultUnits,
|
|
84
|
+
rem,
|
|
85
|
+
em: tempEm,
|
|
86
|
+
width,
|
|
87
|
+
height,
|
|
88
|
+
...screenUnits
|
|
89
|
+
}), [height, rem, screenUnits, tempEm, width]);
|
|
90
90
|
// apply media queries
|
|
91
|
-
const mediaQuery = (0, features_1.useMediaQuery)(rnStyle.media,
|
|
92
|
-
if (mediaQuery)
|
|
93
|
-
Object.assign(finalStyle, mediaQuery);
|
|
91
|
+
const mediaQuery = (0, features_1.useMediaQuery)(rnStyle.media, baseUnits);
|
|
94
92
|
// Handle em units
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const style = react_1.default.useMemo(() =>
|
|
108
|
-
|
|
109
|
-
style
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}, [props.style, styleConvertedFromCSS, zIndex]);
|
|
116
|
-
const newProps = { style, onMouseEnter, onMouseLeave, onLayout };
|
|
117
|
-
// Handle ellipsis
|
|
118
|
-
if (react_native_1.StyleSheet.flatten(style).textOverflow === 'ellipsis')
|
|
119
|
-
Object.assign(newProps, { numberOfLines: 1 });
|
|
93
|
+
const fontSize = (mediaQuery && mediaQuery.fontSize) || tempStyle.fontSize;
|
|
94
|
+
const { em } = (0, features_1.useFontSize)(fontSize, baseUnits.rem, parentEm);
|
|
95
|
+
const finalStyle = react_1.default.useMemo(() => {
|
|
96
|
+
const style = { ...tempStyle, ...mediaQuery };
|
|
97
|
+
if (style.fontSize)
|
|
98
|
+
style.fontSize = em + 'px';
|
|
99
|
+
if (style.zIndex === undefined && react_native_1.Platform.OS === 'web')
|
|
100
|
+
style.zIndex = 'auto';
|
|
101
|
+
return style;
|
|
102
|
+
}, [em, mediaQuery, tempStyle]);
|
|
103
|
+
const units = react_1.default.useMemo(() => ({ ...baseUnits, em }), [baseUnits, em]);
|
|
104
|
+
const styleConvertedFromCSS = react_1.default.useMemo(() => (0, convertStyle_1.default)(finalStyle, units), [finalStyle, units]);
|
|
105
|
+
const style = react_1.default.useMemo(() => react_native_1.StyleSheet.flatten([props.style, styleConvertedFromCSS]), [props.style, styleConvertedFromCSS]);
|
|
106
|
+
const newProps = react_1.default.useMemo(() => {
|
|
107
|
+
const newProps = { style, onMouseEnter, onMouseLeave, onLayout };
|
|
108
|
+
if (style.textOverflow === 'ellipsis')
|
|
109
|
+
Object.assign(newProps, { numberOfLines: 1 });
|
|
110
|
+
return newProps;
|
|
111
|
+
}, [onLayout, onMouseEnter, onMouseLeave, style]);
|
|
112
|
+
console.log(style, styleConvertedFromCSS, tempStyle, rnStyle, css);
|
|
120
113
|
// The lines below can improve perfs, but it causes the component to remount when the font size changes
|
|
121
114
|
// const currentFontSize = React.useContext(FontSizeContext)
|
|
122
115
|
// if (em !== currentFontSize) {
|
|
123
116
|
if (finalStyle.fontSize) {
|
|
124
|
-
return react_1.default.createElement(
|
|
117
|
+
return react_1.default.createElement(exports.FontSizeContext.Provider, { value: em },
|
|
125
118
|
react_1.default.createElement(Component, { ref: ref, ...props, ...newProps }));
|
|
126
119
|
}
|
|
127
120
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-css",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"test": "jest",
|
|
6
6
|
"prepare": "tsc",
|
|
@@ -35,18 +35,19 @@
|
|
|
35
35
|
"eslint-plugin-node": "^11.1.0",
|
|
36
36
|
"eslint-plugin-promise": "^5.2.0",
|
|
37
37
|
"eslint-plugin-react": "^7.27.1",
|
|
38
|
+
"eslint-plugin-react-hooks": "^4.4.0",
|
|
38
39
|
"eslint-plugin-standard": "^5.0.0",
|
|
39
40
|
"html-loader": "^3.0.1",
|
|
40
41
|
"html-webpack-plugin": "^5.5.0",
|
|
41
42
|
"husky": "^7.0.4",
|
|
42
|
-
"jest": "^27.
|
|
43
|
+
"jest": "^27.5.1",
|
|
43
44
|
"lint-staged": "^12.1.2",
|
|
44
|
-
"metro-react-native-babel-preset": "^0.
|
|
45
|
+
"metro-react-native-babel-preset": "^0.70.0",
|
|
45
46
|
"react": "^17.0.2",
|
|
46
47
|
"react-dom": "^17.0.2",
|
|
47
|
-
"react-native": "^0.
|
|
48
|
+
"react-native": "^0.68.0",
|
|
48
49
|
"react-native-typescript-transformer": "^1.2.13",
|
|
49
|
-
"react-native-web": "^0.17.
|
|
50
|
+
"react-native-web": "^0.17.7",
|
|
50
51
|
"react-test-renderer": "17.0.2",
|
|
51
52
|
"release-it": "^14.11.8",
|
|
52
53
|
"ts-jest": "^27.1.1",
|
package/src/convertUnits.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Platform } from 'react-native'
|
|
|
5
5
|
/** Take a css value like 12em and return [12, 'em'] */
|
|
6
6
|
export function parseValue (value: string): [number, string | undefined] {
|
|
7
7
|
// Match a single unit
|
|
8
|
-
const unit = value.match(/([+-]?\b\d+(\.\d+)?)([a-z]+\b|%)
|
|
8
|
+
const unit = value.match(/([+-]?\b\d+(\.\d+)?)([a-z]+\b|%)?/i)
|
|
9
9
|
return [parseFloat(unit![1]), unit![3] as (string | undefined)]
|
|
10
10
|
}
|
|
11
11
|
|
package/src/features.tsx
CHANGED
|
@@ -1,85 +1,75 @@
|
|
|
1
1
|
/* eslint-disable react/display-name */
|
|
2
2
|
import React, { MouseEvent } from 'react'
|
|
3
|
-
import type { Style, Units, MediaQuery } from './types'
|
|
4
|
-
import { useWindowDimensions, LayoutChangeEvent
|
|
3
|
+
import type { Style, Units, MediaQuery, PartialStyle } from './types'
|
|
4
|
+
import { useWindowDimensions, LayoutChangeEvent } from 'react-native'
|
|
5
5
|
import { parseValue } from './convertUnits'
|
|
6
6
|
import { createContext } from './cssToRN/mediaQueries'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
|
|
10
|
-
export const zIndexContext = React.createContext((_zIndex: number) => {})
|
|
11
|
-
|
|
12
|
-
/** HOC that will apply the screen size to the styles defined with vmin, vmax, vw, vh units, and handle media queries constraints */
|
|
8
|
+
/** Hook that will apply the screen size to the styles defined with vmin, vmax, vw, vh units, and handle media queries constraints */
|
|
13
9
|
export const useScreenSize = () => {
|
|
14
10
|
const { width, height } = useWindowDimensions()
|
|
15
|
-
return
|
|
11
|
+
return React.useMemo(() => ({
|
|
12
|
+
vw: width / 100, vh: height / 100, vmin: Math.min(width, height) / 100, vmax: Math.max(width, height) / 100
|
|
13
|
+
}), [height, width])
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
/**
|
|
19
|
-
export const useHover = (
|
|
16
|
+
/** Hook that will apply the style reserved for hover state if needed */
|
|
17
|
+
export const useHover = (onMouseEnter: undefined | ((event: MouseEvent) => void), onMouseLeave: undefined | ((event: MouseEvent) => void | undefined), needsHover: boolean) => {
|
|
20
18
|
const [hover, setHover] = React.useState(false)
|
|
21
|
-
const hoverStart = React.
|
|
19
|
+
const hoverStart = React.useMemo(() => needsHover ? (event: MouseEvent) => {
|
|
22
20
|
if (onMouseEnter) onMouseEnter(event)
|
|
23
21
|
setHover(true)
|
|
24
|
-
}, [onMouseEnter])
|
|
25
|
-
const hoverStop = React.
|
|
22
|
+
} : undefined, [needsHover, onMouseEnter])
|
|
23
|
+
const hoverStop = React.useMemo(() => needsHover ? (event: MouseEvent) => {
|
|
26
24
|
if (onMouseLeave) onMouseLeave(event)
|
|
27
25
|
setHover(false)
|
|
28
|
-
}, [onMouseLeave])
|
|
29
|
-
return {
|
|
26
|
+
} : undefined, [needsHover, onMouseLeave])
|
|
27
|
+
return { hover, onMouseEnter: hoverStart || onMouseEnter, onMouseLeave: hoverStop || onMouseLeave }
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
/**
|
|
30
|
+
/** Hook that will apply the style provided in the media queries */
|
|
33
31
|
export const useMediaQuery = (media: undefined | MediaQuery[], units: Units): Style | undefined => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
const mediaStyle = React.useMemo(() => {
|
|
33
|
+
if (media) {
|
|
34
|
+
const context = createContext(units)
|
|
35
|
+
const mediaStyles = media.map(m => m(context)).filter(m => !!m) as PartialStyle[]
|
|
36
|
+
if (!mediaStyles.length) return
|
|
37
|
+
const mq = {} as Style
|
|
38
|
+
Object.assign(mq, ...mediaStyles)
|
|
39
|
+
return mq
|
|
40
|
+
}
|
|
41
|
+
}, [media, units])
|
|
42
|
+
return mediaStyle
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
/**
|
|
45
|
-
export const useLayout = (onLayout
|
|
45
|
+
/** Hook that will measure the layout to handle styles that use % units */
|
|
46
|
+
export const useLayout = (onLayout: undefined | ((event: LayoutChangeEvent) => void), needsLayout: boolean) => {
|
|
46
47
|
const [layout, setLayout] = React.useState({ width: 0, height: 0 })
|
|
47
48
|
// Prevent calling setState if the component is unmounted
|
|
48
49
|
const unmounted = React.useRef(false)
|
|
49
50
|
React.useEffect(() => () => { unmounted.current = true }, [])
|
|
50
|
-
const updateLayout = React.
|
|
51
|
-
if (onLayout) onLayout(event)
|
|
51
|
+
const updateLayout = React.useMemo(() => needsLayout ? (event: LayoutChangeEvent) => {
|
|
52
52
|
if (unmounted.current) return
|
|
53
|
+
if (onLayout) onLayout(event)
|
|
53
54
|
const { width, height } = event.nativeEvent.layout
|
|
54
55
|
setLayout(layout => layout.width === width && layout.height === height ? layout : { width, height })
|
|
55
|
-
}, [onLayout])
|
|
56
|
-
return { onLayout: updateLayout, ...layout }
|
|
56
|
+
} : undefined, [needsLayout, onLayout])
|
|
57
|
+
return { onLayout: updateLayout || onLayout, ...layout }
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/** Apply the new fontSize to the component before we can calculate em units */
|
|
60
|
-
export const useFontSize = (
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export const useZIndex = (zIndexFromStyle: number) => {
|
|
78
|
-
const [zIndex, setZIndex] = React.useState<number>()
|
|
79
|
-
const updateParentZIndex = React.useContext(zIndexContext)
|
|
80
|
-
React.useEffect(() => {
|
|
81
|
-
setZIndex(zIndexFromStyle)
|
|
82
|
-
if (Platform.OS === 'ios' && zIndexFromStyle && zIndexFromStyle !== zIndex) updateParentZIndex(zIndexFromStyle)
|
|
83
|
-
}, [zIndexFromStyle, updateParentZIndex])
|
|
84
|
-
return Platform.OS === 'web' ? (zIndex || 'auto') : zIndex
|
|
61
|
+
export const useFontSize = (fontSizeTarget: string | undefined, rem: number, em: number): { em: number } => {
|
|
62
|
+
const [fontSize, fontUnit] = React.useMemo(() => fontSizeTarget === undefined ? [fontSizeTarget] : parseValue(fontSizeTarget), [fontSizeTarget])
|
|
63
|
+
const isRelative = fontUnit && ['rem', 'em', '%'].includes(fontUnit)
|
|
64
|
+
const newSize = React.useMemo(() => {
|
|
65
|
+
if (fontSize && isRelative) {
|
|
66
|
+
const newSize = fontUnit === 'em' ? em * fontSize
|
|
67
|
+
: fontUnit === 'rem' ? fontSize * rem
|
|
68
|
+
: fontUnit === '%' ? em * (1 + fontSize / 100)
|
|
69
|
+
: fontSize
|
|
70
|
+
return newSize
|
|
71
|
+
}
|
|
72
|
+
else return fontSize || em
|
|
73
|
+
}, [em, fontSize, fontUnit, isRelative, rem])
|
|
74
|
+
return { em: newSize }
|
|
85
75
|
}
|
package/src/index.tsx
CHANGED
|
@@ -2,8 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import * as RN from 'react-native'
|
|
3
3
|
import styledComponent, { styledFlatList, styledSectionList, styledVirtualizedList } from './styleComponent'
|
|
4
4
|
export { cssToRNStyle } from './cssToRN'
|
|
5
|
-
export { FontSizeContext } from './
|
|
6
|
-
export { SharedValue } from './styleComponent'
|
|
5
|
+
export { SharedValue, FontSizeContext, RemContext } from './styleComponent'
|
|
7
6
|
export * from './useTheme'
|
|
8
7
|
|
|
9
8
|
const styled = <T, >(Component: React.ComponentType<T>) => styledComponent<T>(Component)
|
package/src/styleComponent.tsx
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
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,
|
|
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
|
-
import {
|
|
7
|
+
import { useFontSize, useHover, useLayout, useScreenSize, useMediaQuery } from './features'
|
|
8
8
|
import calculHash from './generateHash'
|
|
9
9
|
import type { Style, StyleMap, Units } from './types'
|
|
10
10
|
|
|
11
|
+
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
|
+
export const RemContext = React.createContext<number>(defaultUnits.rem)
|
|
13
|
+
export const FontSizeContext = React.createContext(defaultUnits.em)
|
|
14
|
+
|
|
11
15
|
// We use this to cache the computed styles
|
|
12
16
|
const styleMap: StyleMap = {}
|
|
13
17
|
|
|
@@ -32,15 +36,13 @@ function buildCSSString<T extends { rnCSS?: string }> (chunks: TemplateStringsAr
|
|
|
32
36
|
const styled = <Props, >(Component: React.ComponentType<Props>) => {
|
|
33
37
|
const styledComponent = <S, >(chunks: TemplateStringsArray, ...functs: (Primitive | Functs<S & Props>)[]) => {
|
|
34
38
|
const ForwardRefComponent = React.forwardRef<React.ComponentType<S & Props & OptionalProps>, S & Props & OptionalProps>((props: S & Props & OptionalProps, ref) => {
|
|
35
|
-
const
|
|
39
|
+
const rem = React.useContext(RemContext)
|
|
36
40
|
const shared = React.useContext(SharedValue)
|
|
37
|
-
// Store the style for mutualization
|
|
38
|
-
const cssString = React.useRef(buildCSSString(chunks, functs, props, shared))
|
|
39
|
-
const [rnStyle, setRNStyle] = React.useState<Style>(cssToStyle(cssString.current))
|
|
40
|
-
React.useEffect(() => {
|
|
41
41
|
// Build the css string with the context
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const css = React.useMemo(() => buildCSSString(chunks, functs, props, shared), [props, shared])
|
|
43
|
+
// Store the style in RN format
|
|
44
|
+
const [rnStyle, setRNStyle] = React.useState<Style>({})
|
|
45
|
+
React.useEffect(() => {
|
|
44
46
|
// Try to load an existing style from the style map or save it for next time
|
|
45
47
|
const hash = calculHash(css)
|
|
46
48
|
const style = styleMap[hash]
|
|
@@ -59,72 +61,67 @@ const styled = <Props, >(Component: React.ComponentType<Props>) => {
|
|
|
59
61
|
style.usages--
|
|
60
62
|
if (style.usages <= 0) delete styleMap[hash]
|
|
61
63
|
}
|
|
62
|
-
}, [
|
|
64
|
+
}, [css])
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// setNeedsFontSize(!!css.match(/\b(\d+)(\.\d+)?em\b/)) // Do we need em units
|
|
71
|
-
// setNeedsScreenSize(!!css.match(/\b(\d+)(\.\d+)?v([hw]|min|max)\b/)) // Do we need vx units
|
|
72
|
-
setNeedsLayout(!!css.match(/\d%/)) // Do we need % units
|
|
73
|
-
// setNeedsHover(!!css.match(/&:hover/)) // Do we need to track the mouse
|
|
74
|
-
}, [cssString.current])
|
|
75
|
-
|
|
76
|
-
const finalStyle = { ...rnStyle }
|
|
77
|
-
delete finalStyle.media
|
|
78
|
-
delete finalStyle.hover
|
|
79
|
-
// Read all the data we might need
|
|
66
|
+
const { needsLayout, needsHover } = React.useMemo(() => ({
|
|
67
|
+
// needsFontSize: !!css.match(/\b(\d+)(\.\d+)?em\b/)
|
|
68
|
+
// needsScreenSize: !!cssString.current.match(/\b(\d+)(\.\d+)?v([hw]|min|max)\b/) || !!rnStyle.media,
|
|
69
|
+
needsLayout: !!css.match(/\d%/),
|
|
70
|
+
needsHover: !!rnStyle.hover
|
|
71
|
+
}), [css, rnStyle.hover])
|
|
80
72
|
|
|
81
73
|
// Handle hover
|
|
82
|
-
const { onMouseEnter, onMouseLeave,
|
|
83
|
-
|
|
74
|
+
const { onMouseEnter, onMouseLeave, hover } = useHover(props.onMouseEnter, props.onMouseLeave, needsHover)
|
|
75
|
+
const tempStyle = React.useMemo<Style>(() => {
|
|
76
|
+
const style = { ...rnStyle }
|
|
77
|
+
delete style.media
|
|
78
|
+
delete style.hover
|
|
79
|
+
if (hover) Object.assign(style, rnStyle.hover)
|
|
80
|
+
return style
|
|
81
|
+
}, [hover, rnStyle])
|
|
84
82
|
|
|
85
83
|
// Calculate current em unit for media-queries
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
const parentEm = React.useContext(FontSizeContext)
|
|
85
|
+
const { em: tempEm } = useFontSize(tempStyle.fontSize, rem, parentEm)
|
|
89
86
|
// Handle layout data needed for % units
|
|
90
|
-
const { width, height, onLayout } = useLayout(props.onLayout)
|
|
91
|
-
if (needsLayout && (units.current.width !== width || units.current.height !== height)) {
|
|
92
|
-
units.current = { ...units.current, width, height }
|
|
93
|
-
}
|
|
94
|
-
|
|
87
|
+
const { width, height, onLayout } = useLayout(props.onLayout, needsLayout)
|
|
95
88
|
// Handle screen size needed for vw and wh units
|
|
96
89
|
const screenUnits = useScreenSize()
|
|
97
|
-
if (/* needsScreenSize && */(Object.keys(screenUnits) as (keyof typeof screenUnits)[]).find(key => units.current[key] !== screenUnits[key])) {
|
|
98
|
-
units.current = { ...units.current, ...screenUnits }
|
|
99
|
-
}
|
|
100
90
|
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
91
|
+
/** The units before re-updating the em */
|
|
92
|
+
const baseUnits = React.useMemo<Units>(() => ({
|
|
93
|
+
...defaultUnits,
|
|
94
|
+
rem,
|
|
95
|
+
em: tempEm,
|
|
96
|
+
width,
|
|
97
|
+
height,
|
|
98
|
+
...screenUnits
|
|
99
|
+
}), [height, rem, screenUnits, tempEm, width])
|
|
104
100
|
|
|
101
|
+
// apply media queries
|
|
102
|
+
const mediaQuery = useMediaQuery(rnStyle.media, baseUnits)
|
|
105
103
|
// Handle em units
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (StyleSheet.flatten(style).textOverflow === 'ellipsis') Object.assign(newProps, { numberOfLines: 1 })
|
|
104
|
+
const fontSize = (mediaQuery && mediaQuery.fontSize) || tempStyle.fontSize
|
|
105
|
+
const { em } = useFontSize(fontSize, baseUnits.rem, parentEm)
|
|
106
|
+
|
|
107
|
+
const finalStyle = React.useMemo<Style>(() => {
|
|
108
|
+
const style: Style = { ...tempStyle, ...mediaQuery }
|
|
109
|
+
if (style.fontSize) style.fontSize = em + 'px'
|
|
110
|
+
if (style.zIndex === undefined && Platform.OS === 'web') style.zIndex = 'auto'
|
|
111
|
+
return style
|
|
112
|
+
}, [em, mediaQuery, tempStyle])
|
|
113
|
+
|
|
114
|
+
const units = React.useMemo<Units>(() => ({ ...baseUnits, em }), [baseUnits, em])
|
|
115
|
+
|
|
116
|
+
const styleConvertedFromCSS = React.useMemo(() => convertStyle(finalStyle, units), [finalStyle, units])
|
|
117
|
+
const style: StyleProp<any> = React.useMemo(() => StyleSheet.flatten([props.style, styleConvertedFromCSS]), [props.style, styleConvertedFromCSS])
|
|
118
|
+
const newProps = React.useMemo(() => {
|
|
119
|
+
const newProps: OptionalProps = { style, onMouseEnter, onMouseLeave, onLayout }
|
|
120
|
+
if (style.textOverflow === 'ellipsis') Object.assign(newProps, { numberOfLines: 1 })
|
|
121
|
+
return newProps
|
|
122
|
+
}, [onLayout, onMouseEnter, onMouseLeave, style])
|
|
123
|
+
|
|
124
|
+
console.log(style, styleConvertedFromCSS, tempStyle, rnStyle, css)
|
|
128
125
|
|
|
129
126
|
// The lines below can improve perfs, but it causes the component to remount when the font size changes
|
|
130
127
|
// const currentFontSize = React.useContext(FontSizeContext)
|