@shoutem/ui 5.4.0 → 5.6.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/README.md CHANGED
@@ -20,84 +20,6 @@ All the documentation is available on the [Developer portal](http://shoutem.gith
20
20
 
21
21
  Join [our community](https://www.facebook.com/groups/shoutem.community/) on Facebook. Also, feel free to ask a question on Stack Overflow using ["shoutem" tag](http://stackoverflow.com/tags/shoutem).
22
22
 
23
- ## Examples
24
-
25
- To see how Shoutem UI works, you can:
26
-
27
- - include the `Examples` component into your React Native app or
28
- - run `Restaurants` app in `examples` folder.
29
-
30
- ### Examples component
31
-
32
- **If you are using Expo, see [this
33
- project](https://github.com/shoutem/ui/blob/develop/examples/create-react-native-app/App.js)
34
- for example usage. Otherwise, follow the steps below.**
35
-
36
- Create new React Native project and locate to it:
37
-
38
- ```bash
39
- $ react-native init HelloWorld && cd HelloWorld
40
- ```
41
-
42
- Install `@shoutem/ui` in your project:
43
-
44
- ```bash
45
- $ npm install --save @shoutem/ui
46
- ```
47
-
48
- Now, simply copy the following to your `App.js` files of the React Native project:
49
-
50
- ```JavaScript
51
- import React, { PureComponent } from 'react';
52
- import { Examples } from '@shoutem/ui';
53
-
54
- export default class App extends PureComponent {
55
- render() {
56
- return (
57
- <Examples />
58
- );
59
- }
60
- }
61
- ```
62
-
63
- Finally, run the app!
64
-
65
- ```bash
66
- $ react-native run-ios
67
- ```
68
-
69
- To see other components, just import them from `@shoutem/ui` and render them.
70
-
71
- You can also use standard React Native components in your layouts anywhere you want, but they will not inherit either the theme or the parent styles, so you will need to style them manually.
72
-
73
- ### Restaurants app
74
-
75
- Clone the [Shoutem UI](https://github.com/shoutem/ui) repository:
76
-
77
- ```bash
78
- git clone https://github.com/shoutem/ui.git
79
- ```
80
-
81
- Locate to `RestaurantsApp` folder:
82
-
83
- ```bash
84
- cd ui/examples/RestaurantsApp
85
- ```
86
-
87
- Install and link dependencies:
88
-
89
- ```bash
90
- npm install
91
- react-native link
92
- ```
93
-
94
- Finally, run the app!
95
-
96
- ```bash
97
- react-native run-ios
98
- react-native run-android
99
- ```
100
-
101
23
  ## UI Toolkit
102
24
 
103
25
  Shoutem UI is a part of the [Shoutem UI Toolkit](https://shoutem.github.io/ui/) that enables you to build professional looking React Native apps with ease.
@@ -54,7 +54,7 @@ class DropDownMenu extends PureComponent {
54
54
  <View style={style.horizontalContainer}>
55
55
  <Button onPress={this.collapse} style={style.selectedOption}>
56
56
  {showSelectedOption && <Text>{selectedOption[titleProperty]}</Text>}
57
- <Icon name={iconName} />
57
+ <Icon name={iconName} style={style.icon} />
58
58
  </Button>
59
59
  </View>
60
60
  ) : null;
@@ -65,11 +65,11 @@ class NumberInput extends PureComponent {
65
65
  return (
66
66
  <View style={style.container} styleName="horizontal">
67
67
  <Button
68
- style={style.button}
68
+ style={[style.button, style.buttonLeft]}
69
69
  styleName="secondary"
70
70
  onPress={this.decreaseValue}
71
71
  >
72
- <Icon name="minus-button" style={style.icon} />
72
+ <Icon name="minus-button" style={[style.icon, style.iconLeft]} />
73
73
  </Button>
74
74
  <View style={style.inputContainer} styleName="horizontal">
75
75
  <TextInput
@@ -80,11 +80,11 @@ class NumberInput extends PureComponent {
80
80
  />
81
81
  </View>
82
82
  <Button
83
- style={style.button}
83
+ style={[style.button, style.buttonRight]}
84
84
  styleName="secondary"
85
85
  onPress={this.increaseValue}
86
86
  >
87
- <Icon name="plus-button" style={style.icon} />
87
+ <Icon name="plus-button" style={[style.icon, style.iconRight]} />
88
88
  </Button>
89
89
  </View>
90
90
  );
@@ -24,7 +24,7 @@ class ShareButton extends PureComponent {
24
24
  onShare() {
25
25
  const { title, message, url } = this.props;
26
26
 
27
- Share.share({
27
+ return Share.share({
28
28
  title,
29
29
  // URL property isn't supported on Android, so we are
30
30
  // including it as the message for now.
@@ -34,7 +34,11 @@ class ShareButton extends PureComponent {
34
34
  }
35
35
 
36
36
  render() {
37
- const { animationName, iconProps, ...otherProps } = this.props;
37
+ const { animationName, iconProps, url, ...otherProps } = this.props;
38
+
39
+ if (!url) {
40
+ return null;
41
+ }
38
42
 
39
43
  return (
40
44
  <Button onPress={this.onShare} {...otherProps}>
@@ -12,26 +12,91 @@ class TabMenu extends PureComponent {
12
12
  super(props);
13
13
 
14
14
  autoBindReact(this);
15
+
16
+ this.state = {
17
+ itemWidths: {},
18
+ scrollOffset: 0,
19
+ };
15
20
  }
16
21
 
17
- handleOptionSelected(option) {
22
+ handleOptionSelected(key, option) {
18
23
  const { onOptionSelected } = this.props;
24
+ const { scrollOffset, itemWidths, viewportHorizontal } = this.state;
25
+
26
+ let itemStart = 0;
27
+
28
+ _.forEach(itemWidths, (itemWidth, index) => {
29
+ if (index < key) {
30
+ itemStart += itemWidth;
31
+ }
32
+ });
33
+
34
+ const itemEnd = itemStart + itemWidths[key];
19
35
 
20
36
  LayoutAnimation.easeInEaseOut();
21
37
  onOptionSelected(option);
38
+
39
+ if (itemEnd > _.last(viewportHorizontal)) {
40
+ const extraScroll = itemEnd - _.last(viewportHorizontal);
41
+
42
+ this.scroll.scrollTo({
43
+ x: extraScroll + scrollOffset,
44
+ y: 0,
45
+ animated: true,
46
+ });
47
+ }
48
+
49
+ if (itemStart < _.head(viewportHorizontal)) {
50
+ const extraScroll = _.head(viewportHorizontal) - itemStart;
51
+
52
+ this.scroll.scrollTo({
53
+ x: scrollOffset - extraScroll,
54
+ y: 0,
55
+ animated: true,
56
+ });
57
+ }
58
+ }
59
+
60
+ handleItemLayout(key, width) {
61
+ const { itemWidths } = this.state;
62
+
63
+ this.setState({
64
+ itemWidths: {
65
+ ...itemWidths,
66
+ [key]: width,
67
+ },
68
+ });
69
+ }
70
+
71
+ handleScroll({
72
+ nativeEvent: {
73
+ contentOffset: { x },
74
+ layoutMeasurement: { width },
75
+ },
76
+ }) {
77
+ this.setState({ scrollOffset: x, viewportHorizontal: [x, x + width] });
78
+ }
79
+
80
+ handleScrollLayout({
81
+ nativeEvent: {
82
+ layout: { width },
83
+ },
84
+ }) {
85
+ this.setState({ viewportHorizontal: [0, width] });
22
86
  }
23
87
 
24
- renderOption(option) {
88
+ renderOption(option, key) {
25
89
  const { selectedOption } = this.props;
26
90
 
27
91
  const isSelected = selectedOption && option.title === selectedOption.title;
28
92
 
29
93
  return (
30
94
  <TabMenuItem
31
- key={option.title}
95
+ key={key}
32
96
  isSelected={isSelected}
33
97
  item={option}
34
- onItemPressed={this.handleOptionSelected}
98
+ onItemPressed={option => this.handleOptionSelected(key, option)}
99
+ onLayoutMeasured={width => this.handleItemLayout(key, width)}
35
100
  />
36
101
  );
37
102
  }
@@ -43,6 +108,11 @@ class TabMenu extends PureComponent {
43
108
  <ScrollView
44
109
  horizontal
45
110
  contentContainerStyle={style.container}
111
+ onScroll={this.handleScroll}
112
+ onLayout={this.handleScrollLayout}
113
+ ref={ref => {
114
+ this.scroll = ref;
115
+ }}
46
116
  showsHorizontalScrollIndicator={false}
47
117
  style={style.list}
48
118
  >
@@ -1,7 +1,6 @@
1
1
  import React, { PureComponent } from 'react';
2
2
  import { LayoutAnimation } from 'react-native';
3
3
  import autoBindReact from 'auto-bind/react';
4
- import _ from 'lodash';
5
4
  import PropTypes from 'prop-types';
6
5
  import { connectStyle } from '@shoutem/theme';
7
6
  import { Text } from '../Text';
@@ -26,7 +25,7 @@ class TabMenuItem extends PureComponent {
26
25
  onItemPressed(item);
27
26
  }
28
27
 
29
- handleLayout({
28
+ handleTextLayout({
30
29
  nativeEvent: {
31
30
  layout: { width },
32
31
  },
@@ -35,6 +34,16 @@ class TabMenuItem extends PureComponent {
35
34
  this.setState({ baseWidth: width });
36
35
  }
37
36
 
37
+ handleContainerLayout({
38
+ nativeEvent: {
39
+ layout: { width },
40
+ },
41
+ }) {
42
+ const { onLayoutMeasured } = this.props;
43
+
44
+ onLayoutMeasured(width);
45
+ }
46
+
38
47
  render() {
39
48
  const { style, isSelected, item } = this.props;
40
49
  const { baseWidth } = this.state;
@@ -42,11 +51,12 @@ class TabMenuItem extends PureComponent {
42
51
  return (
43
52
  <TouchableOpacity
44
53
  onPress={this.handleItemPressed}
54
+ onLayout={this.handleContainerLayout}
45
55
  styleName="vertical h-center"
46
56
  >
47
57
  <Text
48
58
  style={[style.text, isSelected && style.selectedText]}
49
- onLayout={this.handleLayout}
59
+ onLayout={this.handleTextLayout}
50
60
  >
51
61
  {item.title}
52
62
  </Text>
@@ -58,6 +68,7 @@ class TabMenuItem extends PureComponent {
58
68
 
59
69
  TabMenuItem.propTypes = {
60
70
  style: PropTypes.object.isRequired,
71
+ onLayoutMeasured: PropTypes.func.isRequired,
61
72
  isSelected: PropTypes.bool,
62
73
  item: optionShape,
63
74
  onItemPressed: PropTypes.func,
package/index.js CHANGED
@@ -72,9 +72,6 @@ export { Video } from './components/Video';
72
72
  export { View } from './components/View';
73
73
  export { YearRangePicker } from './components/YearRangePicker';
74
74
 
75
- // Examples
76
- export { Examples } from './examples/components';
77
-
78
75
  // Helpers
79
76
  export { calculateKeyboardOffset, Device, Keyboard } from './helpers';
80
77
 
@@ -89,7 +86,6 @@ export {
89
86
  IPHONE_X_NOTCH_PADDING,
90
87
  IPHONE_XR_LONG_SIDE,
91
88
  IPHONE_XR_NOTCH_PADDING,
92
- nativeDependencies,
93
89
  NAVIGATION_BAR_HEIGHT,
94
90
  NAVIGATION_HEADER_HEIGHT,
95
91
  STATUS_BAR_OFFSET,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shoutem/ui",
3
- "version": "5.4.0",
3
+ "version": "5.6.0",
4
4
  "description": "Styleable set of components for React Native applications",
5
5
  "scripts": {
6
6
  "lint": "eslint .",
@@ -121,8 +121,7 @@
121
121
  "jest": {
122
122
  "preset": "react-native",
123
123
  "testPathIgnorePatterns": [
124
- "/node_modules/",
125
- "examples/"
124
+ "/node_modules/"
126
125
  ],
127
126
  "transformIgnorePatterns": [
128
127
  "!/node_modules/@shoutem"
package/theme.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Dimensions, Platform, StatusBar, StyleSheet } from 'react-native';
1
+ import { Dimensions, Platform, StyleSheet } from 'react-native';
2
2
  import {
3
3
  changeColorAlpha,
4
4
  createSharedStyle,
@@ -2899,7 +2899,6 @@ export default () => {
2899
2899
  borderRadius: 1,
2900
2900
  flexDirection: 'row',
2901
2901
  marginBottom: 8,
2902
- marginLeft: 8,
2903
2902
  },
2904
2903
  text: {
2905
2904
  marginTop: 12,
@@ -1,95 +0,0 @@
1
- import React from 'react';
2
- import { Button } from '../../components/Button';
3
- import { Icon } from '../../components/Icon';
4
- import { Text } from '../../components/Text';
5
- import { View } from '../../components/View';
6
- import { Stage } from './Stage';
7
-
8
- export function Buttons() {
9
- return (
10
- <View styleName="vertical collapsed">
11
- <Stage title="Button / Text only / Light">
12
- <Button>
13
- <Text>CHECK IN HERE</Text>
14
- </Button>
15
- </Stage>
16
-
17
- <Stage title="Button / Text only / Dark">
18
- <Button styleName="secondary">
19
- <Text>CHECK IN HERE</Text>
20
- </Button>
21
- </Stage>
22
-
23
- <Stage title="Button / Icon + Text / Light">
24
- <Button>
25
- <Icon name="add-event" />
26
- <Text>ADD TO CALENDAR</Text>
27
- </Button>
28
- </Stage>
29
-
30
- <Stage title="Button / Icon + Text / Dark">
31
- <Button styleName="secondary">
32
- <Icon name="add-event" />
33
- <Text>ADD TO CALENDAR</Text>
34
- </Button>
35
- </Stage>
36
-
37
- <Stage title="Button / Fixed size">
38
- <View styleName="horizontal flexible">
39
- <Button styleName="confirmation">
40
- <Text>REMOVE</Text>
41
- </Button>
42
-
43
- <Button styleName="confirmation secondary">
44
- <Text>UPDATE</Text>
45
- </Button>
46
- </View>
47
- </Stage>
48
-
49
- <Stage title="Button / Full width">
50
- <Button styleName="full-width">
51
- <Text>SEE ALL COMMENTS</Text>
52
- </Button>
53
- </Stage>
54
-
55
- <Stage title="Button / Navbar">
56
- <Button styleName="textual">
57
- <Icon name="add-to-favorites-on" />
58
- </Button>
59
- </Stage>
60
-
61
- <Stage title="Button / Vertical / Icon + Text">
62
- <Button styleName="stacked textual">
63
- <Icon name="tweet" />
64
- <Text>Text description</Text>
65
- </Button>
66
- </Stage>
67
-
68
- <Stage title="Button / Full width - Muted">
69
- <View styleName="horizontal flexible">
70
- <Button styleName="full-width muted">
71
- <Icon name="add-to-favorites-on" />
72
- <Text>LIKE</Text>
73
- </Button>
74
- <Button styleName="full-width muted">
75
- <Icon name="comment" />
76
- <Text>COMMENT</Text>
77
- </Button>
78
- </View>
79
- </Stage>
80
-
81
- <Stage title="Button / Full width - Active">
82
- <View styleName="horizontal flexible">
83
- <Button styleName="full-width">
84
- <Icon name="add-to-favorites-on" />
85
- <Text>LIKE</Text>
86
- </Button>
87
- <Button styleName="full-width">
88
- <Icon name="comment" />
89
- <Text>COMMENT</Text>
90
- </Button>
91
- </View>
92
- </Stage>
93
- </View>
94
- );
95
- }
@@ -1,79 +0,0 @@
1
- import React from 'react';
2
- import { Button } from '../../components/Button';
3
- import { Card } from '../../components/Card';
4
- import { Icon } from '../../components/Icon';
5
- import { Image } from '../../components/Image';
6
- import { Caption, Subtitle } from '../../components/Text';
7
- import { View } from '../../components/View';
8
- import { Stage } from './Stage';
9
-
10
- export function Cards() {
11
- return (
12
- <View styleName="vertical collapsed">
13
- <Stage title="Card">
14
- <Card>
15
- <Image
16
- styleName="medium-wide"
17
- source={{
18
- uri:
19
- 'https://shoutem.github.io/img/ui-toolkit/examples/image-10.png',
20
- }}
21
- />
22
- <View styleName="content">
23
- <Subtitle>Choosing The Right Boutique Hotel For You</Subtitle>
24
- <Caption>21 hours ago</Caption>
25
- </View>
26
- </Card>
27
- </Stage>
28
-
29
- <Stage title="Card + Icon">
30
- <Card>
31
- <Image
32
- styleName="medium-wide"
33
- source={{
34
- uri:
35
- 'https://shoutem.github.io/img/ui-toolkit/examples/image-12.png',
36
- }}
37
- />
38
- <View styleName="content">
39
- <Subtitle>Choosing The Right Boutique Hotel For You</Subtitle>
40
- <View styleName="horizontal v-center space-between">
41
- <Caption>Dec 21, 13:45</Caption>
42
- <Button styleName="tight clear">
43
- <Icon name="add-event" />
44
- </Button>
45
- </View>
46
- </View>
47
- </Card>
48
- </Stage>
49
-
50
- <Stage title="Card + Icon (Shop)">
51
- <Card>
52
- <Image
53
- styleName="medium-wide"
54
- source={{
55
- uri:
56
- 'https://shoutem.github.io/img/ui-toolkit/examples/image-11.png',
57
- }}
58
- />
59
- <View styleName="content">
60
- <Subtitle>Choosing The Right Boutique Hotel For You</Subtitle>
61
- <View styleName="horizontal v-center space-between">
62
- <View styleName="horizontal">
63
- <Subtitle styleName="md-gutter-right">$99.99</Subtitle>
64
- <Caption styleName="line-through">$120.00</Caption>
65
- </View>
66
- {
67
- // TODO (zeljko): Should we add an IconButton?
68
- // <IconButton icon="cart" onPress={...} />
69
- }
70
- <Button styleName="tight clear">
71
- <Icon name="cart" />
72
- </Button>
73
- </View>
74
- </View>
75
- </Card>
76
- </Stage>
77
- </View>
78
- );
79
- }
@@ -1,36 +0,0 @@
1
- import React from 'react';
2
- import { Divider } from '../../components/Divider';
3
- import { Caption } from '../../components/Text';
4
- import { View } from '../../components/View';
5
- import { Stage } from './Stage';
6
-
7
- export function Dividers() {
8
- return (
9
- <View styleName="vertical collapsed">
10
- <Stage title="Divider">
11
- <Divider />
12
- </Stage>
13
-
14
- <Stage title="Line divider">
15
- <Divider styleName="line" />
16
- </Stage>
17
-
18
- <Stage title="Section divider">
19
- <Divider styleName="section-header" />
20
- </Stage>
21
-
22
- <Stage title="Section divider + label">
23
- <Divider styleName="section-header">
24
- <Caption>INFORMATION</Caption>
25
- </Divider>
26
- </Stage>
27
-
28
- <Stage title="Section divider + label + caption">
29
- <Divider styleName="section-header">
30
- <Caption>PRODUCT NAME</Caption>
31
- <Caption>PRICE</Caption>
32
- </Divider>
33
- </Stage>
34
- </View>
35
- );
36
- }
@@ -1,93 +0,0 @@
1
- import React, { PureComponent } from 'react';
2
- import { DropDownMenu } from '../../components/DropDownMenu';
3
- import { FormGroup } from '../../components/FormGroup';
4
- import { Caption } from '../../components/Text';
5
- import { View } from '../../components/View';
6
- import { Stage } from './Stage';
7
-
8
- const options = [
9
- {
10
- name: 'Lifestyle',
11
- id: '7',
12
- },
13
- {
14
- name: 'Food',
15
- id: '8',
16
- },
17
- {
18
- name: 'Nature',
19
- id: '9',
20
- },
21
- ];
22
-
23
- const emptyOption = { id: '', name: 'Select' };
24
- const optionsWithEmptyOption = [emptyOption, ...options];
25
-
26
- export class DropDownMenus extends PureComponent {
27
- constructor() {
28
- super();
29
- this.state = {
30
- selectedOption: options[0],
31
- selectedOptionForFormGroupDropdown: emptyOption,
32
- };
33
- }
34
-
35
- render() {
36
- const { selectedOption, selectedOptionForFormGroupDropdown } = this.state;
37
-
38
- return (
39
- <View styleName="vertical collapsed">
40
- <Stage title="Dropdown">
41
- <DropDownMenu
42
- options={options}
43
- selectedOption={selectedOption}
44
- onOptionSelected={option =>
45
- this.setState({ selectedOption: option })
46
- }
47
- titleProperty="name"
48
- valueProperty="id"
49
- />
50
- </Stage>
51
- <Stage title="Dropdown (horizontal)">
52
- <DropDownMenu
53
- styleName="horizontal"
54
- options={options}
55
- selectedOption={selectedOption}
56
- onOptionSelected={option =>
57
- this.setState({ selectedOption: option })
58
- }
59
- titleProperty="name"
60
- valueProperty="id"
61
- />
62
- </Stage>
63
- <Stage title="Dropdown (large)">
64
- <DropDownMenu
65
- styleName="large"
66
- options={options}
67
- selectedOption={selectedOption}
68
- onOptionSelected={option =>
69
- this.setState({ selectedOption: option })
70
- }
71
- titleProperty="name"
72
- valueProperty="id"
73
- />
74
- </Stage>
75
- <Stage title="Dropdown (inside a form group)">
76
- <FormGroup styleName="stretch">
77
- <Caption>CATEGORY</Caption>
78
- <DropDownMenu
79
- styleName={selectedOptionForFormGroupDropdown.id ? '' : 'empty'}
80
- options={optionsWithEmptyOption}
81
- selectedOption={selectedOptionForFormGroupDropdown}
82
- onOptionSelected={option =>
83
- this.setState({ selectedOptionForFormGroupDropdown: option })
84
- }
85
- titleProperty="name"
86
- valueProperty="id"
87
- />
88
- </FormGroup>
89
- </Stage>
90
- </View>
91
- );
92
- }
93
- }