@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
package/dist/d.d.ts CHANGED
@@ -2,3 +2,7 @@ declare module '*.png' {
2
2
  const value: any;
3
3
  export = value;
4
4
  }
5
+ declare module '*.jpg' {
6
+ const value: any;
7
+ export = value;
8
+ }
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useBreakpoint } from '../../hooks';
3
3
  import { StyledButtonIcon, StyledButtonText, StyledButtonWrapper, StyledInner, } from './Button.styles';
4
- const Button = ({ iconLeft, iconRight, children, fullWidth = false, variant = 'surface', inverted = false, size = 'default', accessibilityRole = 'button', activeOpacity = 0.8, ref, ...rest }) => {
4
+ export const DEFAULT_BUTTON_VARIANT = 'surface';
5
+ const Button = ({ iconLeft, iconRight, children, fullWidth = false, variant = DEFAULT_BUTTON_VARIANT, inverted = false, size = 'default', accessibilityRole = 'button', activeOpacity = 0.8, ref, ...rest }) => {
5
6
  const { smallAndUp } = useBreakpoint();
6
7
  const isIconOnly = !children;
7
8
  const iconSizes = {
@@ -94,7 +94,10 @@ const getBorderColor = (theme, variant, inverted) => {
94
94
  if (variant === 'outline-variant') {
95
95
  return button.surface.color.bgVariant;
96
96
  }
97
- return inverted ? button.surface.color.bgInverted : button[variant].color.bg;
97
+ if (inverted) {
98
+ return button.surface.color.bgInverted;
99
+ }
100
+ return button[variant].color.bg;
98
101
  };
99
102
  const getIconColor = (theme, inverted, variant) => {
100
103
  const button = theme.button;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { CurrencyInput } from '../CurrencyInput';
3
3
  import { Field } from '../Field';
4
- export const CurrencyField = ({ ...props }) => {
5
- return (_jsx(Field, { affixWidth: 40, ...props, children: _jsx(CurrencyInput, { ref: props.ref }) }));
4
+ export const CurrencyField = ({ ref, ...props }) => {
5
+ return (_jsx(Field, { affixWidth: 40, ...props, children: _jsx(CurrencyInput, { ref: ref }) }));
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { EmailInput } from '../EmailInput';
3
3
  import { Field } from '../Field';
4
- export const EmailField = ({ ...rest }) => {
5
- return (_jsx(Field, { ...rest, children: _jsx(EmailInput, {}) }));
4
+ export const EmailField = ({ ref, ...rest }) => {
5
+ return (_jsx(Field, { ...rest, children: _jsx(EmailInput, { ref: ref }) }));
6
6
  };
@@ -1,25 +1,27 @@
1
1
  import { numToPx } from '@ovotech/element-core';
2
2
  import styled, { css } from '../../styled.native';
3
3
  import { styledComponentWithBreakpoints } from '../../utils';
4
- const StyledCol = styled.View(({ theme, span = 12, small, medium, large, smallAndUp, mediumAndUp, largeAndUp, }) => {
4
+ const StyledCol = styled.View(({ theme, span, xsmallAndUp, small, smallAndUp, medium, mediumAndUp }) => {
5
5
  const COLS = 12;
6
- const gutter = mediumAndUp ? theme.space[800] : theme.space[400];
7
- const percentage = (span / COLS) * 100;
6
+ const gutter = mediumAndUp ? theme.space[600] : theme.space[400];
7
+ const percentage = span ? (span / COLS) * 100 : 100;
8
8
  const smallPercentage = small ? (small / COLS) * 100 : percentage;
9
9
  const mediumPercentage = medium ? (medium / COLS) * 100 : smallPercentage;
10
- const largePercentage = large ? (large / COLS) * 100 : mediumPercentage;
11
- const maxWidth = largeAndUp
12
- ? largePercentage
13
- : mediumAndUp
14
- ? mediumPercentage
15
- : smallAndUp
16
- ? smallPercentage
17
- : percentage;
10
+ let maxWidth;
11
+ if (mediumAndUp) {
12
+ maxWidth = mediumPercentage;
13
+ }
14
+ else if (smallAndUp || xsmallAndUp) {
15
+ maxWidth = span ? 50 : 100;
16
+ }
17
+ else {
18
+ maxWidth = 100;
19
+ }
18
20
  return css `
19
21
  flex: 0 0 auto;
20
22
  padding-left: ${numToPx(gutter / 2)};
21
23
  padding-right: ${numToPx(gutter / 2)};
22
- margin-bottom: ${numToPx(gutter)};
24
+ margin-bottom: ${numToPx(theme.space[smallAndUp ? 800 : 600])};
23
25
  max-width: ${maxWidth}%;
24
26
  width: 100%;
25
27
  `;
@@ -1,23 +1,19 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { numToPx } from '@ovotech/element-core';
2
+ import { numToPx } from '../..';
3
3
  import styled, { css } from '../../styled.native';
4
4
  import { styledComponentWithBreakpoints } from '../../utils';
5
5
  const StyledRow = styled.View(({ theme, isNested, smallAndUp, mediumAndUp }) => {
6
- const gutter = mediumAndUp ? theme.space[800] : theme.space[400];
7
- const margin = smallAndUp ? theme.space[800] : theme.space[400];
8
- /* We need to make up for the half gutter that is added to the left and right of a column, between the small and medium breakpoints */
9
- const additionalMargin = smallAndUp ? (margin - gutter) / 2 : 0;
6
+ const rowGap = mediumAndUp || smallAndUp ? theme.space[800] : theme.space[600];
7
+ const colGap = mediumAndUp ? theme.space[600] : theme.space[400];
10
8
  return css `
11
- padding-left: ${numToPx(margin / 2 + additionalMargin)};
12
- padding-right: ${numToPx(margin / 2 + additionalMargin)};
13
9
  margin: 0 auto;
14
- max-width: ${theme.breakpoint.large + margin}px;
10
+ max-width: ${theme.breakpoint.large + rowGap}px;
15
11
  width: 100%;
16
12
 
17
13
  ${isNested
18
14
  ? `
19
- margin-left: -${numToPx(margin / 2)};
20
- margin-right: -${numToPx(margin / 2)};
15
+ margin-left: -${numToPx(colGap / 2)};
16
+ margin-right: -${numToPx(colGap / 2)};
21
17
  width: auto;
22
18
  max-width: 200%; /* max width none not supported in RN */
23
19
  `
@@ -21,7 +21,7 @@ const StyledHeading = styled.Text(({ theme, smallAndUp, inverted, level, variant
21
21
  color: ${color};
22
22
  `;
23
23
  });
24
- const Heading = ({ accessibilityRole = 'header', ...rest }) => {
24
+ export const Heading = ({ accessibilityRole = 'header', ...rest }) => {
25
25
  const breakpoints = useBreakpoint();
26
26
  return (_jsx(StyledHeading, { accessible: true, accessibilityRole: accessibilityRole, ...breakpoints, ...rest }));
27
27
  };
@@ -1,33 +1,130 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import Animated, { Easing, FadeInDown, FadeInUp, FadeOutDown, FadeOutUp, useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated';
3
- import { Action } from '../ActionList';
4
- import { Button } from '../Button';
5
- import { BackActionWrapper, CollapsedTitle, CollapsedTitleWrapper, ExpandedTitle, HeaderGap, NavContainer, NavWrapper, } from './NavHeader.styles';
6
- const FADE_ANIMATION_DURATION = 200;
7
- export const NavHeader = ({ title, scrolled, canGoBack, rightActionConfig, topOffset = 0, backButtonText = 'Back', onHeaderHeightChange, onBackButtonPress, }) => {
8
- const hasRightAction = !!rightActionConfig;
9
- const hasTitle = title !== undefined && title?.length > 0;
10
- const layoutHeight = useSharedValue(0);
11
- const animatedHeightStyle = useAnimatedStyle(() => {
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo } from 'react';
3
+ import { Platform } from 'react-native';
4
+ import { ComplexAnimationBuilder, Easing, FadeInUp, LinearTransition, useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated';
5
+ import { useBreakpoint } from '../../hooks';
6
+ import { Heading, Heading2, Heading3, Heading4, } from '../Heading/Heading';
7
+ import { P } from '../P';
8
+ import { Strong } from '../Strong';
9
+ import { HeaderContainer, NavBackButton, NavContainer, ShadowContainer, SideContainer, SkeletonNavHeading, TitleContainer, } from './NavHeader.styles';
10
+ import { ActionButtons } from './NavHeaderComponents/ActionButtons';
11
+ import { StepCounterBars } from './NavHeaderComponents/StepCounterBars';
12
+ import { getTitleLength } from './utils';
13
+ const baseStyle = {
14
+ hasDropShadow: false,
15
+ isCollapsed: false,
16
+ };
17
+ const hasDropShadowOnly = {
18
+ hasDropShadow: true,
19
+ isCollapsed: false,
20
+ };
21
+ const isCollapsedAndDropShadow = {
22
+ hasDropShadow: true,
23
+ isCollapsed: true,
24
+ };
25
+ const scrollStyleMap = {
26
+ 0: {
27
+ initial: baseStyle,
28
+ scrollStart: baseStyle,
29
+ scrolling: isCollapsedAndDropShadow,
30
+ scrollBack: hasDropShadowOnly,
31
+ },
32
+ 1: {
33
+ initial: baseStyle,
34
+ scrollStart: hasDropShadowOnly,
35
+ scrolling: isCollapsedAndDropShadow,
36
+ scrollBack: hasDropShadowOnly,
37
+ },
38
+ 2: {
39
+ initial: hasDropShadowOnly,
40
+ scrollStart: hasDropShadowOnly,
41
+ scrolling: hasDropShadowOnly,
42
+ scrollBack: hasDropShadowOnly,
43
+ },
44
+ };
45
+ const LayerHeadingMap = {
46
+ 0: '3xl',
47
+ 1: '2xl',
48
+ 2: 'xl',
49
+ 3: 'xl',
50
+ 4: 'xl',
51
+ };
52
+ const ANIMATION_DURATION = 300;
53
+ export const NavHeader = ({ layer, onBackPress, actionButtons, title, stepCounter, scrollState, loading = false, }) => {
54
+ const displayTitle = getTitleLength({ layer, title });
55
+ const platform = Platform.OS;
56
+ const titleIsCentreAligned = layer > 1 && platform === 'ios';
57
+ const { isCollapsed, hasDropShadow } = scrollStyleMap[Math.min(layer, 2)][scrollState];
58
+ const shadowOpacity = useSharedValue(0.5);
59
+ const elevation = useSharedValue(9);
60
+ const LT = LinearTransition.duration(ANIMATION_DURATION);
61
+ const FADE_IN_DISTANCE = layer === 0 ? -68 : -50;
62
+ const FADE_OUT_DISTANCE = layer === 0 ? -72 : -52;
63
+ const ExitAnimation = useMemo(() => {
64
+ var _a;
65
+ return _a = class FadeOutUp extends ComplexAnimationBuilder {
66
+ constructor() {
67
+ super(...arguments);
68
+ this.build = () => {
69
+ const delayFunction = this.getDelayFunction();
70
+ const [animation, config] = this.getAnimationAndConfig();
71
+ const callback = this.callbackV;
72
+ const initialValues = this.initialValues;
73
+ const delay = this.getDelay();
74
+ return () => {
75
+ 'worklet';
76
+ return {
77
+ animations: {
78
+ opacity: delayFunction(delay, animation(0, config)),
79
+ transform: [
80
+ {
81
+ translateY: delayFunction(delay, animation(FADE_OUT_DISTANCE, config)),
82
+ },
83
+ ],
84
+ },
85
+ initialValues: {
86
+ opacity: 1,
87
+ transform: [{ translateY: 0 }],
88
+ ...initialValues,
89
+ },
90
+ callback,
91
+ };
92
+ };
93
+ };
94
+ }
95
+ static createInstance() {
96
+ return new _a();
97
+ }
98
+ },
99
+ _a.presetName = 'FadeOutUp',
100
+ _a;
101
+ }, [layer]);
102
+ const FadingAnimation = {
103
+ Enter: FadeInUp.duration(ANIMATION_DURATION)
104
+ .easing(Easing.ease)
105
+ .withInitialValues({ transform: [{ translateY: FADE_IN_DISTANCE }] }),
106
+ Exit: ExitAnimation.duration(ANIMATION_DURATION).easing(Easing.linear),
107
+ };
108
+ useEffect(() => {
109
+ shadowOpacity.value = withTiming(hasDropShadow ? 0.5 : 0, {
110
+ duration: ANIMATION_DURATION,
111
+ easing: Easing.ease,
112
+ });
113
+ elevation.value = withTiming(hasDropShadow ? 9 : 0, {
114
+ duration: ANIMATION_DURATION,
115
+ easing: Easing.ease,
116
+ });
117
+ }, [scrollState, shadowOpacity, hasDropShadow, elevation, isCollapsed]);
118
+ const animatedShadowStyle = useAnimatedStyle(() => {
12
119
  return {
13
- height: layoutHeight.value,
120
+ shadowOpacity: shadowOpacity.value,
121
+ elevation: elevation.value,
14
122
  };
15
123
  });
16
- const onLayout = (e) => {
17
- const { height } = e.nativeEvent.layout;
18
- if (height === layoutHeight.value) {
19
- return;
20
- }
21
- // we want to only update height of the expanded header to set content offset on the client side
22
- if (!scrolled) {
23
- onHeaderHeightChange?.(height);
24
- }
25
- layoutHeight.value = withTiming(height, {
26
- duration: FADE_ANIMATION_DURATION,
27
- easing: Easing.linear,
28
- });
29
- };
30
- // NavWrapper will recieve height of the NavContainer and animate it
31
- // content inside NavContainer is the only dynamic part of NavHeader
32
- return (_jsx(NavWrapper, { collapsed: scrolled, style: animatedHeightStyle, children: _jsxs(NavContainer, { topOffset: topOffset, hasTitle: hasTitle, onLayout: onLayout, children: [_jsx(BackActionWrapper, { collapsed: scrolled, hasBackButton: canGoBack, children: canGoBack && (_jsx(Action, { standalone: true, iconLeft: "chevron-left-small", onPress: onBackButtonPress, children: backButtonText })) }), scrolled ? (_jsxs(_Fragment, { children: [_jsx(CollapsedTitleWrapper, { entering: FadeInDown.delay(FADE_ANIMATION_DURATION), exiting: FadeOutDown.duration(FADE_ANIMATION_DURATION), children: _jsx(CollapsedTitle, { numberOfLines: 1, ellipsizeMode: "tail", children: title }) }), _jsx(HeaderGap, { hasRightAction: hasRightAction }), hasRightAction && (_jsx(_Fragment, { children: _jsx(Animated.View, { entering: FadeInDown.delay(FADE_ANIMATION_DURATION), exiting: FadeOutDown.duration(FADE_ANIMATION_DURATION), accessibilityRole: "button", accessibilityLabel: rightActionConfig?.accessibilityLabel, accessibilityHint: rightActionConfig?.accessibilityHint, children: _jsx(Button, { iconRight: rightActionConfig.iconName, onPress: rightActionConfig.onActionPress }) }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(ExpandedTitle, { hasRightAction: !hasRightAction, hasTitle: hasTitle, numberOfLines: 2, ellipsizeMode: "tail", hasBackButton: canGoBack, entering: FadeInUp.delay(FADE_ANIMATION_DURATION), exiting: FadeOutUp.duration(FADE_ANIMATION_DURATION), children: title }), hasRightAction && (_jsx(Animated.View, { entering: FadeInUp.delay(FADE_ANIMATION_DURATION), exiting: FadeOutUp.duration(FADE_ANIMATION_DURATION), accessibilityRole: "button", accessibilityLabel: rightActionConfig?.accessibilityLabel, accessibilityHint: rightActionConfig?.accessibilityHint, children: _jsx(Button, { iconRight: rightActionConfig.iconName, onPress: rightActionConfig.onActionPress }) }))] }))] }) }));
124
+ const breakpoints = useBreakpoint();
125
+ return (_jsx(ShadowContainer, { style: [animatedShadowStyle], children: _jsx(NavContainer, { breakpoints: breakpoints, layer: layer, layout: LT, isCollapsed: isCollapsed, children: !isCollapsed && (_jsxs(_Fragment, { children: [_jsxs(HeaderContainer, { layout: LT, entering: FadingAnimation.Enter, exiting: FadingAnimation.Exit, children: [_jsx(SideContainer, { flex: titleIsCentreAligned ? 1 : 0, side: "start", children: layer > 1 && (_jsx(NavBackButton, { onPress: () => {
126
+ onBackPress && onBackPress();
127
+ }, variant: "bright", iconLeft: platform === 'ios' ? 'chevron-left' : 'arrow-left' })) }), _jsxs(TitleContainer, { layer: layer, isIOS: platform === 'ios', children: [loading ? (_jsx(SkeletonNavHeading, { size: "100%", headingComponent: layer === 0 ? Heading2 : layer === 1 ? Heading3 : Heading4 })) : layer < 2 ? (_jsx(Heading, { inverted: layer < 2, level: LayerHeadingMap[layer], children: displayTitle })) : (_jsx(P, { size: "md", children: _jsx(Strong, { maxLength: 23, children: displayTitle }) })), stepCounter && !loading && (_jsx(P, { size: "sm", children: `Step ${stepCounter.currentStep <= stepCounter.totalSteps
128
+ ? stepCounter.currentStep
129
+ : stepCounter.totalSteps} of ${stepCounter.totalSteps}` }))] }), _jsx(SideContainer, { side: "end", children: actionButtons && (_jsx(ActionButtons, { loading: loading, layer: layer, actionButtons: actionButtons })) })] }), stepCounter && (_jsx(StepCounterBars, { loading: loading, totalSteps: stepCounter.totalSteps, currentStep: stepCounter.currentStep }))] })) }) }));
33
130
  };
@@ -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,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,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,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,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
+ };
@@ -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,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,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';
@@ -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);