UNPKG

4.93 kBJavaScriptView Raw
1import React, { useState } from 'react'
2import {
3 View,
4 TouchableWithoutFeedback,
5 Platform,
6 StyleSheet
7} from 'react-native'
8import propTypes from 'prop-types'
9import { observer, useDidUpdate } from 'startupjs'
10import config from '../../config/rootConfig'
11import colorToRGBA from '../../config/colorToRGBA'
12import './index.styl'
13
14const SHADOWS = config.shadows
15const isWeb = Platform.OS === 'web'
16const { defaultHoverOpacity, defaultActiveOpacity } = config.Div
17
18function Div ({
19 style = [],
20 children,
21 variant,
22 hoverStyle,
23 activeStyle,
24 disabled,
25 level,
26 feedback,
27 shape,
28 pushed, // By some reason prop 'push' was ignored
29 bleed,
30 onPress,
31 onClick,
32 ...props
33}) {
34 const handlePress = onClick || onPress
35 const isClickable = typeof handlePress === 'function' && !disabled
36 const [hover, setHover] = useState()
37 const [active, setActive] = useState()
38 let extraStyle = {}
39 const extraProps = {}
40 const wrapperProps = {}
41
42 // If component become not clickable, for example received 'disabled'
43 // prop while hover or active, state wouldn't update without this effect
44 useDidUpdate(() => {
45 if (isClickable) return
46 if (hover) setHover()
47 if (active) setActive()
48 }, [isClickable])
49
50 if (isClickable) {
51 wrapperProps.onPress = handlePress
52
53 // setup hover and active states styles and props
54 if (feedback) {
55 const { onPressIn, onPressOut } = props
56 wrapperProps.onPressIn = (...args) => {
57 setActive(true)
58 onPressIn && onPressIn(...args)
59 }
60 wrapperProps.onPressOut = (...args) => {
61 setActive()
62 onPressOut && onPressOut(...args)
63 }
64
65 if (isWeb) {
66 const { onMouseEnter, onMouseLeave } = props
67 props.onMouseEnter = (...args) => {
68 setHover(true)
69 onMouseEnter && onMouseEnter(...args)
70 }
71 props.onMouseLeave = (...args) => {
72 setHover()
73 onMouseLeave && onMouseLeave(...args)
74 }
75 }
76
77 if (active) {
78 extraStyle = activeStyle || getDefaultStyle(style, 'active', variant)
79 } else if (hover) {
80 extraStyle = hoverStyle || getDefaultStyle(style, 'hover', variant)
81 }
82 }
83 }
84
85 let pushedModifier
86 const pushedSize = typeof pushed === 'boolean' && pushed ? 'm' : pushed
87 if (pushedSize) pushedModifier = `pushed-${pushedSize}`
88
89 function maybeWrapToClickable (children) {
90 if (isClickable) {
91 return pug`
92 TouchableWithoutFeedback(
93 ...wrapperProps
94 )
95 = children
96 `
97 } else {
98 return children
99 }
100 }
101
102 // backgroundColor in style can override extraStyle backgroundColor
103 // so passing the extraStyle to the end is important in this case
104 return maybeWrapToClickable(pug`
105 View.root(
106 style=[SHADOWS[level], style, extraStyle]
107 styleName=[
108 {
109 ['with-shadow']: !!level,
110 clickable: isWeb && isClickable,
111 bleed,
112 disabled
113 },
114 shape,
115 pushedModifier
116 ]
117 ...extraProps
118 ...props
119 )
120 = children
121 `)
122}
123
124Div.defaultProps = {
125 variant: 'opacity',
126 level: 0,
127 feedback: true,
128 disabled: false,
129 bleed: false,
130 pushed: false
131}
132
133Div.propTypes = {
134 style: propTypes.oneOfType([propTypes.object, propTypes.array]),
135 children: propTypes.node,
136 variant: propTypes.oneOf(['opacity', 'highlight']),
137 feedback: propTypes.bool,
138 hoverStyle: propTypes.oneOfType([propTypes.object, propTypes.array]),
139 activeStyle: propTypes.oneOfType([propTypes.object, propTypes.array]),
140 disabled: propTypes.bool,
141 level: propTypes.oneOf(SHADOWS.map((key, index) => index)),
142 shape: propTypes.oneOf(['squared', 'rounded', 'circle']),
143 pushed: propTypes.oneOfType([propTypes.bool, propTypes.oneOf(['xs', 's', 'm', 'l', 'xl', 'xxl'])]),
144 bleed: propTypes.bool,
145 onPress: propTypes.func,
146 onClick: propTypes.func
147}
148
149export default observer(Div)
150
151function getDefaultStyle (style, type, variant) {
152 if (variant === 'opacity') {
153 if (type === 'hover') return { opacity: defaultHoverOpacity }
154 if (type === 'active') return { opacity: defaultActiveOpacity }
155 } else {
156 style = StyleSheet.flatten(style)
157 let backgroundColor = style.backgroundColor
158 if (backgroundColor === 'transparent') backgroundColor = undefined
159
160 if (type === 'hover') {
161 if (backgroundColor) {
162 return { backgroundColor: colorToRGBA(backgroundColor, defaultHoverOpacity) }
163 } else {
164 // If no color exists, we treat it as a light background and just dim it a bit
165 return { backgroundColor: 'rgba(0,0,0,0.05)' }
166 }
167 }
168
169 if (type === 'active') {
170 if (backgroundColor) {
171 return { backgroundColor: colorToRGBA(backgroundColor, defaultActiveOpacity) }
172 } else {
173 // If no color exists, we treat it as a light background and just dim it a bit
174 return { backgroundColor: 'rgba(0,0,0,0.2)' }
175 }
176 }
177 }
178}