@widergy/mobile-ui 1.26.5 → 1.27.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/components/CheckList/index.js +4 -0
  3. package/lib/components/Checkbox/index.js +4 -0
  4. package/lib/components/UTActionCard/README.md +25 -0
  5. package/lib/components/UTActionCard/components/AdditionalMessage/index.js +28 -0
  6. package/lib/components/UTActionCard/components/AdditionalMessage/styles.js +19 -0
  7. package/lib/components/UTActionCard/components/BottomActions/index.js +62 -0
  8. package/lib/components/UTActionCard/components/BottomActions/styles.js +48 -0
  9. package/lib/components/UTActionCard/components/Header/components/Avatar/index.js +44 -0
  10. package/lib/components/UTActionCard/components/Header/components/Avatar/styles.js +9 -0
  11. package/lib/components/UTActionCard/components/Header/components/Status/index.js +33 -0
  12. package/lib/components/UTActionCard/components/Header/components/Status/styles.js +18 -0
  13. package/lib/components/UTActionCard/components/Header/constants.js +12 -0
  14. package/lib/components/UTActionCard/components/Header/index.js +101 -0
  15. package/lib/components/UTActionCard/components/Header/styles.js +30 -0
  16. package/lib/components/UTActionCard/components/Header/utils.js +80 -0
  17. package/lib/components/UTActionCard/components/HeaderActions/index.js +80 -0
  18. package/lib/components/UTActionCard/components/HeaderActions/styles.js +21 -0
  19. package/lib/components/UTActionCard/components/HeaderActions/utils.js +17 -0
  20. package/lib/components/UTActionCard/constants.js +20 -0
  21. package/lib/components/UTActionCard/index.js +157 -0
  22. package/lib/components/UTActionCard/styles.js +39 -0
  23. package/lib/components/UTBaseInputField/components/IconAdornment/utils.js +2 -3
  24. package/lib/components/UTCheckBox/constants.js +2 -0
  25. package/lib/components/UTCheckBox/index.js +25 -16
  26. package/lib/components/UTCheckBox/theme.js +1 -3
  27. package/lib/components/UTCheckList/constants.js +0 -2
  28. package/lib/components/UTCheckList/index.js +7 -6
  29. package/lib/components/UTCheckList/styles.js +2 -1
  30. package/lib/components/UTCheckList/utils.js +2 -2
  31. package/lib/components/UTFieldLabel/index.js +4 -3
  32. package/lib/components/UTIcon/index.js +4 -3
  33. package/lib/components/UTMenu/proptypes.js +1 -1
  34. package/lib/index.js +1 -0
  35. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.27.1](https://github.com/widergy/mobile-ui/compare/v1.27.0...v1.27.1) (2024-10-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fixes on utcheckbox ([#360](https://github.com/widergy/mobile-ui/issues/360)) ([db36603](https://github.com/widergy/mobile-ui/commit/db3660373bbba349c6b798f46bd91687096ffdc8))
7
+
8
+ # [1.27.0](https://github.com/widergy/mobile-ui/compare/v1.26.5...v1.27.0) (2024-10-07)
9
+
10
+
11
+ ### Features
12
+
13
+ * ut action card ([#363](https://github.com/widergy/mobile-ui/issues/363)) ([9d539f0](https://github.com/widergy/mobile-ui/commit/9d539f0584489daf1b0f170ffc696402cf333446))
14
+
1
15
  ## [1.26.5](https://github.com/widergy/mobile-ui/compare/v1.26.4...v1.26.5) (2024-10-04)
2
16
 
3
17
 
@@ -141,4 +141,8 @@ class CheckList extends PureComponent {
141
141
 
142
142
  CheckList.propTypes = propTypes;
143
143
 
144
+ /**
145
+ * @deprecated The CheckList component is deprecated and will be removed in a future release.
146
+ * Please use the UTCheckList component instead.
147
+ */
144
148
  export default CheckList;
@@ -89,4 +89,8 @@ Checkbox.defaultProps = {
89
89
 
90
90
  Checkbox.propTypes = propTypes;
91
91
 
92
+ /**
93
+ * @deprecated The Checkbox component is deprecated and will be removed in a future release.
94
+ * Please use the UTCheckBox component instead.
95
+ */
92
96
  export default withTheme(Checkbox);
@@ -0,0 +1,25 @@
1
+ # UTButton
2
+
3
+ ## Props
4
+
5
+
6
+ | Name | Type | Default | Description |
7
+ | :----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------ |
8
+ | `additionalMessage` | `shape({ Icon: elementType, iconProps: shape({ colorTheme: string, size: string }), labelProps: shape({ colorTheme: string, variant: string }), message: string })` | | Additional message information, including icons and labels. |
9
+ | `adornment` | `shape({ alignment: oneOf(['center', 'end', 'start']), position: string, props: object, type: string })` | | Adornment object to customize alignment, position, and type. |
10
+ | `BackgroundImage` | `elementType` | | A React component for the background image. |
11
+ | `bottomActions` | `arrayOf(shape({ colorTheme: string, disabled: bool, Icon: elementType, label: string, loading: bool, onPress: func }))` | | Defines the list of actions to be displayed at the bottom of the card. |
12
+ | `bottomActionsVariant` | `oneOf(['default', 'redirection'])` | | Sets the style variant for bottom actions. |
13
+ | `classNames` | `objectOf(string)` | | Css classes |
14
+ | `description` | `string` | | Description text to be displayed. |
15
+ | `descriptionProps` | `shape({ colorTheme: string, variant: string })` | | Props to customize the description's style. |
16
+ | `headerActions` | `arrayOf(shape({ Icon: oneOfType([elementType, string]), id: oneOfType([number, string]), label: string, loading: bool, onPress: func }))` | | List of actions available in the header section. |
17
+ | `headerActionsProps` | `shape({ alignment: oneOf(['center', 'end', 'start']), buttonGroupProps: shape({ colorTheme: string, shape: string }), variant: oneOf(['default', 'buttonGroup']) })` | | Props to configure header actions alignment, button group, etc. |
18
+ | `mainAction` | `func` | | Main action function for the card. |
19
+ | `size` | `oneOf(['medium', 'small'])` | `'medium'` | Size of the card, either small or medium. |
20
+ | `status` | `string` | | Status text to be displayed. |
21
+ | `statusLabel` | `string` | | Status label for additional status info. |
22
+ | `statusAlignment` | `oneOf(['center', 'end', 'start'])` | | Alignment of the status element. |
23
+ | `title` | `string` | | The title of the card. |
24
+ | `titleProps` | `shape({ variant: string, weight: string })` | | Props to configure the title style. |
25
+ | `withBodyPadding` | `bool` | `false` | Adds padding to the card body if`true`. |
@@ -0,0 +1,28 @@
1
+ import React, { memo } from 'react';
2
+ import { oneOf, shape, string } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import UTIcon from '../../../UTIcon';
6
+ import UTLabel from '../../../UTLabel';
7
+ import { SIZES } from '../../constants';
8
+
9
+ import styles from './styles';
10
+
11
+ const AdditionalMessage = ({ icon, iconProps = {}, labelProps = {}, message, size = SIZES.SMALL }) => (
12
+ <View style={[styles.additionalMessageContainer, styles[size]]}>
13
+ {icon && <UTIcon area colorTheme="gray" name={icon} size={16} {...iconProps} />}
14
+ <UTLabel colorTheme="gray" variant="small" withMarkdown {...labelProps}>
15
+ {message}
16
+ </UTLabel>
17
+ </View>
18
+ );
19
+
20
+ AdditionalMessage.propTypes = {
21
+ icon: string,
22
+ iconProps: shape({ colorTheme: string, size: string }),
23
+ labelProps: shape({ colorTheme: string, variant: string }),
24
+ message: string,
25
+ size: oneOf([SIZES.MEDIUM, SIZES.SMALL])
26
+ };
27
+
28
+ export default memo(AdditionalMessage);
@@ -0,0 +1,19 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ additionalMessageContainer: {
5
+ alignItems: 'center',
6
+ flexDirection: 'row',
7
+ gap: 8
8
+ },
9
+ medium: {
10
+ paddingTop: 0,
11
+ paddingHorizontal: 24,
12
+ paddingBottom: 24
13
+ },
14
+ small: {
15
+ paddingTop: 0,
16
+ paddingHorizontal: 16,
17
+ paddingBottom: 16
18
+ }
19
+ });
@@ -0,0 +1,62 @@
1
+ import React, { Fragment, memo } from 'react';
2
+ import { arrayOf, bool, elementType, func, oneOfType, shape, string } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import UTButton from '../../../UTButton';
6
+ import { ACTION_TYPES } from '../../constants';
7
+ import { useTheme } from '../../../../theming';
8
+ import { mergeMultipleStyles } from '../../../../utils/styleUtils';
9
+
10
+ import styles, { getThemeStyles } from './styles';
11
+
12
+ const BottomActions = ({ actions = [], bottomActionsVariant }) => {
13
+ const theme = useTheme();
14
+ const type = actions.length > 2 ? ACTION_TYPES.REDIRECTION : bottomActionsVariant || ACTION_TYPES.DEFAULT;
15
+ const isRedirection = type === ACTION_TYPES.REDIRECTION;
16
+ const themedStyles = mergeMultipleStyles(styles, getThemeStyles(theme, isRedirection));
17
+
18
+ return (
19
+ <View style={[themedStyles.actionsContainer, themedStyles.redirectionActionsContainer]}>
20
+ {actions.map(({ colorTheme = 'primary', disabled, Icon, label, loading, onClick }, i) => (
21
+ <Fragment key={label}>
22
+ {!isRedirection && i > 0 && <View style={[themedStyles.horizontalSeparator]} />}
23
+ <UTButton
24
+ style={{
25
+ icon: themedStyles.icon,
26
+ root: themedStyles.actionButton
27
+ }}
28
+ colorTheme={colorTheme}
29
+ disabled={disabled}
30
+ Icon={isRedirection ? 'IconChevronRight' : Icon}
31
+ iconPlacement={isRedirection ? 'right' : 'left'}
32
+ loading={loading}
33
+ onClick={onClick}
34
+ size="large"
35
+ variant="text"
36
+ >
37
+ {label}
38
+ </UTButton>
39
+ {isRedirection && actions.length > 1 && i !== actions.length - 1 && (
40
+ <View style={[themedStyles.verticalSeparator]} />
41
+ )}
42
+ </Fragment>
43
+ ))}
44
+ </View>
45
+ );
46
+ };
47
+
48
+ BottomActions.propTypes = {
49
+ actions: arrayOf(
50
+ shape({
51
+ colorTheme: string,
52
+ disabled: bool,
53
+ Icon: oneOfType([elementType, string]),
54
+ label: string,
55
+ loading: bool,
56
+ onClick: func
57
+ })
58
+ ),
59
+ bottomActionsVariant: string
60
+ };
61
+
62
+ export default memo(BottomActions);
@@ -0,0 +1,48 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const getThemeStyles = (theme = {}, isRedirection) => {
4
+ const lightPalette = theme.Palette.light;
5
+
6
+ const commonStyles = {
7
+ actionsContainer: {
8
+ borderTopColor: lightPalette['04']
9
+ }
10
+ };
11
+
12
+ if (isRedirection) {
13
+ return {
14
+ ...commonStyles,
15
+ verticalSeparator: {
16
+ backgroundColor: lightPalette['04'],
17
+ height: 1
18
+ },
19
+ redirectionActionsContainer: {
20
+ flexDirection: 'column'
21
+ }
22
+ };
23
+ }
24
+
25
+ return {
26
+ ...commonStyles,
27
+ actionButton: {
28
+ alignItems: 'center',
29
+ flex: 1
30
+ },
31
+ horizontalSeparator: {
32
+ backgroundColor: lightPalette['04'],
33
+ width: 1
34
+ }
35
+ };
36
+ };
37
+
38
+ const styles = StyleSheet.create({
39
+ actionsContainer: {
40
+ borderTopWidth: 1,
41
+ flexDirection: 'row'
42
+ },
43
+ icon: {
44
+ marginLeft: 'auto'
45
+ }
46
+ });
47
+
48
+ export default styles;
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { string, number } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import { useTheme } from '../../../../../../theming';
6
+ import UTLabel from '../../../../../UTLabel';
7
+
8
+ import styles from './styles';
9
+
10
+ const Avatar = ({ avatarColor, avatarSize, text = '', textColor = 'dark', textVariant = 'title1' }) => {
11
+ const theme = useTheme();
12
+ const backgroundColor = avatarColor || theme.colors.primary;
13
+ const avatarText = text?.trim().substring(0, 1) || '';
14
+
15
+ return (
16
+ <View
17
+ style={[
18
+ {
19
+ width: avatarSize,
20
+ height: avatarSize,
21
+ borderRadius: avatarSize / 2,
22
+ backgroundColor
23
+ },
24
+ styles.container
25
+ ]}
26
+ >
27
+ {avatarText && (
28
+ <UTLabel colorTheme={textColor || theme.colors.secondary} numberOfLines={1} variant={textVariant}>
29
+ {avatarText}
30
+ </UTLabel>
31
+ )}
32
+ </View>
33
+ );
34
+ };
35
+
36
+ Avatar.propTypes = {
37
+ avatarColor: string,
38
+ avatarSize: number,
39
+ text: string,
40
+ textColor: string,
41
+ textVariant: string
42
+ };
43
+
44
+ export default Avatar;
@@ -0,0 +1,9 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ justifyContent: 'center',
6
+ alignItems: 'center',
7
+ elevation: 1
8
+ }
9
+ });
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { string } from 'prop-types';
3
+ import { View } from 'react-native';
4
+
5
+ import UTLabel from '../../../../../UTLabel';
6
+
7
+ import styles from './styles';
8
+
9
+ const Status = ({
10
+ alignment = 'start',
11
+ backgroundColor,
12
+ colorTheme = 'dark',
13
+ value,
14
+ variant = 'small',
15
+ weight = 'medium'
16
+ }) => (
17
+ <View style={[styles.container, styles[`alignSelf_${alignment}`], { backgroundColor }]}>
18
+ <UTLabel colorTheme={colorTheme} variant={variant} weight={weight}>
19
+ {value}
20
+ </UTLabel>
21
+ </View>
22
+ );
23
+
24
+ Status.propTypes = {
25
+ alignment: string,
26
+ backgroundColor: string,
27
+ colorTheme: string,
28
+ value: string,
29
+ variant: string,
30
+ weight: string
31
+ };
32
+
33
+ export default Status;
@@ -0,0 +1,18 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ borderRadius: 4,
6
+ paddingHorizontal: 8,
7
+ paddingVertical: 4
8
+ },
9
+ alignSelf_center: {
10
+ alignSelf: 'center'
11
+ },
12
+ alignSelf_start: {
13
+ alignSelf: 'flex-start'
14
+ },
15
+ alignSelf_end: {
16
+ alignSelf: 'flex-end'
17
+ }
18
+ });
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { Image } from 'react-native';
3
+
4
+ import UTIcon from '../../../UTIcon';
5
+
6
+ import Avatar from './components/Avatar';
7
+
8
+ export const ADORNMENT_COMPONENT_MAPPER = {
9
+ icon: UTIcon,
10
+ avatar: props => <Avatar text={props.userName} avatarSize={64} {...props} />,
11
+ image: props => <Image resizeMode="contain" {...props} />
12
+ };
@@ -0,0 +1,101 @@
1
+ import React, { memo } from 'react';
2
+ import { View } from 'react-native';
3
+ import {
4
+ arrayOf,
5
+ bool,
6
+ elementType,
7
+ func,
8
+ number,
9
+ object,
10
+ objectOf,
11
+ oneOf,
12
+ oneOfType,
13
+ shape,
14
+ string
15
+ } from 'prop-types';
16
+ import { isEmpty } from '@widergy/web-utils/lib/array';
17
+
18
+ import UTLabel from '../../../UTLabel';
19
+ import HeaderActions from '../HeaderActions';
20
+ import { HEADER_ACTIONS_VARIANTS, PLACE_SELF_TYPES, SIZES } from '../../constants';
21
+ import { useTheme } from '../../../../theming';
22
+
23
+ import { renderAdornment, statusPropsMapper } from './utils';
24
+ import Status from './components/Status';
25
+ import styles from './styles';
26
+
27
+ const Header = ({
28
+ adornment,
29
+ classNames,
30
+ description,
31
+ descriptionProps,
32
+ headerActions,
33
+ headerActionsProps,
34
+ setMainActionHoverVisible,
35
+ size,
36
+ status,
37
+ statusAlignment,
38
+ statusLabel,
39
+ title,
40
+ titleProps
41
+ }) => {
42
+ const theme = useTheme();
43
+
44
+ return (
45
+ <View style={[styles.header, styles[size], classNames.header]}>
46
+ {renderAdornment(adornment, 'left', size)}
47
+ <View style={styles.headerTitles}>
48
+ {renderAdornment(adornment, 'top', size)}
49
+ <UTLabel variant="title3" weight="medium" {...titleProps}>
50
+ {title}
51
+ </UTLabel>
52
+ {description && (
53
+ <UTLabel colorTheme="gray" {...descriptionProps}>
54
+ {description}
55
+ </UTLabel>
56
+ )}
57
+ </View>
58
+ {status ? (
59
+ <Status
60
+ {...statusPropsMapper(status, theme)}
61
+ alignment={statusAlignment}
62
+ value={statusLabel || status}
63
+ />
64
+ ) : null}
65
+ {!isEmpty(headerActions) && (
66
+ <HeaderActions {...{ headerActions, headerActionsProps, setMainActionHoverVisible }} />
67
+ )}
68
+ </View>
69
+ );
70
+ };
71
+
72
+ Header.propTypes = {
73
+ adornment: shape({ position: string, props: object, type: string }),
74
+ classNames: objectOf(string),
75
+ description: string,
76
+ descriptionProps: shape({ colorTheme: string, variant: string }),
77
+ headerActions: arrayOf(
78
+ shape({
79
+ Icon: oneOfType([elementType, string]),
80
+ id: oneOfType([number, string]),
81
+ isPrimary: bool,
82
+ label: string,
83
+ loading: bool,
84
+ onPress: func
85
+ })
86
+ ),
87
+ headerActionsProps: shape({
88
+ alignment: oneOf([PLACE_SELF_TYPES.CENTER, PLACE_SELF_TYPES.END, PLACE_SELF_TYPES.START]),
89
+ buttonGroupProps: shape({ colorTheme: string, shape: string }),
90
+ variant: oneOf([HEADER_ACTIONS_VARIANTS.DEFAULT, HEADER_ACTIONS_VARIANTS.BUTTON_GROUP])
91
+ }),
92
+ setMainActionHoverVisible: string,
93
+ size: oneOf([SIZES.MEDIUM, SIZES.SMALL]),
94
+ status: string,
95
+ statusAlignment: oneOf([PLACE_SELF_TYPES.CENTER, PLACE_SELF_TYPES.END, PLACE_SELF_TYPES.START]),
96
+ statusLabel: string,
97
+ title: string,
98
+ titleProps: shape({ variant: string, weight: string })
99
+ };
100
+
101
+ export default memo(Header);
@@ -0,0 +1,30 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ header: {
5
+ flexDirection: 'row',
6
+ gap: 16,
7
+ justifyContent: 'space-between'
8
+ },
9
+ headerTitles: {
10
+ flex: 1,
11
+ flexDirection: 'column',
12
+ gap: 8,
13
+ marginRight: 'auto'
14
+ },
15
+ medium: {
16
+ padding: 24
17
+ },
18
+ small: {
19
+ padding: 16
20
+ },
21
+ alignSelf_center: {
22
+ alignSelf: 'center'
23
+ },
24
+ alignSelf_start: {
25
+ alignSelf: 'flex-start'
26
+ },
27
+ alignSelf_end: {
28
+ alignSelf: 'flex-end'
29
+ }
30
+ });
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import { isEmpty } from 'lodash';
3
+
4
+ import { PLACE_SELF_TYPES } from '../../constants';
5
+
6
+ import { ADORNMENT_COMPONENT_MAPPER } from './constants';
7
+ import styles from './styles';
8
+
9
+ const ACTIVE = 'active';
10
+ const APPROVED = 'approved';
11
+ const CHARGED_BILL = 'factura cobrada';
12
+ const FINISHED = 'finished';
13
+ const INACTIVE = 'inactive';
14
+ const PENDING = 'pending';
15
+ const BILLED = 'cobrada';
16
+ const PAID = 'pagado';
17
+ const ABOUT_TO_EXPIRE = 'por vencer';
18
+ const UNPAID = 'impaga';
19
+ const SUBSCRIBED = 'subscribed_from_utility';
20
+ const UNSUBSCRIBED = 'unsubscribed';
21
+ const CLOSED_STATUS = 'completed';
22
+ const OBSERVED_STATUS = 'observed';
23
+ const ONGOING_STATUS = 'pending';
24
+ const FORM_STATUS = {
25
+ CANCELED: 'cancelled',
26
+ FINISHED: 'finished',
27
+ IN_PROGRESS: 'in_progress',
28
+ ON_HOLD: 'on_hold',
29
+ COMPLETED: 'completed'
30
+ };
31
+ const PAYMENTPLAN_STATUS = {
32
+ ACTIVE: 'active',
33
+ CANCELLED: 'canceled',
34
+ DRAFT: 'draft',
35
+ FINISHED: 'finished',
36
+ PENDING: 'pending',
37
+ PAYMENT_IN_PROGRESS: 'payment_in_progress',
38
+ PENDING_OFFLINE_PAYMENT: 'pending_offline_payment',
39
+ PENDING_PAYMENT: 'pending_payment'
40
+ };
41
+
42
+ export const statusPropsMapper = (status, theme) => {
43
+ const paletteVariantMap = {
44
+ success: [ACTIVE, APPROVED, BILLED, CHARGED_BILL, FINISHED, PAID, SUBSCRIBED],
45
+ error: [INACTIVE, PAYMENTPLAN_STATUS.CANCELLED, UNSUBSCRIBED, FORM_STATUS.CANCELED],
46
+ warning: [
47
+ OBSERVED_STATUS,
48
+ PAYMENTPLAN_STATUS.PAYMENT_IN_PROGRESS,
49
+ PAYMENTPLAN_STATUS.PENDING_OFFLINE_PAYMENT,
50
+ PAYMENTPLAN_STATUS.PENDING_PAYMENT,
51
+ UNPAID,
52
+ FORM_STATUS.ON_HOLD
53
+ ],
54
+ information: [ONGOING_STATUS, PENDING, FORM_STATUS.IN_PROGRESS],
55
+ unassigned: [CLOSED_STATUS, PAYMENTPLAN_STATUS.DRAFT, ABOUT_TO_EXPIRE]
56
+ };
57
+
58
+ const variant =
59
+ Object.entries(paletteVariantMap).find(([, statuses]) => statuses.includes(status?.toLowerCase()))?.[0] ||
60
+ 'error';
61
+
62
+ return { backgroundColor: theme.Palette[variant]['01'] };
63
+ };
64
+
65
+ export const renderAdornment = (adornment, position, size) => {
66
+ if (isEmpty(adornment) || adornment.position !== position) return null;
67
+ const AdornmentComponent = ADORNMENT_COMPONENT_MAPPER[adornment.type];
68
+ const defaultPlaceSelf = adornment.position === 'left' ? PLACE_SELF_TYPES.CENTER : PLACE_SELF_TYPES.START;
69
+
70
+ return (
71
+ <AdornmentComponent
72
+ style={[
73
+ styles[`alignSelf_${adornment.alignment || defaultPlaceSelf}`],
74
+ position === 'top' && styles[`gap_${size}`],
75
+ adornment.type === 'image' && { width: adornment.props?.width, height: adornment.props?.height }
76
+ ]}
77
+ {...(adornment.props || {})}
78
+ />
79
+ );
80
+ };
@@ -0,0 +1,80 @@
1
+ import React, { memo, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import isEmpty from 'lodash/isEmpty';
4
+ import { arrayOf, bool, elementType, func, number, oneOf, oneOfType, shape, string } from 'prop-types';
5
+
6
+ import UTButton from '../../../UTButton';
7
+ import UTButtonGroup from '../../../UTButtonGroup';
8
+ import UTMenu from '../../../UTMenu';
9
+ import UTIcon from '../../../UTIcon';
10
+ import { HEADER_ACTIONS_VARIANTS, PLACE_SELF_TYPES } from '../../constants';
11
+
12
+ import { processActions } from './utils';
13
+ import styles from './styles';
14
+
15
+ const HeaderActions = ({ headerActions, headerActionsProps }) => {
16
+ const [selectedAction, setSelectedAction] = useState(null);
17
+ const showButtonGroup = headerActionsProps.variant === HEADER_ACTIONS_VARIANTS.BUTTON_GROUP;
18
+ const { colorTheme: buttonGroupColorTheme = 'secondary', shape: buttonGroupShape } =
19
+ headerActionsProps?.buttonGroupProps || {};
20
+
21
+ const { primaryActions, secondaryActions } = processActions(headerActions);
22
+
23
+ return (
24
+ <View style={[styles.headerActionsContainer, styles[`alignSelf_${headerActionsProps.alignment}`]]}>
25
+ {primaryActions.map((buttonProps, i) =>
26
+ !buttonProps.onPress ? (
27
+ <UTIcon
28
+ name={buttonProps.Icon}
29
+ style={styles.notClickableIconContainer}
30
+ {...buttonProps}
31
+ key={buttonProps?.id || i}
32
+ />
33
+ ) : (
34
+ <UTButton size="medium" variant="text" {...buttonProps} key={buttonProps?.id || i} />
35
+ )
36
+ )}
37
+ {!isEmpty(secondaryActions) &&
38
+ (!showButtonGroup ? (
39
+ <View>
40
+ <UTMenu options={secondaryActions} horizontalOffset={-80}>
41
+ <UTIcon name="IconDots" />
42
+ </UTMenu>
43
+ </View>
44
+ ) : (
45
+ <UTButtonGroup
46
+ actions={secondaryActions.map(action => ({
47
+ ...action,
48
+ onPress: () => {
49
+ setSelectedAction(action.id);
50
+ action.onPress();
51
+ }
52
+ }))}
53
+ colorTheme={buttonGroupColorTheme}
54
+ selected={selectedAction || secondaryActions[0]?.id}
55
+ shape={buttonGroupShape}
56
+ />
57
+ ))}
58
+ </View>
59
+ );
60
+ };
61
+
62
+ HeaderActions.propTypes = {
63
+ headerActions: arrayOf(
64
+ shape({
65
+ Icon: oneOfType([elementType, string]),
66
+ id: oneOfType([number, string]),
67
+ isPrimary: bool,
68
+ label: string,
69
+ loading: bool,
70
+ onPress: func
71
+ })
72
+ ),
73
+ headerActionsProps: shape({
74
+ alignment: oneOf([PLACE_SELF_TYPES.CENTER, PLACE_SELF_TYPES.END, PLACE_SELF_TYPES.START]),
75
+ buttonGroupProps: shape({ colorTheme: string, shape: string }),
76
+ variant: oneOf([HEADER_ACTIONS_VARIANTS.DEFAULT, HEADER_ACTIONS_VARIANTS.BUTTON_GROUP])
77
+ })
78
+ };
79
+
80
+ export default memo(HeaderActions);
@@ -0,0 +1,21 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ headerActionsContainer: {
5
+ alignItems: 'center',
6
+ flexDirection: 'row',
7
+ gap: 8
8
+ },
9
+ alignSelf_center: {
10
+ alignSelf: 'center'
11
+ },
12
+ alignSelf_start: {
13
+ alignSelf: 'flex-start'
14
+ },
15
+ alignSelf_end: {
16
+ alignSelf: 'flex-end'
17
+ },
18
+ notClickableIconContainer: {
19
+ padding: 8
20
+ }
21
+ });
@@ -0,0 +1,17 @@
1
+ import { isEmpty } from 'lodash';
2
+
3
+ const mapAction = action => ({
4
+ ...action,
5
+ action: () => {
6
+ action?.onPress?.();
7
+ }
8
+ });
9
+
10
+ export const processActions = headerActions =>
11
+ headerActions.reduce(
12
+ (final, current) => {
13
+ if (current.isPrimary && isEmpty(final.primaryActions)) return { ...final, primaryActions: [current] };
14
+ return { ...final, secondaryActions: [...final.secondaryActions, mapAction(current)] };
15
+ },
16
+ { primaryActions: [], secondaryActions: [] }
17
+ );
@@ -0,0 +1,20 @@
1
+ export const SIZES = {
2
+ MEDIUM: 'medium',
3
+ SMALL: 'small'
4
+ };
5
+
6
+ export const ACTION_TYPES = {
7
+ DEFAULT: 'default',
8
+ REDIRECTION: 'redirection'
9
+ };
10
+
11
+ export const HEADER_ACTIONS_VARIANTS = {
12
+ BUTTON_GROUP: 'buttonGroup',
13
+ DEFAULT: 'default'
14
+ };
15
+
16
+ export const PLACE_SELF_TYPES = {
17
+ CENTER: 'center',
18
+ END: 'end',
19
+ START: 'start'
20
+ };
@@ -0,0 +1,157 @@
1
+ import React, { memo } from 'react';
2
+ import { View } from 'react-native';
3
+ import {
4
+ arrayOf,
5
+ bool,
6
+ elementType,
7
+ func,
8
+ number,
9
+ object,
10
+ objectOf,
11
+ oneOf,
12
+ oneOfType,
13
+ shape,
14
+ string
15
+ } from 'prop-types';
16
+ import { isEmpty } from 'lodash';
17
+
18
+ import Surface from '../Surface';
19
+ import Touchable from '../Touchable';
20
+
21
+ import { ACTION_TYPES, HEADER_ACTIONS_VARIANTS, PLACE_SELF_TYPES, SIZES } from './constants';
22
+ import Header from './components/Header';
23
+ import AdditionalMessage from './components/AdditionalMessage';
24
+ import BottomActions from './components/BottomActions';
25
+ import styles from './styles';
26
+
27
+ const UTActionCard = ({
28
+ additionalMessage,
29
+ adornment,
30
+ backgroundHeight = '100%',
31
+ BackgroundImage,
32
+ backgroundWidth = '100%',
33
+ bottomActions,
34
+ bottomActionsVariant,
35
+ children,
36
+ classNames = {},
37
+ description,
38
+ descriptionProps = {},
39
+ headerActions,
40
+ headerActionsProps = { variant: HEADER_ACTIONS_VARIANTS.DEFAULT },
41
+ mainAction,
42
+ size = SIZES.SMALL,
43
+ status,
44
+ statusAlignment,
45
+ statusLabel,
46
+ title,
47
+ titleProps = {},
48
+ withBodyPadding = true
49
+ }) => {
50
+ return (
51
+ <Surface elevation={1} style={[styles.cardContainer, classNames.container]}>
52
+ <Touchable
53
+ onPress={mainAction && (() => mainAction?.())}
54
+ style={[styles.innerContainer, classNames.innerContainer, mainAction && styles.withMainAction]}
55
+ >
56
+ <View>
57
+ {BackgroundImage && (
58
+ <BackgroundImage
59
+ height={backgroundHeight}
60
+ style={[styles.backgroundImage, classNames.backgroundImage]}
61
+ width={backgroundWidth}
62
+ />
63
+ )}
64
+ <View style={[styles.headerAndChildrenContainer, classNames.headerAndChildrenContainer]}>
65
+ <Header
66
+ {...{
67
+ adornment,
68
+ classNames,
69
+ description,
70
+ descriptionProps,
71
+ headerActions,
72
+ headerActionsProps,
73
+ mainAction,
74
+ size,
75
+ status,
76
+ statusAlignment,
77
+ statusLabel,
78
+ title,
79
+ titleProps
80
+ }}
81
+ />
82
+ {children && (
83
+ <View
84
+ style={[
85
+ withBodyPadding ? styles[`bodyPadding_${size}`] : styles[`withoutBodyPadding_${size}`]
86
+ ]}
87
+ >
88
+ {children}
89
+ </View>
90
+ )}
91
+ </View>
92
+ </View>
93
+ </Touchable>
94
+ {!isEmpty(additionalMessage) && <AdditionalMessage {...additionalMessage} size={size} />}
95
+ {!isEmpty(bottomActions) && (
96
+ <BottomActions actions={bottomActions} bottomActionsVariant={bottomActionsVariant} />
97
+ )}
98
+ </Surface>
99
+ );
100
+ };
101
+
102
+ UTActionCard.propTypes = {
103
+ additionalMessage: shape({
104
+ Icon: elementType,
105
+ iconProps: shape({ colorTheme: string, size: string }),
106
+ labelProps: shape({ colorTheme: string, variant: string }),
107
+ message: string
108
+ }),
109
+ adornment: shape({
110
+ alignment: oneOf([PLACE_SELF_TYPES.CENTER, PLACE_SELF_TYPES.END, PLACE_SELF_TYPES.START]),
111
+ position: string,
112
+ props: object,
113
+ type: string
114
+ }),
115
+ backgroundHeight: oneOfType([string, number]),
116
+ BackgroundImage: elementType,
117
+ backgroundWidth: oneOfType([string, number]),
118
+ bottomActions: arrayOf(
119
+ shape({
120
+ colorTheme: string,
121
+ disabled: bool,
122
+ Icon: elementType,
123
+ label: string,
124
+ loading: bool,
125
+ onClick: func
126
+ })
127
+ ),
128
+ bottomActionsVariant: oneOf([ACTION_TYPES.DEFAULT, ACTION_TYPES.REDIRECTION]),
129
+ classNames: objectOf(string),
130
+ description: string,
131
+ descriptionProps: shape({ colorTheme: string, variant: string }),
132
+ headerActions: arrayOf(
133
+ shape({
134
+ Icon: oneOfType([elementType, string]),
135
+ id: oneOfType([number, string]),
136
+ isPrimary: bool,
137
+ label: string,
138
+ loading: bool,
139
+ onPress: func
140
+ })
141
+ ),
142
+ headerActionsProps: shape({
143
+ alignment: oneOf([PLACE_SELF_TYPES.CENTER, PLACE_SELF_TYPES.END, PLACE_SELF_TYPES.START]),
144
+ buttonGroupProps: shape({ colorTheme: string, shape: string }),
145
+ variant: oneOf([HEADER_ACTIONS_VARIANTS.DEFAULT, HEADER_ACTIONS_VARIANTS.BUTTON_GROUP])
146
+ }),
147
+ mainAction: func,
148
+ size: oneOf([SIZES.MEDIUM, SIZES.SMALL]),
149
+ status: string,
150
+ statusLabel: string,
151
+ statusAlignment: oneOf([PLACE_SELF_TYPES.CENTER, PLACE_SELF_TYPES.END, PLACE_SELF_TYPES.START]),
152
+ title: string,
153
+ titleProps: shape({ variant: string, weight: string }),
154
+ withBodyPadding: bool
155
+ };
156
+
157
+ export default memo(UTActionCard);
@@ -0,0 +1,39 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ cardContainer: {
5
+ borderRadius: 8,
6
+ overflow: 'hidden'
7
+ },
8
+ innerContainer: {
9
+ position: 'relative',
10
+ width: '100%'
11
+ },
12
+ backgroundImage: {
13
+ position: 'absolute',
14
+ zIndex: 0
15
+ },
16
+ headerAndChildrenContainer: {
17
+ flexDirection: 'column',
18
+ width: '100%',
19
+ zIndex: 1
20
+ },
21
+ bodyPadding_medium: {
22
+ paddingTop: 0,
23
+ paddingLeft: 24,
24
+ paddingRight: 24,
25
+ paddingBottom: 24
26
+ },
27
+ bodyPadding_small: {
28
+ paddingTop: 0,
29
+ paddingLeft: 16,
30
+ paddingRight: 16,
31
+ paddingBottom: 16
32
+ },
33
+ withoutBodyPadding_small: {
34
+ paddingBottom: 16
35
+ },
36
+ withoutBodyPadding_medium: {
37
+ paddingBottom: 24
38
+ }
39
+ });
@@ -1,7 +1,6 @@
1
- export const getIconColorProps = (changeOnError, changeOnFocus, colorTheme, error, focused, shade) => {
2
- return changeOnError && error
1
+ export const getIconColorProps = (changeOnError, changeOnFocus, colorTheme, error, focused, shade) =>
2
+ changeOnError && error
3
3
  ? { colorTheme: 'error', shade: '04' }
4
4
  : changeOnFocus && focused
5
5
  ? { colorTheme: 'accent', shade: '04' }
6
6
  : { colorTheme: colorTheme || 'gray', shade };
7
- };
@@ -5,3 +5,5 @@ export const SPACING = {
5
5
  SMALL: 'small',
6
6
  LARGE: 'large'
7
7
  };
