@widergy/mobile-ui 1.3.4 → 1.4.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.4.1](https://github.com/widergy/mobile-ui/compare/v1.4.0...v1.4.1) (2024-04-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * minor fixes ([#271](https://github.com/widergy/mobile-ui/issues/271)) ([4cf7246](https://github.com/widergy/mobile-ui/commit/4cf724618ced022d4dbe515bf90395875957442d))
7
+
8
+ # [1.4.0](https://github.com/widergy/mobile-ui/compare/v1.3.4...v1.4.0) (2024-03-26)
9
+
10
+
11
+ ### Features
12
+
13
+ * added UTProductItem ([#268](https://github.com/widergy/mobile-ui/issues/268)) ([c28b3e8](https://github.com/widergy/mobile-ui/commit/c28b3e83e9f3aea060857778dc15d5537aca7702))
14
+
1
15
  ## [1.3.4](https://github.com/widergy/mobile-ui/compare/v1.3.3...v1.3.4) (2024-03-19)
2
16
 
3
17
 
@@ -10,14 +10,16 @@ import propTypes from './propTypes';
10
10
  const IconButton = ({
11
11
  color,
12
12
  disabled,
13
+ effectColor,
14
+ height,
15
+ iconMargin,
16
+ iconStyle,
13
17
  name,
14
18
  onPress,
15
- effectColor,
16
19
  size,
17
20
  style,
18
- iconStyle,
19
21
  type,
20
- iconMargin
22
+ width
21
23
  }) => (
22
24
  <Touchable
23
25
  borderless
@@ -26,15 +28,15 @@ const IconButton = ({
26
28
  effectColor={effectColor}
27
29
  style={[styles.container, getContainerSize(size, iconMargin), style]}
28
30
  >
29
- <Icon style={iconStyle} color={color} name={name} size={size} type={type} />
31
+ <Icon style={iconStyle} {...{ color, height, name, size, type, width }} />
30
32
  </Touchable>
31
33
  );
32
34
 
33
35
  IconButton.propTypes = propTypes;
34
36
 
35
37
  IconButton.defaultProps = {
36
- size: DEFAULT_ICON_SIZE,
37
- iconMargin: ICON_MARGIN
38
+ iconMargin: ICON_MARGIN,
39
+ size: DEFAULT_ICON_SIZE
38
40
  };
39
41
 
40
42
  export default IconButton;
@@ -15,26 +15,27 @@ import ownStyles from './styles';
15
15
  import { CLOSE_ICON } from './constants';
16
16
 
17
17
  const UTModal = ({
18
- children,
19
- onRequestClose,
20
18
  acceptButton,
21
- cancelButton,
22
- visible,
23
- title,
24
- subtitle,
25
- subtitleProps = {},
26
- loading,
19
+ backgroundImg,
27
20
  backgroundStyles,
28
- imageStyles,
29
- disableTouchable,
30
- loadingText,
31
- modalBackgroundColor,
21
+ cancelButton,
22
+ children,
32
23
  color,
24
+ disableTouchable,
25
+ hideCloseButton,
26
+ hideSeparatorBar,
33
27
  imageComponent,
28
+ imageStyles,
34
29
  labelProps,
30
+ loading,
31
+ loadingText,
32
+ modalBackgroundColor,
35
33
  modalStyles,
36
- backgroundImg,
37
- hideSeparatorBar
34
+ onRequestClose,
35
+ subtitle,
36
+ subtitleProps = {},
37
+ title,
38
+ visible
38
39
  }) => {
39
40
  const theme = useTheme();
40
41
  const themeStyles = theme.simpleButton;
@@ -67,18 +68,20 @@ const UTModal = ({
67
68
  )}
68
69
  {!!imageComponent && (
69
70
  <View style={[styles.content, imageStyles]}>
70
- <Touchable
71
- borderless
72
- style={styles.closeIcon}
73
- onPress={onRequestClose || cancelButton.onPress}
74
- >
75
- <Icon name={CLOSE_ICON} color={color || 'black'} style={styles.iconSpacing} />
76
- </Touchable>
71
+ {!hideCloseButton && (
72
+ <Touchable
73
+ borderless
74
+ style={styles.closeIcon}
75
+ onPress={onRequestClose || cancelButton.onPress}
76
+ >
77
+ <Icon name={CLOSE_ICON} color={color || 'black'} style={styles.iconSpacing} />
78
+ </Touchable>
79
+ )}
77
80
  <View style={styles.imageContainer}>{imageComponent}</View>
78
81
  </View>
79
82
  )}
80
83
  <View style={styles.content}>
81
- {!backgroundImg && !imageComponent && (
84
+ {!backgroundImg && !imageComponent && !hideCloseButton && (
82
85
  <Touchable borderless style={styles.closeIcon} onPress={onRequestClose}>
83
86
  <Icon name={CLOSE_ICON} color={color || 'black'} style={styles.iconSpacing} />
84
87
  </Touchable>
@@ -6,30 +6,31 @@ import { IS_ANDROID } from '../../utils/platformUtils/constants';
6
6
 
7
7
  const buttonPropType = shape({
8
8
  backgroundColor: string,
9
- text: string,
10
- onPress: func,
11
9
  disabled: bool,
12
10
  loadingText: string,
11
+ onPress: func,
12
+ text: string,
13
13
  textColor: string
14
14
  });
15
15
 
16
16
  export default {
17
- onRequestClose: IS_ANDROID ? func.isRequired : func,
18
- cancelButton: buttonPropType,
19
17
  acceptButton: buttonPropType,
20
- visible: bool.isRequired,
21
- loading: bool,
22
- title: string,
18
+ backgroundImg: number,
19
+ backgroundStyles: ViewPropTypes.style,
20
+ cancelButton: buttonPropType,
23
21
  disableTouchable: bool,
24
- modalBackgroundColor: string,
25
- onKeyboardDismiss: func,
26
- loadingText: string,
27
22
  fill: string,
28
- imageStyles: ViewPropTypes.style,
29
- backgroundStyles: ViewPropTypes.style,
30
- modalStyles: ViewPropTypes.style,
23
+ hideCloseButton: bool,
31
24
  image: element,
32
- backgroundImg: number,
25
+ imageStyles: ViewPropTypes.style,
33
26
  labelProps: shape(bool),
34
- theme: themeType
27
+ loading: bool,
28
+ loadingText: string,
29
+ modalBackgroundColor: string,
30
+ modalStyles: ViewPropTypes.style,
31
+ onKeyboardDismiss: func,
32
+ onRequestClose: IS_ANDROID ? func.isRequired : func,
33
+ theme: themeType,
34
+ title: string,
35
+ visible: bool.isRequired
35
36
  };
@@ -0,0 +1,28 @@
1
+ # UTProductItem
2
+
3
+ ### Description
4
+
5
+ This component displays a product with its corresponding image, price and/or discounts. It also provides handlers for adding/removing the amount of products selected.
6
+
7
+ ## Props
8
+
9
+ | Name | Type | Default | Description |
10
+ | ---------------- | -------------- | ------- | ------------------------------------------------------------------------------- |
11
+ | action | actionType | | Renders a button on the rightSection of the component |
12
+ | additionalInfo | string | | Renders a smaller text under the title |
13
+ | amount | number | | Specifies the number to be displayed as a price |
14
+ | counter | counterType | | Renders a chip on the top right corner with a current number and a limit number |
15
+ | discount | number | | Renders a chip above the amount with the given discount as a percentage |
16
+ | imageProps | imagePropsType | | Renders an image at the leftmost part of the component |
17
+ | previousAmount | number | | Renders a text with strikethrough alongside the discount chip |
18
+ | secondaryAction | actionType | | Renders a button alongside the main action |
19
+ | selectedQuantity | number | | Renders a number between the action and the secondary action |
20
+ | title | string | | Renders the main text on the left section of the component |
21
+
22
+ ### Custom Types
23
+
24
+ | Type | Shape |
25
+ | -------------- | ---------------------------------------------------------------------------- |
26
+ | actionType | `{ color: string, name: string, onPress: func, size: number, type: string }` |
27
+ | counterType | `{ current: number, limit: number }` |
28
+ | imagePropsType | `{ image: element \| string, withUri: bool }` |
@@ -0,0 +1,19 @@
1
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
2
+ import { bool, string } from 'prop-types';
3
+ import React from 'react';
4
+
5
+ import Label from '../../../Label';
6
+
7
+ const AdditionalInfo = ({ additionalInfo, shouldUseGap, themedStyles }) => (
8
+ <Label small style={[themedStyles.additionalInfo, shouldUseGap ? themedStyles.gap : '']}>
9
+ {additionalInfo}
10
+ </Label>
11
+ );
12
+
13
+ AdditionalInfo.propTypes = {
14
+ additionalInfo: string,
15
+ shouldUseGap: bool,
16
+ themedStyles: ViewPropTypes.style
17
+ };
18
+
19
+ export default AdditionalInfo;
@@ -0,0 +1,17 @@
1
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
2
+ import { bool, number } from 'prop-types';
3
+ import React from 'react';
4
+
5
+ import Label from '../../../Label';
6
+
7
+ const Amount = ({ amount, shouldUseGap, themedStyles }) => (
8
+ <Label style={[themedStyles.amount, shouldUseGap ? themedStyles.gap : '']}>{`$ ${amount}`}</Label>
9
+ );
10
+
11
+ Amount.propTypes = {
12
+ amount: number,
13
+ shouldUseGap: bool,
14
+ themedStyles: ViewPropTypes.style
15
+ };
16
+
17
+ export default Amount;
@@ -0,0 +1,28 @@
1
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
2
+ import { bool, number } from 'prop-types';
3
+ import React from 'react';
4
+ import { View } from 'react-native';
5
+
6
+ import Label from '../../../Label';
7
+
8
+ const Discount = ({ previousAmount, shouldUseGap, discount, themedStyles }) => (
9
+ <View style={[themedStyles.amountContainer, shouldUseGap ? themedStyles.gap : '']}>
10
+ <View style={themedStyles.discountChip}>
11
+ <Label small style={themedStyles.discount}>
12
+ {discount}
13
+ </Label>
14
+ </View>
15
+ {previousAmount && (
16
+ <Label small color="gray" style={themedStyles.previousAmount}>{`$ ${previousAmount}`}</Label>
17
+ )}
18
+ </View>
19
+ );
20
+
21
+ Discount.propTypes = {
22
+ discount: number,
23
+ previousAmount: number,
24
+ shouldUseGap: bool,
25
+ themedStyles: ViewPropTypes.style
26
+ };
27
+
28
+ export default Discount;
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import { number } from 'prop-types';
3
+ import { View } from 'react-native';
4
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
5
+ import isEmpty from 'lodash/isEmpty';
6
+ import isNil from 'lodash/isNil';
7
+
8
+ import IconButton from '../../../IconButton';
9
+ import Label from '../../../Label';
10
+ import { ActionPropTypes } from '../../propTypes';
11
+ import ImageButton from '../../../ImageButton';
12
+ import { useTheme } from '../../../../theming';
13
+
14
+ const QuantitySelector = ({ action, secondaryAction, selectedQuantity, themedStyles }) => {
15
+ const { UTProductItem } = useTheme();
16
+ const ICON_SIZE = 24;
17
+
18
+ const renderAction = ({ color, image, name, size, type, styles, onPress }) =>
19
+ image ? (
20
+ <ImageButton image={image} size={size || ICON_SIZE} containerStyle={styles.image} onPress={onPress} />
21
+ ) : (
22
+ <IconButton
23
+ color={color || UTProductItem?.actionIconColor || '#091E42'}
24
+ height={size || ICON_SIZE}
25
+ iconMargin={8}
26
+ name={name}
27
+ onPress={onPress}
28
+ size={size || ICON_SIZE}
29
+ style={styles.icon}
30
+ type={type}
31
+ width={size || ICON_SIZE}
32
+ />
33
+ );
34
+
35
+ return (
36
+ <View style={themedStyles.actionsContainer}>
37
+ {!isEmpty(secondaryAction) &&
38
+ renderAction({
39
+ ...secondaryAction,
40
+ styles: { icon: themedStyles.secondaryActionIcon, image: themedStyles.secondaryActionImage }
41
+ })}
42
+ {!isNil(selectedQuantity) && <Label style={themedStyles.selectedQuantity}>{selectedQuantity}</Label>}
43
+ {!isEmpty(action) &&
44
+ renderAction({
45
+ ...action,
46
+ styles: { icon: themedStyles.actionIcon, image: themedStyles.actionImage }
47
+ })}
48
+ </View>
49
+ );
50
+ };
51
+
52
+ QuantitySelector.propTypes = {
53
+ action: ActionPropTypes,
54
+ secondaryAction: ActionPropTypes,
55
+ selectedQuantity: number,
56
+ themedStyles: ViewPropTypes.style
57
+ };
58
+
59
+ export default QuantitySelector;
@@ -0,0 +1,19 @@
1
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
2
+ import { bool, string } from 'prop-types';
3
+ import React from 'react';
4
+
5
+ import Label from '../../../Label';
6
+
7
+ const Title = ({ shouldUseGap, themedStyles, title }) => (
8
+ <Label medium style={[themedStyles.title, shouldUseGap ? themedStyles.gap : '']}>
9
+ {title}
10
+ </Label>
11
+ );
12
+
13
+ Title.propTypes = {
14
+ shouldUseGap: bool,
15
+ themedStyles: ViewPropTypes.style,
16
+ title: string
17
+ };
18
+
19
+ export default Title;
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import merge from 'lodash/merge';
4
+ import { bool, element, number, oneOf, shape, string } from 'prop-types';
5
+ import isEmpty from 'lodash/isEmpty';
6
+
7
+ import ImageIcon from '../ImageIcon';
8
+ import { useTheme } from '../../theming';
9
+ import Label from '../Label';
10
+
11
+ import { leftSectionComponents } from './utils';
12
+ import QuantitySelector from './components/QuantitySelector';
13
+ import { ActionPropTypes } from './propTypes';
14
+ import { getUTProductItemStyles } from './styles';
15
+
16
+ const UTProductItem = ({
17
+ action,
18
+ additionalInfo,
19
+ amount,
20
+ counter,
21
+ discount,
22
+ imageProps,
23
+ previousAmount,
24
+ secondaryAction,
25
+ selectedQuantity,
26
+ style,
27
+ title
28
+ }) => {
29
+ const theme = useTheme();
30
+ const themedStyles = merge({}, getUTProductItemStyles(theme?.UTProductItem), theme?.UTProductItem, style);
31
+
32
+ const IMAGE_SIZE = 70;
33
+
34
+ return (
35
+ <View style={themedStyles.container}>
36
+ <View style={themedStyles.leftSection}>
37
+ {!isEmpty(imageProps) && <ImageIcon style={themedStyles.image} size={IMAGE_SIZE} {...imageProps} />}
38
+ <View style={themedStyles.mainInfo}>
39
+ {leftSectionComponents({
40
+ additionalInfo,
41
+ amount,
42
+ discount,
43
+ previousAmount,
44
+ themedStyles,
45
+ title
46
+ }).map(({ Component, props }, index, array) => (
47
+ <Component key={Math.random} {...props} shouldUseGap={index !== array.length - 1} />
48
+ ))}
49
+ </View>
50
+ </View>
51
+ <View style={[themedStyles.rightSection, !isEmpty(counter) && themedStyles.spaceBetween]}>
52
+ {!isEmpty(counter) && (
53
+ <Label small style={themedStyles.counter}>{`${counter.current}/${counter.limit}`}</Label>
54
+ )}
55
+ <QuantitySelector {...{ action, secondaryAction, selectedQuantity, themedStyles }} />
56
+ </View>
57
+ </View>
58
+ );
59
+ };
60
+
61
+ UTProductItem.propTypes = {
62
+ action: ActionPropTypes,
63
+ additionalInfo: string,
64
+ amount: number,
65
+ counter: shape({ current: number, limit: number }),
66
+ discount: number,
67
+ imageProps: shape({ image: oneOf([string, element]), withUri: bool }),
68
+ previousAmount: number,
69
+ secondaryAction: ActionPropTypes,
70
+ selectedQuantity: number,
71
+ title: string
72
+ };
73
+
74
+ export default UTProductItem;
@@ -0,0 +1,9 @@
1
+ import { func, number, shape, string } from 'prop-types';
2
+
3
+ export const ActionPropTypes = shape({
4
+ color: string,
5
+ name: string,
6
+ onPress: func,
7
+ size: number,
8
+ type: string
9
+ });
@@ -0,0 +1,80 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const getUTProductItemStyles = (theme = {}) =>
4
+ StyleSheet.create({
5
+ actionsContainer: {
6
+ alignItems: 'center',
7
+ backgroundColor: theme.actionsBackground || '#E4E6EA',
8
+ borderRadius: 8,
9
+ display: 'flex',
10
+ flexDirection: 'row'
11
+ },
12
+ additionalInfo: {
13
+ color: theme.additionalInfoColor || 'gray'
14
+ },
15
+ amountContainer: {
16
+ alignItems: 'center',
17
+ display: 'flex',
18
+ flexDirection: 'row'
19
+ },
20
+ container: {
21
+ borderBottomColor: theme.separatorColor || 'gray',
22
+ borderBottomWidth: 1,
23
+ display: 'flex',
24
+ flexDirection: 'row',
25
+ justifyContent: 'space-between',
26
+ paddingVertical: 24
27
+ },
28
+ counter: {
29
+ backgroundColor: theme.counterBackground || '#EBF8FD',
30
+ borderRadius: 4,
31
+ color: theme.counterColor || '#035B83',
32
+ paddingHorizontal: 8,
33
+ paddingVertical: 4
34
+ },
35
+ discount: {
36
+ color: theme.discountColor || 'white'
37
+ },
38
+ discountChip: {
39
+ backgroundColor: theme.discountBackground || '#285AFF',
40
+ borderRadius: 4,
41
+ marginRight: 8,
42
+ paddingHorizontal: 8,
43
+ paddingVertical: 4
44
+ },
45
+ gap: {
46
+ marginBottom: 4
47
+ },
48
+ image: {
49
+ borderRadius: 14,
50
+ marginRight: 16
51
+ },
52
+ leftSection: {
53
+ alignItems: 'center',
54
+ display: 'flex',
55
+ flexDirection: 'row'
56
+ },
57
+ mainInfo: {
58
+ display: 'flex',
59
+ minWidth: 160
60
+ },
61
+ previousAmount: {
62
+ color: theme.previousAmountColor || 'gray',
63
+ textDecorationLine: 'line-through',
64
+ textDecorationStyle: 'solid'
65
+ },
66
+ rightSection: {
67
+ alignItems: 'flex-end',
68
+ display: 'flex',
69
+ justifyContent: 'center'
70
+ },
71
+ secondaryActionImage: {
72
+ marginHorizontal: 4
73
+ },
74
+ selectedQuantity: {
75
+ paddingHorizontal: 8
76
+ },
77
+ spaceBetween: {
78
+ justifyContent: 'space-between'
79
+ }
80
+ });
@@ -0,0 +1,29 @@
1
+ import Title from './components/Title';
2
+ import AdditionalInfo from './components/AdditionalInfo';
3
+ import Discount from './components/Discount';
4
+ import Amount from './components/Amount';
5
+
6
+ export const leftSectionComponents = ({
7
+ additionalInfo,
8
+ amount,
9
+ discount,
10
+ previousAmount,
11
+ themedStyles,
12
+ title
13
+ }) =>
14
+ [
15
+ { Component: Title, key: 'title', props: { themedStyles, title }, show: !!title },
16
+ {
17
+ Component: AdditionalInfo,
18
+ key: 'additionalInfo',
19
+ props: { additionalInfo, themedStyles },
20
+ show: !!additionalInfo
21
+ },
22
+ {
23
+ Component: Discount,
24
+ key: 'discount',
25
+ props: { discount, previousAmount, themedStyles },
26
+ show: !!discount
27
+ },
28
+ { Component: Amount, key: 'amount', props: { amount, themedStyles }, show: !!amount }
29
+ ].filter(elem => elem.show);
@@ -20,6 +20,7 @@ const ActionButton = ({ hidden, disabled, onPress, label, mode, style }) => {
20
20
  lowerCase
21
21
  title={label}
22
22
  onPress={onPress}
23
+ contentStyle={themedStyles.buttonContent}
23
24
  containerStyle={themedStyles.buttonContainer}
24
25
  />
25
26
  </View>
package/lib/index.js CHANGED
@@ -40,6 +40,7 @@ export { default as UTDetailDrawer } from './components/UTDetailDrawer';
40
40
  export { default as UTImage } from './components/UTImage';
41
41
  export { default as UTWorkflowContainer } from './components/UTWorkflowContainer';
42
42
  export { default as UTSelectableCard } from './components/UTSelectableCard';
43
+ export { default as UTProductItem } from './components/UTProductItem';
43
44
  export { default as UTProgressBar } from './components/UTProgressBar';
44
45
  export { default as UTOnBoarding } from './components/UTOnBoarding';
45
46
  export { default as UTRoundView } from './components/UTRoundView';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@widergy/mobile-ui",
3
3
  "description": "Widergy Mobile Components",
4
4
  "author": "widergy",
5
- "version": "1.3.4",
5
+ "version": "1.4.1",
6
6
  "repository": "https://github.com/widergy/mobile-ui.git",
7
7
  "main": "lib/index.js",
8
8
  "files": [