Compare commits

...

60 Commits

Author SHA1 Message Date
Piotr Pietruszewski
ef8ac83f3b Merge remote-tracking branch 'origin/master' into #126-scrollview_reference
# Conflicts:
#	README.md
#	src/StickyParallaxHeader.js
#	src/index.d.ts
2020-09-22 08:42:02 +02:00
Piotr Pietruszewski
95f2b99fbb
Merge pull request #135 from netguru/#129-keyboardShouldPersistTabs_prop
feat: added keyboardShouldPersistTaps props
2020-09-22 08:38:48 +02:00
Piotr Pietruszewski
3bc004c8f4 fix: added missing keyboardShouldPersistTaps type for typescript 2020-09-22 08:36:59 +02:00
Piotr Pietruszewski
7f59e78f87 feat: added more info to readme about scroll view handling 2020-09-21 15:40:16 +02:00
Piotr Pietruszewski
235d1a8efd fix: added object type for useRef value 2020-09-21 15:38:09 +02:00
Piotr Pietruszewski
c2875ea452 fix: added missing space before comma in import 2020-09-21 15:37:44 +02:00
Piotr Pietruszewski
dd00177c16 feat: added keyboardShouldPersistTaps props 2020-09-21 15:02:58 +02:00
Piotr Pietruszewski
158bcceeb7 feat: added body scrollview ref 2020-09-21 14:32:36 +02:00
Natalia
d5f39e71e2
Merge pull request #130 from netguru/bn-2449_prop_types
docs: add shared props
2020-09-07 13:39:47 +02:00
NataliaMuryn
1643e99c92 docs: Fix typo 2020-09-07 12:04:51 +02:00
NataliaMuryn
0dd8befcc1 docs: add shared props 2020-09-06 17:38:55 +02:00
Natalia
e973344d7f
Merge pull request #127 from netguru/BN-2471-add-containerStyles
[BN-2471] add container styles
2020-09-03 12:13:29 +02:00
NataliaMuryn
d5ddd6d27b Remove console.warn 2020-09-03 11:29:22 +02:00
NataliaMuryn
f8686ad3ef docs: add contentContainerStyles to docs 2020-09-03 11:27:34 +02:00
NataliaMuryn
c55cdba695 feat: bump library version in Example app 2020-09-03 11:12:48 +02:00
NataliaMuryn
57b69fb8aa feat: add contentContainerStyles possibility 2020-09-03 11:11:54 +02:00
Piotr Pietruszewski
89b9409d05
Merge pull request #123 from netguru/release_0.3.0
chore: release 0.3.0
2020-08-17 16:11:49 +02:00
Paweł Wysowski
d9fd875f17
Merge pull request #117 from netguru/#113-flatlist-not-scrollable-on-ios
#113 Flatlist not scrollable on iOS
2020-08-17 10:26:03 +02:00
Piotr Pietruszewski
23cdd7248d docs: updated example implementation info 2020-08-17 09:28:18 +02:00
Piotr Pietruszewski
700cdac419
Merge pull request #119 from netguru/#90-scroll-position-on-tab-change
#90 Scroll position on tab change
2020-08-17 09:10:43 +02:00
Piotr Pietruszewski
0247a0ab6f fix: reverted removed commented code 2020-08-17 08:17:20 +02:00
Piotr Pietruszewski
af9268e14d fix: changed scroll enabled condition for flat list in card screen 2020-08-17 08:05:25 +02:00
Piotr Pietruszewski
32582cc9ba fix: removed commented code 2020-08-17 08:04:07 +02:00
Piotr Pietruszewski
5d79cd3f89 fix: replaced platform check with is android in card screen 2020-08-14 13:50:58 +02:00
Piotr Pietruszewski
08c5958fc4 fix: removed unnecessary space in card screen 2020-08-14 13:44:58 +02:00
Piotr Pietruszewski
15578efb73 fix: fixed screen styles formatting 2020-08-14 13:42:59 +02:00
Piotr Pietruszewski
2bba9893cc docs: added info about rememberTabScrollPosition prop 2020-08-07 16:09:32 +02:00
Piotr Pietruszewski
7c2d1776c8 fix: saved position of scroll for tab 2020-08-07 16:09:06 +02:00
Piotr Pietruszewski
4132ca4d9a
Merge pull request #118 from netguru/#63-tab-scroll-view-height-issue
#63 #92 Content is scrollable when one tab is longer
2020-08-07 15:38:51 +02:00
Piotr Pietruszewski
756ea48a23 fix: variable typo minViewportHeight and styling 2020-08-07 14:21:37 +02:00
Piotr Pietruszewski
b585a6a070 fix: moved prevState currentPage to variable 2020-08-07 14:18:02 +02:00
Piotr Pietruszewski
47594b66fb fix: tabs content scrollable when one tab is longer than others 2020-08-07 11:40:45 +02:00
pwysowski
f4d77f44ce docs: updated flatlist implementation info 2020-08-05 12:28:39 +02:00
pwysowski
7bbe85c416 feat: added flatlist example implementation 2020-08-05 10:46:24 +02:00
pwysowski
0f3d0b800b feat: added onTopReached callback 2020-08-05 10:06:40 +02:00
Daniel Idaszak
620a5ef91f
Merge pull request #115 from vitordino/patch-1
docs: change `left bottom border` to `right bottom border`
2020-07-28 13:30:19 +02:00
Vitor Dino
d7e30e6eb9
docs: change left bottom border to right bottom border
at least on the demos it shows `right bottom` border curved, and not `left bottom` one
2020-07-24 16:37:56 -03:00
pwysowski
99a5bc39f5 chore: release 0.3.0 2020-07-02 09:44:10 +02:00
Paweł Wysowski
2e9d88c446
Merge pull request #112 from netguru/contributing-guidelines-update
docs: updated contributing guidelines
2020-07-01 13:48:00 +02:00
Paweł Wysowski
0cbf384b02
Merge pull request #111 from netguru/#104-set-active-page-feature
feat: created onRef property to call goToPage function
2020-07-01 13:37:50 +02:00
Paweł Wysowski
a06b1a4390
docs: updated contributing guidelines 2020-07-01 13:11:55 +02:00
Paweł Wysowski
5251867f25
Merge branch 'master' into #104-set-active-page-feature 2020-07-01 12:25:00 +02:00
pwysowski
44db4f7882 Merge branch 'master' into #104-set-active-page-feature 2020-07-01 12:23:43 +02:00
pwysowski
30adeccf83 docs: updated readme with onRef property 2020-07-01 12:16:15 +02:00
pwysowski
debf4ff8b9 feat: created onRef property to call goToPage function 2020-07-01 11:19:24 +02:00
Paweł Wysowski
afe974f9e3
Merge pull request #98 from netguru/RNS-1621_Add_type_definitions
[RNS-1621] Add type definitions
2020-06-30 14:30:50 +02:00
Paweł Wysowski
577c7d4aa5
Merge branch 'master' into RNS-1621_Add_type_definitions 2020-06-30 14:28:31 +02:00
Paweł Wysowski
ba77e5742a
Merge pull request #109 from netguru/#102-scroll-node-issue-scrollable-tab-view
fix: fixed scrollNode issue inside ScrollableTabView
2020-06-30 10:32:28 +02:00
pwysowski
1b605f0cba refactor: changed scrollNode check also in scrollToTop 2020-06-30 10:27:09 +02:00
Paweł Wysowski
0297e38391
Merge pull request #108 from netguru/#107-image-proptypes-issue
fix: changed all images propTypes to Image.propTypes.source
2020-06-30 10:02:12 +02:00
pwysowski
a4c91834e5 fix: fixed scrollNode issue inside ScrollableTabView 2020-06-30 09:36:08 +02:00
pwysowski
3300eff474 fix: changed all images propTypes to Image.propTypes.source 2020-06-30 08:31:51 +02:00
Paweł Wysowski
1b7b1f512a
Merge pull request #106 from netguru/#102-scroll-node-issue-fix
fix: fixed issue with scrollNode
2020-06-29 14:53:16 +02:00
Maciej Budziński
b70609c279 feat: move index.d.ts file to src directory 2020-06-07 21:39:05 +02:00
Maciej Budziński
87f55cb4a0 fix: change prop name from type to headerType 2020-06-07 21:38:03 +02:00
Maciej Budziński
b827ca6db0 fix: lint issues 2020-06-07 21:18:58 +02:00
Maciej Budziński
cdb421265a feat: clean up import in type declaration file 2020-06-07 21:18:02 +02:00
Maciej Budziński
3e4c65ec77 fix: add type to header props type 2020-06-07 21:16:54 +02:00
Maciej Budziński
e0035433ca feat: update readme with typescript types 2020-06-07 21:04:53 +02:00
Maciej Budziński
9f67f84c49 feat: add type definitions 2020-06-07 19:54:01 +02:00
16 changed files with 602 additions and 222 deletions

