@hero-design/rn 8.55.1 → 8.56.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 (82) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/es/index.js +5205 -4772
  4. package/lib/index.js +5211 -4775
  5. package/package.json +1 -1
  6. package/src/components/AnimatedScroller/AnimatedFAB.tsx +105 -0
  7. package/src/components/AnimatedScroller/AnimatedScrollable.tsx +55 -0
  8. package/src/components/AnimatedScroller/FlatListWithFAB.tsx +23 -0
  9. package/src/components/AnimatedScroller/ScrollViewWithFAB.tsx +21 -0
  10. package/src/components/AnimatedScroller/SectionListWithFAB.tsx +26 -0
  11. package/src/components/AnimatedScroller/__tests__/ScrollablesWithFAB.spec.tsx +172 -0
  12. package/src/components/AnimatedScroller/__tests__/__snapshots__/ScrollablesWithFAB.spec.tsx.snap +3435 -0
  13. package/src/components/AnimatedScroller/index.tsx +5 -0
  14. package/src/components/FAB/FAB.tsx +6 -6
  15. package/src/index.ts +8 -0
  16. package/types/components/Accordion/StyledAccordion.d.ts +5 -5
  17. package/types/components/Alert/StyledAlert.d.ts +5 -5
  18. package/types/components/AnimatedScroller/AnimatedFAB.d.ts +10 -0
  19. package/types/components/AnimatedScroller/AnimatedScrollable.d.ts +16 -0
  20. package/types/components/AnimatedScroller/FlatListWithFAB.d.ts +12 -0
  21. package/types/components/AnimatedScroller/ScrollViewWithFAB.d.ts +12 -0
  22. package/types/components/AnimatedScroller/SectionListWithFAB.d.ts +12 -0
  23. package/types/components/AnimatedScroller/index.d.ts +4 -0
  24. package/types/components/Attachment/StyledAttachment.d.ts +4 -4
  25. package/types/components/Avatar/AvatarStack/StyledAvatarStack.d.ts +3 -3
  26. package/types/components/Avatar/StyledAvatar.d.ts +4 -4
  27. package/types/components/Badge/StyledBadge.d.ts +3 -3
  28. package/types/components/BottomNavigation/StyledBottomNavigation.d.ts +7 -7
  29. package/types/components/BottomSheet/StyledBottomSheet.d.ts +8 -8
  30. package/types/components/Box/StyledBox.d.ts +1 -1
  31. package/types/components/Button/LoadingIndicator/StyledLoadingIndicator.d.ts +2 -2
  32. package/types/components/Button/StyledButton.d.ts +5 -5
  33. package/types/components/Button/UtilityButton/StyledUtilityButton.d.ts +3 -3
  34. package/types/components/Calendar/StyledCalendar.d.ts +9 -9
  35. package/types/components/Card/DataCard/StyledDataCard.d.ts +2 -2
  36. package/types/components/Card/StyledCard.d.ts +1 -1
  37. package/types/components/Carousel/StyledCardCarousel.d.ts +4 -4
  38. package/types/components/Carousel/StyledCarousel.d.ts +9 -9
  39. package/types/components/Checkbox/StyledCheckbox.d.ts +5 -5
  40. package/types/components/Chip/StyledChip.d.ts +4 -4
  41. package/types/components/Collapse/StyledCollapse.d.ts +3 -3
  42. package/types/components/ContentNavigator/StyledContentNavigator.d.ts +1 -1
  43. package/types/components/DatePicker/StyledDatePicker.d.ts +1 -1
  44. package/types/components/Divider/StyledDivider.d.ts +1 -1
  45. package/types/components/Drawer/StyledDrawer.d.ts +7 -7
  46. package/types/components/Empty/StyledEmpty.d.ts +3 -3
  47. package/types/components/Error/StyledError.d.ts +10 -10
  48. package/types/components/FAB/ActionGroup/StyledActionGroup.d.ts +5 -5
  49. package/types/components/FAB/ActionGroup/StyledActionItem.d.ts +3 -3
  50. package/types/components/FAB/StyledFAB.d.ts +4 -4
  51. package/types/components/Icon/HeroIcon/index.d.ts +1 -1
  52. package/types/components/List/StyledBasicListItem.d.ts +4 -4
  53. package/types/components/List/StyledListItem.d.ts +7 -7
  54. package/types/components/PageControl/StyledPageControl.d.ts +2 -2
  55. package/types/components/PinInput/StyledPinInput.d.ts +10 -10
  56. package/types/components/Progress/StyledProgressBar.d.ts +2 -2
  57. package/types/components/Progress/StyledProgressCircle.d.ts +6 -6
  58. package/types/components/Radio/StyledRadio.d.ts +3 -3
  59. package/types/components/Rate/StyledRate.d.ts +2 -2
  60. package/types/components/RichTextEditor/StyledRichTextEditor.d.ts +2 -2
  61. package/types/components/RichTextEditor/StyledToolbar.d.ts +3 -3
  62. package/types/components/SectionHeading/StyledHeading.d.ts +3 -3
  63. package/types/components/Select/StyledSelect.d.ts +3 -3
  64. package/types/components/Skeleton/StyledSkeleton.d.ts +2 -2
  65. package/types/components/Spinner/StyledSpinner.d.ts +4 -4
  66. package/types/components/Success/StyledSuccess.d.ts +9 -9
  67. package/types/components/Swipeable/StyledSwipeable.d.ts +1 -1
  68. package/types/components/Switch/SelectorSwitch/StyledSelectorSwitch.d.ts +4 -4
  69. package/types/components/Switch/StyledSwitch.d.ts +2 -2
  70. package/types/components/Tabs/StyledScrollableTabs.d.ts +8 -8
  71. package/types/components/Tabs/StyledTabs.d.ts +8 -8
  72. package/types/components/Tag/StyledTag.d.ts +2 -2
  73. package/types/components/TextInput/StyledTextInput.d.ts +13 -13
  74. package/types/components/TimePicker/StyledTimePicker.d.ts +1 -1
  75. package/types/components/Toast/StyledToast.d.ts +6 -6
  76. package/types/components/Toolbar/StyledToolbar.d.ts +5 -5
  77. package/types/components/Typography/Body/StyledBody.d.ts +1 -1
  78. package/types/components/Typography/Caption/StyledCaption.d.ts +1 -1
  79. package/types/components/Typography/Label/StyledLabel.d.ts +1 -1
  80. package/types/components/Typography/Text/StyledText.d.ts +1 -1
  81. package/types/components/Typography/Title/StyledTitle.d.ts +1 -1
  82. package/types/index.d.ts +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/rn",