8
+
9
+ export const BUTTON_VARIANT = 'button';
@@ -5,23 +5,24 @@ import { useTheme } from '../../theming';
5
5
  import UTFieldLabel from '../UTFieldLabel';
6
6
  import UTIcon from '../UTIcon';
7
7
 
8
- import { CHECKED_ICON, INDETERMINATE_ICON } from './constants';
8
+ import { BUTTON_VARIANT, CHECKED_ICON, INDETERMINATE_ICON } from './constants';
9
9
  import { propTypes, defaultProps } from './proptypes';
10
10
  import { retrieveStyle } from './theme';
11
11
  import styles from './styles';
12
12
 
13
13
  const UTCheckBox = ({
14
- checked,
14
+ value,
15
+ onChange,
15
16
  disabled,
16
17
  indeterminate,
17
18
  isSimple,
18
- onPress,
19
19
  required,
20
20
  reversed,
21
21
  spacing,
22
22
  style,
23
23
  title,
24
- variant
24
+ variant,
25
+ withMarkdown
25
26
  }) => {
26
27
  const theme = useTheme();
27
28
  const [pressed, setPressed] = useState(false);
@@ -29,7 +30,7 @@ const UTCheckBox = ({
29
30
  const { containerStyles, iconContainerStyles, boxStyles, pressableStyles, titleStyles } = useMemo(
30
31
  () =>
31
32
  retrieveStyle({
32
- checked,
33
+ checked: value,
33
34
  disabled,
34
35
  indeterminate,
35
36
  pressed,
@@ -39,33 +40,36 @@ const UTCheckBox = ({
39
40
  theme,
40
41
  variant
41
42
  }),
42
- [checked, disabled, indeterminate, reversed, spacing, style, theme, variant, pressed]
43
+ [value, disabled, indeterminate, reversed, spacing, style, theme, variant, pressed]
43
44
  );
44
45
 
45
46
  const iconName = useMemo(
46
- () => (indeterminate ? INDETERMINATE_ICON : checked ? CHECKED_ICON : ''),
47
- [indeterminate, checked]
47
+ () => (indeterminate ? INDETERMINATE_ICON : value ? CHECKED_ICON : ''),
48
+ [indeterminate, value]
48
49
  );
49
50
 
50
51
  const handlePressIn = useCallback(() => setPressed(true), []);
51
52
  const handlePressOut = useCallback(() => setPressed(false), []);
52
53
 
54
+ const handlePress = useCallback(() => {
55
+ if (!disabled && onChange) {
56
+ onChange(!value);
57
+ }
58
+ }, [disabled, onChange, value]);
59
+
60
+ const shouldHighlightLabel = value && variant === BUTTON_VARIANT;
61
+
53
62
  return (
54
63
  <Pressable
55
64
  style={pressableStyles}
56
65
  disabled={disabled}
57
- onPress={onPress}
66
+ onPress={handlePress}
58
67
  onPressIn={handlePressIn}
59
68
  onPressOut={handlePressOut}
60
69
  >
61
70
  <View style={containerStyles}>
62
71
  {isSimple ? (
63
- <UTIcon
64
- name="IconCheck"
65
- shade="04"
66
- colorTheme="accent"
67
- style={checked ? undefined : styles.hidden}
68
- />
72
+ <UTIcon name="IconCheck" shade="04" colorTheme="accent" style={value ? undefined : styles.hidden} />
69
73
  ) : (
70
74
  <View style={boxStyles}>
71
75
  <View style={iconContainerStyles}>
@@ -73,7 +77,12 @@ const UTCheckBox = ({
73
77
  </View>
74
78
  </View>
75
79
  )}
76
- <UTFieldLabel colorTheme={checked ? 'accent' : 'dark'} required={required} style={titleStyles}>
80
+ <UTFieldLabel
81
+ colorTheme={shouldHighlightLabel ? 'accent' : 'dark'}
82
+ required={required}
83
+ style={titleStyles}
84
+ withMarkdown={withMarkdown}
85
+ >
77
86
  {title}
78
87
  </UTFieldLabel>
79
88
  </View>
@@ -1,6 +1,4 @@
1
- import { BUTTON_VARIANT } from '../UTCheckList/constants';
2
-
3
- import { SPACING } from './constants';
1
+ import { BUTTON_VARIANT, SPACING } from './constants';
4
2
 
5
3
  const NORMAL_SPACING = 16;
6
4
  const SMALL_SPACING = 8;
@@ -2,5 +2,3 @@ export const SPACING = {
2
2
  SMALL: 'small',
3
3
  LARGE: 'large'
4
4
  };
5
-
6
- export const BUTTON_VARIANT = 'button';
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useCallback, useMemo } from 'react';
2
2
  import { View } from 'react-native';
3
3
 
4
+ import { BUTTON_VARIANT } from '../UTCheckBox/constants';
4
5
  import { formatErrorToValidation } from '../UTValidation/utils';
5
6
  import UTCheckBox from '../UTCheckBox';
6
7
  import UTFieldLabel from '../UTFieldLabel';
@@ -9,7 +10,7 @@ import UTValidation from '../UTValidation';
9
10
  import { keyExtractor, isChecked, convertIfIsString, getPropValueBasedOnVariant } from './utils';
10
11
  import styles from './styles';
11
12
  import { defaultProps, propTypes } from './proptypes';
12
- import { BUTTON_VARIANT, SPACING } from './constants';
13
+ import { SPACING } from './constants';
13
14
 
14
15
  const UTCheckList = ({
15
16
  error,
@@ -54,7 +55,7 @@ const UTCheckList = ({
54
55
  );
55
56
 
56
57
  const handleChange = useCallback(
57
- receivedValue => () => {
58
+ receivedValue => {
58
59
  if (isSimple) {
59
60
  onChange([receivedValue]);
60
61
  } else {
@@ -89,10 +90,10 @@ const UTCheckList = ({
89
90
  >
90
91
  {showSelectAll && !isSimple && (
91
92
  <UTCheckBox
92
- checked={areAllSelected}
93
+ value={areAllSelected}
93
94
  indeterminate={isIndeterminate}
94
95
  title={selectAllLabel}
95
- onPress={handleCheckAll}
96
+ onChange={handleCheckAll}
96
97
  reversed={reversedBasedOnVariant}
97
98
  spacing={horizontalSpacing}
98
99
  style={style.selectAll}
@@ -102,11 +103,11 @@ const UTCheckList = ({
102
103
  {options?.map((item, index) => (
103
104
  <UTCheckBox
104
105
  isSimple={isSimple}
105
- checked={isChecked(item, value)}
106
+ value={isChecked(item, value)}
106
107
  disabled={item.disabled}
107
108
  key={keyExtractor(item, index)}
108
109
  title={item.label}
109
- onPress={handleChange(item.value)}
110
+ onChange={() => handleChange(item.value)}
110
111
  reversed={reversedBasedOnVariant}
111
112
  spacing={horizontalSpacing}
112
113
  style={style.item}
@@ -11,7 +11,8 @@ export default StyleSheet.create({
11
11
  },
12
12
  checkboxesContainer: {
13
13
  alignSelf: 'flex-start',
14
- rowGap: SPACING.LARGE
14
+ rowGap: SPACING.LARGE,
15
+ width: '100%'
15
16
  },
16
17
  container: {
17
18
  rowGap: SPACING.LARGE
@@ -1,11 +1,11 @@
1
1
  import { isString } from 'lodash';
2
2
 
3
- import { BUTTON_VARIANT } from './constants';
3
+ import { BUTTON_VARIANT } from '../UTCheckBox/constants';
4
4
 
5
5
  export const keyExtractor = (_, index) => `CB-${index}`;
6
6
 
7
7
  export const isChecked = (item, inputValue) =>
8
- !!inputValue?.find(elem => elem === item.value) || (item.disabled && item.checked);
8
+ (inputValue && !!inputValue?.find(elem => elem === item.value)) || (item.disabled && item.checked);
9
9
 
10
10
  export const convertIfIsString = value =>
11
11
  isString(value) ? (value.length === 0 ? [] : JSON.parse(value.replace(/'/g, '"'))) : value;
@@ -7,9 +7,9 @@ import UTLabel from '../UTLabel';
7
7
  import { REQUIRED_LABEL } from './constants';
8
8
  import styles from './styles';
9
9
 
10
- const UTFieldLabel = ({ children, colorTheme, required, style, variant, weight }) => (
10
+ const UTFieldLabel = ({ children, colorTheme, required, style, variant, weight, withMarkdown }) => (
11
11
  <View style={[styles.label, style]}>
12
- <UTLabel colorTheme={colorTheme} variant={variant}>
12
+ <UTLabel colorTheme={colorTheme} variant={variant} withMarkdown={withMarkdown}>
13
13
  {children}
14
14
  </UTLabel>
15
15
  {required && (
@@ -24,7 +24,8 @@ UTFieldLabel.propTypes = {
24
24
  colorTheme: string,
25
25
  required: bool,
26
26
  variant: string,
27
- weight: string
27
+ weight: string,
28
+ withMarkdown: bool
28
29
  };
29
30
 
30
31
  export default UTFieldLabel;
@@ -40,13 +40,14 @@ const UTIcon = ({
40
40
 
41
41
  return (
42
42
  <View
43
- style={
43
+ style={[
44
44
  area && [
45
45
  ownStyles.withArea,
46
46
  ownStyles[`size${size}`],
47
47
  getVariantStyle({ color: colorTheme || color, shade, theme, variant })
48
- ]
49
- }
48
+ ],
49
+ iconProps.style
50
+ ]}
50
51
  >
51
52
  <IconComponent {...iconProps} />
52
53
  </View>
@@ -2,7 +2,7 @@ import { arrayOf, bool, func, number, oneOfType, shape, string } from 'prop-type
2
2
 
3
3
  const optionType = shape({
4
4
  label: string.isRequired,
5
- id: string.isRequired,
5
+ id: oneOfType([number, string]).isRequired,
6
6
  action: func,
7
7
  value: oneOfType([number, string])
8
8
  });
package/lib/index.js CHANGED
@@ -35,6 +35,7 @@ export { default as Touchable } from './components/Touchable';
35
35
  export { default as TransformView } from './components/TransformView';
36
36
  export { default as TransitionText } from './components/TransitionText';
37
37
  export { default as TransitionView } from './components/TransitionView';
38
+ export { default as UTActionCard } from './components/UTActionCard';
38
39
  export { default as UTAutocomplete } from './components/UTAutocomplete';
39
40
  export { default as UTBadge } from './components/UTBadge';
40
41
  export { default as UTBottomSheet } from './components/UTBottomSheet';
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.26.5",
5
+ "version": "1.27.1",
6
6
  "repository": "https://github.com/widergy/mobile-ui.git",
7
7
  "main": "lib/index.js",
8
8
  "files": [