@shoutem/ui 4.6.5 → 4.6.7

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.
@@ -13,26 +13,42 @@ import { View } from './View';
13
13
 
14
14
  const isIos = Platform.OS === 'ios';
15
15
 
16
+ export const DATEPICKER_MODES = {
17
+ DATE: 'date',
18
+ DATETIME: 'datetime',
19
+ TIME: 'time',
20
+ };
21
+
22
+ const DISPLAY_MODES = {
23
+ [DATEPICKER_MODES.DATE]: 'inline',
24
+ [DATEPICKER_MODES.DATETIME]: 'inline',
25
+ [DATEPICKER_MODES.TIME]: 'spinner',
26
+ };
27
+
28
+ function getDisplayMode(mode) {
29
+ if (!isIos || !Object.keys(DISPLAY_MODES).includes(mode)) {
30
+ return 'default';
31
+ }
32
+
33
+ return DISPLAY_MODES[mode];
34
+ }
35
+
16
36
  class DateTimePicker extends PureComponent {
17
37
  constructor(props) {
18
38
  super(props);
19
39
 
20
40
  autoBindReact(this);
21
41
 
42
+ const { mode, value } = props;
43
+
22
44
  this.state = {
45
+ display: getDisplayMode(mode),
23
46
  showPicker: false,
24
- value: props.value,
47
+ showTimePicker: false,
48
+ value,
25
49
  };
26
50
  }
27
51
 
28
- componentDidUpdate() {
29
- if (!isIos) {
30
- const { value } = this.props;
31
-
32
- this.setState({ value });
33
- }
34
- }
35
-
36
52
  handleValueChanged(event, value) {
37
53
  if (isIos) {
38
54
  return this.setState({ value });
@@ -42,10 +58,33 @@ class DateTimePicker extends PureComponent {
42
58
  return this.handleHidePicker();
43
59
  }
44
60
 
61
+ const { mode, onValueChanged } = this.props;
62
+
63
+ const showTimePicker = mode === DATEPICKER_MODES.DATETIME;
64
+
65
+ this.setState({ value, showPicker: false, showTimePicker });
66
+ return !showTimePicker && onValueChanged(value);
67
+ }
68
+
69
+ handleTimeValueChanged(event, selectedValue) {
70
+ if (event.type === 'dismissed') {
71
+ return this.handleHidePicker();
72
+ }
73
+
45
74
  const { onValueChanged } = this.props;
75
+ const { value } = this.state;
46
76
 
47
- this.setState({ showPicker: false });
48
- return onValueChanged(value);
77
+ // Time picker value will override preselected date
78
+ // Use current date from state and only set hours & minutes
79
+ const currentDate = new Date(value);
80
+ const selectedDate = new Date(selectedValue);
81
+ const hour = selectedDate.getHours();
82
+ const minutes = selectedDate.getMinutes();
83
+
84
+ currentDate.setHours(hour, minutes, 0);
85
+
86
+ this.setState({ value: currentDate, showTimePicker: false });
87
+ return onValueChanged(currentDate);
49
88
  }
50
89
 
51
90
  handleShowPicker() {
@@ -66,6 +105,7 @@ class DateTimePicker extends PureComponent {
66
105
 
67
106
  render() {
68
107
  const {
108
+ cancelButtonText,
69
109
  confirmButtonText,
70
110
  is24Hour,
71
111
  mode,
@@ -73,7 +113,7 @@ class DateTimePicker extends PureComponent {
73
113
  style,
74
114
  ...otherProps
75
115
  } = this.props;
76
- const { showPicker, value } = this.state;
116
+ const { display, showPicker, showTimePicker, value } = this.state;
77
117
 
78
118
  return (
79
119
  <View styleName="horizontal">
@@ -82,7 +122,7 @@ class DateTimePicker extends PureComponent {
82
122
  style={style.textContainer}
83
123
  onPress={this.handleShowPicker}
84
124
  >
85
- <Text> {textValue}</Text>
125
+ <Text>{textValue}</Text>
86
126
  </TouchableOpacity>
87
127
  <TouchableOpacity
88
128
  onPress={this.handleShowPicker}
@@ -91,7 +131,7 @@ class DateTimePicker extends PureComponent {
91
131
  >
92
132
  <Icon name="drop-down" style={style.icon} />
93
133
  </TouchableOpacity>
94
- {isIos && (
134
+ {isIos && showPicker && (
95
135
  <Modal
96
136
  backdropOpacity={0.5}
97
137
  isVisible={showPicker}
@@ -103,7 +143,7 @@ class DateTimePicker extends PureComponent {
103
143
  >
104
144
  <View styleName="md-gutter" style={style.modalContainer}>
105
145
  <RNCDateTimePicker
106
- display="spinner"
146
+ display={display}
107
147
  is24Hour={is24Hour}
108
148
  mode={mode}
109
149
  onChange={this.handleValueChanged}
@@ -114,8 +154,14 @@ class DateTimePicker extends PureComponent {
114
154
  />
115
155
  <View
116
156
  style={style.modalButtonContainer}
117
- styleName="md-gutter-top"
157
+ styleName="horizontal md-gutter-top"
118
158
  >
159
+ <Button
160
+ style={style.modalButton}
161
+ onPress={this.handleHidePicker}
162
+ >
163
+ <Text>{cancelButtonText}</Text>
164
+ </Button>
119
165
  <Button
120
166
  style={style.modalButton}
121
167
  onPress={this.handleConfirmPress}
@@ -128,7 +174,7 @@ class DateTimePicker extends PureComponent {
128
174
  )}
129
175
  {!isIos && showPicker && (
130
176
  <RNCDateTimePicker
131
- display="default"
177
+ display={display}
132
178
  is24Hour={is24Hour}
133
179
  mode={mode}
134
180
  onChange={this.handleValueChanged}
@@ -136,21 +182,33 @@ class DateTimePicker extends PureComponent {
136
182
  {...otherProps}
137
183
  />
138
184
  )}
185
+ {!isIos && !showPicker && showTimePicker && (
186
+ <RNCDateTimePicker
187
+ display={display}
188
+ is24Hour={is24Hour}
189
+ mode="time"
190
+ onChange={this.handleTimeValueChanged}
191
+ value={value}
192
+ {...otherProps}
193
+ />
194
+ )}
139
195
  </View>
140
196
  );
141
197
  }
142
198
  }
143
199
 
144
200
  DateTimePicker.propTypes = {
201
+ cancelButtonText: PropTypes.string,
145
202
  confirmButtonText: PropTypes.string,
146
203
  is24Hour: PropTypes.bool,
147
- mode: PropTypes.string,
204
+ mode: PropTypes.oneOf(Object.values(DATEPICKER_MODES)),
148
205
  onValueChanged: PropTypes.func,
149
206
  textValue: PropTypes.string,
150
207
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
151
208
  };
152
209
 
153
210
  DateTimePicker.defaultProps = {
211
+ cancelButtonText: 'Cancel',
154
212
  confirmButtonText: 'Confirm',
155
213
  is24Hour: false,
156
214
  mode: 'datetime',
@@ -79,14 +79,20 @@ class YearRangePicker extends PureComponent {
79
79
  }
80
80
 
81
81
  render() {
82
- const { rangeEnd, rangeStart, resetButtonTitle, confirmButtonTitle } = this.props;
82
+ const {
83
+ rangeEnd,
84
+ rangeStart,
85
+ resetButtonTitle,
86
+ confirmButtonTitle,
87
+ } = this.props;
83
88
  const { buttonTooltip, collapsed } = this.state;
84
89
 
85
90
  return (
86
- <View onLayout={this.handleLayout}>
91
+ <View>
87
92
  <YearPickerButton
88
93
  tooltip={buttonTooltip}
89
94
  onPress={this.handleButtonPressed}
95
+ collapsed={collapsed}
90
96
  />
91
97
  <YearPickerModal
92
98
  visible={collapsed}
@@ -1,7 +1,6 @@
1
1
  import React, { PureComponent } from 'react';
2
2
  import { Animated } from 'react-native';
3
3
  import PropTypes from 'prop-types';
4
- import _ from 'lodash';
5
4
  import autoBindReact from 'auto-bind/react';
6
5
  import { connectStyle } from '@shoutem/theme';
7
6
  import { TouchableOpacity } from '../TouchableOpacity';
@@ -14,6 +13,7 @@ class YearRangePickerButton extends PureComponent {
14
13
  static propTypes = {
15
14
  onPress: PropTypes.func,
16
15
  tooltip: PropTypes.string,
16
+ collapsed: PropTypes.bool,
17
17
  style: PropTypes.any,
18
18
  };
19
19
 
@@ -23,55 +23,41 @@ class YearRangePickerButton extends PureComponent {
23
23
  autoBindReact(this);
24
24
 
25
25
  this.dropDownIconValue = new Animated.Value(0);
26
-
27
- this.state = {
28
- collapsed: false,
29
- };
30
26
  }
31
27
 
32
- handlePress() {
33
- const { collapsed } = this.state;
34
- const { onPress } = this.props;
35
-
36
- const toValue = collapsed ? 0 : 1;
28
+ componentDidUpdate(prevProps) {
29
+ const { collapsed } = this.props;
30
+ const { collapsed: prevCollapsed } = prevProps;
37
31
 
38
- this.setState(
39
- { collapsed: !collapsed },
40
- () => Animated.timing(
41
- this.dropDownIconValue,
42
- {
43
- toValue,
44
- useNativeDriver: true,
45
- duration: 300,
46
- }
47
- ).start());
32
+ if (collapsed !== prevCollapsed) {
33
+ const toValue = collapsed ? 1 : 0;
48
34
 
49
- if (onPress) {
50
- onPress();
35
+ Animated.timing(this.dropDownIconValue, {
36
+ toValue,
37
+ useNativeDriver: true,
38
+ duration: 300,
39
+ }).start();
51
40
  }
52
41
  }
53
42
 
54
43
  render() {
55
- const { style, tooltip } = this.props;
44
+ const { style, tooltip, onPress } = this.props;
56
45
 
57
46
  return (
58
- <TouchableOpacity
59
- onPress={this.handlePress}
60
- style={style.container}
61
- >
62
- <Text>
63
- {tooltip}
64
- </Text>
47
+ <TouchableOpacity onPress={onPress} style={style.container}>
48
+ <Text>{tooltip}</Text>
65
49
  <AnimatedIcon
66
50
  name="drop-down"
67
51
  style={{
68
52
  ...style.icon,
69
- transform: [{
70
- rotate: this.dropDownIconValue.interpolate({
71
- inputRange: [0, 1],
72
- outputRange: ['0deg', '180deg'],
73
- }),
74
- }],
53
+ transform: [
54
+ {
55
+ rotate: this.dropDownIconValue.interpolate({
56
+ inputRange: [0, 1],
57
+ outputRange: ['0deg', '180deg'],
58
+ }),
59
+ },
60
+ ],
75
61
  }}
76
62
  />
77
63
  </TouchableOpacity>
@@ -79,4 +65,6 @@ class YearRangePickerButton extends PureComponent {
79
65
  }
80
66
  }
81
67
 
82
- export default connectStyle('shoutem.ui.YearRangePickerButton')(YearRangePickerButton);
68
+ export default connectStyle('shoutem.ui.YearRangePickerButton')(
69
+ YearRangePickerButton,
70
+ );
@@ -20,10 +20,7 @@ function resolveVisibleYears(props) {
20
20
  const yearsPerPage = NUMBER_OF_ROWS * YEARS_IN_ROW;
21
21
 
22
22
  if (_.isEmpty(selectedYears)) {
23
- const fullRange = _.times(
24
- yearsPerPage,
25
- index => rangeStart + index,
26
- );
23
+ const fullRange = _.times(yearsPerPage, index => rangeStart + index);
27
24
 
28
25
  return _.filter(fullRange, year => year <= rangeEnd);
29
26
  }
@@ -31,11 +28,8 @@ function resolveVisibleYears(props) {
31
28
  const firstSelectedYear = _.head(selectedYears);
32
29
  const prevPagesNumber = Math.trunc(firstSelectedYear / yearsPerPage);
33
30
 
34
- const fullRangeStart = (prevPagesNumber * yearsPerPage) + 1;
35
- const fullRange = _.times(
36
- yearsPerPage,
37
- index => fullRangeStart + index,
38
- );
31
+ const fullRangeStart = prevPagesNumber * yearsPerPage + 1;
32
+ const fullRange = _.times(yearsPerPage, index => fullRangeStart + index);
39
33
 
40
34
  return _.filter(fullRange, year => year <= rangeEnd);
41
35
  }
@@ -83,7 +77,6 @@ class YearRangePickerModal extends PureComponent {
83
77
  const { onRangeConfirmed } = this.props;
84
78
 
85
79
  if (onRangeConfirmed) {
86
- LayoutAnimation.easeInEaseOut();
87
80
  onRangeConfirmed(selectedYears);
88
81
  }
89
82
  }
@@ -91,7 +84,6 @@ class YearRangePickerModal extends PureComponent {
91
84
  handleResetPress() {
92
85
  const { onRangeConfirmed } = this.props;
93
86
 
94
- LayoutAnimation.easeInEaseOut();
95
87
  this.setState({ selectedYears: [] });
96
88
 
97
89
  if (onRangeConfirmed) {
@@ -104,9 +96,15 @@ class YearRangePickerModal extends PureComponent {
104
96
  const { visibleYears } = this.state;
105
97
 
106
98
  const nextYearStart = _.last(visibleYears) + 1;
107
- const nextYearEnd = Math.min(rangeEnd, nextYearStart + this.YEARS_PER_PAGE - 1);
99
+ const nextYearEnd = Math.min(
100
+ rangeEnd,
101
+ nextYearStart + this.YEARS_PER_PAGE - 1,
102
+ );
108
103
 
109
- const nextVisibleYears = _.times(nextYearEnd + 1 - nextYearStart, index => nextYearStart + index);
104
+ const nextVisibleYears = _.times(
105
+ nextYearEnd + 1 - nextYearStart,
106
+ index => nextYearStart + index,
107
+ );
110
108
 
111
109
  LayoutAnimation.easeInEaseOut();
112
110
  this.setState({ visibleYears: nextVisibleYears });
@@ -118,7 +116,12 @@ class YearRangePickerModal extends PureComponent {
118
116
  const prevYearStart = _.head(visibleYears) - this.YEARS_PER_PAGE;
119
117
 
120
118
  LayoutAnimation.easeInEaseOut();
121
- this.setState({ visibleYears: _.times(this.YEARS_PER_PAGE, index => prevYearStart + index) });
119
+ this.setState({
120
+ visibleYears: _.times(
121
+ this.YEARS_PER_PAGE,
122
+ index => prevYearStart + index,
123
+ ),
124
+ });
122
125
  }
123
126
 
124
127
  handleYearPress(year) {
@@ -137,7 +140,9 @@ class YearRangePickerModal extends PureComponent {
137
140
  }
138
141
 
139
142
  const cutFromEnd = size / 2 <= index;
140
- const newYears = _.filter(selectedYears, value => cutFromEnd ? value <= year : value >= year);
143
+ const newYears = _.filter(selectedYears, value =>
144
+ cutFromEnd ? value <= year : value >= year,
145
+ );
141
146
 
142
147
  this.setState({ selectedYears: newYears });
143
148
  return;
@@ -149,15 +154,23 @@ class YearRangePickerModal extends PureComponent {
149
154
  }
150
155
 
151
156
  const addToEnd = _.last(selectedYears) < year;
152
- const yearsToAdd = addToEnd ? year - _.last(selectedYears) : _.head(selectedYears) - year;
157
+ const yearsToAdd = addToEnd
158
+ ? year - _.last(selectedYears)
159
+ : _.head(selectedYears) - year;
153
160
 
154
161
  const newYears = addToEnd
155
- ? [...selectedYears, ..._.times(yearsToAdd, index => _.last(selectedYears) + index + 1)]
156
- : [...selectedYears, ..._.times(yearsToAdd, index => _.head(selectedYears) - index + -1)]
162
+ ? [
163
+ ...selectedYears,
164
+ ..._.times(yearsToAdd, index => _.last(selectedYears) + index + 1),
165
+ ]
166
+ : [
167
+ ...selectedYears,
168
+ ..._.times(yearsToAdd, index => _.head(selectedYears) - index + -1),
169
+ ];
157
170
  const sortedYears = _.sortBy(newYears, item => item);
158
171
 
159
172
  this.setState({ selectedYears: sortedYears });
160
- }
173
+ };
161
174
  }
162
175
 
163
176
  renderYear(year) {
@@ -174,12 +187,14 @@ class YearRangePickerModal extends PureComponent {
174
187
  style={style.yearContainer}
175
188
  key={year.toString()}
176
189
  >
177
- <View style={[
178
- style.year,
179
- isSelected && style.yearSelected,
180
- isFirst && style.yearFirst,
181
- isLast && style.yearLast,
182
- ]}>
190
+ <View
191
+ style={[
192
+ style.year,
193
+ isSelected && style.yearSelected,
194
+ isFirst && style.yearFirst,
195
+ isLast && style.yearLast,
196
+ ]}
197
+ >
183
198
  <Text>{year.toString()}</Text>
184
199
  </View>
185
200
  </TouchableOpacity>
@@ -191,7 +206,7 @@ class YearRangePickerModal extends PureComponent {
191
206
  <View styleName="horizontal flexible" key={index.toString()}>
192
207
  {_.map(row, this.renderYear)}
193
208
  </View>
194
- )
209
+ );
195
210
  }
196
211
 
197
212
  render() {
@@ -221,6 +236,7 @@ class YearRangePickerModal extends PureComponent {
221
236
  onBackdropPress={onDismiss}
222
237
  useNativeDriver
223
238
  backdropOpacity={Platform.OS === 'ios' ? 0.7 : 0}
239
+ animationIn="slideInUp"
224
240
  >
225
241
  <View style={style.container}>
226
242
  <View style={style.tooltipContainer}>
@@ -229,7 +245,10 @@ class YearRangePickerModal extends PureComponent {
229
245
  onPress={this.handleYearsBackPress}
230
246
  disabled={prevDisabled}
231
247
  >
232
- <Icon name="left-arrow" style={[style.icon, prevDisabled && style.iconDisabled]} />
248
+ <Icon
249
+ name="left-arrow"
250
+ style={[style.icon, prevDisabled && style.iconDisabled]}
251
+ />
233
252
  </Button>
234
253
  <Text>{buttonTooltip}</Text>
235
254
  <Button
@@ -237,22 +256,33 @@ class YearRangePickerModal extends PureComponent {
237
256
  onPress={this.handleYearsForwardPress}
238
257
  disabled={nextDisabled}
239
258
  >
240
- <Icon name="right-arrow" style={[style.icon, nextDisabled && style.iconDisabled]} />
259
+ <Icon
260
+ name="right-arrow"
261
+ style={[style.icon, nextDisabled && style.iconDisabled]}
262
+ />
241
263
  </Button>
242
264
  </View>
243
- {_.times(NUMBER_OF_ROWS, (row) => this.renderYearRow(data[row], row))}
265
+ {_.times(NUMBER_OF_ROWS, row => this.renderYearRow(data[row], row))}
244
266
  <View style={style.buttonContainer}>
245
- <Button styleName="secondary flexible" onPress={this.handleResetPress}>
267
+ <Button
268
+ styleName="secondary flexible"
269
+ onPress={this.handleResetPress}
270
+ >
246
271
  <Text>{resetButtonTitle}</Text>
247
272
  </Button>
248
- <Button styleName="primary flexible md-gutter-left" onPress={this.handleConfirmPress}>
273
+ <Button
274
+ styleName="primary flexible md-gutter-left"
275
+ onPress={this.handleConfirmPress}
276
+ >
249
277
  <Text>{confirmButtonTitle}</Text>
250
278
  </Button>
251
279
  </View>
252
- </ View>
280
+ </View>
253
281
  </Modal>
254
282
  );
255
283
  }
256
284
  }
257
285
 
258
- export default connectStyle('shoutem.ui.YearRangePickerModal')(YearRangePickerModal);
286
+ export default connectStyle('shoutem.ui.YearRangePickerModal')(
287
+ YearRangePickerModal,
288
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shoutem/ui",
3
- "version": "4.6.5",
3
+ "version": "4.6.7",
4
4
  "description": "Styleable set of components for React Native applications",
5
5
  "scripts": {
6
6
  "lint": "eslint .",
package/theme.js CHANGED
@@ -900,6 +900,8 @@ export default (variables = defaultThemeVariables) => ({
900
900
  },
901
901
 
902
902
  'shoutem.ui.Screen': {
903
+ [INCLUDE]: ['commonVariants', 'guttersPadding'],
904
+
903
905
  '.full-screen': {
904
906
  marginTop: -(NAVIGATION_BAR_HEIGHT + StyleSheet.hairlineWidth),
905
907
  },