Skip to content

Commit

Permalink
feat: box-shadow (android, windows)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zivsteve committed Apr 28, 2021
1 parent 3502e13 commit e8b19df
Show file tree
Hide file tree
Showing 12 changed files with 365 additions and 86 deletions.
3 changes: 2 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export default function App() {
<StyleProvider>
<StatusBar backgroundColor='#fff' style='dark' />
<NavigationContainer linking={linking}>
<Stack.Navigator screenOptions={{ animationEnabled: true, ...TransitionPresets.SlideFromRightIOS }}>
<Stack.Navigator
screenOptions={{ animationEnabled: true, gestureEnabled: true, ...TransitionPresets.SlideFromRightIOS }}>
<Stack.Screen name='Home' component={HomeScreen} />

<Stack.Screen name='BasicExample' component={BasicExampleScreen} />
Expand Down
20 changes: 8 additions & 12 deletions example/src/screens/ShadowsExampleScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@ const ShadowsExampleScreen = () => {

const styles = StyleSheet.create(() => ({
view: {
width: 300,
height: 300,
marginTop: 100,
width: 200,
height: 200,
alignSelf: 'center',
backgroundColor: 'red',
boxShadow: '5px 5px 5px orange',
transition: [['boxShadow', 'backgroundColor'], 500],

'&:active': {
backgroundColor: 'blue',
boxShadow: '10px 10px 10px yellow',
},
marginTop: 100,
border: [15, 'solid', 'green'],
backgroundColor: '#ff0000',
boxShadow: '10px 10px 20px blue',
transition: ['shadowColor', 3000],
},
text: {
marginTop: 50,
Expand All @@ -37,7 +33,7 @@ const styles = StyleSheet.create(() => ({
color: 'blue',
fontSize: 25,
transition: [['textShadowColor', 'textShadowRadius', 'textShadowOffsetWidth', 'textShadowOffsetHeight'], 1000],
textShadow: '0px 2px 5px #ff0000',
textShadow: '0px 2px 10px #ff0000',

'&:active': {
textShadow: '15px 15px 30px #0044ff',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
},
"dependencies": {
"color": "^3.1.3",
"react-native-svg": "^12.1.1"
"react-native-svg": "^12.1.1",
"react-native-view-shot": "^3.1.2"
},
"devDependencies": {
"@commitlint/config-conventional": "^12.1.1",
Expand Down
117 changes: 80 additions & 37 deletions src/Animated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { flattenStyle, getDefaultStyleValue, wrapStyles } from './utils/styles';
import { calc, toCamelCase, toDuration, toEasing } from './utils/values';
import StyleSheet from './StyleSheet';
import { removeInvalidStyles, validStyles } from './utils/valid-styles';
import BoxShadow, { NATIVELY_SUPPORTED_PLATFORMS } from './components/BoxShadow';

overrideNative(RN.View);
overrideNative(RN.Text);
Expand All @@ -30,8 +31,12 @@ function overrideNative(nativeComp: any) {
nativeComp.render = (props: any, ref: any) => {
nativeComp.render.displayName = nativeComp.displayName;
if (
!JSON.stringify(props.style)?.includes('&:') &&
Object.keys(Object.values(props.style || {}))?.every((k) => validStyles.includes(k))
Object.keys(Object.values(StyleSheet.flatten(props.style) || {}))?.every(
(k) =>
validStyles.includes(k) &&
!k.includes('&:') &&
!(k.includes('shadow') && !NATIVELY_SUPPORTED_PLATFORMS.includes(RN.Platform.OS)),
)
) {
return <NativeComp ref={ref} {...props} />;
}
Expand Down Expand Up @@ -258,7 +263,7 @@ export function createComponent<T extends NativeComponents>(WrappedComponent: T)
return wrapStyles(Object.assign({}, finish, styles));
}

getStyleProps(e: StateCallbackType) {
getStyleProps(e: StateCallbackType, mockShadow = false) {
const props = this.props;
const propKeys = Object.keys(this.props) as (keyof typeof props)[];
const stylePropKeys = propKeys.filter((key) => key.toLowerCase().startsWith('style'));
Expand All @@ -268,12 +273,40 @@ export function createComponent<T extends NativeComponents>(WrappedComponent: T)
const styleArr = [props[key]].flat() as Styles[];
const st = styleArr
.map((c) => (Array.isArray(c) ? RN.StyleSheet.flatten(c) : c))
.map((style) => {
.map((style: Styles) => {
if (Number.isInteger(style)) {
numberStyles.push(style);
return;
}

if (mockShadow) {
const {
/* eslint-disable @typescript-eslint/no-unused-vars */
margin,
marginHorizontal,
marginVertical,
marginTop,
marginRight,
marginBottom,
marginLeft,

position,
left,
right,
bottom,
top,

flex,
alignSelf,
flexBasis,
flexGrow,
flexShrink,
/* eslint-enable */
...remainingStyle
} = style;
style = remainingStyle;
}

let combinedStyles: Styles = Object.assign({}, style, this.getAnimatedStyle(style, e));
combinedStyles = removeInvalidStyles(combinedStyles);

Expand Down Expand Up @@ -319,13 +352,11 @@ export function createComponent<T extends NativeComponents>(WrappedComponent: T)
}

render() {
let { forwardRef, style, as, onFocus, onBlur, ...otherProps } = this.props;
style = StyleSheet.flatten(style);
const { forwardRef, style: rawStyle, as, onFocus, onBlur, ...otherProps } = this.props;
const style = StyleSheet.flatten(rawStyle);
const styleKeys = Object.keys(style ?? {});

// @ts-ignore
const before = (style?.['&::before'] || {}) as RN.TextStyle;
// @ts-ignore
const after = (style?.['&::after'] || {}) as RN.TextStyle;

const beforeContent = before?.content;
Expand All @@ -337,43 +368,55 @@ export function createComponent<T extends NativeComponents>(WrappedComponent: T)
const AnimatedComponent = this.AnimatedComponent || RN.Animated.createAnimatedComponent(as || WrappedComponent);
this.AnimatedComponent = AnimatedComponent;

const content = (e: StateCallbackType) => (
const needsPressable = styleKeys.some(
(k) => k.includes(':hover') || k.includes(':active') || k.includes(':focus'),
);

const mockShadow =
!NATIVELY_SUPPORTED_PLATFORMS.includes(RN.Platform.OS) &&
(style.elevation || 0) <= 0 &&
styleKeys.some((k) => k.includes('shadowColor'));

const renderComponent = (e: StateCallbackType) => (
<AnimatedComponent
ref={(r: NativeComponents) => {
typeof forwardRef === 'function' && forwardRef(r);
this._ref = r;
}}
{...this.getPseudoProps()}
{...this.getStyleProps(e, mockShadow)}
pointerEvents={StyleSheet.flatten(style)?.pointerEvents}
onFocus={(ev: FocusEvent) => {
if (typeof this._ref?.isFocused === 'function') {
this.forceUpdate();
}
onFocus?.(ev);
}}
onBlur={(ev: FocusEvent) => {
if (typeof this._ref?.isFocused === 'function') {
this.forceUpdate();
}
onBlur?.(ev);
}}
{...otherProps}
/>
);

const renderContent = (e: StateCallbackType) => (
<>
{!!BeforeComponent && <BeforeComponent style={before}>{beforeContent}</BeforeComponent>}
<AnimatedComponent
ref={(r: NativeComponents) => {
typeof forwardRef === 'function' && forwardRef(r);
this._ref = r;
}}
{...this.getPseudoProps()}
{...this.getStyleProps(e)}
pointerEvents={StyleSheet.flatten(style)?.pointerEvents}
onFocus={(ev: FocusEvent) => {
if (typeof this._ref?.isFocused === 'function') {
this.forceUpdate();
}
onFocus?.(ev);
}}
onBlur={(ev: FocusEvent) => {
if (typeof this._ref?.isFocused === 'function') {
this.forceUpdate();
}
onBlur?.(ev);
}}
{...otherProps}
/>

{!mockShadow && renderComponent(e)}
{mockShadow && <BoxShadow style={this.getStyleProps(e).style}>{renderComponent(e)}</BoxShadow>}

{!!AfterComponent && <AfterComponent style={after}>{afterContent}</AfterComponent>}
</>
);

const needsPressable = styleKeys.some(
(k) => k.includes(':hover') || k.includes(':active') || k.includes(':focus'),
);

return needsPressable ? (
<RN.Pressable>{(e: StateCallbackType) => content(e)}</RN.Pressable>
<RN.Pressable>{(e: StateCallbackType) => renderContent(e)}</RN.Pressable>
) : (
content({ hovered: false, pressed: false, focused: false })
renderContent({ hovered: false, pressed: false, focused: false })
);
}
};
Expand Down
Loading

0 comments on commit e8b19df

Please sign in to comment.