-
Notifications
You must be signed in to change notification settings - Fork 1
/
responsive.tsx
119 lines (105 loc) · 3.78 KB
/
responsive.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import React, { useContext } from 'react';
import { ThemeContext } from '@emotion/core';
import { useEffect } from 'react';
import { Dimensions, ScaledSize, Platform } from 'react-native';
import { useCallback } from 'react';
import { Theme, ReactNativeStyle, AnyStyle } from './types/StyleSheet';
import { StyleSheet, findBreakpoints } from './StyleSheet';
import memoize from 'lodash.memoize';
import { useMemo } from 'react';
import { useState } from 'react';
type AnyProps = {
style: AnyStyle;
[key: string]: any;
} & any;
function withResponsive<T extends React.ComponentClass<any>>(
Component: T
): React.ForwardRefExoticComponent<
React.ComponentPropsWithoutRef<T> & {
ref?: React.Ref<InstanceType<T>>;
} & AnyProps
>;
function withResponsive<P extends AnyProps & { ref?: React.Ref<any> }>(
Component: React.ForwardRefExoticComponent<P & AnyProps>
): React.ForwardRefExoticComponent<P & AnyProps>;
function withResponsive<P = AnyProps>(
Component: React.FunctionComponent<P & AnyProps>
): React.ForwardRefExoticComponent<P & AnyProps>;
function withResponsive<P = AnyProps>(
Component: React.ComponentType<P & AnyProps>
) {
// I don't know how to implement this without breaking out of the types.
// The overloads are ensuring correct usage, so we should be good?
const Responsive = React.forwardRef<typeof Component, P & AnyProps>(
({ style, children, ...props }, ref) => {
const theme = useContext(ThemeContext) as Theme;
const getStylesheet = memoize(
(styles: any, _breakpoint: number | undefined) => {
return StyleSheet.create({
styles,
}).styles as any;
},
(_styles: any, _breakpoint: number | undefined) => breakpoint
);
const styles = useMemo<ReactNativeStyle>(() => {
if (!style) return {};
// Adds support for `screen` media queries to support Styled System
// @see https://github.com/styled-system/styled-system/pull/1133
// @see https://github.com/vitalets/react-native-extended-stylesheet/pull/132
const _styles = Object.keys(style).reduce<AnyStyle>(
(acc: AnyStyle, key) => {
acc[key.replace('screen', Platform.OS)] = style[key];
return acc;
},
{}
);
return _styles;
}, [style]);
const remValue = useMemo<number>(() => {
try {
return Number(StyleSheet.value('$rem'));
} catch {
return Number(theme?.rem || 16);
}
}, [theme]);
const breakpoints = useMemo(() => {
return findBreakpoints(styles, remValue);
}, [styles, remValue]);
const getBreakpoint = useCallback(
(width: number): number | undefined => {
return (breakpoints || []).find((item: number) => width < item);
},
[breakpoints]
);
const [breakpoint, setBreakpoint] = useState<number | undefined>(
getBreakpoint(Dimensions.get('window').width)
);
const onDimesionsChange = useCallback(
({ window }: { window: ScaledSize; screen: ScaledSize }) => {
StyleSheet.build({
$theme: getBreakpoint(window.width),
});
setBreakpoint(getBreakpoint(window.width));
},
[getBreakpoint]
);
useEffect(() => {
if (style && breakpoints.length) {
Dimensions.addEventListener('change', onDimesionsChange);
return () =>
Dimensions.removeEventListener('change', onDimesionsChange);
} else {
return () => null;
}
}, [breakpoints]);
const stylesheet = getStylesheet(styles, breakpoint);
return (
<Component {...props} ref={ref} style={stylesheet}>
{children}
</Component>
);
}
);
return Responsive;
}
export { withResponsive };