144
README.md
View File

@ -19,7 +19,7 @@
</div>
# Introduction
# Introduction
<p align="center">
Stickyheader.js is a simple React Native library, enabling to create a fully custom header for your iOS and Android apps.
@ -34,7 +34,7 @@
<h2> Features </h2>
Stickyheader.js ships with 3 different use cases for sticky headers and a possibility to create fully custom header!
| Tabbed Header | Avatar Header | Details Header|
| Tabbed Header | Avatar Header | Details Header|
| :------: | :------: | :------: |
| ![Tabbed Header Gif](./assets/readme_TabbedHeader.gif) |![Avatar Header Gif](./assets/readme_AvatarHeader.gif)| ![Details Header Gif](./assets/readme_DetailsHeader.gif)|
@ -62,34 +62,57 @@ export default TestScreen
Below are examples of those components and description of the props they are accepting.
## Shared props
### Tabbed Header, Details Header, Avatar Header
| Property | Type | Optional | Default | Description |
| :---------------------------: | :---------------------------------------------------: | :-------:| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------:|
| `backgroundColor` | `string` | Yes | `#1ca75d` | Header background color |
| `backgroundImage` | `number` | Yes | `null` | Sets header background image |
| `bounces` | `bool` | Yes | `true` | Bounces on swiping up |
| `contentContainerStyles` | `View.propTypes.style` | Yes | | Set style for content container |
| `headerHeight` | `number` | Yes | `ifIphoneX(92, constants.responsiveHeight(13))` |
| `renderBody` | `func` | Yes | `title => <RenderContent title={title} />` | Function that renders body of the header (can be empty) |
| `snapToEdge` | `bool` | Yes | `true` | Boolean to fire the function for snap To Edge |
| `scrollRef` | `func or object` | Yes | `null` | ScrollView body ref. Allows programmatically scroll body [ScrollView](https://reactnative.dev/docs/scrollview#methods) |
| `keyboardShouldPersistTaps` | `'always', 'never', 'handled', false, true` | Yes | `undefined` | Determines when the keyboard should stay visible after a tap.|
### Details Header, Avatar Header
| Property | Type | Optional | Default | Description |
| :---------------------------: | :---------------------------------------------------: | :-------:| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------:|
| `hasBorderRadius` | `boolean` | No | `true` | Adds radius to header's right bottom border |
| `image` | `ImageSourcePropType` | No | `require('../../assets/images/photosPortraitBrandon.png')` | Sets header image |
| `leftTopIcon` | `ImageSourcePropType` | No | `require('../../assets/icons/iconCloseWhite.png')` | Set icon for left top button |
| `leftTopIconOnPress` | `() => void` | No | `() => {}` | Define action on left top button press |
| `rightTopIcon` | `ImageSourcePropType` | No | `require('../../assets/icons/Icon-Menu.png') ` | Set icon for right top button |
| `rightTopIconOnPress` | `() => void` | No | `() => {}` | Define action on right top button press |
## Tabbed Header
![Tabbed Header Gif](./assets/readme_Tabbed.gif)
| Property | Type | Optional | Default | Description |
| :---------------------------: | :---------------------------------------------------: | :-------:| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------:|
| `backgroundColor` | `string` | Yes | `#1ca75d` | Header background color |
| `headerHeight` | `number` | Yes | `ifIphoneX(92, constants.responsiveHeight(13))` | Sets height of folded header |
| `backgroundImage` | `number` | Yes | `null` | Sets header background image |
| `title` | `string` | Yes | `"Mornin' Mark! \nReady for a quiz?"` | Sets header title |
| `bounces` | `bool` | Yes | `true` | Bounces on swiping up |
| `snapToEdge` | `bool` | Yes | `true` | Boolean to fire the function for snap To Edge |
| `renderBody` | `func` | Yes | `title => <RenderContent title={title} />` | Function that renders body of the header (can be empty) |
| `tabs` | `arrayOf(shape({}))` | Yes | `[{title: 'Popular',content: <RenderContent title="Popular Quizes" />},...]` | Array with tabs names and content |
| `foregroundImage` |`oneOfType([object, number])` | Yes | | Set image in the foreground |
| `header` |`func` | Yes | | Function that renders custom header |
| `logo` | `func` | Yes | `require('../../assets/images/logo.png')` | Set header logo |
| `logoStyle` | `style` | Yes | `{ height: 24, width: 142 }` | Set header logo style |
| `logoContainerStyle` | `style` | Yes | `{ width: '100%', paddingHorizontal: 24, paddingTop: Platform.select({ ios: ifIphoneX(50, 40), android: 55 }), flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}`| Set header logo container style |
| `logoResizeMode` | `"contain", "cover", "stretch", "center", "repeat" ` | Yes | `"contain"` | Set header logo resize mode |
| `foregroundImage` |`oneOfType([object, number])` | Yes | | Set image in the foreground |
| `titleStyle` |`Text.propTypes.style` | Yes | | Set style for text in foreground |
| `onRef` |`func` | Yes | | Reference callback. You can call goToPage(pageNumber) method through ref to programmatically navigate to given tab |
| `rememberTabScrollPosition` |`bool` | Yes |`false` | When switching between tabs remember current scroll position |
| `scrollEvent` |`func` | Yes | | Scroll event to apply custom animations |
| `tabs` | `arrayOf(shape({}))` | Yes | `[{title: 'Popular',content: <RenderContent title="Popular Quizes" />},...]` | Array with tabs names and content |
| `tabText` |`Text.propTypes.style` | Yes |`{fontSize: 16, lineHeight: 20, paddingHorizontal: 12, paddingVertical: 8, color: colors.white}` | Set inactive tab style |
| `tabTextActiveStyle` |`Text.propTypes.style` | Yes |`{fontSize: 16, lineHeight: 20, paddingHorizontal: 12, paddingVertical: 8, color: colors.white}` | Set active tab stylee |
| `tabTextContainerStyle` |`ViewPropTypes.style` | Yes |`{backgroundColor: colors.transparent, borderRadius: 18}` | Set inactive tab container style |
| `tabTextContainerActiveStyle` |`ViewPropTypes.style` | Yes |`{backgroundColor: colors.darkMint}` | Set active tab container style |
| `tabWrapperStyle` |`ViewPropTypes.style` | Yes |`{paddingVertical: 12}` | Set single tab container style |
| `tabsContainerStyle` |`ViewPropTypes.style` | Yes | | Set whole tab bar container style |
| `header` |`func` | Yes | | Fuction that renders custom header |
| `scrollEvent` |`func` | Yes | | Scroll event to apply custom animations |
| `title` | `string` | Yes | `"Mornin' Mark! \nReady for a quiz?"` | Sets header title |
| `titleStyle` |`Text.propTypes.style` | Yes | | Set style for text in foreground |
[Check how to customise Tabbed Header example](docs/TABBEDHEADER.MD)
@ -97,55 +120,68 @@ Below are examples of those components and description of the props they are acc
![Details Header Gif](./assets/readme_Details.gif)
| Property | Type | Optional | Default | Description |
| :-------------------: | :--------------------:| :-------:| :---------------------------------------------------------------------------:| :-------------------------------------------------------:|
| `leftTopIconOnPress` | `func` | Yes | `() => {}` | Define action on left top button press |
| `rightTopIconOnPress` | `func` | Yes | `() => {}` | Define action on right top button press |
| `leftTopIcon` | `number` | Yes | `require('../../assets/icons/iconCloseWhite.png')` | Set icon for left top button |
| `rightTopIcon` | `number` | Yes | `require('../../assets/icons/Icon-Menu.png') ` | Set icon for right top button |
| `backgroundColor` | `string` | Yes | `#1ca75d` | Header background color |
| `headerHeight` | `number` | Yes | `ifIphoneX(92, constants.responsiveHeight(13))` | Sets height of folded header |
| `backgroundImage` | `number` | Yes | `null` | Sets header background image |
| `tag` | `string` | Yes | `"Product Designer"` | Sets header tag name |
| `title` | `string` | Yes | `"Design System"` | Sets header title |
| `image` | `number` | Yes | `require('../../assets/images/photosPortraitBrandon.png')` | Sets header image |
| `renderBody` | `func` | Yes | `title => <RenderContent title={title} />` | Function that renders body of the header (can be empty) |
| `bounces` | `bool` | Yes | `true` | Bounces on swiping up |
| `snapToEdge` | `bool` | Yes | `true` | Boolean to fire the function for snap To Edge |
| `hasBorderRadius` | `bool` | Yes | `true` | Adds radius to header's left bottom border |
| `iconNumber` | `number` | Yes | `10` | Set amount of cards shown on icon |
| Property | Type | Required | Default | Description |
| :-------------------: | :--------------------------------:| :-------:| :---------------------------------------------------------------------------:| :-------------------------------------------------------:|
| `iconNumber` | `number` | No | `10` | Set amount of cards shown on icon |
| `tag` | `string` | No | `"Product Designer"` | Sets header tag name |
| `title` | `string` | No | `"Design System"` | Sets header title |
## Avatar Header
![Avatar Header Gif](./assets/readme_Avatar.gif)
| Property | Type | Optional | Default | Description |
| :-------------------: | :--------------------:| :-------:| :---------------------------------------------------------------------------:| :-------------------------------------------------------:|
| `leftTopIconOnPress` | `func` | Yes | `() => {}` | Define action on left top button press |
| `rightTopIconOnPress` | `func` | Yes | `() => {}` | Define action on right top button press |
| `leftTopIcon` | `number` | Yes | `require('../../assets/icons/iconCloseWhite.png')` | Set icon for left top button |
| `rightTopIcon` | `number` | Yes | `require('../../assets/icons/Icon-Menu.png') ` | Set icon for right top button |
| `backgroundColor` | `string` | Yes | `#1ca75d` | Header background color |
| `headerHeight` | `number` | Yes | `ifIphoneX(92, constants.responsiveHeight(13))` | Sets height of folded header |
| `backgroundImage` | `number` | Yes | `null` | Sets header background image |
| `title` | `string` | Yes | `"Brandon` | Sets header title |
| `subtitle` | `string` | Yes | `"Coffee buff. Web enthusiast. Unapologetic student. Gamer. Avid organizer."`| Sets description(subtitle) section |
| `image` | `number` | Yes | `require('../../assets/images/photosPortraitBrandon.png')` | Sets header image |
| `renderBody` | `func` | Yes | `title => <RenderContent title={title} />` | Function that renders body of the header (can be empty) |
| `bounces` | `bool` | Yes | `true` | Bounces on swiping up |
| `snapToEdge` | `bool` | Yes | `true` | Boolean to fire the function for snap To Edge |
| `hasBorderRadius` | `bool` | Yes | `true` | Adds radius to header's left bottom border |
| `parallaxHeight` | `number` | Yes | | Set parallax header height |
| `transparentHeader` | `bool` | Yes | `false` | Set header transparency to render custom header |
| `snapStartThreshold` | `number` | Yes | | Set start value Threshold of snap |
| `snapStopThreshold` | `number` | Yes | | Set stop value Threshold of snap |
| `snapValue` | `number` | Yes | | Set value where header is closed |
| Property | Type | Optional | Default | Description |
| :-------------------: | :---------------------------------------------------------:| :-------:| :---------------------------------------------------------------------------:| :-------------------------------------------------------:|
| `foreground` | `() => ReactElement` | No | - | Function that renders the foreground of the header |
| `header` | `() => ReactElement` | No | - | Function that renders custom header |
| `parallaxHeight` | `number` | No | - | Set parallax header height |
| `scrollEvent` | `(event: NativeSyntheticEvent<NativeScrollEvent>) => void` | No | `require('../../assets/icons/Icon-Menu.png') ` | Scroll event to apply custom animations |
| `snapStartThreshold` | `number` | No | - | Set start value Threshold of snap |
| `snapStopThreshold` | `number` | No | - | Set stop value Threshold of snap |
| `snapValue` | `number` | No | - | Set value where header is closed |
| `subtitle` | `string` | No | `"Coffee buff. Web enthusiast. Unapologetic student. Gamer. Avid organizer."`| Sets description(subtitle) section |
| `title` | `string` | No | `"Brandon` | Sets header title |
| `transparentHeader` | `boolean` | No | `false` | Set header transparency to render custom header |
## Custom Header
## Custom Header
[Custom header props and example](docs/CUSTOM.md)
## Handling StickyParallaxHeader body ScrollView reference
### As callback function
```
<StickyParallaxHeader
scrollRef={(ref) => {
paralaxScrollRef.current = ref;
}}
foreground={this.renderForeground()}
header={this.renderHeader()}
/>
{renderBody()}
<StickyParallaxHeader/>
```
### As useRef value
```
const paralaxScrollRef = useRef(null);
<StickyParallaxHeader
scrollRef={paralaxScrollRef}
foreground={this.renderForeground()}
header={this.renderHeader()}
/>
{renderBody()}
<StickyParallaxHeader/>
```
## Handling nested scrollables
[Handling nested flatlist props and example](docs/CUSTOM.md#Tips)
<h1 id="Getting-Started">Getting Started</h1>
## Prerequisites
@ -204,4 +240,4 @@ $ yarn add react-native-sticky-parallax-header
# License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

View File

@ -21,6 +21,7 @@ $ yarn test
* adding video or screenshot is very beneficial but it's not mandatory
* additionally please remember to add appropriate Pull Request title from following:
* `[RNS-XX] short description` - for normal feature branches
* remember one pull request should always address one issue or feature
## Code structure
```
@ -36,3 +37,4 @@ src/
* Name branch according to your ticket following this pattern: RNS-XX-short_description
* Imports and exports inside `index.js` files eg. `screens/index.js`, `components/index.js` should be alfabetically
* Style names in `ComponentName.styles.js` should be ordered alfabetically
* Please use commit lint and follow commit naming convention (https://www.conventionalcommits.org/en/v1.0.0/)

View File

@ -2,30 +2,36 @@
## Custom Header Props
| Property | Type | Required | Default | Description |
| :------------------------------: | :-----------------: | :--------: | :-------: | :-------------------------------------------------------------: |
| `background` | `node` | No | - | This renders background component |
| `backgroundImage` | `number` | No | - | This renders background image instead of background component |
| `backgroundColor` | `string` | Yes |`""` | Header background color |
| `bounces` | `bool` | Yes | `true` | Bounces on swiping up |
| `children` | `node` | No | - | This renders all the children inside the component |
| `foreground` | `node` | Yes | - | This renders foreground component |
| `header` | `node` | Yes | - | This renders header component |
| `headerHeight` | `number` | No | `92` | Sets height of folded header |
| `headerSize` | `func` | No | - | Returns size of header for current device |
| `initialPage` | `number` | No | `0` | Set initial page of tab bar |
| `onChangeTab` | `func` | No | - | Tab change event |
| `onEndReached` | `func` | No | - | Tab change event |
| `parallaxHeight` | `number` | No | `0` | Sets height of opened header |
| `snapToEdge` | `bool` | No | `true` | Boolean to fire the function for snap To Edge |
| `scrollEvent` | `func` | No | - | Returns offset of header to apply custom animations |
| `tabs` | `arrayOf(string)` | No | - | Array of tab names |
| `tabTextStyle` | `shape({})` | No | {} | Text styles of tab |
| `tabTextActiveStyle` | `shape({})` | No | {} | Text styles of active tab |
| `tabTextContainerStyle` | `shape({})` | No | {} | Container styles of tab |
| `tabTextContainerActiveStyle` | `shape({})` | No | {} | Container styles of active tab |
| `tabsContainerBackgroundColor` | `string` | No | - | Background color of tab bar container |
| `tabsWrapperStyle` | `shape({})` | No | {} | Tabs Wrapper styles |
| Property | Type | Required | Default | Description |
| :------------------------------: | :-------------------------------------------------------------------: | :--------: | :-------: | :-------------------------------------------------------------: |
| `backgroundColor` | `string` | Yes | `""` | Header background color |
| `backgroundImage` | `ImageSourcePropType` | No | - | This renders background image instead of background component |
| `background` | `ReactElement` | No | - | This renders background component |
| `bounces` | `boolean` | Yes | `true` | Bounces on swiping up |
| `children` | `ReactElement` | No | - | This renders all the children inside the component |
| `foreground` | `ReactElement` | Yes | - | This renders foreground component |
| `headerHeight` | `number` | No | `92` | Sets height of folded header |
| `headerSize` | `({ x, y, width, height }: HeaderSizeProps) => void` | No | - | Handler that is called when header's size changes |
| `header` | `ReactElement` | Yes | - | This renders header component |
| `initialPage` | `number` | No | `0` | Set initial page of tab bar |
| `onChangeTab` | `({ i, ref, from }: { from: number; i: number; ref: any; }) => void;` | No | - | Tab change event |
| `onEndReached` | `() => void` | No | - | Reached end event
| `onTopReached` | `() => void` | No | - | Reached top event |
| `parallaxHeight` | `number` | No | `0` | Sets height of opened header |
| `scrollEvent` | `(event: NativeSyntheticEvent<NativeScrollEvent>) => void` | No | - | Returns offset of header to apply custom animations |
| `snapStartThreshold` | `number` | No | - | Set start value Threshold of snap |
| `snapStopThreshold` | `number` | No | - | Set stop value Threshold of snap |
| `snapToEdge` | `boolean` | No | `true` | Boolean to fire the function for snap To Edge |
| `snapValue` | `number` | No | - | Set value where header is closed |
| `tabTextActiveStyle` | `TextStyle` | No | {} | Text styles of active tab |
| `tabTextContainerActiveStyle` | `ViewStyle` | No | {} | Container styles of active tab |
| `tabTextContainerStyle` | `ViewStyle` | No | {} | Container styles of tab |
| `tabTextStyle` | `TextStyle` | No | {} | Text styles of tab |
| `tabsContainerBackgroundColor` | `ViewStyle` | No | - | Background color of tab bar container |
| `tabsWrapperStyle` | `ViewStyle` | No | {} | Tabs Wrapper styles |
| `tabs` | `{ content: ReactElement; title: string; }[]` | No | - | Array of tab names |
| `tabsContainerStyle` | `ViewStyle` | No | - | Set whole tab bar container style |
| `transparentHeader` | `boolean` | No | `false` | Set header transparency to render custom header |
<h1 id="Usage">Usage</h1>
@ -180,4 +186,55 @@ class TabScreen extends React.Component {
```
## Tips
In order to nest scrollable component use `scrollEnabled={false}` on it and move all the logic to the header eg. by using `onEndReached` prop.
In order to nest scrollable component use `scrollEnabled={false}` on it and move all the logic to the header eg. by using `onEndReached` and `onTopReached` props. You can find example in CardScreen.js it's really basic so probably you will want to extend it somehow:
```jsx
shouldBeEnabled = () => {
const {
endReached,
stickyHeaderEndReached,
topReached,
stickyHeaderTopReached
} = this.state
const bottomCondition =
endReached && stickyHeaderEndReached
const topCondition =
topReached && stickyHeaderTopReached
return bottomCondition || !topCondition
}
onScroll = ({nativeEvent}) => {
const {contentOffset, layoutMeasurement, contentSize} = nativeEvent;
if (layoutMeasurement.height + contentOffset.y >= contentSize.height - 20) {
this.setState({endReached: true, topReached: false})
}
if (contentOffset.y <= 0) {
this.setState({topReached: true, endReached: false, stickyHeaderTopReached:true})
}
}
renderFlatlistContent = (user) => (
<View style={styles.flatlistContainer}>
<FlatList
data={user.cards}
renderItem={({item, index}) => (
<QuizCard
data={item}
num={index}
key={item.question}
cardsAmount={100}
/>
)}
onScroll={this.onScroll}
scrollEnabled={
Platform.OS === 'android' ? true : this.shouldBeEnabled()
}
nestedScrollEnabled
/>
</View>
)
```

View File

@ -16,7 +16,7 @@
"react-native-gesture-handler": "^1.2.1",
"react-native-iphone-x-helper": "^1.2.1",
"react-native-screens": "^2.5.0",
"react-native-sticky-parallax-header": "^0.2.1",
"react-native-sticky-parallax-header": "^0.3.0",
"react-navigation": "^3.0.9"
},
"devDependencies": {

View File

@ -1,5 +1,5 @@
import { StyleSheet, Platform } from 'react-native'
import { ifIphoneX } from '../constants/utils'
import { ifIphoneX } from './utils'
import colors from './colors'
import constants from './constants'
@ -16,6 +16,13 @@ const screenStyles = StyleSheet.create({
justifyContent: 'center',
paddingBottom: 24
},
flatlistContainer: {
width: constants.deviceWidth,
height: constants.deviceHeight - 80,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 24
},
contentText: {
fontSize: 24,
lineHeight: 28,

View File

@ -1,5 +1,13 @@
import React from 'react'
import { Text, View, Image, TouchableOpacity, StatusBar, Animated } from 'react-native'
import {
Text,
View,
Image,
TouchableOpacity,
StatusBar,
Animated,
FlatList,
} from 'react-native'
import StickyParallaxHeader from 'react-native-sticky-parallax-header'
import { withNavigation } from 'react-navigation'
import { constants, sizes, colors } from '../../constants'
@ -12,9 +20,13 @@ class CardScreen extends React.Component {
super(props)
this.state = {
headerLayout: {
height: 0
}
}
height: 0,
},
topReached: true,
endReached: false,
stickyHeaderEndReached: false,
stickyHeaderTopReached: true,
};
this.scrollY = new ValueXY()
}
@ -125,6 +137,52 @@ class CardScreen extends React.Component {
)
}
shouldBeEnabled = () => {
const {
endReached,
stickyHeaderEndReached,
topReached,
stickyHeaderTopReached
} = this.state
const bottomCondition =
endReached && stickyHeaderEndReached
const topCondition =
topReached && stickyHeaderTopReached
return bottomCondition || !topCondition
}
onScroll = ({nativeEvent}) => {
const {contentOffset, layoutMeasurement, contentSize} = nativeEvent;
if (layoutMeasurement.height + contentOffset.y >= contentSize.height - 20) {
this.setState({endReached: true, topReached: false})
}
if (contentOffset.y <= 0) {
this.setState({topReached: true, endReached: false, stickyHeaderTopReached:true})
}
}
renderFlatlistContent = (user) => (
<View style={styles.flatlistContainer}>
<FlatList
data={user.cards}
renderItem={({item, index}) => (
<QuizCard
data={item}
num={index}
key={item.question}
cardsAmount={100}
/>
)}
onScroll={this.onScroll}
scrollEnabled={
constants.isAndroid ? true : this.shouldBeEnabled()
}
nestedScrollEnabled
/>
</View>
)
renderContent = user => (
<View style={styles.content}>
{user.cards.map((data, i, arr) => (
@ -133,6 +191,20 @@ class CardScreen extends React.Component {
</View>
)
stickyHeaderEndReached = () => {
this.setState({
stickyHeaderEndReached: true,
stickyHeaderTopReached: false,
})
}
stickyHeaderTopReached = () => {
this.setState({
stickyHeaderTopReached: true,
stickyHeaderEndReached: false,
})
}
render() {
const { navigation } = this.props
const user = navigation.getParam('user', {})
@ -149,8 +221,10 @@ class CardScreen extends React.Component {
headerSize={this.setHeaderSize}
headerHeight={sizes.cardScreenHeaderHeight}
background={this.renderBackground(user)}
>
{this.renderContent(user)}
onEndReached={this.stickyHeaderEndReached}
onTopReached={this.stickyHeaderTopReached}>
{/* {this.renderContent(user)} */}
{this.renderFlatlistContent(user)}
</StickyParallaxHeader>
</React.Fragment>
)

View File

@ -5058,10 +5058,10 @@ react-native-screens@^2.5.0:
dependencies:
debounce "^1.2.0"
react-native-sticky-parallax-header@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/react-native-sticky-parallax-header/-/react-native-sticky-parallax-header-0.2.1.tgz#ac399e685d01997a8dd0d5d94db1580602cf23f1"
integrity sha512-fbIySVvr4fa6b6JPHgtphAxqZyV8mmQIbpJkXwxuD89Qj/VjKpjhQfsnBDOJ3wDDvngs1PRcyVv7gs1lZYehEw==
react-native-sticky-parallax-header@^0.3.0:
version "0.3.1"
resolved "https://registry.npmjs.org/react-native-sticky-parallax-header/-/react-native-sticky-parallax-header-0.3.1.tgz#6946ed62ef916859108aba013e4652e3a4c6c3fa"
integrity sha512-6tDOu2N9UzjDyHpOXQXWmNWhQjfC/qtddGwu1k04zr1vCYbF6ws+HaySK8k3Da2m9YRd0MHxem9UAI5zOL7fQw==
dependencies:
prop-types "^15.7.2"

View File

@ -1,7 +1,8 @@
{
"name": "react-native-sticky-parallax-header",
"version": "0.2.1",
"version": "0.3.0",
"main": "src/index.js",
"types": "src/index.d.ts",
"repository": "https://github.com/netguru/sticky-parallax-header",
"author": "IdaszakDaniel <idaszak1@gmail.com>",
"license": "MIT",

View File

@ -1,10 +1,30 @@
import React, { Component } from 'react'
import { arrayOf, bool, func, node, number, shape, string, oneOfType } from 'prop-types'
import { Dimensions, ImageBackground, ScrollView, View, Animated, Easing, ViewPropTypes, Image } from 'react-native'
import {
arrayOf,
bool,
func,
node,
number,
shape,
string,
oneOfType,
oneOf,
instanceOf
} from 'prop-types'
import {
Dimensions,
ImageBackground,
ScrollView,
View,
Animated,
Easing,
ViewPropTypes,
Image
} from 'react-native'
import { ScrollableTabBar, ScrollableTabView } from './components'
import { constants } from './constants'
import styles from './styles'
import { getSafelyScrollNode } from './utils'
import { getSafelyScrollNode, setRef } from './utils'
const { divide, Value, createAnimatedComponent, event, timing, ValueXY } = Animated
const AnimatedScrollView = createAnimatedComponent(ScrollView)
@ -16,6 +36,7 @@ class StickyParallaxHeader extends Component {
const { width } = Dimensions.get('window')
const scrollXIOS = new Value(initialPage * width)
const containerWidthAnimatedValue = new Value(width)
this.tabsScrollPosition = []
// eslint-disable-next-line no-underscore-dangle
containerWidthAnimatedValue.__makeNative()
@ -32,10 +53,35 @@ class StickyParallaxHeader extends Component {
componentDidMount() {
// eslint-disable-next-line
this.scrollY.addListener(({ value }) => (this._value = value))
this.props.onRef?.(this)
}
componentDidUpdate(prevProps, prevState) {
const { headerHeight, parallaxHeight, tabs, rememberTabScrollPosition } = this.props
const prevPage = prevState.currentPage
const { currentPage, isFolded } = this.state
const isRenderingTabs = tabs && tabs.length > 0
if (isRenderingTabs && prevPage !== currentPage && isFolded) {
const currentScrollPosition = this.scrollY.__getValue().y
const scrollHeight = Math.max(parallaxHeight, headerHeight * 2)
this.tabsScrollPosition[prevPage] = currentScrollPosition
setTimeout(() => {
const scrollTargetPosition =
rememberTabScrollPosition && this.tabsScrollPosition[currentPage]
? this.tabsScrollPosition[currentPage]
: scrollHeight
const scrollNode = getSafelyScrollNode(this.scroll)
scrollNode.scrollTo({ y: scrollTargetPosition, duration: 1000 })
}, 250)
}
}
componentWillUnmount() {
this.scrollY.removeAllListeners()
this.props.onRef?.(null)
}
spring = () => {
@ -72,42 +118,42 @@ class StickyParallaxHeader extends Component {
if (y > 0 && y < snapToEdgeThreshold) {
return constants.isAndroid
? this.setState(
{
isFolded: false
},
scrollNode.scrollTo({ x: 0, y: 0, animated: true })
)
{
isFolded: false
},
scrollNode.scrollTo({ x: 0, y: 0, animated: true })
)
: timing(snapToEdgeAnimatedValue, {
toValue: { x: 0, y: 0 },
duration: 400,
easing: Easing.out(Easing.cubic),
useNativeDriver: true
}).start(() => {
snapToEdgeAnimatedValue.removeListener(id)
this.setState({
isFolded: false
})
toValue: { x: 0, y: 0 },
duration: 400,
easing: Easing.out(Easing.cubic),
useNativeDriver: true
}).start(() => {
snapToEdgeAnimatedValue.removeListener(id)
this.setState({
isFolded: false
})
})
}
if (y >= snapToEdgeThreshold && y < scrollHeight) {
return constants.isAndroid
? this.setState(
{
isFolded: true
},
scrollNode.scrollTo({ x: 0, y: scrollHeight, animated: true })
)
{
isFolded: true
},
scrollNode.scrollTo({ x: 0, y: scrollHeight, animated: true })
)
: timing(snapToEdgeAnimatedValue, {
toValue: { x: 0, y: snap },
duration: 400,
easing: Easing.out(Easing.cubic),
useNativeDriver: true
}).start(() => {
snapToEdgeAnimatedValue.removeListener(id)
this.setState({
isFolded: true
})
toValue: { x: 0, y: snap },
duration: 400,
easing: Easing.out(Easing.cubic),
useNativeDriver: true
}).start(() => {
snapToEdgeAnimatedValue.removeListener(id)
this.setState({
isFolded: true
})
})
}
}
@ -159,6 +205,15 @@ class StickyParallaxHeader extends Component {
return null
}
isCloseToTop = ({ contentOffset }) => {
const { onTopReached } = this.props
if (contentOffset.y <= 0) {
return onTopReached && onTopReached()
}
return null
}
renderHeader = () => {
const { header, headerHeight, backgroundColor, transparentHeader } = this.props
@ -275,13 +330,16 @@ class StickyParallaxHeader extends Component {
const {
backgroundImage,
children,
contentContainerStyles,
header,
headerHeight,
initialPage,
parallaxHeight,
tabs,
bounces,
scrollEvent
scrollEvent,
keyboardShouldPersistTaps,
scrollRef
} = this.props
const { currentPage, isFolded } = this.state
const scrollHeight = Math.max(parallaxHeight, headerHeight * 2)
@ -292,7 +350,11 @@ class StickyParallaxHeader extends Component {
headerStyle.map((el) => Object.assign(arrayHeaderStyle, el))
}
const scrollViewMinHeight = Dimensions.get('window').height + parallaxHeight - headerHeight
const innerScrollHeight = Dimensions.get('window').height - headerHeight - parallaxHeight
const shouldRenderTabs = tabs && tabs.length > 0
const shouldUseBgColor = contentContainerStyles && contentContainerStyles.backgroundColor
return (
<View style={styles.container}>
@ -305,11 +367,14 @@ class StickyParallaxHeader extends Component {
nestedScrollEnabled
ref={(c) => {
this.scroll = c
setRef(scrollRef, c)
}}
contentContainerStyle={{ minHeight: scrollViewMinHeight, backgroundColor: shouldUseBgColor }}
onScrollEndDrag={() => this.onScrollEndSnapToEdge(scrollHeight)}
scrollEventThrottle={1}
stickyHeaderIndices={shouldRenderTabs ? [1] : []}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
onScroll={event(
[
{
@ -324,6 +389,7 @@ class StickyParallaxHeader extends Component {
useNativeDriver: true,
listener: (e) => {
this.isCloseToBottom(e.nativeEvent)
this.isCloseToTop(e.nativeEvent)
scrollEvent(e)
}
}
@ -334,15 +400,20 @@ class StickyParallaxHeader extends Component {
style={[
styles.overScrollPadding,
{
backgroundColor: isArray ? arrayHeaderStyle.backgroundColor : headerStyle?.backgroundColor
backgroundColor: isArray
? arrayHeaderStyle.backgroundColor
: headerStyle?.backgroundColor
}
]}
/>
{backgroundImage ? this.renderImageBackground(scrollHeight) : this.renderPlainBackground(scrollHeight)}
{backgroundImage
? this.renderImageBackground(scrollHeight)
: this.renderPlainBackground(scrollHeight)}
{this.renderForeground(scrollHeight)}
</View>
{shouldRenderTabs && this.renderTabs()}
<ScrollableTabView
contentContainerStyles={contentContainerStyles}
initialPage={initialPage}
onChangeTab={(i) => this.onChangeTabHandler(i)}
tabs={tabs}
@ -351,6 +422,8 @@ class StickyParallaxHeader extends Component {
scrollRef={this.scroll}
scrollHeight={scrollHeight}
isHeaderFolded={isFolded}
minScrollHeight={innerScrollHeight}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
>
{!tabs && children}
{tabs &&
@ -379,6 +452,7 @@ StickyParallaxHeader.propTypes = {
backgroundImage: Image.propTypes.source,
bounces: bool,
children: node,
contentContainerStyles: ViewPropTypes.style,
foreground: node,
header: node,
headerHeight: number,
@ -387,6 +461,7 @@ StickyParallaxHeader.propTypes = {
onChangeTab: func,
onEndReached: func,
parallaxHeight: number,
rememberTabScrollPosition: bool,
scrollEvent: func,
snapToEdge: bool,
tabTextActiveStyle: shape({}),
@ -400,11 +475,16 @@ StickyParallaxHeader.propTypes = {
snapStartThreshold: oneOfType([bool, number]),
snapStopThreshold: oneOfType([bool, number]),
snapValue: oneOfType([bool, number]),
transparentHeader: bool
transparentHeader: bool,
onRef: func,
onTopReached: func,
scrollRef: oneOfType([func, shape({ current: instanceOf(ScrollView) })]),
keyboardShouldPersistTaps: oneOf(['never', 'always', 'handled', false, true, undefined])
}
StickyParallaxHeader.defaultProps = {
bounces: true,
contentContainerStyles: {},
headerHeight: 92,
backgroundColor: '',
initialPage: 0,
@ -415,10 +495,14 @@ StickyParallaxHeader.defaultProps = {
tabTextContainerStyle: {},
tabTextStyle: {},
tabWrapperStyle: {},
rememberTabScrollPosition: false,
snapStartThreshold: false,
snapStopThreshold: false,
snapValue: false,
transparentHeader: false
transparentHeader: false,
onRef: null,
scrollRef: null,
keyboardShouldPersistTaps: undefined
}
export default StickyParallaxHeader

View File

@ -1,9 +1,15 @@
/* eslint-disable react/destructuring-assignment */
import React from 'react'
import { Animated, StyleSheet, View } from 'react-native'
import { func, node, number, shape, bool } from 'prop-types'
import {
Animated,
StyleSheet,
View,
ViewPropTypes
} from 'react-native'
import { func, node, number, shape, bool, oneOf } from 'prop-types'
import SceneComponent from './SceneComponent'
import constants from '../../constants/constants'
import { getSafelyScrollNode } from '../../utils'
const styles = StyleSheet.create({
container: {
@ -76,18 +82,6 @@ class ScrollableTabView extends React.Component {
})
}
scrollToTop = () => {
const { scrollRef, scrollHeight, isHeaderFolded } = this.props
return (
isHeaderFolded &&
scrollRef.scrollTo({
y: scrollHeight,
duration: 1000
})
)
}
updateSelectedPage = (nextPage) => {
let localNextPage = nextPage
if (typeof localNextPage === 'object') {
@ -103,12 +97,20 @@ class ScrollableTabView extends React.Component {
this.children().map((child, idx) => {
const key = this.makeSceneKey(child, idx)
const { currentPage, containerWidth, sceneKeys } = this.state
const { contentContainerStyles, minScrollHeight } = this.props
return (
<SceneComponent
key={child.key}
shouldUpdated={this.shouldRenderSceneKey(idx, currentPage)}
style={{ width: containerWidth }}
/* eslint-disable-next-line react-native/no-inline-styles */
style={[{
width: containerWidth,
minHeight: minScrollHeight,
maxHeight: idx === currentPage ? null : minScrollHeight,
},
contentContainerStyles
]}
>
{this.keyExists(sceneKeys, key) ? child : null}
</SceneComponent>
@ -157,7 +159,7 @@ class ScrollableTabView extends React.Component {
return newKeys
}
updateSceneKeys = ({ page, children = this.props.children, callback = () => {} }) => {
updateSceneKeys = ({ page, children = this.props.children, callback = () => { } }) => {
const { sceneKeys } = this.state
const newKeys = this.newSceneKeys({ previousKeys: sceneKeys, currentPage: page, children })
this.setState({ currentPage: page, sceneKeys: newKeys }, callback)
@ -166,8 +168,9 @@ class ScrollableTabView extends React.Component {
goToPage = (pageNumber) => {
const { containerWidth } = this.state
const offset = pageNumber * containerWidth
if (this.scrollView) {
this.scrollView.scrollTo({ x: offset, y: 0, animated: true })
const scrollNode = getSafelyScrollNode(this.scrollView);
if (scrollNode) {
scrollNode.scrollTo({ x: offset, y: 0, animated: true })
}
const { currentPage } = this.state
@ -175,8 +178,6 @@ class ScrollableTabView extends React.Component {
page: pageNumber,
callback: this.onChangeTab.bind(this, currentPage, pageNumber)
})
this.scrollToTop()
}
onScroll = (e) => {
@ -206,11 +207,14 @@ class ScrollableTabView extends React.Component {
const scenes = this.composeScenes()
const { initialPage } = this.props
const { containerWidth, scrollXIOS } = this.state
const { minScrollHeight, keyboardShouldPersistTaps } = this.props
return (
<Animated.ScrollView
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
horizontal
pagingEnabled
contentContainerStyle={{ minHeight: minScrollHeight }}
automaticallyAdjustContentInsets={false}
contentOffset={{ x: initialPage * containerWidth }}
ref={(scrollView) => {
@ -245,19 +249,24 @@ class ScrollableTabView extends React.Component {
ScrollableTabView.propTypes = {
children: node,
contentContainerStyles: ViewPropTypes.style,
initialPage: number,
page: number,
onChangeTab: func,
swipedPage: func,
scrollHeight: number,
minScrollHeight: number,
isHeaderFolded: bool,
scrollRef: shape({})
scrollRef: shape({}),
keyboardShouldPersistTaps: oneOf(['never', 'always', 'handled', false, true, undefined])
}
ScrollableTabView.defaultProps = {
contentContainerStyles: {},
initialPage: 0,
page: -1,
onChangeTab: () => {}
onChangeTab: () => { },
keyboardShouldPersistTaps: undefined
}
export default ScrollableTabView

119
src/index.d.ts vendored Normal file
View File

@ -0,0 +1,119 @@
import { ReactElement, Component } from 'react';
import { ImageSourcePropType, HeaderTypeProp, ScrollView, NativeScrollEvent, NativeSyntheticEvent, TextStyle, ViewStyle, ImageResizeMode } from 'react-native';
export interface HeaderTypeProp {
headerType?: 'TabbedHeader' | 'DetailsHeader' | 'AvatarHeader';
keyboardShouldPersistTaps?: 'always' | 'never' | 'handled' | false | true;
scrollRef?:(ref:ScrollView)=>void | object;
}
export interface HeaderSizeProps {
height: number;
width: number;
x: number;
y: number;
}
export interface OnChangeTabArguments {
from: number;
i: number;
ref: any;
}
export interface Tab {
content: ReactElement;
title: string;
}
export interface SharedProps {
backgroundImage?: ImageSourcePropType;
headerHeight?: number;
snapToEdge?: boolean;
bounces?: boolean;
}
export interface IconProps {
leftTopIcon?: ImageSourcePropType;
leftTopIconOnPress?: () => void;
rightTopIcon?: ImageSourcePropType;
rightTopIconOnPress?: () => void;
}
export interface TabsSharedProps {
tabTextActiveStyle: TextStyle;
tabTextContainerActiveStyle: ViewStyle;
tabTextContainerStyle: ViewStyle;
tabTextStyle: TextStyle;
tabWrapperStyle: ViewStyle;
tabs: Tab[];
tabsContainerStyle: ViewStyle;
}
export type TabbedHeaderProps = SharedProps & TabsSharedProps & {
headerType: 'TabbedHeader';
backgroundColor?: string;
foregroundImage?: ImageSourcePropType;
header?: () => ReactElement;
logo?: ImageSourcePropType;
logoContainerStyle?: ViewStyle;
logoResizeMode?: ImageResizeMode;
logoStyle?: ViewStyle;
rememberTabScrollPosition?: boolean;
renderBody?: (title: string) => ReactElement;
scrollEvent?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
title?: string;
titleStyle?: TextStyle;
}
export type DetailsHeaderProps = SharedProps & IconProps & {
headerType: 'DetailsHeader';
backgroundColor?: string;
hasBorderRadius?: boolean;
iconNumber?: number;
image?: number;
renderBody?: (title: string) => ReactElement;
tag?: string;
title?: string;
}
export type AvatarHeaderProps = SharedProps & IconProps & {
headerType: 'AvatarHeader';
backgroundColor?: string;
foreground?: () => ReactElement;
hasBorderRadius?: boolean;
header?: () => ReactElement;
image?: number;
parallaxHeight?: number;
renderBody?: (title: string) => ReactElement;
scrollEvent?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
snapStartThreshold?: number;
snapStopThreshold?: number;
snapValue?: number;
subtitle?: string;
title?: string;
transparentHeader?: boolean;
}
export type CustomHeaderProps = SharedProps & TabsSharedProps & {
headerType: undefined;
background: ReactElement;
backgroundColor: string;
children?: ReactElement;
foreground?: ReactElement;
header: ReactElement;
headerSize?: ({ x, y, width, height }: HeaderSizeProps) => void;
initialPage?: number;
onChangeTab?: ({ i, ref, from }: OnChangeTabArguments) => void;
onEndReached?: () => void;
parallaxHeight?: number;
scrollEvent?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
snapStartThreshold?: number;
snapStopThreshold?: number;
snapValue?: number;
tabsContainerBackgroundColor?: string;
transparentHeader?: boolean;
}
type StickyParallaxHeaderProps = HeaderTypeProp & (DetailsHeaderProps | AvatarHeaderProps | TabbedHeaderProps | CustomHeaderProps)
export default class StickyParallaxHeader extends Component<StickyParallaxHeaderProps, any> { }

View File

@ -159,10 +159,10 @@ class AvatarHeader extends React.Component {
const { backgroundColor, hasBorderRadius } = this.props
const headerBorderRadius = this.scrollY.y.interpolate({
inputRange: [0, height],
outputRange: [80, 0],
extrapolate: 'extend'
})
inputRange: [0, height],
outputRange: [80, 0],
extrapolate: 'extend'
})
const borderBottomRightRadius = hasBorderRadius ? headerBorderRadius : 0
@ -235,18 +235,18 @@ AvatarHeader.propTypes = {
rightTopIcon: number,
backgroundColor: string,
headerHeight: number,
backgroundImage: number,
backgroundImage: Image.propTypes.source,
title: string,
subtitle: string,
image: number,
image: Image.propTypes.source,
renderBody: func,
scrollEvent: func,
parallaxHeight: number,
foreground: func,
header: func,
snapStartThreshold: oneOfType([ bool, number]),
snapStopThreshold: oneOfType([ bool, number]),
snapValue: oneOfType([ bool, number]),
snapStartThreshold: oneOfType([bool, number]),
snapStopThreshold: oneOfType([bool, number]),
snapValue: oneOfType([bool, number]),
transparentHeader: bool
}
AvatarHeader.defaultProps = {
@ -265,9 +265,6 @@ AvatarHeader.defaultProps = {
snapToEdge: true,
hasBorderRadius: true,
parallaxHeight: sizes.userScreenParallaxHeader,
snapStartThreshold: false,
snapStopThreshold: false,
snapValue: false,
transparentHeader: false
}

View File

@ -130,14 +130,14 @@ class DetailsHeader extends React.Component {
const { backgroundColor, backgroundImage, renderBody, headerHeight, snapToEdge, bounces } = this.props
return (
<React.Fragment>
<>
<StatusBar barStyle="light-content" backgroundColor={backgroundColor} translucent />
<StickyParallaxHeader
foreground={this.renderForeground(user)}
header={this.renderHeader(user)}
deviceWidth={constants.deviceWidth}
parallaxHeight={sizes.cardScreenParallaxHeader}
scrollEvent={event([{ nativeEvent: { contentOffset: { y: this.scrollY.y } } }], {useNativeDriver: false})}
scrollEvent={event([{ nativeEvent: { contentOffset: { y: this.scrollY.y } } }], { useNativeDriver: false })}
headerSize={this.setHeaderSize}
headerHeight={headerHeight}
background={this.renderBackground(user)}
@ -147,7 +147,7 @@ class DetailsHeader extends React.Component {
>
{renderBody(user)}
</StickyParallaxHeader>
</React.Fragment>
</>
)
}
}
@ -159,10 +159,10 @@ DetailsHeader.propTypes = {
rightTopIcon: number,
backgroundColor: string,
headerHeight: number,
backgroundImage: number,
backgroundImage: Image.propTypes.source,
title: string,
tag: string,
image: number,
image: Image.propTypes.source,
renderBody: func,
bounces: bool,
snapToEdge: bool,

View File

@ -1,20 +1,6 @@
import React from 'react'
import {
Text,
View,
Image,
StatusBar,
Animated,
ViewPropTypes
} from 'react-native'
import {
arrayOf,
bool,
number,
shape,
string,
func
} from 'prop-types'
import { Text, View, Image, StatusBar, Animated, ViewPropTypes } from 'react-native'
import { arrayOf, bool, number, shape, string, func } from 'prop-types'
import StickyParallaxHeader from '../../index'
import { constants, colors, sizes } from '../../constants'
import styles from './TabbedHeader.styles'
@ -53,21 +39,11 @@ export default class TabbedHeader extends React.Component {
}
renderLogoHeader = () => {
const {
backgroundColor,
logo,
logoResizeMode,
logoStyle,
logoContainerStyle
} = this.props
const { backgroundColor, logo, logoResizeMode, logoStyle, logoContainerStyle } = this.props
return (
<View style={[logoContainerStyle, { backgroundColor }]}>
<Image
resizeMode={logoResizeMode}
source={logo}
style={logoStyle}
/>
<Image resizeMode={logoResizeMode} source={logo} style={logoStyle} />
</View>
)
}
@ -105,10 +81,10 @@ export default class TabbedHeader extends React.Component {
const renderImage = () => {
const logo = isUndefined(foregroundImage)
? require('../../assets/images/photosPortraitMe.png')
: foregroundImage
? require('../../assets/images/photosPortraitMe.png')
: foregroundImage
if(foregroundImage !== null){
if (foregroundImage !== null) {
return (
<Animated.View style={{ opacity: imageOpacity }}>
<Animated.Image
@ -152,7 +128,8 @@ export default class TabbedHeader extends React.Component {
return marginBottom
}
marginBottom = constants.deviceHeight - padding * 2 - sizes.headerHeight - contentHeight[title]
marginBottom =
constants.deviceHeight - padding * 2 - sizes.headerHeight - contentHeight[title]
return marginBottom
}
@ -175,18 +152,22 @@ export default class TabbedHeader extends React.Component {
tabTextContainerStyle,
tabTextContainerActiveStyle,
tabWrapperStyle,
tabsContainerStyle
tabsContainerStyle,
onRef
} = this.props
return (
<React.Fragment>
<>
<StatusBar barStyle="light-content" backgroundColor={backgroundColor} translucent />
<StickyParallaxHeader
foreground={this.renderForeground(this.scrollY)}
header={this.renderHeader()}
deviceWidth={constants.deviceWidth}
parallaxHeight={sizes.homeScreenParallaxHeader}
scrollEvent={event([{ nativeEvent: { contentOffset: { y: this.scrollY.y } } }], {useNativeDriver: false, listener: e => scrollEvent && scrollEvent(e)})}
scrollEvent={event([{ nativeEvent: { contentOffset: { y: this.scrollY.y } } }], {
useNativeDriver: false,
listener: (e) => scrollEvent && scrollEvent(e)
})}
headerSize={this.setHeaderSize}
headerHeight={headerHeight}
tabs={tabs}
@ -200,10 +181,11 @@ export default class TabbedHeader extends React.Component {
bounces={bounces}
snapToEdge={snapToEdge}
tabsContainerStyle={tabsContainerStyle}
onRef={onRef}
>
{renderBody('Popular Quizes')}
</StickyParallaxHeader>
</React.Fragment>
</>
)
}
}
@ -217,7 +199,7 @@ TabbedHeader.propTypes = {
snapToEdge: bool,
tabs: arrayOf(shape({})),
renderBody: func,
logo: number,
logo: Image.propTypes.source,
logoResizeMode: string,
logoStyle: ViewPropTypes.style,
logoContainerStyle: ViewPropTypes.style,
@ -230,7 +212,8 @@ TabbedHeader.propTypes = {
tabsContainerStyle: ViewPropTypes.style,
foregroundImage: Image.propTypes.source,
titleStyle: Text.propTypes.style,
header: func
header: func,
onRef: func
}
TabbedHeader.defaultProps = {
@ -241,10 +224,10 @@ TabbedHeader.defaultProps = {
bounces: true,
snapToEdge: true,
logo: require('../../assets/images/logo.png'),
logoResizeMode: "contain",
logoResizeMode: 'contain',
logoStyle: styles.logo,
logoContainerStyle: styles.headerWrapper,
renderBody: title => <RenderContent title={title} />,
renderBody: (title) => <RenderContent title={title} />,
tabs: [
{
title: 'Popular',
@ -267,5 +250,6 @@ TabbedHeader.defaultProps = {
tabTextActiveStyle: styles.tabText,
tabTextContainerStyle: styles.tabTextContainerStyle,
tabTextContainerActiveStyle: styles.tabTextContainerActiveStyle,
tabWrapperStyle: styles.tabsWrapper
tabWrapperStyle: styles.tabsWrapper,
onRef: null
}

View File

@ -28,7 +28,8 @@ const RenderContent = ({ title }) => {
return marginBottom
}
marginBottom = constants.deviceHeight - padding * 2 - sizes.headerHeight - contentHeight[title]
marginBottom =
constants.deviceHeight - padding * 2 - sizes.headerHeight - contentHeight[title]
return marginBottom
}
@ -38,21 +39,22 @@ const RenderContent = ({ title }) => {
const marginBottom = Platform.select({ ios: calcMargin(title), android: 0 })
return (
<View style={[styles.content, { marginBottom }]} onLayout={e => onLayoutContent(e, title)}>
<View style={[styles.content, { marginBottom }]} onLayout={(e) => onLayoutContent(e, title)}>
<Text style={styles.contentText}>{title}</Text>
{users.map(
user => (title === 'Popular Quizes' || title === user.type) && (
<QuizListElement
key={JSON.stringify(user)}
elements={user.cardsAmount}
authorName={user.author}
mainText={user.label}
labelText={user.type}
imageSource={user.image}
onPress={() => {}}
pressUser={() => {}}
/>
)
(user) =>
(title === 'Popular Quizes' || title === user.type) && (
<QuizListElement
key={JSON.stringify(user)}
elements={user.cardsAmount}
authorName={user.author}
mainText={user.label}
labelText={user.type}
imageSource={user.image}
onPress={() => {}}
pressUser={() => {}}
/>
)
)}
</View>
)

View File

@ -24,3 +24,11 @@ export function getSafelyScrollNode(scrollNode) {
// before react-native 0.62
return scrollNode.getNode()
}
export function setRef(ref, value) {
if (typeof ref === 'function') {
ref(value);
} else if (ref !== null) {
ref.current = value;
}
}