@shoutem/ui 4.6.2 → 4.6.6-rc.0
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.
- package/components/DateTimePicker.js +76 -10
- package/components/ListView.js +15 -2
- package/html/components/SimpleHtml.js +54 -25
- package/package.json +2 -2
- package/theme.js +7 -6
|
@@ -13,15 +13,39 @@ 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
|
-
|
|
47
|
+
showTimePicker: false,
|
|
48
|
+
value,
|
|
25
49
|
};
|
|
26
50
|
}
|
|
27
51
|
|
|
@@ -42,10 +66,33 @@ class DateTimePicker extends PureComponent {
|
|
|
42
66
|
return this.handleHidePicker();
|
|
43
67
|
}
|
|
44
68
|
|
|
69
|
+
const { mode, onValueChanged } = this.props;
|
|
70
|
+
|
|
71
|
+
const showTimePicker = mode === DATEPICKER_MODES.DATETIME;
|
|
72
|
+
|
|
73
|
+
this.setState({ value, showPicker: false, showTimePicker });
|
|
74
|
+
return !showTimePicker && onValueChanged(value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
handleTimeValueChanged(event, selectedValue) {
|
|
78
|
+
if (event.type === 'dismissed') {
|
|
79
|
+
return this.handleHidePicker();
|
|
80
|
+
}
|
|
81
|
+
|
|
45
82
|
const { onValueChanged } = this.props;
|
|
83
|
+
const { value } = this.state;
|
|
46
84
|
|
|
47
|
-
|
|
48
|
-
|
|
85
|
+
// Time picker value will override preselected date
|
|
86
|
+
// Use current date from state and only set hours & minutes
|
|
87
|
+
const currentDate = new Date(value);
|
|
88
|
+
const selectedDate = new Date(selectedValue);
|
|
89
|
+
const hour = selectedDate.getHours();
|
|
90
|
+
const minutes = selectedDate.getMinutes();
|
|
91
|
+
|
|
92
|
+
currentDate.setHours(hour, minutes, 0);
|
|
93
|
+
|
|
94
|
+
this.setState({ value: currentDate, showTimePicker: false });
|
|
95
|
+
return onValueChanged(currentDate);
|
|
49
96
|
}
|
|
50
97
|
|
|
51
98
|
handleShowPicker() {
|
|
@@ -66,6 +113,7 @@ class DateTimePicker extends PureComponent {
|
|
|
66
113
|
|
|
67
114
|
render() {
|
|
68
115
|
const {
|
|
116
|
+
cancelButtonText,
|
|
69
117
|
confirmButtonText,
|
|
70
118
|
is24Hour,
|
|
71
119
|
mode,
|
|
@@ -73,7 +121,7 @@ class DateTimePicker extends PureComponent {
|
|
|
73
121
|
style,
|
|
74
122
|
...otherProps
|
|
75
123
|
} = this.props;
|
|
76
|
-
const { showPicker, value } = this.state;
|
|
124
|
+
const { display, showPicker, showTimePicker, value } = this.state;
|
|
77
125
|
|
|
78
126
|
return (
|
|
79
127
|
<View styleName="horizontal">
|
|
@@ -82,7 +130,7 @@ class DateTimePicker extends PureComponent {
|
|
|
82
130
|
style={style.textContainer}
|
|
83
131
|
onPress={this.handleShowPicker}
|
|
84
132
|
>
|
|
85
|
-
<Text>
|
|
133
|
+
<Text>{textValue}</Text>
|
|
86
134
|
</TouchableOpacity>
|
|
87
135
|
<TouchableOpacity
|
|
88
136
|
onPress={this.handleShowPicker}
|
|
@@ -91,7 +139,7 @@ class DateTimePicker extends PureComponent {
|
|
|
91
139
|
>
|
|
92
140
|
<Icon name="drop-down" style={style.icon} />
|
|
93
141
|
</TouchableOpacity>
|
|
94
|
-
{isIos && (
|
|
142
|
+
{isIos && showPicker && (
|
|
95
143
|
<Modal
|
|
96
144
|
backdropOpacity={0.5}
|
|
97
145
|
isVisible={showPicker}
|
|
@@ -103,7 +151,7 @@ class DateTimePicker extends PureComponent {
|
|
|
103
151
|
>
|
|
104
152
|
<View styleName="md-gutter" style={style.modalContainer}>
|
|
105
153
|
<RNCDateTimePicker
|
|
106
|
-
display=
|
|
154
|
+
display={display}
|
|
107
155
|
is24Hour={is24Hour}
|
|
108
156
|
mode={mode}
|
|
109
157
|
onChange={this.handleValueChanged}
|
|
@@ -114,8 +162,14 @@ class DateTimePicker extends PureComponent {
|
|
|
114
162
|
/>
|
|
115
163
|
<View
|
|
116
164
|
style={style.modalButtonContainer}
|
|
117
|
-
styleName="md-gutter-top"
|
|
165
|
+
styleName="horizontal md-gutter-top"
|
|
118
166
|
>
|
|
167
|
+
<Button
|
|
168
|
+
style={style.modalButton}
|
|
169
|
+
onPress={this.handleHidePicker}
|
|
170
|
+
>
|
|
171
|
+
<Text>{cancelButtonText}</Text>
|
|
172
|
+
</Button>
|
|
119
173
|
<Button
|
|
120
174
|
style={style.modalButton}
|
|
121
175
|
onPress={this.handleConfirmPress}
|
|
@@ -128,7 +182,7 @@ class DateTimePicker extends PureComponent {
|
|
|
128
182
|
)}
|
|
129
183
|
{!isIos && showPicker && (
|
|
130
184
|
<RNCDateTimePicker
|
|
131
|
-
display=
|
|
185
|
+
display={display}
|
|
132
186
|
is24Hour={is24Hour}
|
|
133
187
|
mode={mode}
|
|
134
188
|
onChange={this.handleValueChanged}
|
|
@@ -136,21 +190,33 @@ class DateTimePicker extends PureComponent {
|
|
|
136
190
|
{...otherProps}
|
|
137
191
|
/>
|
|
138
192
|
)}
|
|
193
|
+
{!isIos && !showPicker && showTimePicker && (
|
|
194
|
+
<RNCDateTimePicker
|
|
195
|
+
display={display}
|
|
196
|
+
is24Hour={is24Hour}
|
|
197
|
+
mode="time"
|
|
198
|
+
onChange={this.handleTimeValueChanged}
|
|
199
|
+
value={value}
|
|
200
|
+
{...otherProps}
|
|
201
|
+
/>
|
|
202
|
+
)}
|
|
139
203
|
</View>
|
|
140
204
|
);
|
|
141
205
|
}
|
|
142
206
|
}
|
|
143
207
|
|
|
144
208
|
DateTimePicker.propTypes = {
|
|
209
|
+
cancelButtonText: PropTypes.string,
|
|
145
210
|
confirmButtonText: PropTypes.string,
|
|
146
211
|
is24Hour: PropTypes.bool,
|
|
147
|
-
mode: PropTypes.
|
|
212
|
+
mode: PropTypes.oneOf(Object.values(DATEPICKER_MODES)),
|
|
148
213
|
onValueChanged: PropTypes.func,
|
|
149
214
|
textValue: PropTypes.string,
|
|
150
215
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
|
|
151
216
|
};
|
|
152
217
|
|
|
153
218
|
DateTimePicker.defaultProps = {
|
|
219
|
+
cancelButtonText: 'Cancel',
|
|
154
220
|
confirmButtonText: 'Confirm',
|
|
155
221
|
is24Hour: false,
|
|
156
222
|
mode: 'datetime',
|
package/components/ListView.js
CHANGED
|
@@ -77,6 +77,7 @@ class ListView extends PureComponent {
|
|
|
77
77
|
|
|
78
78
|
this.state = {
|
|
79
79
|
status: props.loading ? Status.LOADING : Status.IDLE,
|
|
80
|
+
isScrolling: false,
|
|
80
81
|
};
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -196,17 +197,21 @@ class ListView extends PureComponent {
|
|
|
196
197
|
// reference
|
|
197
198
|
mappedProps.ref = this.handleListViewRef;
|
|
198
199
|
|
|
200
|
+
mappedProps.onMomentumScrollBegin = this.setStartedScrolling;
|
|
201
|
+
|
|
202
|
+
mappedProps.onMomentumScrollEnd = this.setEndedScrolling;
|
|
203
|
+
|
|
199
204
|
return mappedProps;
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
// eslint-disable-next-line consistent-return
|
|
203
208
|
createOnLoadMore() {
|
|
204
209
|
const { onLoadMore, data } = this.props;
|
|
205
|
-
const { status } = this.state;
|
|
210
|
+
const { isScrolling, status } = this.state;
|
|
206
211
|
if (onLoadMore) {
|
|
207
212
|
return _.throttle(
|
|
208
213
|
() => {
|
|
209
|
-
if (!_.isEmpty(data) && status === Status.IDLE) {
|
|
214
|
+
if (!_.isEmpty(data) && isScrolling && status === Status.IDLE) {
|
|
210
215
|
onLoadMore();
|
|
211
216
|
}
|
|
212
217
|
},
|
|
@@ -216,6 +221,14 @@ class ListView extends PureComponent {
|
|
|
216
221
|
}
|
|
217
222
|
}
|
|
218
223
|
|
|
224
|
+
setStartedScrolling() {
|
|
225
|
+
this.setState({ isScrolling: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
setEndedScrolling() {
|
|
229
|
+
this.setState({ isScrolling: true });
|
|
230
|
+
}
|
|
231
|
+
|
|
219
232
|
autoHideHeader({
|
|
220
233
|
nativeEvent: {
|
|
221
234
|
layout: { height },
|
|
@@ -25,6 +25,34 @@ import getEmptyObjectKeys from '../services/getEmptyObjectKeys';
|
|
|
25
25
|
import isValidVideoFormat from '../services/isValidVideoFormat';
|
|
26
26
|
import Image from './Image';
|
|
27
27
|
|
|
28
|
+
function resolveMaxWidth(style) {
|
|
29
|
+
// parentContainerPadding - padding between screen & SimpleHtml. Parent container
|
|
30
|
+
// can have it's own padding. If it does, we have to include it in calculation,
|
|
31
|
+
// otherwise maxWidth will be more than max & images will go over the edge
|
|
32
|
+
const parentContainerPadding = style.outerPadding || 0;
|
|
33
|
+
const paddingValue =
|
|
34
|
+
style.container.paddingLeft ||
|
|
35
|
+
0 + style.container.paddingRight ||
|
|
36
|
+
0 + parentContainerPadding;
|
|
37
|
+
|
|
38
|
+
return Dimensions.get('window').width - paddingValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveDimensions(objectToResize, style) {
|
|
42
|
+
const { width, height } = objectToResize;
|
|
43
|
+
|
|
44
|
+
if (!width || !height) {
|
|
45
|
+
return { width, height };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const maxWidth = resolveMaxWidth(style);
|
|
49
|
+
const objectToResizeRatio = height / width;
|
|
50
|
+
const resolvedWidth = width > maxWidth ? maxWidth : width;
|
|
51
|
+
const resolvedHeight = Math.round(resolvedWidth * objectToResizeRatio);
|
|
52
|
+
|
|
53
|
+
return { width: resolvedWidth, height: resolvedHeight };
|
|
54
|
+
}
|
|
55
|
+
|
|
28
56
|
class SimpleHtml extends PureComponent {
|
|
29
57
|
static propTypes = {
|
|
30
58
|
body: PropTypes.string,
|
|
@@ -33,6 +61,7 @@ class SimpleHtml extends PureComponent {
|
|
|
33
61
|
customTagStyles: PropTypes.object,
|
|
34
62
|
customHandleLinkPress: PropTypes.func,
|
|
35
63
|
unsupportedVideoFormatMessage: PropTypes.string,
|
|
64
|
+
customAlterNode: PropTypes.func,
|
|
36
65
|
};
|
|
37
66
|
|
|
38
67
|
constructor(props) {
|
|
@@ -56,10 +85,22 @@ class SimpleHtml extends PureComponent {
|
|
|
56
85
|
* video iframe tags in when video format is unsupported
|
|
57
86
|
*/
|
|
58
87
|
alterNode(node) {
|
|
59
|
-
const { style } = this.props;
|
|
88
|
+
const { customAlterNode, style } = this.props;
|
|
60
89
|
|
|
61
90
|
const styleAttrib = _.get(node, 'attribs.style', '').trim();
|
|
62
91
|
|
|
92
|
+
if (customAlterNode && _.isFunction(customAlterNode)) {
|
|
93
|
+
const resolvedNode = customAlterNode(
|
|
94
|
+
node,
|
|
95
|
+
cssObjectToString,
|
|
96
|
+
cssStringToObject,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (resolvedNode) {
|
|
100
|
+
return resolvedNode;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
63
104
|
if (node.name === 'table') {
|
|
64
105
|
return tableAlterNode(node);
|
|
65
106
|
}
|
|
@@ -81,23 +122,15 @@ class SimpleHtml extends PureComponent {
|
|
|
81
122
|
}
|
|
82
123
|
}
|
|
83
124
|
|
|
84
|
-
const nodeWidth =
|
|
125
|
+
const nodeWidth = node.attribs?.width;
|
|
85
126
|
|
|
86
127
|
if (!styleAttrib && !nodeWidth) {
|
|
87
128
|
return false;
|
|
88
129
|
}
|
|
89
130
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const parentContainerPadding = style.outerPadding || 0;
|
|
94
|
-
const paddingValue =
|
|
95
|
-
_.get(style, 'container.paddingLeft') * 2 + parentContainerPadding;
|
|
96
|
-
const maxWidth = Dimensions.get('window').width - paddingValue;
|
|
97
|
-
const nodeHeight = _.get(node, 'attribus.height');
|
|
98
|
-
const nodeRatio = nodeWidth / nodeHeight;
|
|
99
|
-
const resolvedWidth = nodeWidth > maxWidth ? maxWidth : nodeWidth;
|
|
100
|
-
const resolvedHeight = Math.round(resolvedWidth * nodeRatio);
|
|
131
|
+
const nodeHeight = node.attribus?.height;
|
|
132
|
+
const nodeDimensions = { width: nodeWidth, height: nodeHeight };
|
|
133
|
+
const { width, height } = resolveDimensions(nodeDimensions, style);
|
|
101
134
|
|
|
102
135
|
const nodeStyle = cssStringToObject(styleAttrib);
|
|
103
136
|
const invalidKeys = getEmptyObjectKeys(nodeStyle);
|
|
@@ -105,8 +138,8 @@ class SimpleHtml extends PureComponent {
|
|
|
105
138
|
if (invalidKeys.length || nodeWidth) {
|
|
106
139
|
const styleFiltered = _.omit(style, invalidKeys);
|
|
107
140
|
node.attribs.style = cssObjectToString(styleFiltered);
|
|
108
|
-
node.attribs.width =
|
|
109
|
-
node.attribs.height =
|
|
141
|
+
node.attribs.width = width;
|
|
142
|
+
node.attribs.height = height;
|
|
110
143
|
|
|
111
144
|
return node;
|
|
112
145
|
}
|
|
@@ -162,7 +195,7 @@ class SimpleHtml extends PureComponent {
|
|
|
162
195
|
* Currently only supports rendering image attachments.
|
|
163
196
|
*/
|
|
164
197
|
renderAttachments({ id, type }) {
|
|
165
|
-
const { attachments } = this.props;
|
|
198
|
+
const { attachments, style } = this.props;
|
|
166
199
|
|
|
167
200
|
if (!attachments) {
|
|
168
201
|
return null;
|
|
@@ -173,9 +206,11 @@ class SimpleHtml extends PureComponent {
|
|
|
173
206
|
|
|
174
207
|
if (image && image.src) {
|
|
175
208
|
const source = { uri: image.src };
|
|
176
|
-
const
|
|
209
|
+
const imageSize = { width: image.width, height: image.height };
|
|
210
|
+
const { height, width } = resolveDimensions(imageSize, style);
|
|
211
|
+
const resolvedStyle = { alignSelf: 'center', height, width };
|
|
177
212
|
|
|
178
|
-
return <Image source={source} key={id} style={
|
|
213
|
+
return <Image source={source} key={id} style={resolvedStyle} />;
|
|
179
214
|
}
|
|
180
215
|
}
|
|
181
216
|
|
|
@@ -225,13 +260,7 @@ class SimpleHtml extends PureComponent {
|
|
|
225
260
|
render() {
|
|
226
261
|
const { style, body, customTagStyles, ...otherProps } = this.props;
|
|
227
262
|
|
|
228
|
-
|
|
229
|
-
// can have it's own padding. If it does, we have to include it in calculation,
|
|
230
|
-
// otherwise maxWidth will be more than max & images will go over the edge
|
|
231
|
-
const parentContainerPadding = style.outerPadding || 0;
|
|
232
|
-
const paddingValue =
|
|
233
|
-
_.get(style, 'container.paddingLeft') * 2 + parentContainerPadding;
|
|
234
|
-
const maxWidth = Dimensions.get('window').width - paddingValue;
|
|
263
|
+
const maxWidth = resolveMaxWidth(style);
|
|
235
264
|
|
|
236
265
|
const tagStyles = {
|
|
237
266
|
...style.tags,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoutem/ui",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.6-rc.0",
|
|
4
4
|
"description": "Styleable set of components for React Native applications",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"react-native-transformable-image": "shoutem/react-native-transformable-image#v0.0.20",
|
|
36
36
|
"react-native-vector-icons": "~6.6.0",
|
|
37
37
|
"react-native-webview": "~11.0.0",
|
|
38
|
-
"react-native-youtube-iframe": "~2.
|
|
38
|
+
"react-native-youtube-iframe": "~2.1.2",
|
|
39
39
|
"stream": "0.0.2",
|
|
40
40
|
"tinycolor2": "1.4.1"
|
|
41
41
|
},
|
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
|
},
|
|
@@ -2041,10 +2043,9 @@ export default (variables = defaultThemeVariables) => ({
|
|
|
2041
2043
|
minHeight: 40,
|
|
2042
2044
|
maxHeight: 200,
|
|
2043
2045
|
justifyContent: 'center',
|
|
2044
|
-
backgroundColor:
|
|
2045
|
-
variables.
|
|
2046
|
-
5,
|
|
2047
|
-
),
|
|
2046
|
+
backgroundColor:
|
|
2047
|
+
variables.categoryDropdownBackgroundColor ||
|
|
2048
|
+
inverseColorBrightnessForAmount(variables.paperColor, 5),
|
|
2048
2049
|
width: window.width,
|
|
2049
2050
|
marginTop: 0,
|
|
2050
2051
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
@@ -2052,7 +2053,7 @@ export default (variables = defaultThemeVariables) => ({
|
|
|
2052
2053
|
},
|
|
2053
2054
|
selectedOption: {
|
|
2054
2055
|
'shoutem.ui.Icon': {
|
|
2055
|
-
color: variables.text.color,
|
|
2056
|
+
color: variables.categoryDropdownTextColor || variables.text.color,
|
|
2056
2057
|
},
|
|
2057
2058
|
'shoutem.ui.Text': {
|
|
2058
2059
|
...variables.navBarText,
|
|
@@ -2061,7 +2062,7 @@ export default (variables = defaultThemeVariables) => ({
|
|
|
2061
2062
|
'normal',
|
|
2062
2063
|
variables.navBarText.fontStyle,
|
|
2063
2064
|
),
|
|
2064
|
-
color: variables.text.color,
|
|
2065
|
+
color: variables.categoryDropdownTextColor || variables.text.color,
|
|
2065
2066
|
fontWeight: resolveFontWeight('normal'),
|
|
2066
2067
|
fontStyle: resolveFontStyle(variables.navBarText.fontStyle),
|
|
2067
2068
|
textAlign: 'center',
|