@shoutem/ui 7.1.0-beta.2 → 7.1.0-beta.21
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/ActionSheet/ActionSheet.js +61 -170
- package/components/ActionSheet/ActionSheetOption.js +13 -2
- package/components/EmptyListImage.js +20 -3
- package/components/Icon/Icon.web.js +38 -0
- package/components/Icon/assets/index.js +4 -0
- package/components/Icon/assets/queue.svg +1 -0
- package/components/Icon/assets/wheelchair.svg +4 -0
- package/components/ListView.js +30 -7
- package/components/ShareButton.js +2 -1
- package/components/Switch.js +21 -11
- package/components/TextInput.js +5 -2
- package/components/ToastMessage/components/ToastProgressBar.js +7 -7
- package/components/Video/Video.js +1 -1
- package/html/components/SimpleHtml.js +0 -2
- package/package.json +8 -5
- package/services/index.js +4 -2
- package/services/platform.js +5 -0
- package/services/unavailableInWeb.js +29 -0
- package/theme.js +19 -22
- package/web/style/Alert.scss +69 -0
|
@@ -1,183 +1,74 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import
|
|
3
|
-
import autoBindReact from 'auto-bind/react';
|
|
1
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import ActionSheetNative from 'react-native-actions-sheet';
|
|
4
3
|
import _ from 'lodash';
|
|
5
4
|
import PropTypes from 'prop-types';
|
|
6
5
|
import { connectStyle } from '@shoutem/theme';
|
|
7
|
-
import { IPHONE_X_HOME_INDICATOR_PADDING } from '../../const';
|
|
8
|
-
import { Device } from '../../helpers';
|
|
9
6
|
import { View } from '../View';
|
|
10
7
|
import ActionSheetOption, { optionPropType } from './ActionSheetOption';
|
|
11
8
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
const ActionSheet = ({
|
|
10
|
+
active = false,
|
|
11
|
+
cancelOptions = undefined,
|
|
12
|
+
confirmOptions = undefined,
|
|
13
|
+
style,
|
|
14
|
+
onDismiss = undefined,
|
|
15
|
+
...otherProps
|
|
16
|
+
}) => {
|
|
17
|
+
const actionSheetRef = useRef(null);
|
|
18
|
+
|
|
19
|
+
const hasConfirmOptions = useMemo(() => !_.isEmpty(confirmOptions), [
|
|
20
|
+
confirmOptions,
|
|
21
|
+
]);
|
|
22
|
+
const hasCancelOptions = useMemo(() => !_.isEmpty(cancelOptions), [
|
|
23
|
+
cancelOptions,
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (active) {
|
|
28
|
+
actionSheetRef.current?.show();
|
|
29
|
+
} else {
|
|
30
|
+
actionSheetRef.current?.hide();
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!active && prevActive) {
|
|
57
|
-
this.handleExitAnimation();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
hideComponent() {
|
|
62
|
-
this.setState({ visible: false });
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
handleContentLayout(event) {
|
|
66
|
-
const height = _.get(event, 'nativeEvent.layout.height', 0);
|
|
67
|
-
|
|
68
|
-
this.setState({ height });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
handleEntryAnimation() {
|
|
72
|
-
const { yPos, opacity } = this.state;
|
|
73
|
-
|
|
74
|
-
const toValue = 1;
|
|
75
|
-
|
|
76
|
-
Animated.parallel([
|
|
77
|
-
Animated.spring(yPos, {
|
|
78
|
-
toValue,
|
|
79
|
-
useNativeDriver: true,
|
|
80
|
-
}),
|
|
81
|
-
Animated.timing(opacity, {
|
|
82
|
-
toValue,
|
|
83
|
-
useNativeDriver: false,
|
|
84
|
-
}),
|
|
85
|
-
]).start();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
handleExitAnimation() {
|
|
89
|
-
const { yPos, opacity } = this.state;
|
|
90
|
-
|
|
91
|
-
const toValue = 0;
|
|
92
|
-
|
|
93
|
-
Animated.parallel([
|
|
94
|
-
Animated.spring(yPos, {
|
|
95
|
-
toValue,
|
|
96
|
-
useNativeDriver: true,
|
|
97
|
-
}),
|
|
98
|
-
Animated.timing(opacity, {
|
|
99
|
-
toValue,
|
|
100
|
-
delay: 100,
|
|
101
|
-
duration: 150,
|
|
102
|
-
useNativeDriver: false,
|
|
103
|
-
}),
|
|
104
|
-
]).start(this.hideComponent);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
handleInactiveAreaPress() {
|
|
108
|
-
const { onDismiss } = this.props;
|
|
109
|
-
|
|
110
|
-
if (onDismiss) {
|
|
111
|
-
onDismiss();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
render() {
|
|
116
|
-
const { confirmOptions, cancelOptions, style } = this.props;
|
|
117
|
-
const { opacity, yPos, visible, height } = this.state;
|
|
118
|
-
|
|
119
|
-
const hasConfirmOptions = !_.isEmpty(confirmOptions);
|
|
120
|
-
const hasCancelOptions = !_.isEmpty(cancelOptions);
|
|
121
|
-
|
|
122
|
-
if (!visible || (!hasConfirmOptions && !hasCancelOptions)) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<AnimatedTouchable
|
|
128
|
-
activeOpacity={1}
|
|
129
|
-
onPress={this.handleInactiveAreaPress}
|
|
130
|
-
style={[
|
|
131
|
-
style.container,
|
|
132
|
-
{
|
|
133
|
-
backgroundColor: opacity.interpolate({
|
|
134
|
-
inputRange: [0, 1],
|
|
135
|
-
outputRange: ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 0.3)'],
|
|
136
|
-
}),
|
|
137
|
-
},
|
|
138
|
-
]}
|
|
139
|
-
>
|
|
140
|
-
<Animated.View
|
|
141
|
-
onLayout={this.handleContentLayout}
|
|
142
|
-
style={[
|
|
143
|
-
style.contentContainer,
|
|
144
|
-
{
|
|
145
|
-
transform: [
|
|
146
|
-
{
|
|
147
|
-
translateY: yPos.interpolate({
|
|
148
|
-
inputRange: [0, 1],
|
|
149
|
-
outputRange: [getContentOffset(height), 0],
|
|
150
|
-
}),
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
},
|
|
154
|
-
]}
|
|
155
|
-
>
|
|
156
|
-
{hasConfirmOptions && (
|
|
32
|
+
}, [active]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<ActionSheetNative
|
|
36
|
+
gestureEnabled
|
|
37
|
+
ref={actionSheetRef}
|
|
38
|
+
onClose={onDismiss}
|
|
39
|
+
containerStyle={style.container}
|
|
40
|
+
{...otherProps}
|
|
41
|
+
>
|
|
42
|
+
<View style={style.contentContainer}>
|
|
43
|
+
{hasConfirmOptions && (
|
|
44
|
+
<View style={style.segmentContainer}>
|
|
45
|
+
{_.map(confirmOptions, option => (
|
|
46
|
+
<ActionSheetOption
|
|
47
|
+
key={option.title}
|
|
48
|
+
option={option}
|
|
49
|
+
style={style.option}
|
|
50
|
+
/>
|
|
51
|
+
))}
|
|
52
|
+
</View>
|
|
53
|
+
)}
|
|
54
|
+
{hasCancelOptions && (
|
|
55
|
+
<View styleName="md-gutter-top">
|
|
157
56
|
<View style={style.segmentContainer}>
|
|
158
|
-
{_.map(
|
|
159
|
-
<ActionSheetOption
|
|
57
|
+
{_.map(cancelOptions, option => (
|
|
58
|
+
<ActionSheetOption
|
|
59
|
+
key={option.title}
|
|
60
|
+
cancelOption
|
|
61
|
+
option={option}
|
|
62
|
+
style={style.option}
|
|
63
|
+
/>
|
|
160
64
|
))}
|
|
161
65
|
</View>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
key={option.title}
|
|
169
|
-
cancelOption
|
|
170
|
-
option={option}
|
|
171
|
-
/>
|
|
172
|
-
))}
|
|
173
|
-
</View>
|
|
174
|
-
</View>
|
|
175
|
-
)}
|
|
176
|
-
</Animated.View>
|
|
177
|
-
</AnimatedTouchable>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
66
|
+
</View>
|
|
67
|
+
)}
|
|
68
|
+
</View>
|
|
69
|
+
</ActionSheetNative>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
181
72
|
|
|
182
73
|
ActionSheet.propTypes = {
|
|
183
74
|
style: PropTypes.object.isRequired,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { connectStyle } from '@shoutem/theme';
|
|
4
|
+
import { isIos } from '../../services';
|
|
4
5
|
import { Text } from '../Text';
|
|
5
6
|
import { TouchableOpacity } from '../TouchableOpacity';
|
|
6
7
|
|
|
@@ -9,16 +10,24 @@ export const optionPropType = PropTypes.shape({
|
|
|
9
10
|
onPress: PropTypes.func,
|
|
10
11
|
});
|
|
11
12
|
|
|
12
|
-
function ActionSheetOption({ style, option, cancelOption }) {
|
|
13
|
+
function ActionSheetOption({ style, option, cancelOption, nativeStyle }) {
|
|
13
14
|
const { title, onPress } = option;
|
|
14
15
|
|
|
16
|
+
const useIosTextColor = nativeStyle && isIos;
|
|
17
|
+
|
|
15
18
|
return (
|
|
16
19
|
<TouchableOpacity
|
|
17
20
|
onPress={onPress}
|
|
18
21
|
style={style.container}
|
|
19
22
|
styleName="horizontal"
|
|
20
23
|
>
|
|
21
|
-
<Text
|
|
24
|
+
<Text
|
|
25
|
+
style={[
|
|
26
|
+
style.text,
|
|
27
|
+
cancelOption && style.cancelText,
|
|
28
|
+
useIosTextColor && style.iosBlueTextColor,
|
|
29
|
+
]}
|
|
30
|
+
>
|
|
22
31
|
{title}
|
|
23
32
|
</Text>
|
|
24
33
|
</TouchableOpacity>
|
|
@@ -28,12 +37,14 @@ function ActionSheetOption({ style, option, cancelOption }) {
|
|
|
28
37
|
ActionSheetOption.propTypes = {
|
|
29
38
|
style: PropTypes.object.isRequired,
|
|
30
39
|
cancelOption: PropTypes.bool,
|
|
40
|
+
nativeStyle: PropTypes.bool,
|
|
31
41
|
option: optionPropType,
|
|
32
42
|
};
|
|
33
43
|
|
|
34
44
|
ActionSheetOption.defaultProps = {
|
|
35
45
|
option: undefined,
|
|
36
46
|
cancelOption: false,
|
|
47
|
+
nativeStyle: false,
|
|
37
48
|
};
|
|
38
49
|
|
|
39
50
|
export default connectStyle('shoutem.ui.ActionSheetOption')(ActionSheetOption);
|
|
@@ -37,13 +37,29 @@ class EmptyListImage extends PureComponent {
|
|
|
37
37
|
} = this.props;
|
|
38
38
|
const EmptyStateImage = this.resolveImage();
|
|
39
39
|
|
|
40
|
+
// SVG cannot receive array of style elements on Web
|
|
41
|
+
const resolvedImageStyle = {
|
|
42
|
+
...style.image,
|
|
43
|
+
...imageStyle,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const resolvedTitleStyle = {
|
|
47
|
+
...style.title,
|
|
48
|
+
...titleStyle,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const resolvedTextStyle = {
|
|
52
|
+
...style.message,
|
|
53
|
+
...messageStyle,
|
|
54
|
+
};
|
|
55
|
+
|
|
40
56
|
return (
|
|
41
57
|
<View styleName="vertical h-center ">
|
|
42
|
-
<EmptyStateImage style={
|
|
43
|
-
<Title styleName="title" style={
|
|
58
|
+
<EmptyStateImage style={resolvedImageStyle} />
|
|
59
|
+
<Title styleName="title" style={resolvedTitleStyle}>
|
|
44
60
|
{title}
|
|
45
61
|
</Title>
|
|
46
|
-
<Text styleName="description" style={
|
|
62
|
+
<Text styleName="description" style={resolvedTextStyle}>
|
|
47
63
|
{message}
|
|
48
64
|
</Text>
|
|
49
65
|
</View>
|
|
@@ -52,6 +68,7 @@ class EmptyListImage extends PureComponent {
|
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
EmptyListImage.propTypes = {
|
|
71
|
+
style: PropTypes.object.isRequired,
|
|
55
72
|
image: PropTypes.func,
|
|
56
73
|
imageStyle: PropTypes.object,
|
|
57
74
|
message: PropTypes.string,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { connectStyle } from '@shoutem/theme';
|
|
4
|
+
import { View } from '../View';
|
|
5
|
+
import { getIcon } from './services';
|
|
6
|
+
|
|
7
|
+
function Icon({ name, style, ...otherProps }) {
|
|
8
|
+
const { color, width, height, ...otherStyle } = style;
|
|
9
|
+
|
|
10
|
+
const NamedIcon = getIcon(name);
|
|
11
|
+
|
|
12
|
+
if (!NamedIcon) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.warn(`Icon with name '${name}' not found within the provided set`);
|
|
15
|
+
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
// Sometimes, in web, svg icons are not resized as expected, unless svg is wrapped inside View.
|
|
21
|
+
<View>
|
|
22
|
+
<NamedIcon
|
|
23
|
+
fill={color}
|
|
24
|
+
width={width}
|
|
25
|
+
height={height}
|
|
26
|
+
style={{ ...otherStyle }}
|
|
27
|
+
{...otherProps}
|
|
28
|
+
/>
|
|
29
|
+
</View>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Icon.propTypes = {
|
|
34
|
+
name: PropTypes.string.isRequired,
|
|
35
|
+
style: PropTypes.object.isRequired,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default connectStyle('shoutem.ui.Icon')(Icon);
|
|
@@ -83,6 +83,7 @@ import playlistPlay from './playlist-play.svg';
|
|
|
83
83
|
import plusButton from './plus-button.svg';
|
|
84
84
|
import podcasts from './podcasts.svg';
|
|
85
85
|
import products from './products.svg';
|
|
86
|
+
import queue from './queue.svg';
|
|
86
87
|
import radio from './radio.svg';
|
|
87
88
|
import radiobuttonOff from './radiobutton-off.svg';
|
|
88
89
|
import radiobuttonOn from './radiobutton-on.svg';
|
|
@@ -123,6 +124,7 @@ import videoCam from './video-cam.svg';
|
|
|
123
124
|
import videoCamOff from './video-cam-off.svg';
|
|
124
125
|
import videoChat from './video-chat.svg';
|
|
125
126
|
import web from './web.svg';
|
|
127
|
+
import wheelchair from './wheelchair.svg';
|
|
126
128
|
|
|
127
129
|
export const defaultConfig = [
|
|
128
130
|
{ name: 'about', icon: about },
|
|
@@ -210,6 +212,7 @@ export const defaultConfig = [
|
|
|
210
212
|
{ name: 'plus-button', icon: plusButton },
|
|
211
213
|
{ name: 'podcasts', icon: podcasts },
|
|
212
214
|
{ name: 'products', icon: products },
|
|
215
|
+
{ name: 'queue', icon: queue },
|
|
213
216
|
{ name: 'radio', icon: radio },
|
|
214
217
|
{ name: 'radiobutton-off', icon: radiobuttonOff },
|
|
215
218
|
{ name: 'radiobutton-on', icon: radiobuttonOn },
|
|
@@ -250,4 +253,5 @@ export const defaultConfig = [
|
|
|
250
253
|
{ name: 'video-cam-off', icon: videoCamOff },
|
|
251
254
|
{ name: 'video-chat', icon: videoChat },
|
|
252
255
|
{ name: 'web', icon: web },
|
|
256
|
+
{ name: 'wheelchair', icon: wheelchair },
|
|
253
257
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" ?><svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M0 2h20v4H0V2zm0 8h20v2H0v-2zm0 6h20v2H0v-2z"/></svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 22 24">
|
|
2
|
+
<title>wheelchair</title>
|
|
3
|
+
<path d="M13.701 15.897l1.366 2.732c-1.031 3.188-4.004 5.371-7.353 5.371-4.246 0-7.714-3.469-7.714-7.714 0-3.241 2.036-6.134 5.076-7.246l0.228 1.754c-2.183 0.964-3.589 3.107-3.589 5.491 0 3.308 2.692 6 6 6 3.442 0 6.228-2.946 5.987-6.388zM21.040 17.237l0.777 1.527-3.429 1.714c-0.121 0.067-0.254 0.094-0.388 0.094-0.321 0-0.629-0.188-0.763-0.469l-3.201-6.388h-6.321c-0.429 0-0.804-0.335-0.857-0.763l-1.286-10.433c-0.013-0.134 0.040-0.429 0.080-0.563 0.254-0.924 1.112-1.527 2.063-1.527 1.179 0 2.143 0.964 2.143 2.143 0 1.219-1.071 2.263-2.304 2.129l0.496 3.871h5.665v1.714h-5.451l0.214 1.714h6.094c0.321 0 0.629 0.188 0.763 0.469l3.054 6.094z"></path>
|
|
4
|
+
</svg>
|
package/components/ListView.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
StatusBar,
|
|
8
8
|
View,
|
|
9
9
|
} from 'react-native';
|
|
10
|
+
import { RefreshControl as WebRefreshControl } from 'react-native-web-refresh-control';
|
|
10
11
|
import autoBindReact from 'auto-bind/react';
|
|
11
12
|
import _ from 'lodash';
|
|
12
13
|
import PropTypes from 'prop-types';
|
|
@@ -37,15 +38,18 @@ class ListView extends PureComponent {
|
|
|
37
38
|
static getDerivedStateFromProps(props, state) {
|
|
38
39
|
const isLoading = props.loading;
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// We are already in a loading status
|
|
43
|
-
return state;
|
|
44
|
-
}
|
|
45
|
-
|
|
41
|
+
// If data is loading but no status updates were triggered, set status to loading.
|
|
42
|
+
if (isLoading && state.status === Status.IDLE) {
|
|
46
43
|
return { status: Status.LOADING };
|
|
47
44
|
}
|
|
48
|
-
|
|
45
|
+
|
|
46
|
+
// If loading is done, set status back to idle.
|
|
47
|
+
if (!isLoading && state.status === Status.LOADING) {
|
|
48
|
+
return { status: Status.IDLE}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Return all other statuses as they are.
|
|
52
|
+
return { status: state.status ?? Status.IDLE };
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
constructor(props, context) {
|
|
@@ -78,6 +82,14 @@ class ListView extends PureComponent {
|
|
|
78
82
|
|
|
79
83
|
if (onRefresh) {
|
|
80
84
|
onRefresh();
|
|
85
|
+
|
|
86
|
+
// Adding timeout, until onRefresh becomes async function. Then, we'll be able to know when
|
|
87
|
+
// refresh is done and set status back to idle.
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
this.setState({
|
|
90
|
+
status: Status.IDLE,
|
|
91
|
+
});
|
|
92
|
+
}, 500);
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
|
|
@@ -302,6 +314,17 @@ class ListView extends PureComponent {
|
|
|
302
314
|
};
|
|
303
315
|
delete refreshControlStyle.tintColor;
|
|
304
316
|
|
|
317
|
+
if (Platform.OS === 'web') {
|
|
318
|
+
return (
|
|
319
|
+
<WebRefreshControl
|
|
320
|
+
onRefresh={this.onRefresh}
|
|
321
|
+
refreshing={status === Status.REFRESHING}
|
|
322
|
+
tintColor={style.refreshControl.tintColor}
|
|
323
|
+
style={refreshControlStyle}
|
|
324
|
+
/>
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
305
328
|
return (
|
|
306
329
|
<RefreshControl
|
|
307
330
|
onRefresh={this.onRefresh}
|
|
@@ -3,6 +3,7 @@ import { Platform, Share } from 'react-native';
|
|
|
3
3
|
import autoBindReact from 'auto-bind/react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import { connectStyle } from '@shoutem/theme';
|
|
6
|
+
import { unavailableInWeb } from '../services';
|
|
6
7
|
import { Button } from './Button';
|
|
7
8
|
import { Icon } from './Icon';
|
|
8
9
|
|
|
@@ -41,7 +42,7 @@ class ShareButton extends PureComponent {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
return (
|
|
44
|
-
<Button onPress={this.onShare} {...otherProps}>
|
|
45
|
+
<Button onPress={unavailableInWeb(this.onShare)} {...otherProps}>
|
|
45
46
|
<Icon
|
|
46
47
|
name={Platform.OS === 'ios' ? 'share' : 'share-android'}
|
|
47
48
|
animationName={animationName}
|
package/components/Switch.js
CHANGED
|
@@ -2,19 +2,29 @@ import React from 'react';
|
|
|
2
2
|
import { Switch as RNSwitch } from 'react-native';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import { connectStyle } from '@shoutem/theme';
|
|
5
|
+
import { isWeb } from '../services/platform';
|
|
5
6
|
import { View } from './View';
|
|
6
7
|
|
|
7
|
-
const Switch = ({ value, onValueChange, style }) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
const Switch = ({ value, onValueChange, style }) => {
|
|
9
|
+
const webColors = {
|
|
10
|
+
trackColor: style.track?.false,
|
|
11
|
+
activeThumbColor: style.thumb,
|
|
12
|
+
activeTrackColor: style.track.true,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<View style={style.container}>
|
|
17
|
+
<RNSwitch
|
|
18
|
+
trackColor={isWeb ? style.track?.false : style.track}
|
|
19
|
+
thumbColor={style.thumb}
|
|
20
|
+
ios_backgroundColor={style.track?.false}
|
|
21
|
+
onValueChange={onValueChange}
|
|
22
|
+
value={value}
|
|
23
|
+
{...(isWeb && webColors)}
|
|
24
|
+
/>
|
|
25
|
+
</View>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
18
28
|
|
|
19
29
|
Switch.propTypes = {
|
|
20
30
|
onValueChange: PropTypes.func.isRequired,
|
package/components/TextInput.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-multi-comp */
|
|
2
2
|
import React, { forwardRef, PureComponent } from 'react';
|
|
3
|
-
import { TextInput as RNTextInput } from 'react-native';
|
|
3
|
+
import { Platform, TextInput as RNTextInput } from 'react-native';
|
|
4
4
|
import autoBindReact from 'auto-bind/react';
|
|
5
5
|
import { TextInputPropTypes } from 'deprecated-react-native-prop-types';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
@@ -60,14 +60,16 @@ class TextInput extends PureComponent {
|
|
|
60
60
|
withoutBorder,
|
|
61
61
|
wiggleAnimation,
|
|
62
62
|
errorText,
|
|
63
|
+
height,
|
|
63
64
|
...otherStyle
|
|
64
65
|
} = style;
|
|
65
66
|
|
|
67
|
+
const containerStyle = Platform.OS === 'web' ? { height } : undefined;
|
|
66
68
|
const hasBorder = (isFocused && highlightOnFocus) || !!errorMessage;
|
|
67
69
|
const startErrorAnimation = animate && !!errorMessage;
|
|
68
70
|
|
|
69
71
|
return (
|
|
70
|
-
<View>
|
|
72
|
+
<View style={containerStyle}>
|
|
71
73
|
<Wiggle style={wiggleAnimation} startAnimation={startErrorAnimation}>
|
|
72
74
|
<RNTextInput
|
|
73
75
|
{...otherProps}
|
|
@@ -80,6 +82,7 @@ class TextInput extends PureComponent {
|
|
|
80
82
|
...(hasBorder ? withBorder : withoutBorder),
|
|
81
83
|
...(errorMessage ? errorBorderColor : {}),
|
|
82
84
|
...otherStyle,
|
|
85
|
+
height,
|
|
83
86
|
}}
|
|
84
87
|
ref={forwardedRef}
|
|
85
88
|
/>
|
|
@@ -12,6 +12,13 @@ function ToastProgressBar({
|
|
|
12
12
|
}) {
|
|
13
13
|
const progressValue = useRef(new Animated.Value(0)).current;
|
|
14
14
|
|
|
15
|
+
const handleAnimationEnd = useCallback(() => {
|
|
16
|
+
progressValue.setValue(0);
|
|
17
|
+
if (onProgressComplete) {
|
|
18
|
+
onProgressComplete();
|
|
19
|
+
}
|
|
20
|
+
}, [onProgressComplete, progressValue]);
|
|
21
|
+
|
|
15
22
|
useEffect(() => {
|
|
16
23
|
if (!visible) {
|
|
17
24
|
progressValue.setValue(0);
|
|
@@ -25,13 +32,6 @@ function ToastProgressBar({
|
|
|
25
32
|
}).start(handleAnimationEnd);
|
|
26
33
|
}, [visible, duration, handleAnimationEnd]);
|
|
27
34
|
|
|
28
|
-
const handleAnimationEnd = useCallback(() => {
|
|
29
|
-
progressValue.setValue(0);
|
|
30
|
-
if (onProgressComplete) {
|
|
31
|
-
onProgressComplete();
|
|
32
|
-
}
|
|
33
|
-
}, [onProgressComplete, progressValue]);
|
|
34
|
-
|
|
35
35
|
return (
|
|
36
36
|
<View style={style.container}>
|
|
37
37
|
<Animated.View
|
|
@@ -6,7 +6,6 @@ import { iframeModel } from '@native-html/iframe-plugin';
|
|
|
6
6
|
import table, {
|
|
7
7
|
cssRulesFromSpecs,
|
|
8
8
|
defaultTableStylesSpecs,
|
|
9
|
-
IGNORED_TAGS,
|
|
10
9
|
tableModel,
|
|
11
10
|
} from '@native-html/table-plugin';
|
|
12
11
|
import autoBindReact from 'auto-bind/react';
|
|
@@ -119,7 +118,6 @@ class SimpleHtml extends PureComponent {
|
|
|
119
118
|
...customHtmlElementModels,
|
|
120
119
|
},
|
|
121
120
|
WebView,
|
|
122
|
-
ignoredTags: IGNORED_TAGS,
|
|
123
121
|
domVisitors: { onElement, ...customDomVisitors },
|
|
124
122
|
};
|
|
125
123
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoutem/ui",
|
|
3
|
-
"version": "7.1.0-beta.
|
|
3
|
+
"version": "7.1.0-beta.21",
|
|
4
4
|
"description": "Styleable set of components for React Native applications",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"@native-html/table-plugin": "5.3.1",
|
|
16
16
|
"@openspacelabs/react-native-zoomable-view": "2.0.4",
|
|
17
17
|
"@react-native-community/datetimepicker": "6.2.0",
|
|
18
|
-
"@shoutem/animation": "
|
|
18
|
+
"@shoutem/animation": "0.17.0-beta.0",
|
|
19
19
|
"@shoutem/eslint-config-react": "~1.0.6",
|
|
20
|
-
"@shoutem/theme": "~0.
|
|
20
|
+
"@shoutem/theme": "~0.14.0-beta.1",
|
|
21
21
|
"auto-bind": "4.0.0",
|
|
22
22
|
"babel-plugin-transform-decorators-legacy": "1.3.5",
|
|
23
23
|
"buffer": "5.6.0",
|
|
@@ -35,18 +35,21 @@
|
|
|
35
35
|
"react-native-linear-gradient": "2.8.3",
|
|
36
36
|
"react-native-modal": "13.0.1",
|
|
37
37
|
"react-native-render-html": "6.3.4",
|
|
38
|
-
"react-native-svg": "
|
|
38
|
+
"react-native-svg": "15.4.0",
|
|
39
39
|
"react-native-svg-transformer": "1.3.0",
|
|
40
40
|
"react-native-toast-message": "2.1.5",
|
|
41
41
|
"react-native-vimeo-iframe": "^1.2.1",
|
|
42
|
+
"react-native-web-refresh-control": "1.1.2",
|
|
42
43
|
"react-native-webview": "11.23.0",
|
|
43
44
|
"react-native-youtube-iframe": "2.2.2",
|
|
45
|
+
"react-native-actions-sheet": "0.9.3",
|
|
44
46
|
"stream": "0.0.2",
|
|
45
47
|
"tinycolor2": "1.4.1"
|
|
46
48
|
},
|
|
47
49
|
"peerDependencies": {
|
|
48
50
|
"react": ">=16.9.0",
|
|
49
|
-
"react-native": ">=0.61.5"
|
|
51
|
+
"react-native": ">=0.61.5",
|
|
52
|
+
"react-native-gesture-handler": "*"
|
|
50
53
|
},
|
|
51
54
|
"devDependencies": {
|
|
52
55
|
"@babel/core": "7.16.7",
|
package/services/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
export { isAndroid, isIos, isWeb } from './platform';
|
|
2
|
+
export { unavailableInWeb } from './unavailableInWeb';
|
|
1
3
|
export {
|
|
2
|
-
|
|
4
|
+
createScopedResolver,
|
|
3
5
|
defaultResolver,
|
|
4
6
|
resolveVariable,
|
|
5
|
-
|
|
7
|
+
ThemeVariableResolver,
|
|
6
8
|
} from './variableResolver';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { Toast } from '@shoutem/ui';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
|
|
5
|
+
const isWeb = Platform.OS === 'web';
|
|
6
|
+
|
|
7
|
+
export const unavailableInWeb = callback => {
|
|
8
|
+
if (!isWeb) {
|
|
9
|
+
return callback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// If callback is undefined, show toast immediatelly.
|
|
13
|
+
if (_.isUndefined(callback)) {
|
|
14
|
+
Toast.showInfo({
|
|
15
|
+
title: 'Feature currently unavailable',
|
|
16
|
+
message:
|
|
17
|
+
'This feature is currently unavailable in Web Preview. For better preview experience download Disclose app.',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Otherwise, return callback to be executed on user action.
|
|
22
|
+
|
|
23
|
+
return () =>
|
|
24
|
+
Toast.showInfo({
|
|
25
|
+
title: 'Feature currently unavailable',
|
|
26
|
+
message:
|
|
27
|
+
'This feature is currently unavailable in Web Preview. For better preview experience download Disclose app.',
|
|
28
|
+
});
|
|
29
|
+
};
|
package/theme.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Dimensions,
|
|
1
|
+
import { Dimensions, StyleSheet } from 'react-native';
|
|
2
2
|
import {
|
|
3
3
|
changeColorAlpha,
|
|
4
4
|
createSharedStyle,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
STATUS_BAR_OFFSET,
|
|
17
17
|
} from './const';
|
|
18
18
|
import { Device } from './helpers';
|
|
19
|
-
import { resolveVariable } from './services';
|
|
19
|
+
import { isAndroid, isIos, resolveVariable } from './services';
|
|
20
20
|
|
|
21
21
|
const window = Dimensions.get('window');
|
|
22
22
|
|
|
@@ -72,7 +72,7 @@ export function resolveFontFamily(
|
|
|
72
72
|
fontWeight = 'normal',
|
|
73
73
|
fontStyle = 'normal',
|
|
74
74
|
) {
|
|
75
|
-
if (
|
|
75
|
+
if (isIos) {
|
|
76
76
|
return fontName;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -103,22 +103,22 @@ export function resolveFontFamily(
|
|
|
103
103
|
// being provided to fontWeight will cause the default system font to be used, so we conditionally
|
|
104
104
|
// resolve it.
|
|
105
105
|
export function resolveFontWeight(fontWeight) {
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
106
|
+
if (!isIos) {
|
|
107
|
+
return 'normal';
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
return
|
|
110
|
+
return fontWeight;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// Currently, resolveFontFamily will provide fontStyle styling, but any value other than 'normal'
|
|
114
114
|
// being provided to fontStyle will cause the default system font to be used, so we conditionally
|
|
115
115
|
// resolve it.
|
|
116
116
|
export function resolveFontStyle(fontStyle) {
|
|
117
|
-
if (
|
|
118
|
-
return
|
|
117
|
+
if (!isIos) {
|
|
118
|
+
return 'normal';
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
return
|
|
121
|
+
return fontStyle;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// This function is deprecated and replaced with calculateLineHeight.
|
|
@@ -1819,7 +1819,7 @@ export default () => {
|
|
|
1819
1819
|
top: Device.select({
|
|
1820
1820
|
iPhoneX: 6,
|
|
1821
1821
|
iPhoneXR: 8,
|
|
1822
|
-
default:
|
|
1822
|
+
default: isAndroid ? 0 : -4,
|
|
1823
1823
|
}),
|
|
1824
1824
|
left: 0,
|
|
1825
1825
|
right: 0,
|
|
@@ -2427,7 +2427,7 @@ export default () => {
|
|
|
2427
2427
|
number: {
|
|
2428
2428
|
// Font should be monospace so that item content has same offset
|
|
2429
2429
|
// Can not apply width to the Text for some reason
|
|
2430
|
-
fontFamily:
|
|
2430
|
+
fontFamily: isIos ? 'Menlo-Regular' : 'monospace',
|
|
2431
2431
|
fontSize: 12,
|
|
2432
2432
|
},
|
|
2433
2433
|
bullet: {},
|
|
@@ -2475,8 +2475,7 @@ export default () => {
|
|
|
2475
2475
|
'shoutem.ui.Video': {
|
|
2476
2476
|
container: {
|
|
2477
2477
|
backgroundColor: resolveVariable('paperColor'),
|
|
2478
|
-
|
|
2479
|
-
height: 240,
|
|
2478
|
+
height: responsiveHeight(240),
|
|
2480
2479
|
},
|
|
2481
2480
|
},
|
|
2482
2481
|
|
|
@@ -2714,13 +2713,7 @@ export default () => {
|
|
|
2714
2713
|
|
|
2715
2714
|
'shoutem.ui.ActionSheet': {
|
|
2716
2715
|
container: {
|
|
2717
|
-
|
|
2718
|
-
top: 0,
|
|
2719
|
-
bottom: 0,
|
|
2720
|
-
left: 0,
|
|
2721
|
-
right: 0,
|
|
2722
|
-
flexDirection: 'column',
|
|
2723
|
-
justifyContent: 'flex-end',
|
|
2716
|
+
backgroundColor: 'transparent',
|
|
2724
2717
|
},
|
|
2725
2718
|
contentContainer: {
|
|
2726
2719
|
marginHorizontal: 8,
|
|
@@ -2740,17 +2733,22 @@ export default () => {
|
|
|
2740
2733
|
paddingVertical: 16,
|
|
2741
2734
|
borderBottomWidth: 1,
|
|
2742
2735
|
borderColor: 'rgba(130, 130, 130, 0.1)',
|
|
2736
|
+
alignItems: 'center',
|
|
2743
2737
|
},
|
|
2744
2738
|
text: {
|
|
2745
2739
|
fontSize: 15,
|
|
2746
2740
|
letterSpacing: 0.38,
|
|
2747
|
-
color: '
|
|
2741
|
+
color: resolveVariable('primaryButtonText.color'),
|
|
2748
2742
|
lineHeight: 24,
|
|
2749
2743
|
},
|
|
2750
2744
|
cancelText: {
|
|
2745
|
+
color: resolveVariable('errorText.color'),
|
|
2751
2746
|
textAlign: 'center',
|
|
2752
2747
|
fontWeight: resolveFontWeight('700'),
|
|
2753
2748
|
},
|
|
2749
|
+
iosBlueTextColor: {
|
|
2750
|
+
color: '#007AFF',
|
|
2751
|
+
},
|
|
2754
2752
|
},
|
|
2755
2753
|
|
|
2756
2754
|
//
|
|
@@ -3132,7 +3130,6 @@ export default () => {
|
|
|
3132
3130
|
},
|
|
3133
3131
|
textContainer: {
|
|
3134
3132
|
flex: 1,
|
|
3135
|
-
height: responsiveHeight(48),
|
|
3136
3133
|
justifyContent: 'space-between',
|
|
3137
3134
|
},
|
|
3138
3135
|
title: {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
.alert-container {
|
|
2
|
+
border-bottom: 1px solid rgba(0,0,0,0.1);
|
|
3
|
+
font-size: 15px;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.alert-popup {
|
|
7
|
+
border-radius: 18px;
|
|
8
|
+
padding-bottom: 0px;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.alert-title {
|
|
13
|
+
color: black;
|
|
14
|
+
font-size: 20px;
|
|
15
|
+
margin-top: 10px;
|
|
16
|
+
margin-bottom: -10px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.alert-actions {
|
|
20
|
+
border-top: 1px solid #DEDEDE;
|
|
21
|
+
margin: 20px 0px 0px 0px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.alert-confirm {
|
|
25
|
+
color: cornflowerblue;
|
|
26
|
+
background-color: transparent;
|
|
27
|
+
margin: 0;
|
|
28
|
+
height: 100%;
|
|
29
|
+
border-radius: 0;
|
|
30
|
+
padding: 15px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.alert-cancel {
|
|
34
|
+
color: cornflowerblue;
|
|
35
|
+
background-color: transparent;
|
|
36
|
+
margin: 0;
|
|
37
|
+
height: 100%;
|
|
38
|
+
border-radius: 0;
|
|
39
|
+
padding: 15px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.alert-deny {
|
|
43
|
+
color: red;
|
|
44
|
+
background-color: transparent;
|
|
45
|
+
margin: 0;
|
|
46
|
+
height: 100%;
|
|
47
|
+
border-radius: 0;
|
|
48
|
+
padding: 15px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.full-width {
|
|
52
|
+
width: 100%;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.half-width {
|
|
56
|
+
width: 50%;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.button-height {
|
|
60
|
+
height: 50px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.border-right {
|
|
64
|
+
border-right: 1px solid #DEDEDE;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.border-bottom {
|
|
68
|
+
border-bottom: 1px solid #DEDEDE;
|
|
69
|
+
}
|