@ovotech/element-native 5.3.0 → 5.4.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.
Files changed (85) hide show
  1. package/dist/d.d.ts +4 -0
  2. package/dist/esm/src/components/Button/Button.js +2 -1
  3. package/dist/esm/src/components/Button/Button.styles.js +4 -1
  4. package/dist/esm/src/components/CurrencyField/CurrencyField.js +2 -2
  5. package/dist/esm/src/components/EmailField/EmailField.js +2 -2
  6. package/dist/esm/src/components/Grid/Col.js +14 -12
  7. package/dist/esm/src/components/Grid/Row.js +6 -10
  8. package/dist/esm/src/components/Heading/Heading.js +1 -1
  9. package/dist/esm/src/components/NavHeader/NavHeader.js +126 -29
  10. package/dist/esm/src/components/NavHeader/NavHeader.styles.js +84 -54
  11. package/dist/esm/src/components/NavHeader/NavHeaderComponents/ActionButtons.js +12 -0
  12. package/dist/esm/src/components/NavHeader/NavHeaderComponents/NavButton.js +23 -0
  13. package/dist/esm/src/components/NavHeader/NavHeaderComponents/StepCounterBars.js +5 -0
  14. package/dist/esm/src/components/NavHeader/utils.js +23 -0
  15. package/dist/esm/src/components/PasswordField/PasswordField.js +2 -2
  16. package/dist/esm/src/components/PasswordInput/PasswordInput.js +1 -1
  17. package/dist/esm/src/components/PhoneField/PhoneField.js +2 -2
  18. package/dist/esm/src/components/ScreenView/ScreenView.js +33 -0
  19. package/dist/esm/src/components/ScreenView/index.js +1 -0
  20. package/dist/esm/src/components/SegmentedControls/components/SegmentButton.js +1 -1
  21. package/dist/esm/src/components/SelectField/Select.js +5 -4
  22. package/dist/esm/src/components/SelectField/SelectField.js +1 -1
  23. package/dist/esm/src/components/TextField/TextField.js +2 -2
  24. package/dist/esm/src/components/TextInput/TextInput.js +1 -1
  25. package/dist/esm/src/components/TextareaField/TextareaField.js +2 -2
  26. package/dist/esm/src/components/Toggle/styles.js +1 -1
  27. package/dist/esm/src/components/index.js +1 -0
  28. package/dist/esm/src/hooks/index.js +1 -1
  29. package/dist/esm/src/hooks/use-full-width.js +1 -1
  30. package/dist/src/components/Button/Button.d.ts +3 -2
  31. package/dist/src/components/Button/Button.js +2 -1
  32. package/dist/src/components/Button/Button.styles.d.ts +1 -1
  33. package/dist/src/components/Button/Button.styles.js +4 -1
  34. package/dist/src/components/Cards/IconDataCard/IconDataCard.styles.d.ts +2 -2
  35. package/dist/src/components/Cards/LinkTextCard/LinkTextCard.styles.d.ts +1 -1
  36. package/dist/src/components/CurrencyField/CurrencyField.d.ts +1 -1
  37. package/dist/src/components/CurrencyField/CurrencyField.js +2 -2
  38. package/dist/src/components/EmailField/EmailField.d.ts +1 -1
  39. package/dist/src/components/EmailField/EmailField.js +2 -2
  40. package/dist/src/components/Grid/Col.js +14 -12
  41. package/dist/src/components/Grid/Row.js +6 -10
  42. package/dist/src/components/Heading/Heading.d.ts +3 -1
  43. package/dist/src/components/Heading/Heading.js +1 -1
  44. package/dist/src/components/NavHeader/NavHeader.d.ts +24 -21
  45. package/dist/src/components/NavHeader/NavHeader.js +126 -29
  46. package/dist/src/components/NavHeader/NavHeader.styles.d.ts +2320 -20
  47. package/dist/src/components/NavHeader/NavHeader.styles.js +84 -54
  48. package/dist/src/components/NavHeader/NavHeaderComponents/ActionButtons.d.ts +3 -0
  49. package/dist/src/components/NavHeader/NavHeaderComponents/ActionButtons.js +12 -0
  50. package/dist/src/components/NavHeader/NavHeaderComponents/NavButton.d.ts +11 -0
  51. package/dist/src/components/NavHeader/NavHeaderComponents/NavButton.js +23 -0
  52. package/dist/src/components/NavHeader/NavHeaderComponents/StepCounterBars.d.ts +7 -0
  53. package/dist/src/components/NavHeader/NavHeaderComponents/StepCounterBars.js +5 -0
  54. package/dist/src/components/NavHeader/utils.d.ts +9 -0
  55. package/dist/src/components/NavHeader/utils.js +23 -0
  56. package/dist/src/components/PasswordField/PasswordField.d.ts +1 -1
  57. package/dist/src/components/PasswordField/PasswordField.js +2 -2
  58. package/dist/src/components/PasswordInput/PasswordInput.js +1 -1
  59. package/dist/src/components/PhoneField/PhoneField.d.ts +1 -1
  60. package/dist/src/components/PhoneField/PhoneField.js +2 -2
  61. package/dist/src/components/ScreenView/ScreenView.d.ts +5 -0
  62. package/dist/src/components/ScreenView/ScreenView.js +33 -0
  63. package/dist/src/components/ScreenView/index.d.ts +1 -0
  64. package/dist/src/components/ScreenView/index.js +1 -0
  65. package/dist/src/components/SegmentedControls/components/SegmentButton.js +1 -1
  66. package/dist/src/components/SelectField/Select.d.ts +3 -1
  67. package/dist/src/components/SelectField/Select.js +5 -4
  68. package/dist/src/components/SelectField/SelectField.d.ts +4 -1
  69. package/dist/src/components/SelectField/SelectField.js +1 -1
  70. package/dist/src/components/TextField/TextField.d.ts +1 -1
  71. package/dist/src/components/TextField/TextField.js +2 -2
  72. package/dist/src/components/TextInput/TextInput.d.ts +5 -3
  73. package/dist/src/components/TextInput/TextInput.js +1 -1
  74. package/dist/src/components/TextareaField/TextareaField.d.ts +1 -1
  75. package/dist/src/components/TextareaField/TextareaField.js +2 -2
  76. package/dist/src/components/Toggle/styles.js +1 -1
  77. package/dist/src/components/index.d.ts +1 -0
  78. package/dist/src/components/index.js +1 -0
  79. package/dist/src/hooks/index.d.ts +1 -1
  80. package/dist/src/hooks/index.js +1 -1
  81. package/dist/src/hooks/use-full-width.js +1 -1
  82. package/package.json +2 -1
  83. /package/dist/esm/src/hooks/{use-breakpoint.js → use-breakpoint.native.js} +0 -0
  84. /package/dist/src/hooks/{use-breakpoint.d.ts → use-breakpoint.native.d.ts} +0 -0
  85. /package/dist/src/hooks/{use-breakpoint.js → use-breakpoint.native.js} +0 -0