3
- "version": "8.55.1",
3
+ "version": "8.56.1",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -0,0 +1,105 @@
1
+ import React from 'react';
2
+ import { Animated } from 'react-native';
3
+ import FAB from '../FAB';
4
+ import ActionGroup, {
5
+ ActionGroupHandles,
6
+ ActionGroupProps,
7
+ } from '../FAB/ActionGroup';
8
+ import { FABHandles, FABProps } from '../FAB/FAB';
9
+
10
+ const COLLAPSE_BREAKPOINT = 10;
11
+ const SHOW_AND_HIDE_BREAKPOINT = 200;
12
+ const REF_ACTIONS_BY_COMPONENT = {
13
+ FAB: {
14
+ show: 'show',
15
+ hide: 'hide',
16
+ collapse: 'collapse',
17
+ },
18
+ ActionGroup: {
19
+ show: 'showFAB',
20
+ hide: 'hideFAB',
21
+ collapse: 'collapseFAB',
22
+ },
23
+ } as const;
24
+
25
+ interface AnimatedFABProps {
26
+ fabProps: FABProps | ActionGroupProps;
27
+ contentOffsetY: Animated.Value;
28
+ }
29
+
30
+ const AnimatedFAB = ({ fabProps, contentOffsetY }: AnimatedFABProps) => {
31
+ const component = 'items' in fabProps ? 'ActionGroup' : 'FAB';
32
+ const [currentScrollDirection, setCurrentScrollDirection] = React.useState<
33
+ 'up' | 'down'
34
+ >('down');
35
+ const [lastScrollY, setLastScrollY] = React.useState(0);
36
+ const [fabState, setFabState] = React.useState<'show' | 'hide' | 'collapse'>(
37
+ 'show'
38
+ );
39
+ const [remainingScrollOffset, setRemainingScrollOffset] = React.useState(
40
+ SHOW_AND_HIDE_BREAKPOINT
41
+ );
42
+ const ref = React.useRef<FABHandles & ActionGroupHandles>(null);
43
+
44
+ const animateFab = React.useCallback(
45
+ (newState: 'show' | 'hide' | 'collapse') => {
46
+ if (fabState !== newState) {
47
+ if (newState === 'show') {
48
+ ref.current?.[REF_ACTIONS_BY_COMPONENT[component].show]();
49
+ } else if (newState === 'hide') {
50
+ ref.current?.[REF_ACTIONS_BY_COMPONENT[component].hide]();
51
+ } else {
52
+ ref.current?.[REF_ACTIONS_BY_COMPONENT[component].collapse]();
53
+ }
54
+ setFabState(newState);
55
+ }
56
+ },
57
+ [fabState, component]
58
+ );
59
+
60
+ // Listen to ScrollView's contentOffsetY value
61
+ contentOffsetY.addListener(({ value }) => {
62
+ if (value < 0) {
63
+ return;
64
+ }
65
+
66
+ const newScrollDirection = value > lastScrollY ? 'down' : 'up';
67
+ const isScrollingDown = newScrollDirection === 'down';
68
+
69
+ if (newScrollDirection !== currentScrollDirection || lastScrollY === 0) {
70
+ setLastScrollY(value);
71
+ setCurrentScrollDirection(newScrollDirection);
72
+ }
73
+ const offsetFromLastDirection = Math.abs(value - lastScrollY);
74
+ const offsetDiff = Math.round(
75
+ Math.max(remainingScrollOffset - offsetFromLastDirection, 0)
76
+ );
77
+
78
+ if (remainingScrollOffset > 0) {
79
+ if (offsetDiff === SHOW_AND_HIDE_BREAKPOINT) {
80
+ animateFab(isScrollingDown ? 'show' : 'hide');
81
+ } else if (
82
+ offsetDiff <= SHOW_AND_HIDE_BREAKPOINT &&
83
+ offsetDiff > COLLAPSE_BREAKPOINT
84
+ ) {
85
+ animateFab('collapse');
86
+ } else if (offsetDiff <= COLLAPSE_BREAKPOINT) {
87
+ animateFab(isScrollingDown ? 'hide' : 'show');
88
+ }
89
+
90
+ setRemainingScrollOffset(offsetDiff);
91
+ }
92
+ });
93
+
94
+ React.useEffect(() => {
95
+ setRemainingScrollOffset(SHOW_AND_HIDE_BREAKPOINT);
96
+ }, [currentScrollDirection]);
97
+
98
+ return component === 'FAB' ? (
99
+ <FAB ref={ref} {...(fabProps as FABProps)} />
100
+ ) : (
101
+ <ActionGroup ref={ref} {...(fabProps as ActionGroupHandles)} />
102
+ );
103
+ };
104
+
105
+ export default AnimatedFAB;
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import {
3
+ Animated,
4
+ FlatListProps,
5
+ ScrollViewProps as RnScrollViewProps,
6
+ SectionListProps,
7
+ } from 'react-native';
8
+ import { FABProps } from '../FAB/FAB';
9
+ import AnimatedFAB from './AnimatedFAB';
10
+ import { ActionGroupProps } from '../FAB/ActionGroup';
11
+
12
+ export interface AnimatedScrollerProps<T> {
13
+ /**
14
+ * Scroll component, it can be ScrollView, FlatList or SectionList.
15
+ */
16
+ ScrollComponent: React.ReactElement<
17
+ RnScrollViewProps | FlatListProps<T> | SectionListProps<T>
18
+ >;
19
+ /**
20
+ * FAB or FAB.ActionGroup props.
21
+ */
22
+ fabProps: FABProps | ActionGroupProps;
23
+ }
24
+
25
+ function AnimatedScroller<T>({
26
+ ScrollComponent,
27
+ fabProps,
28
+ }: AnimatedScrollerProps<T>) {
29
+ const contentOffsetY = React.useRef(new Animated.Value(0)).current;
30
+
31
+ // Common props for all ScrollView, FlatList and SectionList.
32
+ const { onScroll, scrollEventThrottle } = ScrollComponent.props;
33
+
34
+ return (
35
+ <>
36
+ {React.cloneElement(ScrollComponent, {
37
+ ...ScrollComponent.props,
38
+ scrollEventThrottle: scrollEventThrottle || 100,
39
+ onScroll: Animated.event(
40
+ [{ nativeEvent: { contentOffset: { y: contentOffsetY } } }],
41
+ {
42
+ useNativeDriver: false,
43
+ listener: onScroll,
44
+ }
45
+ ),
46
+ })}
47
+
48
+ {!!fabProps && (
49
+ <AnimatedFAB fabProps={fabProps} contentOffsetY={contentOffsetY} />
50
+ )}
51
+ </>
52
+ );
53
+ }
54
+
55
+ export default AnimatedScroller;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { FlatList, FlatListProps } from 'react-native';
3
+ import { FABProps } from '../FAB/FAB';
4
+ import AnimatedScroller from './AnimatedScrollable';
5
+ import { ActionGroupProps } from '../FAB/ActionGroup';
6
+
7
+ export interface FlatListWithFABProps<T> extends FlatListProps<T> {
8
+ /**
9
+ * FAB or FAB.ActionGroup props props.
10
+ */
11
+ fabProps: FABProps | ActionGroupProps;
12
+ }
13
+
14
+ function FlatListWithFAB<T>({ fabProps, ...props }: FlatListWithFABProps<T>) {
15
+ return (
16
+ <AnimatedScroller
17
+ ScrollComponent={<FlatList {...props} />}
18
+ fabProps={fabProps}
19
+ />
20
+ );
21
+ }
22
+
23
+ export default FlatListWithFAB;
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { ScrollView, ScrollViewProps } from 'react-native';
3
+ import AnimatedScroller from './AnimatedScrollable';
4
+ import { ActionGroupProps } from '../FAB/ActionGroup';
5
+ import { FABProps } from '../FAB/FAB';
6
+
7
+ export interface ScrollViewWithFABProps extends ScrollViewProps {
8
+ /**
9
+ * FAB or FAB.ActionGroup props props.
10
+ */
11
+ fabProps: FABProps | ActionGroupProps;
12
+ }
13
+
14
+ const ScrollViewWithFAB = ({ fabProps, ...props }: ScrollViewWithFABProps) => (
15
+ <AnimatedScroller
16
+ ScrollComponent={<ScrollView {...props} />}
17
+ fabProps={fabProps}
18
+ />
19
+ );
20
+
21
+ export default ScrollViewWithFAB;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { SectionList, SectionListProps } from 'react-native';
3
+ import AnimatedScroller from './AnimatedScrollable';
4
+ import { ActionGroupProps } from '../FAB/ActionGroup';
5
+ import { FABProps } from '../FAB/FAB';
6
+
7
+ export interface SectionListWithFABProps<T> extends SectionListProps<T> {
8
+ /**
9
+ * FAB or FAB.ActionGroup props props.
10
+ */
11
+ fabProps: FABProps | ActionGroupProps;
12
+ }
13
+
14
+ function SectionListWithFAB<T>({
15
+ fabProps,
16
+ ...props
17
+ }: SectionListWithFABProps<T>) {
18
+ return (
19
+ <AnimatedScroller
20
+ ScrollComponent={<SectionList {...props} />}
21
+ fabProps={fabProps}
22
+ />
23
+ );
24
+ }
25
+
26
+ export default SectionListWithFAB;
@@ -0,0 +1,172 @@
1
+ import React from 'react';
2
+ import { useTheme } from '@emotion/react';
3
+ import { fireEvent } from '@testing-library/react-native';
4
+ import renderWithTheme from '../../../testHelpers/renderWithTheme';
5
+ import { FlatListWithFAB, ScrollViewWithFAB, SectionListWithFAB } from '..';
6
+ import Box from '../../Box';
7
+ import { ActionGroupProps } from '../../FAB/ActionGroup';
8
+ import { FABProps } from '../../FAB/FAB';
9
+ import Typography from '../../Typography';
10
+ import { ScrollViewWithFABProps } from '../ScrollViewWithFAB';
11
+ import { FlatListWithFABProps } from '../FlatListWithFAB';
12
+ import SectionHeading from '../../SectionHeading';
13
+
14
+ const ExampleScrollViewWithFAB = ({ fabProps }: ScrollViewWithFABProps) => (
15
+ <ScrollViewWithFAB fabProps={fabProps} testID="scrollable-with-fab">
16
+ <Box style={{ height: 2000 }} justifyContent="space-between">
17
+ <Typography.Body>Content 1</Typography.Body>
18
+ <Typography.Body>Content 2</Typography.Body>
19
+ <Typography.Body>Content 3</Typography.Body>
20
+ </Box>
21
+ </ScrollViewWithFAB>
22
+ );
23
+
24
+ function ExampleFlatListWithFAB<T>({ fabProps }: FlatListWithFABProps<T>) {
25
+ return (
26
+ <FlatListWithFAB
27
+ testID="scrollable-with-fab"
28
+ fabProps={fabProps}
29
+ data={Array.from({ length: 30 }, (_, i) => ({
30
+ id: i,
31
+ title: `Content ${i}`,
32
+ }))}
33
+ renderItem={({ item }) => (
34
+ <Box marginBottom="xxxxlarge">
35
+ <Typography.Title level="h5">{item.title}</Typography.Title>
36
+ <Typography.Body>Item {item.id}</Typography.Body>
37
+ </Box>
38
+ )}
39
+ />
40
+ );
41
+ }
42
+
43
+ function ExampleSectionListWithFAB<T>({ fabProps }: FlatListWithFABProps<T>) {
44
+ const theme = useTheme();
45
+
46
+ return (
47
+ <SectionListWithFAB
48
+ testID="scrollable-with-fab"
49
+ fabProps={fabProps}
50
+ sections={[
51
+ {
52
+ title: 'Title 1',
53
+ data: ['Content 1', 'Content 2', 'Content 3', 'Content 4'],
54
+ },
55
+ {
56
+ title: 'Title 2',
57
+ data: ['Content 5', 'Content 6', 'Content 7'],
58
+ },
59
+ ]}
60
+ renderSectionHeader={({ section: { title } }) => (
61
+ <SectionHeading text={title} />
62
+ )}
63
+ renderItem={({ item }) => (
64
+ <Typography.Title style={{ marginBottom: theme.space.xxxxlarge }}>
65
+ {item}
66
+ </Typography.Title>
67
+ )}
68
+ />
69
+ );
70
+ }
71
+
72
+ const defaultFabProps: FABProps = {
73
+ icon: 'speaker',
74
+ title: 'Shout out',
75
+ animated: true,
76
+ };
77
+
78
+ const defaultActionGroupProps: ActionGroupProps = {
79
+ fabTitle: 'Shout out',
80
+ items: [
81
+ { icon: 'speaker', title: 'Announcements' },
82
+ { icon: 'target', title: 'Goal' },
83
+ ],
84
+ active: false,
85
+ };
86
+
87
+ describe('Scrollables With FAB', () => {
88
+ it.each`
89
+ fabComponent | ScrollComponent
90
+ ${'FAB'} | ${ExampleScrollViewWithFAB}
91
+ ${'ActionGroup'} | ${ExampleScrollViewWithFAB}
92
+ ${'FAB'} | ${ExampleFlatListWithFAB}
93
+ ${'ActionGroup'} | ${ExampleFlatListWithFAB}
94
+ ${'FAB'} | ${ExampleSectionListWithFAB}
95
+ ${'ActionGroup'} | ${ExampleSectionListWithFAB}
96
+ `(
97
+ '$ScrollComponent renders $fabComponent correctly',
98
+ ({ fabComponent, ScrollComponent }) => {
99
+ const fabProps =
100
+ fabComponent === 'FAB' ? defaultFabProps : defaultActionGroupProps;
101
+
102
+ const { getByText, toJSON } = renderWithTheme(
103
+ <ScrollComponent fabProps={fabProps} />
104
+ );
105
+
106
+ expect(toJSON()).toMatchSnapshot();
107
+ expect(getByText('Content 1')).toBeDefined();
108
+ expect(getByText('Content 2')).toBeDefined();
109
+ expect(getByText('Content 3')).toBeDefined();
110
+ expect(getByText('Shout out')).toBeDefined();
111
+ }
112
+ );
113
+
114
+ it.each`
115
+ fabComponent | ScrollComponent
116
+ ${'FAB'} | ${ExampleScrollViewWithFAB}
117
+ ${'ActionGroup'} | ${ExampleScrollViewWithFAB}
118
+ ${'FAB'} | ${ExampleFlatListWithFAB}
119
+ ${'ActionGroup'} | ${ExampleFlatListWithFAB}
120
+ ${'FAB'} | ${ExampleSectionListWithFAB}
121
+ ${'ActionGroup'} | ${ExampleSectionListWithFAB}
122
+ `(
123
+ '$ScrollComponent animates $fabComponent correctly',
124
+ ({ fabComponent, ScrollComponent }) => {
125
+ const fabProps =
126
+ fabComponent === 'FAB' ? defaultFabProps : defaultActionGroupProps;
127
+
128
+ const { getByText, queryByText, getByTestId, queryByTestId } =
129
+ renderWithTheme(<ScrollComponent fabProps={fabProps} />);
130
+
131
+ // Scrolling down
132
+ expect(getByText('Shout out')).toBeDefined();
133
+
134
+ fireEvent.scroll(getByTestId('scrollable-with-fab'), {
135
+ nativeEvent: {
136
+ contentSize: { height: 1000 },
137
+ contentOffset: { y: 10 },
138
+ layoutMeasurement: { height: 2000, width: 400 },
139
+ },
140
+ });
141
+
142
+ // Collapsed
143
+ expect(queryByText('Shout out')).toBeNull();
144
+ expect(getByTestId('animated-fab-icon')).toBeDefined();
145
+
146
+ fireEvent.scroll(getByTestId('scrollable-with-fab'), {
147
+ nativeEvent: {
148
+ contentSize: { height: 1000 },
149
+ contentOffset: { y: 400 },
150
+ layoutMeasurement: { height: 2000, width: 400 },
151
+ },
152
+ });
153
+
154
+ // Hidden
155
+ expect(queryByText('Shout out')).toBeNull();
156
+ expect(queryByTestId('styled-fab-icon')).toBeNull();
157
+
158
+ // Scrolling up
159
+ fireEvent.scroll(getByTestId('scrollable-with-fab'), {
160
+ nativeEvent: {
161
+ contentSize: { height: 1000 },
162
+ contentOffset: { y: -10 },
163
+ layoutMeasurement: { height: 2000, width: 400 },
164
+ },
165
+ });
166
+
167
+ // Collapsed
168
+ expect(queryByText('Shout out')).toBeNull();
169
+ expect(getByTestId('animated-fab-icon')).toBeDefined();
170
+ }
171
+ );
172
+ });