@@ -8,15 +8,11 @@ import {
88 TextStyle ,
99 ViewStyle ,
1010 ImageStyle ,
11- ImageSourcePropType ,
1211 ColorValue ,
12+ FlatList ,
13+ ImageSourcePropType ,
1314} from 'react-native' ;
1415
15- type HasReachedMaxMin = {
16- hasReachedMax : boolean ;
17- hasReachedMin : boolean ;
18- } ;
19-
2016export type SimpleStepperProps = {
2117 initialValue ?: number ;
2218 minimumValue ?: number ;
@@ -64,8 +60,15 @@ export type SimpleStepperProps = {
6460 decrementImageTestID ?: string ;
6561 incrementButtonTestID ?: string ;
6662 decrementButtonTestID ?: string ;
63+ textTestID ?: string ;
64+ horizontal ?: boolean ;
6765} ;
6866
67+ interface SimpleStepperListItem {
68+ item : React . JSX . Element ;
69+ index : number ;
70+ }
71+
6972const SimpleStepper : React . FunctionComponent < SimpleStepperProps > = ( {
7073 initialValue = 0 ,
7174 minimumValue = 0 ,
@@ -89,26 +92,26 @@ const SimpleStepper: React.FunctionComponent<SimpleStepperProps> = ({
8992 showText = false ,
9093 renderText = undefined ,
9194 textStyle = {
92- marginHorizontal : 8 ,
95+ textAlign : 'center' ,
96+ padding : 8 ,
9397 fontSize : 24 ,
9498 } ,
9599 containerStyle = {
96- flexDirection : 'row ' ,
100+ alignSelf : 'flex-start ' ,
97101 borderWidth : 1 ,
98102 borderRadius : 8 ,
99- alignItems : 'center' ,
100- justifyContent : 'space-evenly' ,
101103 } ,
102104 separatorStyle = {
103- width : StyleSheet . hairlineWidth ,
104105 backgroundColor : 'black' ,
105- height : '100%' ,
106+ width : StyleSheet . hairlineWidth ,
106107 } ,
107108 incrementStepStyle = {
108- padding : 4 ,
109+ alignItems : 'center' ,
110+ padding : 8 ,
109111 } ,
110112 decrementStepStyle = {
111- padding : 4 ,
113+ alignItems : 'center' ,
114+ padding : 8 ,
112115 } ,
113116 incrementImageStyle = {
114117 height : 30 ,
@@ -122,16 +125,18 @@ const SimpleStepper: React.FunctionComponent<SimpleStepperProps> = ({
122125 disableIncrementImageTintColor = false ,
123126 disableDecrementImageTintColor = false ,
124127 useColor = false ,
125- color = 'blue' ,
128+ color = undefined ,
126129 textDecimalPlaces = 2 ,
127130 containerTestID = 'container' ,
128131 separatorTestID = 'separator' ,
129132 incrementImageTestID = 'incrementImage' ,
130133 decrementImageTestID = 'decrementImage' ,
131134 incrementButtonTestID = 'incrementButton' ,
132135 decrementButtonTestID = 'decrementButton' ,
136+ textTestID = 'text' ,
137+ horizontal = true ,
133138} ) => {
134- const [ value , setValue ] = React . useState ( initialValue ) ;
139+ const [ value , setValue ] = React . useState < number > ( initialValue ) ;
135140
136141 function _decrementAction ( ) : void {
137142 const nextValue = value - stepValue ;
@@ -162,59 +167,62 @@ const SimpleStepper: React.FunctionComponent<SimpleStepperProps> = ({
162167 return actualValue ;
163168 }
164169
165- function _getHasMinMax ( ) : HasReachedMaxMin {
166- let hasReachedMax = true ;
167- let hasReachedMin = true ;
168- switch ( true ) {
169- case wraps :
170- hasReachedMin = false ;
171- hasReachedMax = false ;
172- break ;
173- case stepValue >= 0 :
174- hasReachedMax = value >= maximumValue ;
175- hasReachedMin = value <= minimumValue ;
176- break ;
177- case stepValue < 0 :
178- hasReachedMax = value <= minimumValue ;
179- hasReachedMin = value >= maximumValue ;
180- break ;
170+ function hasReachedMax ( ) : boolean {
171+ if ( stepValue < 0 ) {
172+ return value <= minimumValue ;
173+ }
174+ return value >= maximumValue ;
175+ }
176+
177+ function hasReachedMin ( ) : boolean {
178+ if ( stepValue < 0 ) {
179+ return value >= maximumValue ;
181180 }
182- return {
183- hasReachedMax,
184- hasReachedMin,
185- } ;
181+ return value <= minimumValue ;
182+ }
183+
184+ if ( hasReachedMin ( ) ) {
185+ onMin ( value ) ;
186+ }
187+ if ( hasReachedMax ( ) ) {
188+ onMax ( value ) ;
186189 }
187190
188191 function _renderText ( ) : React . JSX . Element {
189192 if ( renderText ) {
190193 return renderText ( value ) ;
191194 }
192- const _textStyle = textStyle ;
193- if ( useColor && color ) {
194- _textStyle . color = color ;
195- }
196195 let displayValue = value ;
197196 if ( ! Number . isInteger ( value ) ) {
198197 displayValue = Number ( value . toFixed ( textDecimalPlaces ) ) ;
199198 }
200- return < Text style = { textStyle } > { displayValue } </ Text > ;
199+ return (
200+ < Text
201+ testID = { textTestID }
202+ style = { [ textStyle , { color : useColor ? color : textStyle . color } ] }
203+ disabled = { disabled }
204+ >
205+ { displayValue }
206+ </ Text >
207+ ) ;
201208 }
202209
203210 function _renderIncrementImage ( opacity : number ) : React . JSX . Element {
204211 if ( renderIncrementImage ) {
205212 return renderIncrementImage ( opacity ) ;
206213 }
207- const _incrementImageStyle = incrementImageStyle ;
208- if ( useColor && color && ! disableIncrementImageTintColor ) {
209- _incrementImageStyle . tintColor = color ;
210- }
211- if ( disableIncrementImageTintColor && _incrementImageStyle . tintColor ) {
214+ const _incrementImageStyle = Object . assign ( { } , incrementImageStyle ) ;
215+ _incrementImageStyle . opacity = opacity ;
216+
217+ if ( disableIncrementImageTintColor ) {
212218 _incrementImageStyle . tintColor = undefined ;
219+ } else if ( useColor ) {
220+ _incrementImageStyle . tintColor = color ;
213221 }
214222 return (
215223 < Image
216224 testID = { incrementImageTestID }
217- style = { [ _incrementImageStyle , { opacity } ] }
225+ style = { _incrementImageStyle }
218226 source = { incrementImage }
219227 />
220228 ) ;
@@ -224,81 +232,107 @@ const SimpleStepper: React.FunctionComponent<SimpleStepperProps> = ({
224232 if ( renderDecrementImage ) {
225233 return renderDecrementImage ( opacity ) ;
226234 }
227- const _decrementImageStyle = decrementImageStyle ;
228- if ( useColor && color && ! disableDecrementImageTintColor ) {
229- _decrementImageStyle . tintColor = color ;
230- }
231- if ( disableDecrementImageTintColor && _decrementImageStyle . tintColor ) {
235+ const _decrementImageStyle = Object . assign ( { } , decrementImageStyle ) ;
236+ _decrementImageStyle . opacity = opacity ;
237+
238+ if ( disableDecrementImageTintColor ) {
232239 _decrementImageStyle . tintColor = undefined ;
240+ } else if ( useColor ) {
241+ _decrementImageStyle . tintColor = color ;
233242 }
234243 return (
235244 < Image
236245 testID = { decrementImageTestID }
237- style = { [ _decrementImageStyle , { opacity } ] }
246+ style = { _decrementImageStyle }
238247 source = { decrementImage }
239248 />
240249 ) ;
241250 }
242251
243- const { hasReachedMin, hasReachedMax } = _getHasMinMax ( ) ;
244- const decrementOpacity = hasReachedMin || disabled ? disabledOpacity : 1 ;
245- const incrementOpacity = hasReachedMax || disabled ? disabledOpacity : 1 ;
246- const isLeft = showText && textPosition === 'left' ;
247- const isCenter = showText && textPosition === 'center' ;
248- const isRight = showText && textPosition === 'right' ;
252+ function _renderIncrement ( ) : React . JSX . Element {
253+ if ( renderIncrementStep ) {
254+ return renderIncrementStep ( value , _incrementAction ) ;
255+ }
256+ const isDisabled = disabled || ( ! wraps && hasReachedMax ( ) ) ;
257+ const opacity = isDisabled ? disabledOpacity : 1 ;
258+ return (
259+ < TouchableOpacity
260+ testID = { incrementButtonTestID }
261+ style = { incrementStepStyle }
262+ activeOpacity = { activeOpacity }
263+ onPress = { _incrementAction }
264+ disabled = { isDisabled }
265+ >
266+ { _renderIncrementImage ( opacity ) }
267+ </ TouchableOpacity >
268+ ) ;
269+ }
249270
250- if ( hasReachedMin ) {
251- onMin ( value ) ;
271+ function _renderDecrement ( ) : React . JSX . Element {
272+ if ( renderDecrementStep ) {
273+ return renderDecrementStep ( value , _decrementAction ) ;
274+ }
275+ const isDisabled = disabled || ( ! wraps && hasReachedMin ( ) ) ;
276+ const opacity = isDisabled ? disabledOpacity : 1 ;
277+ return (
278+ < TouchableOpacity
279+ testID = { decrementButtonTestID }
280+ style = { decrementStepStyle }
281+ activeOpacity = { activeOpacity }
282+ onPress = { _decrementAction }
283+ disabled = { isDisabled }
284+ >
285+ { _renderDecrementImage ( opacity ) }
286+ </ TouchableOpacity >
287+ ) ;
252288 }
253- if ( hasReachedMax ) {
254- onMax ( value ) ;
289+
290+ function _renderItem ( obj : SimpleStepperListItem ) : React . JSX . Element {
291+ return obj . item ;
255292 }
256293
257- const _containerStyle = containerStyle ;
258- const _separatorStyle = separatorStyle ;
259- if ( useColor && color ) {
260- _containerStyle . borderColor = color ;
261- _separatorStyle . backgroundColor = color ;
294+ function _renderSeparator ( ) : React . JSX . Element {
295+ const _separatorStyle = Object . assign ( { } , separatorStyle ) ;
296+ if ( useColor && color ) {
297+ _separatorStyle . backgroundColor = color ;
298+ }
299+ if ( ! horizontal ) {
300+ _separatorStyle . height = StyleSheet . hairlineWidth ;
301+ _separatorStyle . width = undefined ;
302+ }
303+ return < View testID = { separatorTestID } style = { _separatorStyle } /> ;
304+ }
305+
306+ function _getData ( ) : React . JSX . Element [ ] {
307+ if ( ! showText ) {
308+ return [ _renderDecrement ( ) , _renderIncrement ( ) ] ;
309+ }
310+ switch ( textPosition ) {
311+ case 'left' :
312+ return [ _renderText ( ) , _renderDecrement ( ) , _renderIncrement ( ) ] ;
313+ case 'center' :
314+ return [ _renderDecrement ( ) , _renderText ( ) , _renderIncrement ( ) ] ;
315+ case 'right' :
316+ return [ _renderDecrement ( ) , _renderIncrement ( ) , _renderText ( ) ] ;
317+ }
262318 }
263319
320+ const data = _getData ( ) ;
264321 return (
265- < View >
266- < View testID = { containerTestID } style = { _containerStyle } >
267- { isLeft && _renderText ( ) }
268- { isLeft && < View testID = { separatorTestID } style = { _separatorStyle } /> }
269- { renderDecrementStep ? (
270- renderDecrementStep ( value , _decrementAction )
271- ) : (
272- < TouchableOpacity
273- testID = { decrementButtonTestID }
274- style = { decrementStepStyle }
275- activeOpacity = { activeOpacity }
276- onPress = { _decrementAction }
277- disabled = { hasReachedMin || disabled }
278- >
279- { _renderDecrementImage ( decrementOpacity ) }
280- </ TouchableOpacity >
281- ) }
282- { isCenter && < View testID = { separatorTestID } style = { _separatorStyle } /> }
283- { isCenter && _renderText ( ) }
284- < View style = { _separatorStyle } />
285- { renderIncrementStep ? (
286- renderIncrementStep ( value , _incrementAction )
287- ) : (
288- < TouchableOpacity
289- testID = { incrementButtonTestID }
290- style = { incrementStepStyle }
291- activeOpacity = { activeOpacity }
292- onPress = { _incrementAction }
293- disabled = { hasReachedMax || disabled }
294- >
295- { _renderIncrementImage ( incrementOpacity ) }
296- </ TouchableOpacity >
297- ) }
298- { isRight && < View testID = { separatorTestID } style = { _separatorStyle } /> }
299- { isRight && _renderText ( ) }
300- </ View >
301- </ View >
322+ < FlatList
323+ style = { [
324+ containerStyle ,
325+ { borderColor : useColor ? color : containerStyle . borderColor } ,
326+ ] }
327+ testID = { containerTestID }
328+ data = { data }
329+ keyExtractor = { ( _item , index ) => `${ index } ` }
330+ initialNumToRender = { data . length }
331+ renderItem = { _renderItem }
332+ ItemSeparatorComponent = { _renderSeparator }
333+ horizontal = { horizontal }
334+ scrollEnabled = { false }
335+ />
302336 ) ;
303337} ;
304338
0 commit comments