@@ -1,67 +1,97 @@
1
1
  import { numToPx } from '@ovotech/element-core';
2
2
  import Animated from 'react-native-reanimated';
3
3
  import styled, { css } from '../../styled.native';
4
- const Z_INDEX = 1000;
5
- export const NavWrapper = styled(Animated.View)(({ theme, collapsed }) => css `
4
+ import { SkeletonButton } from '../SkeletonButton';
5
+ import { SkeletonHeading } from '../SkeletonHeading';
6
+ import { NavButton } from './NavHeaderComponents/NavButton';
7
+ export const ShadowContainer = styled(Animated.View)(({ theme }) => css `
8
+ shadow-color: ${theme.color.neutral.black};
9
+ shadow-offset: 0 ${numToPx(theme.space[100])};
10
+ shadow-radius: ${numToPx(theme.space[200])};
11
+ `);
12
+ export const NavContainer = styled(Animated.View)(({ theme, layer, isCollapsed, breakpoints }) => css `
13
+ z-index: 1;
14
+ display: flex;
15
+ flex-direction: column;
16
+ align-items: stretch;
6
17
  width: 100%;
7
- position: absolute;
8
-
9
- /* yes, it is not a good move, but we want header to be the topmost element on the screen and we can't control app zIndex */
10
- z-index: ${Z_INDEX};
11
- background-color: ${theme.color.surface.surface};
18
+ padding-top: ${layer === 0 && !isCollapsed
19
+ ? numToPx(theme.space[2000])
20
+ : numToPx(theme.space[1800])};
12
21
 
13
- ${collapsed
14
- ? css `
15
- border-bottom-width: ${numToPx(theme.border.width.sm)};
16
- border-bottom-style: solid;
17
- border-bottom-color: ${theme.color.input.borderDim};
18
- `
19
- : ''}
22
+ padding-left: ${breakpoints.largeAndUp
23
+ ? numToPx(theme.space[2000])
24
+ : breakpoints.mediumAndUp
25
+ ? numToPx(theme.space[800])
26
+ : numToPx(theme.space[400])};
27
+ padding-right: ${breakpoints.largeAndUp
28
+ ? numToPx(theme.space[2000])
29
+ : breakpoints.mediumAndUp
30
+ ? numToPx(theme.space[800])
31
+ : numToPx(theme.space[400])};
32
+ padding-bottom: ${layer === 0 || layer === 1
33
+ ? numToPx(theme.space[400])
34
+ : 0};
35
+ background-color: ${layer < 2
36
+ ? theme.color.brand.brand
37
+ : theme.color.surface.bright};
38
+ overflow: hidden;
20
39
  `);
21
- export const NavContainer = styled.View(({ theme, topOffset, hasTitle }) => css `
22
- position: absolute;
23
-
24
- /* NavWrapper zIndex + 1 so it is always on top of the wrapper */
25
- z-index: ${Z_INDEX + 1};
26
- width: 100%;
40
+ export const HeaderContainer = styled(Animated.View)(({ theme }) => css `
41
+ display: flex;
27
42
  flex-direction: row;
28
- justify-content: space-between;
43
+ width: 100%;
29
44
  align-items: center;
30
- flex-wrap: wrap;
31
- padding-top: ${topOffset
32
- ? numToPx(topOffset)
33
- : numToPx(theme.space[hasTitle ? 200 : 400])};
34
- padding-bottom: ${numToPx(theme.space[hasTitle ? 200 : 400])};
35
- padding-left: ${numToPx(theme.space[400])};
36
- padding-right: ${numToPx(theme.space[400])};
45
+ gap: ${numToPx(theme.space[100])};
37
46
  `);
38
- export const ExpandedTitle = styled(Animated.Text)(({ theme, hasBackButton, hasRightAction, hasTitle }) => css `
39
- width: ${hasRightAction ? '100%' : '65%'};
40
- font-family: ${theme.native.font.family.bold};
41
- font-size: ${numToPx(theme.font.size['4xl'].mediaQuery[hasBackButton ? 'sm' : 'lg'])};
42
- line-height: ${numToPx(hasBackButton
43
- ? theme.font.lineHeight['4xl'].mediaQuery.sm
44
- : theme.space[1300])};
45
- color: ${theme.color.brand.brand};
46
- ${!hasTitle && 'height: 0;'};
47
+ export const SkeletonNavHeading = styled(SkeletonHeading)(({}) => css `
48
+ min-width: 70%;
49
+ height: 100%;
47
50
  `);
48
- export const CollapsedTitleWrapper = styled(Animated.View) `
49
- width: 33.3%;
50
- align-items: center;
51
- `;
52
- export const CollapsedTitle = styled.Text(({ theme }) => css `
53
- font-family: ${theme.native.font.family.bold};
54
- font-size: ${numToPx(theme.font.size.xl.mediaQuery.sm)};
55
- line-height: ${numToPx(theme.font.lineHeight.xl.mediaQuery.sm)};
56
- color: ${theme.color.brand.brand};
51
+ export const SkeletonNavButton = styled(SkeletonButton)(({ theme }) => css `
52
+ width: ${numToPx(theme.space[1000])};
53
+ height: ${numToPx(theme.space[1000])};
57
54
  `);
58
- export const HeaderGap = styled.View(({ hasRightAction }) => css `
59
- width: ${hasRightAction ? '20%' : '33.3%'};
55
+ export const SideContainer = styled.View(({ theme, side, flex = 1 }) => css `
56
+ display: flex;
57
+ gap: ${side === 'end' ? numToPx(theme.space[200]) : 0};
58
+ flex-direction: row;
59
+ flex: ${flex};
60
+ flex-basis: 0;
61
+ justify-content: ${side === 'start' ? 'flex-start' : 'flex-end'};
62
+ max-height: ${numToPx(theme.space[1000])};
60
63
  `);
61
- export const BackActionWrapper = styled(Animated.View)(({ theme, collapsed, hasBackButton }) => css `
62
- margin-bottom: ${collapsed ? '0' : numToPx(theme.space[400])};
63
- width: ${collapsed ? '33.3%' : '100%'};
64
-
65
- /* Important for maintaining white space above the title on main app screens */
66
- height: ${hasBackButton ? numToPx(theme.space[1000]) : undefined};
64
+ export const TitleContainer = styled.View(({ theme, layer, isIOS }) => css `
65
+ display: flex;
66
+ flex-direction: column;
67
+ align-items: ${isIOS && layer > 1 ? 'center' : 'flex-start'};
68
+ max-height: ${numToPx(theme.space[1000])};
69
+ min-width: ${numToPx(theme.space[1000])};
70
+ `);
71
+ export const StepCounterBar = styled.View(({ theme, count, currentStep }) => css `
72
+ flex-grow: 1;
73
+ height: 2px;
74
+ max-width: 50%;
75
+ min-width: 10%;
76
+ background-color: ${count <= currentStep
77
+ ? theme.color.brand.brand
78
+ : theme.color.surface.dim};
79
+ `);
80
+ export const SkeletonStepCounterBar = styled(SkeletonButton)(({ theme }) => css `
81
+ min-width: 100%;
82
+ height: ${numToPx(theme.space[50])};
83
+ `);
84
+ export const StepCounterContainer = styled.View(({ theme }) => css `
85
+ display: flex;
86
+ flex-direction: row;
87
+ justify-content: space-between;
88
+ width: 100%;
89
+ gap: ${numToPx(theme.space[100])};
90
+ padding-bottom: ${numToPx(theme.space[100])};
91
+ padding-top: ${numToPx(theme.space[200])};
92
+ max-height: ${numToPx(theme.space[100])};
93
+ min-width: 100%;
94
+ `);
95
+ export const NavBackButton = styled(NavButton)(({ theme }) => css `
96
+ margin-left: -${numToPx(theme.space[300])};
67
97
  `);
@@ -0,0 +1,3 @@
1
+ import { Layer, NavHeaderProps } from '../NavHeader';
2
+ export type ActionButtonsProps<L extends Layer> = Required<Pick<NavHeaderProps<L>, 'layer' | 'actionButtons' | 'loading'>>;
3
+ export declare const ActionButtons: <L extends Layer>(props: ActionButtonsProps<L>) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SkeletonNavButton } from '../NavHeader.styles';
3
+ import { checkHasAccount } from '../utils';
4
+ import { NavButton } from './NavButton';
5
+ export const ActionButtons = (props) => {
6
+ const variant = props.layer > 1 ? 'dim' : 'bright';
7
+ const inverted = props.layer < 2;
8
+ return (_jsxs(_Fragment, { children: [checkHasAccount(props) &&
9
+ props.actionButtons.account?.onPress &&
10
+ (props.loading ? (_jsx(SkeletonNavButton, {})) : (_jsx(NavButton, { variant: variant, inverted: inverted, layer: props.layer, onPress: props.actionButtons.account.onPress, iconLeft: props.actionButtons.account.icon || 'user' }))), !!props.actionButtons.help?.onPress &&
11
+ (props.loading ? (_jsx(SkeletonNavButton, {})) : (_jsx(NavButton, { variant: variant, inverted: inverted, layer: props.layer, onPress: props.actionButtons.help.onPress, iconLeft: props.actionButtons.help.icon || 'message' })))] }));
12
+ };
@@ -0,0 +1,11 @@
1
+ import { IconName } from '../../../providers';
2
+ import { ButtonBase } from '../../Button/Button';
3
+ import { Layer } from '../NavHeader';
4
+ export type NavButtonVariantName = 'bright' | 'dim';
5
+ export type NavButtonProps = Omit<ButtonBase, 'variant'> & {
6
+ variant: NavButtonVariantName;
7
+ layer?: Layer;
8
+ iconLeft?: IconName;
9
+ iconRight?: IconName;
10
+ };
11
+ export declare const NavButton: ({ layer, iconLeft, iconRight, fullWidth, variant, inverted, size, ...rest }: NavButtonProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import styled, { css } from '../../../styled.native';
3
+ import { DEFAULT_BUTTON_VARIANT } from '../../Button/Button';
4
+ import { StyledButtonIcon, StyledButtonWrapper, StyledInner, } from '../../Button/Button.styles';
5
+ const ButtonWrapper = ({ children, accessibilityRole = 'button', activeOpacity = 0.8, ...rest }) => (_jsx(StyledButtonWrapper, { accessible: true, accessibilityRole: accessibilityRole, activeOpacity: activeOpacity, ...rest, children: children }));
6
+ const NavStyledButtonInner = styled(StyledInner)(({ theme, navVariant }) => {
7
+ const backgroundAndBorderColor = navVariant === 'bright'
8
+ ? theme.color.surface.bright
9
+ : theme.color.surface.dim;
10
+ return css `
11
+ background-color: ${backgroundAndBorderColor};
12
+ border-color: ${backgroundAndBorderColor};
13
+ color: ${theme.button.surface.color.fgInverted};
14
+ `;
15
+ });
16
+ const NavStyledButtonIcon = styled(StyledButtonIcon)(({ theme, navVariant }) => css `
17
+ color: ${navVariant === 'bright' || navVariant === 'dim'
18
+ ? theme.button.surface.color.fgInverted
19
+ : 'transparent'};
20
+ `);
21
+ export const NavButton = ({ layer, iconLeft, iconRight, fullWidth = false, variant = 'bright', inverted = true, size = 'default', ...rest }) => {
22
+ return (_jsx(ButtonWrapper, { ...rest, children: _jsxs(NavStyledButtonInner, { fullWidth: fullWidth, variant: DEFAULT_BUTTON_VARIANT, navVariant: variant, inverted: inverted, size: size, iconLeft: iconLeft, iconRight: iconRight, iconOnly: true, layer: layer, children: [iconLeft && (_jsx(NavStyledButtonIcon, { iconLeft: true, iconOnly: true, name: iconLeft, inverted: inverted, navVariant: variant, variant: DEFAULT_BUTTON_VARIANT, size: 20 })), iconRight && (_jsx(NavStyledButtonIcon, { iconRight: true, iconOnly: true, name: iconRight, inverted: inverted, navVariant: variant, variant: DEFAULT_BUTTON_VARIANT, size: 20 }))] }) }));
23
+ };
@@ -0,0 +1,7 @@
1
+ type StepCounterProps = {
2
+ totalSteps: number;
3
+ currentStep: number;
4
+ loading?: boolean;
5
+ };
6
+ export declare const StepCounterBars: ({ totalSteps, currentStep, loading, }: StepCounterProps) => import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { SkeletonStepCounterBar, StepCounterBar, StepCounterContainer, } from '../NavHeader.styles';
3
+ export const StepCounterBars = ({ totalSteps, currentStep, loading = false, }) => {
4
+ return (_jsx(StepCounterContainer, { children: loading ? (_jsx(SkeletonStepCounterBar, {})) : (Array.from({ length: totalSteps }).map((_, index) => (_jsx(StepCounterBar, { count: index + 1, currentStep: currentStep }, index + 1)))) }));
5
+ };
@@ -0,0 +1,9 @@
1
+ import { Layer } from './NavHeader';
2
+ import { ActionButtonsProps } from './NavHeaderComponents/ActionButtons';
3
+ export declare const checkHasAccount: (props: ActionButtonsProps<Layer>) => props is ActionButtonsProps<0 | 1>;
4
+ type TitleLengthProps = {
5
+ layer: Layer;
6
+ title?: string;
7
+ };
8
+ export declare const getTitleLength: ({ layer, title }: TitleLengthProps) => string | undefined;
9
+ export {};
@@ -0,0 +1,23 @@
1
+ export const checkHasAccount = (props) => {
2
+ return props.layer === 0 || props.layer === 1;
3
+ };
4
+ export const getTitleLength = ({ layer, title }) => {
5
+ const TITLE_HOME_MAX_LENGTH = 14;
6
+ const TITLE_LAYER_1_MAX_LENGTH = 21;
7
+ const TITLE_LAYER_2_PLUS_MAX_LENGTH = 28;
8
+ if (layer === 0) {
9
+ return title.length > TITLE_HOME_MAX_LENGTH
10
+ ? title.slice(0, TITLE_HOME_MAX_LENGTH) + '...'
11
+ : title;
12
+ }
13
+ if (layer === 1) {
14
+ return title.length > TITLE_LAYER_1_MAX_LENGTH
15
+ ? title.slice(0, TITLE_LAYER_1_MAX_LENGTH)
16
+ : title;
17
+ }
18
+ if (layer > 1) {
19
+ return title.length > TITLE_LAYER_2_PLUS_MAX_LENGTH
20
+ ? title.slice(0, TITLE_LAYER_2_PLUS_MAX_LENGTH) + '...'
21
+ : title;
22
+ }
23
+ };
@@ -3,5 +3,5 @@ import { InputProps } from '../Input/Input';
3
3
  type Props = Omit<FieldProps, 'children'> & InputProps & {
4
4
  hasVisibilityToggle?: boolean;
5
5
  };
6
- export declare const PasswordField: ({ hasVisibilityToggle, ...rest }: Props) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const PasswordField: ({ hasVisibilityToggle, ref, ...rest }: Props) => import("react/jsx-runtime").JSX.Element;
7
7
  export {};
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Field } from '../Field';
3
3
  import { PasswordInput } from '../PasswordInput';
4
- export const PasswordField = ({ hasVisibilityToggle, ...rest }) => {
5
- return (_jsx(Field, { ...rest, children: _jsx(PasswordInput, { hasVisibilityToggle: hasVisibilityToggle }) }));
4
+ export const PasswordField = ({ hasVisibilityToggle, ref, ...rest }) => {
5
+ return (_jsx(Field, { ...rest, children: _jsx(PasswordInput, { ref: ref, hasVisibilityToggle: hasVisibilityToggle }) }));
6
6
  };
@@ -3,6 +3,6 @@ import { Input } from '../Input/Input';
3
3
  import { PasswordVisibilityToggle, useTogglePasswordVisibility, } from './PasswordVisibilityToggle';
4
4
  const PasswordInput = (props) => {
5
5
  const { isPasswordVisible, handlePasswordVisibility } = useTogglePasswordVisibility();
6
- return (_jsx(Input, { rightSlot: props.hasVisibilityToggle ? (_jsx(PasswordVisibilityToggle, { iconName: isPasswordVisible ? 'hide' : 'show', visibilityToggleLabel: isPasswordVisible ? 'Hide password' : 'Show password', handlePasswordVisibility: handlePasswordVisibility })) : null, keyboardType: "default", autoCapitalize: "none", autoCorrect: false, blurOnSubmit: false, secureTextEntry: !isPasswordVisible, ...props }));
6
+ return (_jsx(Input, { rightSlot: props.hasVisibilityToggle ? (_jsx(PasswordVisibilityToggle, { iconName: isPasswordVisible ? 'hide' : 'show', visibilityToggleLabel: isPasswordVisible ? 'Hide password' : 'Show password', handlePasswordVisibility: handlePasswordVisibility })) : null, keyboardType: "default", autoCapitalize: "none", autoCorrect: false, submitBehavior: "submit", secureTextEntry: !isPasswordVisible, ...props }));
7
7
  };
8
8
  export { PasswordInput };
@@ -1,5 +1,5 @@
1
1
  import { FieldProps } from '../Field';
2
2
  import { InputProps } from '../Input/Input';
3
3
  type Props = Omit<FieldProps, 'children'> & InputProps;
4
- export declare const PhoneField: ({ ...props }: Props) => import("react/jsx-runtime").JSX.Element;
4
+ export declare const PhoneField: ({ ref, ...props }: Props) => import("react/jsx-runtime").JSX.Element;
5
5
  export {};
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Field } from '../Field';
3
3
  import { PhoneInput } from '../PhoneInput';
4
- export const PhoneField = ({ ...props }) => {
5
- return (_jsx(Field, { ...props, children: _jsx(PhoneInput, {}) }));
4
+ export const PhoneField = ({ ref, ...props }) => {
5
+ return (_jsx(Field, { ...props, children: _jsx(PhoneInput, { ref: ref }) }));
6
6
  };
@@ -0,0 +1,5 @@
1
+ type Props = {
2
+ children?: React.ReactNode;
3
+ };
4
+ export declare const ScreenView: ({ children }: Props) => import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useWindowDimensions, View } from 'react-native';
3
+ import { useBreakpoint } from '../../hooks';
4
+ import styled, { useTheme } from '../../styled.native';
5
+ const BackgroundWrapper = styled(View)({
6
+ width: '100%',
7
+ alignItems: 'center',
8
+ });
9
+ const ContentContainer = styled(View)(({ paddingHorizontal, minWidth, maxWidth }) => ({
10
+ width: '100%',
11
+ paddingHorizontal,
12
+ minWidth,
13
+ maxWidth,
14
+ }));
15
+ export const ScreenView = ({ children }) => {
16
+ const { xsmallAndUp, smallAndUp, mediumAndUp } = useBreakpoint();
17
+ const { width } = useWindowDimensions();
18
+ const theme = useTheme();
19
+ let maxWidth = width;
20
+ let paddingHorizontal = theme.space[400];
21
+ if (mediumAndUp) {
22
+ maxWidth = 1024;
23
+ paddingHorizontal = theme.space[800];
24
+ }
25
+ else if (smallAndUp || xsmallAndUp) {
26
+ maxWidth = 484;
27
+ paddingHorizontal = theme.space[500];
28
+ }
29
+ else if (width < 359) {
30
+ paddingHorizontal = theme.space[400];
31
+ }
32
+ return (_jsx(BackgroundWrapper, { children: _jsx(ContentContainer, { paddingHorizontal: paddingHorizontal, maxWidth: maxWidth, children: children }) }));
33
+ };
@@ -0,0 +1 @@
1
+ export * from './ScreenView';
@@ -0,0 +1 @@
1
+ export * from './ScreenView';
@@ -47,7 +47,7 @@ export const SegmentButton = ({ accessibilityLabel, accessibilityHint, inline, i
47
47
  // choices go on the second row. The only way I could get it to work
48
48
  // was to hardcode the denominator to 3 for the single row ones
49
49
  // and 1.1 for the multiple row ones.
50
- yRef.current = y - height / (multipleRows ? 1.1 : 3 * fontScale);
50
+ yRef.current = y - height / (multipleRows ? 2.5 : 3 * fontScale);
51
51
  heightRef.current = height;
52
52
  widthRef.current = width;
53
53
  onLayout(width, height, x, yRef.current);
@@ -1,3 +1,4 @@
1
+ import { TouchableOpacityProps } from 'react-native';
1
2
  type SelectOption = {
2
3
  label: string;
3
4
  value: string;
@@ -10,6 +11,7 @@ export type SelectProps = {
10
11
  hasError?: boolean;
11
12
  onSelected?(value: SelectOption): void;
12
13
  testID?: string;
14
+ ref?: React.RefObject<TouchableOpacityProps | null>;
13
15
  };
14
16
  declare const SelectOption: import("styled-components").StyledComponent<typeof import("react-native").TouchableOpacity, {
15
17
  color: {
@@ -586,5 +588,5 @@ declare const SelectOption: import("styled-components").StyledComponent<typeof i
586
588
  }, {
587
589
  isLastOption: boolean;
588
590
  }, never>;
589
- export declare const Select: ({ options, defaultSelected, noOptionMessage, onSelected, hasError, testID, }: SelectProps) => import("react/jsx-runtime").JSX.Element;
591
+ export declare const Select: ({ options, defaultSelected, noOptionMessage, onSelected, hasError, testID, ref, }: SelectProps) => import("react/jsx-runtime").JSX.Element;
590
592
  export {};
@@ -2,7 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { numToPx } from '@ovotech/element-core';
3
3
  import groupBy from 'lodash.groupby';
4
4
  import { Fragment, useState } from 'react';
5
- import { Dimensions, Modal, ScrollView, View } from 'react-native';
5
+ import { Dimensions, Modal, ScrollView, View, } from 'react-native';
6
+ import { SafeAreaView } from 'react-native-safe-area-context';
6
7
  import styled, { css } from '../../styled.native';
7
8
  import { Icon } from '../Icon';
8
9
  import { P } from '../P';
@@ -30,7 +31,7 @@ const DropdownWrapper = styled.TouchableOpacity(({ theme }) => css `
30
31
  /* 80 is an opacity for hex https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4 */
31
32
  background-color: ${theme.color.brand.dark}80;
32
33
  `);
33
- const DropdownContainer = styled.SafeAreaView(({ theme }) => css `
34
+ const DropdownContainer = styled(SafeAreaView)(({ theme }) => css `
34
35
  width: 95%;
35
36
  max-height: 70%;
36
37
  border-radius: ${numToPx(theme.border.radius.sm)};
@@ -74,7 +75,7 @@ const StyledCategory = styled(P)(({ theme }) => css `
74
75
  background-color: ${theme.color.surface.surface};
75
76
  padding: ${numToPx(theme.space[100])} ${numToPx(theme.space[400])};
76
77
  `);
77
- export const Select = ({ options = [], defaultSelected = { label: '', value: 'default' }, noOptionMessage = 'No option selected', onSelected = () => null, hasError = false, testID = 'select', }) => {
78
+ export const Select = ({ options = [], defaultSelected = { label: '', value: 'default' }, noOptionMessage = 'No option selected', onSelected = () => null, hasError = false, testID = 'select', ref, }) => {
78
79
  const [isOpen, setOpen] = useState(false);
79
80
  const [selected, setSelected] = useState(defaultSelected);
80
81
  const height = Dimensions.get('window').height;
@@ -89,5 +90,5 @@ export const Select = ({ options = [], defaultSelected = { label: '', value: 'de
89
90
  label: `-- ${noOptionMessage} --`,
90
91
  value: 'default',
91
92
  };
92
- return (_jsxs(View, { children: [_jsxs(SelectInput, { testID: testID, hasError: hasError, onPress: () => setOpen(!isOpen), children: [_jsx(P, { style: { marginTop: 0, marginBottom: 0 }, children: selected.value === 'default' ? '' : selected.label }), _jsx(Icon, { name: "chevron-down", size: 16, style: { marginLeft: 'auto' } })] }), _jsx(Modal, { transparent: true, visible: isOpen, animationType: "fade", onRequestClose: () => setOpen(false), children: _jsx(DropdownWrapper, { onPress: () => setOpen(false), children: _jsx(DropdownContainer, { style: { maxHeight: dropdownContainerMaxHeight }, children: _jsxs(ScrollView, { nestedScrollEnabled: true, testID: `${testID}-OptionsScrollView`, children: [_jsxs(SelectOption, { accessible: true, accessibilityRole: "radio", onPress: () => handleOptionPress(requiredOption), children: [_jsx(StyledP, { children: _jsx(Strong, { children: requiredOption.label }) }), _jsx(Radio, { isChecked: selected.value === requiredOption.value, children: _jsx(RadioDot, { isChecked: selected.value === requiredOption.value }) })] }), Object.keys(optionsByCategories).map(category => (_jsxs(Fragment, { children: [category !== 'undefined' ? (_jsx(StyledCategory, { children: category })) : null, optionsByCategories[category].map((option, i) => (_jsxs(SelectOption, { accessible: true, accessibilityRole: "radio", isLastOption: i === optionsByCategories[category].length - 1, onPress: () => handleOptionPress(option), children: [_jsx(StyledP, { children: _jsx(Strong, { children: option.label }) }), _jsx(Radio, { isChecked: selected.value === option.value, children: _jsx(RadioDot, { isChecked: selected.value === option.value }) })] }, option.label)))] }, category)))] }) }) }) })] }));
93
+ return (_jsxs(View, { children: [_jsxs(SelectInput, { testID: testID, hasError: hasError, onPress: () => setOpen(!isOpen), ref: ref, children: [_jsx(P, { style: { marginTop: 0, marginBottom: 0 }, children: selected.value === 'default' ? '' : selected.label }), _jsx(Icon, { name: "chevron-down", size: 16, style: { marginLeft: 'auto' } })] }), _jsx(Modal, { transparent: true, visible: isOpen, animationType: "fade", onRequestClose: () => setOpen(false), children: _jsx(DropdownWrapper, { onPress: () => setOpen(false), children: _jsx(DropdownContainer, { style: { maxHeight: dropdownContainerMaxHeight }, children: _jsxs(ScrollView, { nestedScrollEnabled: true, testID: `${testID}-OptionsScrollView`, children: [_jsxs(SelectOption, { accessible: true, accessibilityRole: "radio", onPress: () => handleOptionPress(requiredOption), children: [_jsx(StyledP, { children: _jsx(Strong, { children: requiredOption.label }) }), _jsx(Radio, { isChecked: selected.value === requiredOption.value, children: _jsx(RadioDot, { isChecked: selected.value === requiredOption.value }) })] }), Object.keys(optionsByCategories).map(category => (_jsxs(Fragment, { children: [category !== 'undefined' ? (_jsx(StyledCategory, { children: category })) : null, optionsByCategories[category].map((option, i) => (_jsxs(SelectOption, { accessible: true, accessibilityRole: "radio", isLastOption: i === optionsByCategories[category].length - 1, onPress: () => handleOptionPress(option), children: [_jsx(StyledP, { children: _jsx(Strong, { children: option.label }) }), _jsx(Radio, { isChecked: selected.value === option.value, children: _jsx(RadioDot, { isChecked: selected.value === option.value }) })] }, option.label)))] }, category)))] }) }) }) })] }));
93
94
  };
@@ -1,7 +1,10 @@
1
+ import { RefObject } from 'react';
1
2
  import { FieldProps } from '../Field';
2
3
  import { SelectProps } from './Select';
4
+ import { TouchableOpacityProps } from 'react-native';
3
5
  type SelectFieldProps = SelectProps & Omit<FieldProps, 'children'> & {
4
6
  testID?: string;
7
+ ref?: RefObject<TouchableOpacityProps | null>;
5
8
  };
6
- export declare const SelectField: ({ error, options, onSelected, noOptionMessage, defaultSelected, testID, ...rest }: SelectFieldProps) => import("react/jsx-runtime").JSX.Element;
9
+ export declare const SelectField: ({ error, options, onSelected, noOptionMessage, defaultSelected, testID, ref, ...rest }: SelectFieldProps) => import("react/jsx-runtime").JSX.Element;
7
10
  export {};
@@ -1,4 +1,4 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Field } from '../Field';
3
3
  import { Select } from './Select';
4
- export const SelectField = ({ error, options, onSelected, noOptionMessage, defaultSelected, testID, ...rest }) => (_jsx(Field, { affixWidth: 20, error: error, ...rest, children: _jsx(_Fragment, { children: _jsx(Select, { onSelected: onSelected, options: options, noOptionMessage: noOptionMessage, defaultSelected: defaultSelected, hasError: Boolean(error), testID: testID }) }) }));
4
+ export const SelectField = ({ error, options, onSelected, noOptionMessage, defaultSelected, testID, ref, ...rest }) => (_jsx(Field, { affixWidth: 20, error: error, ...rest, children: _jsx(_Fragment, { children: _jsx(Select, { onSelected: onSelected, options: options, noOptionMessage: noOptionMessage, defaultSelected: defaultSelected, hasError: Boolean(error), testID: testID, ref: ref }) }) }));
@@ -1,5 +1,5 @@
1
1
  import { FieldProps } from '../Field';
2
2
  import { InputProps } from '../Input/Input';
3
3
  type Props = Omit<FieldProps, 'children'> & InputProps;
4
- export declare const TextField: (props: Props) => import("react/jsx-runtime").JSX.Element;
4
+ export declare const TextField: ({ ref, ...props }: Props) => import("react/jsx-runtime").JSX.Element;
5
5
  export {};
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Field } from '../Field';
3
3
  import { TextInput } from '../TextInput';
4
- export const TextField = (props) => {
5
- return (_jsx(Field, { ...props, children: _jsx(TextInput, {}) }));
4
+ export const TextField = ({ ref, ...props }) => {
5
+ return (_jsx(Field, { ...props, children: _jsx(TextInput, { ref: ref }) }));
6
6
  };
@@ -1,6 +1,8 @@
1
+ import { Ref } from 'react';
1
2
  import { TextInput as RNTextInput } from 'react-native';
2
3
  import { InputProps } from '../Input/Input';
3
- declare const TextInput: (props: InputProps & {
4
- ref?: React.RefObject<RNTextInput>;
5
- }) => import("react/jsx-runtime").JSX.Element;
4
+ type Props = InputProps & {
5
+ ref?: Ref<RNTextInput>;
6
+ };
7
+ declare const TextInput: ({ ref, ...props }: Props) => import("react/jsx-runtime").JSX.Element;
6
8
  export { TextInput };
@@ -1,4 +1,4 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Input } from '../Input/Input';
3
- const TextInput = (props) => _jsx(Input, { ...props, keyboardType: "default", ref: props.ref });
3
+ const TextInput = ({ ref, ...props }) => (_jsx(Input, { ...props, keyboardType: "default", ref: ref }));
4
4
  export { TextInput };
@@ -1,5 +1,5 @@
1
1
  import { FieldProps } from '../Field';
2
2
  import { TextareaInputProps } from '../TextareaInput';
3
3
  type Props = Omit<FieldProps, 'children'> & TextareaInputProps;
4
- export declare const TextareaField: ({ ...props }: Props) => import("react/jsx-runtime").JSX.Element;
4
+ export declare const TextareaField: ({ ref, ...props }: Props) => import("react/jsx-runtime").JSX.Element;
5
5
  export {};
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Field } from '../Field';
3
3
  import { TextareaInput } from '../TextareaInput';
4
- export const TextareaField = ({ ...props }) => {
5
- return (_jsx(Field, { ...props, children: _jsx(TextareaInput, {}) }));
4
+ export const TextareaField = ({ ref, ...props }) => {
5
+ return (_jsx(Field, { ...props, children: _jsx(TextareaInput, { ref: ref }) }));
6
6
  };
@@ -16,7 +16,7 @@ export const StyledToggleInputWrapper = styled.View(({ theme, labelPosition }) =
16
16
  ? labelPosition === 'left'
17
17
  ? 'margin-left: 10px;'
18
18
  : 'margin-right: 10px;'
19
- : null}
19
+ : ''}
20
20
  `;
21
21
  });
22
22
  export const StyledToggleInput = styled.View(({ theme, checked }) => {
@@ -36,6 +36,7 @@ export * from './PhoneInput';
36
36
  export * from './Pictogram';
37
37
  export * from './Radio';
38
38
  export * from './RadioCard';
39
+ export * from './ScreenView';
39
40
  export * from './RemoteImage';
40
41
  export * from './SegmentedControls';
41
42
  export * from './SelectField';
@@ -36,6 +36,7 @@ export * from './PhoneInput';
36
36
  export * from './Pictogram';
37
37
  export * from './Radio';
38
38
  export * from './RadioCard';
39
+ export * from './ScreenView';
39
40
  export * from './RemoteImage';
40
41
  export * from './SegmentedControls';
41
42
  export * from './SelectField';
@@ -1,2 +1,2 @@
1
- export * from './use-breakpoint';
1
+ export * from './use-breakpoint.native';
2
2
  export * from './use-full-width';
@@ -1,2 +1,2 @@
1
- export * from './use-breakpoint';
1
+ export * from './use-breakpoint.native';
2
2
  export * from './use-full-width';
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useState } from 'react';
2
- import { useBreakpoint } from './use-breakpoint';
2
+ import { useBreakpoint } from './use-breakpoint.native';
3
3
  export const useFullWidth = (fullWidth = 'small') => {
4
4
  const [isFullWidth, setIsfullWidth] = useState(true);
5
5
  const { smallAndUp } = useBreakpoint();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovotech/element-native",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,6 +26,7 @@
26
26
  "@babel/plugin-transform-flow-strip-types": "^7.27.1",
27
27
  "@babel/plugin-transform-private-methods": "^7.27.1",
28
28
  "@react-native-masked-view/masked-view": "^0.3.2",
29
+ "@styled/typescript-styled-plugin": "^1.0.1",
29
30
  "@testing-library/jest-dom": "^6.6.4",
30
31
  "@testing-library/react": "^16.1.0",
31
32
  "@testing-library/react-native": "^13.3.3",