@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.
@@ -1,183 +1,74 @@
1
- import React, { PureComponent } from 'react';
2
- import { Animated, Dimensions, TouchableOpacity } from 'react-native';
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 { height: windowHeight } = Dimensions.get('window');
13
-
14
- function getContentOffset(contentHeight = windowHeight) {
15
- if (Device.isIphoneXR || Device.isIphoneX) {
16
- return contentHeight + IPHONE_X_HOME_INDICATOR_PADDING;
17
- }
18
-
19
- return contentHeight;
20
- }
21
-
22
- const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
23
-
24
- class ActionSheet extends PureComponent {
25
- static getDerivedStateFromProps(props, state) {
26
- const { active } = props;
27
- const { visible } = state;
28
-
29
- if (active !== visible) {
30
- return { visible: active };
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
- return null;
34
- }
35
-
36
- constructor(props) {
37
- super(props);
38
-
39
- autoBindReact(this);
40
-
41
- this.state = {
42
- opacity: new Animated.Value(0),
43
- yPos: new Animated.Value(0),
44
- visible: false,
45
- };
46
- }
47
-
48
- componentDidUpdate(prevProps) {
49
- const { active } = this.props;
50
- const { active: prevActive } = prevProps;
51
-
52
- if (active && !prevActive) {
53
- this.handleEntryAnimation();
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(confirmOptions, option => (
159
- <ActionSheetOption key={option.title} option={option} />
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
- {hasCancelOptions && (
164
- <View styleName="md-gutter-top">
165
- <View style={style.segmentContainer}>
166
- {_.map(cancelOptions, option => (
167
- <ActionSheetOption
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 style={[style.text, cancelOption && style.cancelText]}>
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={[style.image, imageStyle]} />
43
- <Title styleName="title" style={[style.title, titleStyle]}>
58
+ <EmptyStateImage style={resolvedImageStyle} />
59
+ <Title styleName="title" style={resolvedTitleStyle}>
44
60
  {title}
45
61
  </Title>
46
- <Text styleName="description" style={[style.message, messageStyle]}>
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>
@@ -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
- if (isLoading) {
41
- if (state.status !== Status.IDLE) {
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
- return { status: Status.IDLE };
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}
@@ -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
- <View style={style.container}>
9
- <RNSwitch
10
- trackColor={style.track}
11
- thumbColor={style.thumb}
12
- ios_backgroundColor={style.track?.false}
13
- onValueChange={onValueChange}
14
- value={value}
15
- />
16
- </View>
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,
@@ -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
@@ -44,7 +44,7 @@ class Video extends PureComponent {
44
44
  }
45
45
 
46
46
  render() {
47
- const { width, height, style, poster } = this.props;
47
+ const { width, height = '100%', style, poster } = this.props;
48
48
 
49
49
  return (
50
50
  <View style={style.container}>
@@ -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.2",
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": "~0.15.0",
18
+ "@shoutem/animation": "0.17.0-beta.0",
19
19
  "@shoutem/eslint-config-react": "~1.0.6",
20
- "@shoutem/theme": "~0.13.2",
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": "12.3.0",
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
- ThemeVariableResolver,
4
+ createScopedResolver,
3
5
  defaultResolver,
4
6
  resolveVariable,
5
- createScopedResolver,
7
+ ThemeVariableResolver,
6
8
  } from './variableResolver';
@@ -0,0 +1,5 @@
1
+ import { Platform } from 'react-native';
2
+
3
+ export const isAndroid = Platform.OS === 'android';
4
+ export const isIos = Platform.OS === 'ios';
5
+ export const isWeb = Platform.OS === 'web';
@@ -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, Platform, StyleSheet } from 'react-native';
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 (Platform.OS === 'ios') {
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 (Platform.OS === 'ios') {
107
- return fontWeight;
106
+ if (!isIos) {
107
+ return 'normal';
108
108
  }
109
109
 
110
- return 'normal';
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 (Platform.OS === 'ios') {
118
- return fontStyle;
117
+ if (!isIos) {
118
+ return 'normal';
119
119
  }
120
120
 
121
- return 'normal';
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: Platform.OS === 'android' ? 0 : -4,
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: Platform.OS === 'ios' ? 'Menlo-Regular' : 'monospace',
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
- flex: 1,
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
- position: 'absolute',
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: '#000000',
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
+ }