react-native-collapsible-sticky-header 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +569 -2
- package/lib/commonjs/Badge.js +65 -0
- package/lib/commonjs/Badge.js.map +1 -0
- package/lib/commonjs/CollapsibleStickyHeaderScreen.js +386 -0
- package/lib/commonjs/CollapsibleStickyHeaderScreen.js.map +1 -0
- package/lib/commonjs/HeaderGreeting.js +122 -0
- package/lib/commonjs/HeaderGreeting.js.map +1 -0
- package/lib/commonjs/HeaderNavBar.js +219 -0
- package/lib/commonjs/HeaderNavBar.js.map +1 -0
- package/lib/commonjs/ImageOrNode.js +40 -0
- package/lib/commonjs/ImageOrNode.js.map +1 -0
- package/lib/commonjs/animations.js +34 -0
- package/lib/commonjs/animations.js.map +1 -0
- package/lib/commonjs/index.js +122 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/optionalGradient.js +24 -0
- package/lib/commonjs/optionalGradient.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/theme.js +93 -0
- package/lib/commonjs/theme.js.map +1 -0
- package/lib/commonjs/types.js +6 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/useStickyHeader.js +143 -0
- package/lib/commonjs/useStickyHeader.js.map +1 -0
- package/lib/commonjs/utils.js +90 -0
- package/lib/commonjs/utils.js.map +1 -0
- package/lib/module/Badge.js +61 -0
- package/lib/module/Badge.js.map +1 -0
- package/lib/module/CollapsibleStickyHeaderScreen.js +379 -0
- package/lib/module/CollapsibleStickyHeaderScreen.js.map +1 -0
- package/lib/module/HeaderGreeting.js +118 -0
- package/lib/module/HeaderGreeting.js.map +1 -0
- package/lib/module/HeaderNavBar.js +215 -0
- package/lib/module/HeaderNavBar.js.map +1 -0
- package/lib/module/ImageOrNode.js +36 -0
- package/lib/module/ImageOrNode.js.map +1 -0
- package/lib/module/animations.js +28 -0
- package/lib/module/animations.js.map +1 -0
- package/lib/module/index.js +11 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/optionalGradient.js +20 -0
- package/lib/module/optionalGradient.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/theme.js +88 -0
- package/lib/module/theme.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useStickyHeader.js +138 -0
- package/lib/module/useStickyHeader.js.map +1 -0
- package/lib/module/utils.js +78 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/Badge.d.ts +20 -0
- package/lib/typescript/commonjs/src/Badge.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/CollapsibleStickyHeaderScreen.d.ts +10 -0
- package/lib/typescript/commonjs/src/CollapsibleStickyHeaderScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/HeaderGreeting.d.ts +33 -0
- package/lib/typescript/commonjs/src/HeaderGreeting.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/HeaderNavBar.d.ts +48 -0
- package/lib/typescript/commonjs/src/HeaderNavBar.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ImageOrNode.d.ts +16 -0
- package/lib/typescript/commonjs/src/ImageOrNode.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/animations.d.ts +9 -0
- package/lib/typescript/commonjs/src/animations.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +10 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/optionalGradient.d.ts +13 -0
- package/lib/typescript/commonjs/src/optionalGradient.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/theme.d.ts +81 -0
- package/lib/typescript/commonjs/src/theme.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/types.d.ts +244 -0
- package/lib/typescript/commonjs/src/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/useStickyHeader.d.ts +12 -0
- package/lib/typescript/commonjs/src/useStickyHeader.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/utils.d.ts +30 -0
- package/lib/typescript/commonjs/src/utils.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/Badge.d.ts +20 -0
- package/lib/typescript/module/src/Badge.d.ts.map +1 -0
- package/lib/typescript/module/src/CollapsibleStickyHeaderScreen.d.ts +10 -0
- package/lib/typescript/module/src/CollapsibleStickyHeaderScreen.d.ts.map +1 -0
- package/lib/typescript/module/src/HeaderGreeting.d.ts +33 -0
- package/lib/typescript/module/src/HeaderGreeting.d.ts.map +1 -0
- package/lib/typescript/module/src/HeaderNavBar.d.ts +48 -0
- package/lib/typescript/module/src/HeaderNavBar.d.ts.map +1 -0
- package/lib/typescript/module/src/ImageOrNode.d.ts +16 -0
- package/lib/typescript/module/src/ImageOrNode.d.ts.map +1 -0
- package/lib/typescript/module/src/animations.d.ts +9 -0
- package/lib/typescript/module/src/animations.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +10 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/lib/typescript/module/src/optionalGradient.d.ts +13 -0
- package/lib/typescript/module/src/optionalGradient.d.ts.map +1 -0
- package/lib/typescript/module/src/theme.d.ts +81 -0
- package/lib/typescript/module/src/theme.d.ts.map +1 -0
- package/lib/typescript/module/src/types.d.ts +244 -0
- package/lib/typescript/module/src/types.d.ts.map +1 -0
- package/lib/typescript/module/src/useStickyHeader.d.ts +12 -0
- package/lib/typescript/module/src/useStickyHeader.d.ts.map +1 -0
- package/lib/typescript/module/src/utils.d.ts +30 -0
- package/lib/typescript/module/src/utils.d.ts.map +1 -0
- package/package.json +142 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Santhosh James
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,570 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-native-collapsible-sticky-header
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/react-native-collapsible-sticky-header)
|
|
4
|
+
[](https://github.com/santhoshjamesc/react-native-collapsible-sticky-header/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
A fully configurable collapsing hero header with a sticky compact bar for **React Native (iOS & Android)**. Works in Expo and bare React Native.
|
|
9
|
+
|
|
10
|
+
- Hero image, plain color, or nothing at all, with an optional fade and rounded bottom corners
|
|
11
|
+
- Built-in layout: logo, greeting, title, subtitle, avatar and up to **3** icon actions with badges
|
|
12
|
+
- Swap in your own expanded nav, collapsible block or sticky bar with render props
|
|
13
|
+
- Scroll handling runs on the UI thread through Reanimated. React re-renders only when the sticky state changes
|
|
14
|
+
- `FlatList`, `ScrollView`, `SectionList`, or any Animated scrollable (such as FlashList)
|
|
15
|
+
- Accessible: the hidden sticky bar is skipped by screen readers, and reduced motion is respected
|
|
16
|
+
- Fully typed and themeable, with no bundled icons, images or brand colors
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="docs/demo.gif" alt="Demo: the hero header collapses and the sticky bar fades in" width="320" />
|
|
20
|
+
<br />
|
|
21
|
+
<em>The example app: hero header expanded, with the sticky bar ready to fade in on scroll.</em>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
## Contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick start](#quick-start)
|
|
28
|
+
- [How it works](#how-it-works)
|
|
29
|
+
- [Props](#props)
|
|
30
|
+
- [Actions (icon buttons)](#actions-icon-buttons)
|
|
31
|
+
- [Theming](#theming)
|
|
32
|
+
- [Recipes](#recipes) (including [customizing the collapsed header](#customize-the-collapsed-header))
|
|
33
|
+
- [Building blocks](#building-blocks)
|
|
34
|
+
- [Accessibility & performance](#accessibility--performance)
|
|
35
|
+
- [Example app](#example-app)
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install react-native-collapsible-sticky-header
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Expo
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npx expo install react-native-reanimated react-native-worklets react-native-safe-area-context expo-linear-gradient
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`babel-preset-expo` adds the Reanimated / Worklets Babel plugin for you. `expo-linear-gradient` is **optional** and only used for the fade at the bottom of the hero.
|
|
50
|
+
|
|
51
|
+
### Bare React Native
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npm install react-native-reanimated react-native-worklets react-native-safe-area-context
|
|
55
|
+
# optional, for the gradient fade:
|
|
56
|
+
npx install-expo-modules@latest && npm install expo-linear-gradient
|
|
57
|
+
cd ios && pod install
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Add the Babel plugin. It **must be listed last**:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
// babel.config.js
|
|
64
|
+
module.exports = {
|
|
65
|
+
presets: ['module:@react-native/babel-preset'],
|
|
66
|
+
plugins: [
|
|
67
|
+
// Reanimated 4.x:
|
|
68
|
+
'react-native-worklets/plugin',
|
|
69
|
+
// Reanimated 3.x uses 'react-native-reanimated/plugin' instead
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then wrap your app in `SafeAreaProvider`:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
78
|
+
|
|
79
|
+
export default function App() {
|
|
80
|
+
return <SafeAreaProvider>{/* ... */}</SafeAreaProvider>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Peer dependency | Version |
|
|
85
|
+
| -------------------------------- | --------------------------------------------------- |
|
|
86
|
+
| `react` | ≥ 18 |
|
|
87
|
+
| `react-native` | ≥ 0.76 |
|
|
88
|
+
| `react-native-reanimated` | ≥ 3 (tested on 4.x; 4.x needs the New Architecture) |
|
|
89
|
+
| `react-native-safe-area-context` | ≥ 4 |
|
|
90
|
+
| `expo-linear-gradient` | optional |
|
|
91
|
+
|
|
92
|
+
> **No gradient library?** Pass `renderBackgroundFade` to draw the fade with any gradient component, such as `react-native-linear-gradient`. With neither installed, no fade is drawn. The package loads `expo-linear-gradient` through a `require` inside `try/catch`, which Metro treats as an optional dependency.
|
|
93
|
+
|
|
94
|
+
## Quick start
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import Ionicons from '@expo/vector-icons/Ionicons';
|
|
98
|
+
import { CollapsibleStickyHeaderScreen } from 'react-native-collapsible-sticky-header';
|
|
99
|
+
|
|
100
|
+
export function HomeScreen() {
|
|
101
|
+
return (
|
|
102
|
+
<CollapsibleStickyHeaderScreen
|
|
103
|
+
backgroundImage={require('./hero.jpg')}
|
|
104
|
+
logo={require('./logo.png')}
|
|
105
|
+
greeting="Welcome back"
|
|
106
|
+
title="Ava Thompson"
|
|
107
|
+
actions={[
|
|
108
|
+
{
|
|
109
|
+
key: 'cart',
|
|
110
|
+
accessibilityLabel: 'Cart',
|
|
111
|
+
badgeCount: 3,
|
|
112
|
+
icon: ({ color, size }) => (
|
|
113
|
+
<Ionicons name="bag-outline" color={color} size={size} />
|
|
114
|
+
),
|
|
115
|
+
onPress: openCart,
|
|
116
|
+
},
|
|
117
|
+
]}
|
|
118
|
+
scrollProps={{
|
|
119
|
+
data: products,
|
|
120
|
+
keyExtractor: (p) => p.id,
|
|
121
|
+
renderItem: ({ item }) => <ProductRow product={item} />,
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## How it works
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
┌──────────────────────────────┐ ← sticky compact bar (absolute, zIndex 20)
|
|
132
|
+
│ [logo] (🔔) (👜³) │ opacity 0→1 over [threshold-40, threshold]
|
|
133
|
+
└──────────────────────────────┘ touchable once y ≥ threshold-20
|
|
134
|
+
┌──────────────────────────────┐ ← list header
|
|
135
|
+
│ hero image / color / custom │
|
|
136
|
+
│ [LOGO] (🔔) (👜³) │ ← expanded nav bar, measured with onLayout
|
|
137
|
+
│ Welcome back │ ← collapsible block: opacity 1→0,
|
|
138
|
+
│ Ava Thompson (👤) │ translateY 0→-12 over [0, threshold]
|
|
139
|
+
│░░░░░░░ fade ░░░░░░░░░░░░░░░░░│ ← background overhangs by 60px and fades out
|
|
140
|
+
└──────────────────────────────┘
|
|
141
|
+
list items…
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`threshold = safeAreaTop + navBarHeight + thresholdOffset`. Until the nav bar has been measured, the threshold is `0` and the sticky bar stays hidden.
|
|
145
|
+
|
|
146
|
+
## Props
|
|
147
|
+
|
|
148
|
+
All props are optional except `scrollProps` for list types.
|
|
149
|
+
|
|
150
|
+
### Scroll container
|
|
151
|
+
|
|
152
|
+
| Prop | Type | Default | Description |
|
|
153
|
+
| ----------------- | ---------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
154
|
+
| `as` | `'FlatList' \| 'ScrollView' \| 'SectionList' \| Component` | `'FlatList'` | Scroll container. A custom component must be Animated (`Animated.createAnimatedComponent`). |
|
|
155
|
+
| `scrollProps` | props of the container | | Passed through. `onScroll` is merged (it runs on the JS thread). `refreshControl` and `contentContainerStyle` work as usual. `scrollEventThrottle` defaults to `16`. |
|
|
156
|
+
| `listRef` | `Ref` | | Forwarded to the scrollable, for example to call `scrollToOffset`. |
|
|
157
|
+
| `headerPlacement` | `'ListHeaderComponent' \| 'children'` | `'ListHeaderComponent'` | Custom scrollables only. |
|
|
158
|
+
|
|
159
|
+
### Background
|
|
160
|
+
|
|
161
|
+
| Prop | Type | Default | Description |
|
|
162
|
+
| ---------------------- | ------------------------------------------ | ------------------------------------------------ | ---------------------------------------------------------------------------- |
|
|
163
|
+
| `backgroundImage` | `ImageSourcePropType` | | Optional hero image. |
|
|
164
|
+
| `backgroundColor` | `string` | `theme.colors.headerBg` (`'transparent'`) | Shown with no image, or behind it. |
|
|
165
|
+
| `backgroundStyle` | `ViewStyle` | | Background container. |
|
|
166
|
+
| `backgroundImageStyle` | `ImageStyle` | | The image (`opacity`, `resizeMode`…). |
|
|
167
|
+
| `renderBackground` | `(ctx) => ReactNode` | | Replaces the image entirely. Receives `{ theme, insets, collapseProgress }`. |
|
|
168
|
+
| `backgroundOverhang` | `number` | `60` (`0` when rounded) | How far the background extends below the header. |
|
|
169
|
+
| `headerBottomRadius` | `number` | `theme.radius.headerBottom` (`0`) | Rounds the bottom corners and clips the header. |
|
|
170
|
+
| `showFade` | `boolean` | `true`, or `false` when `headerBottomRadius > 0` | Gradient fade at the bottom of the background. |
|
|
171
|
+
| `fadeColors` | `[string, string]` | `['transparent', '#ffffff']` | |
|
|
172
|
+
| `fadeHeight` | `number` | `60` | |
|
|
173
|
+
| `renderBackgroundFade` | `({ colors, height, style }) => ReactNode` | | Draws the fade with any gradient component. |
|
|
174
|
+
|
|
175
|
+
### Header content
|
|
176
|
+
|
|
177
|
+
| Prop | Type | Default | Description |
|
|
178
|
+
| ------------------------------------------------ | ------------------------------------------------ | -------------------------------- | ------------------------------------------------------------------------------ |
|
|
179
|
+
| `logo` | `ImageSourcePropType \| ReactNode` | | Expanded-bar logo. |
|
|
180
|
+
| `stickyLogo` | `ImageSourcePropType \| ReactNode \| false` | `logo` | Compact-bar logo. `false` hides it in the sticky bar only. |
|
|
181
|
+
| `logoSize` | `{ expanded?, compact? }` | `100×75` / `64×48` | |
|
|
182
|
+
| `logoStyle` | `ImageStyle \| ViewStyle` | | |
|
|
183
|
+
| `logoAccessibilityLabel` | `string` | | |
|
|
184
|
+
| `onLogoPress` | `(variant) => void` | | Makes the logo a button, for example to scroll to the top from the sticky bar. |
|
|
185
|
+
| `greeting` / `title` / `subtitle` | `ReactNode` | | Strings are rendered as styled `Text`. |
|
|
186
|
+
| `greetingStyle` / `titleStyle` / `subtitleStyle` | `TextStyle` | | |
|
|
187
|
+
| `avatar` | `ImageSourcePropType \| ReactNode` | | Profile picture or any element. |
|
|
188
|
+
| `avatarPosition` | `'left' \| 'right'` | `'right'` | |
|
|
189
|
+
| `avatarSize` | `number` | `theme.avatarSize` (`44`) | |
|
|
190
|
+
| `avatarStyle` | `ImageStyle \| ViewStyle` | | |
|
|
191
|
+
| `onAvatarPress` | `() => void` | | Makes the avatar a button. |
|
|
192
|
+
| `avatarAccessibilityLabel` | `string` | `'Profile'` | |
|
|
193
|
+
| `stickyTitle` | `ReactNode` | | Text in the compact bar, next to or instead of the logo. |
|
|
194
|
+
| `stickyTitleStyle` | `TextStyle` | | |
|
|
195
|
+
| `stickyContent` | `('logo' \| 'title' \| 'avatar' \| 'actions')[]` | `['logo', 'title', 'actions']` | What the sticky (collapsed) bar shows. Leading items follow this order. |
|
|
196
|
+
| `stickyAvatarSize` | `number` | `theme.avatarSizeCompact` (`32`) | Avatar size in the sticky bar (when `stickyContent` includes `'avatar'`). |
|
|
197
|
+
| `stickyAvatarStyle` | `ImageStyle \| ViewStyle` | | |
|
|
198
|
+
| `actions` | `HeaderAction[]` | `[]` | Icon buttons, **max 3**. See [Actions](#actions-icon-buttons). |
|
|
199
|
+
|
|
200
|
+
### Slots
|
|
201
|
+
|
|
202
|
+
Each render prop receives `{ progress, isSticky, variant, theme, insets, actions }`. `progress` is a `SharedValue<number>` from 0 to 1. It holds collapse progress for the expanded slots and sticky-reveal progress for the sticky slot.
|
|
203
|
+
|
|
204
|
+
| Prop | Default |
|
|
205
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
206
|
+
| `renderExpandedNav` | `<HeaderNavBar variant="overlay" logo actions />`. It is measured and defines the threshold. |
|
|
207
|
+
| `renderCollapsible` | `<HeaderGreeting greeting title subtitle avatar />` |
|
|
208
|
+
| `renderStickyNav` | `<HeaderNavBar variant="solid" logo={stickyLogo} title={stickyTitle} show={stickyContent} actions />` |
|
|
209
|
+
| `renderStickyBackground` | None. Draws a gradient, image or blur behind the sticky bar's content. The bar's color becomes transparent unless `stickyBarBackground` is set. |
|
|
210
|
+
| `children` | Extra content **inside** the header, on the background. |
|
|
211
|
+
| `ListHeaderExtra` | Extra content **after** the header, on the page background. |
|
|
212
|
+
|
|
213
|
+
### Behavior
|
|
214
|
+
|
|
215
|
+
| Prop | Type | Default | Description |
|
|
216
|
+
| --------------------- | ------------------------------------------ | ------------------------------------- | --------------------------------------------------------------------------------- |
|
|
217
|
+
| `thresholdOffset` | `number` | `0` | Added to the measured threshold. |
|
|
218
|
+
| `stickyFadeDistance` | `number` | `40` | Distance over which the sticky bar fades in. |
|
|
219
|
+
| `stickyPointerOffset` | `number` | `20` | The sticky bar becomes touchable at `threshold - offset`. |
|
|
220
|
+
| `collapseTranslateY` | `number` | `-12` | |
|
|
221
|
+
| `collapseAnimation` | `(progress) => style` (worklet) | | Replaces the collapsible block's animation. |
|
|
222
|
+
| `stickyAnimation` | `(progress) => style` (worklet) | fade | Replaces the sticky bar's animation. |
|
|
223
|
+
| `respectSafeArea` | `boolean` | `true` | Pads the header and sticky bar by the top inset and includes it in the threshold. |
|
|
224
|
+
| `enteringAnimations` | `boolean \| { duration, stagger, easing }` | `650 / 70 / Easing.out(Easing.cubic)` | Mount animation (`FadeInUp`). Pass `false` to disable it. |
|
|
225
|
+
| `onStickyChange` | `(isSticky) => void` | | Fires once per transition. |
|
|
226
|
+
|
|
227
|
+
### Styling
|
|
228
|
+
|
|
229
|
+
| Prop | Type | Default |
|
|
230
|
+
| --------------------- | -------------------------------- | ------------------------------------------------------------ |
|
|
231
|
+
| `theme` | `DeepPartial<StickyHeaderTheme>` | see [Theming](#theming) |
|
|
232
|
+
| `stickyBarStyle` | `ViewStyle` | |
|
|
233
|
+
| `stickyBarBackground` | `string` | `theme.colors.stickyBg` (`'#ffffff'`) |
|
|
234
|
+
| `stickyShadow` | `boolean \| ShadowStyle` | `true` (opacity 0.08, radius 6, offset `{0,2}`, elevation 4) |
|
|
235
|
+
| `containerStyle` | `ViewStyle` | |
|
|
236
|
+
| `headerStyle` | `ViewStyle` | |
|
|
237
|
+
|
|
238
|
+
## Actions (icon buttons)
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
type HeaderAction = {
|
|
242
|
+
key: string;
|
|
243
|
+
icon:
|
|
244
|
+
| ReactNode
|
|
245
|
+
| ((p: {
|
|
246
|
+
color: string;
|
|
247
|
+
size: number;
|
|
248
|
+
variant: 'overlay' | 'solid';
|
|
249
|
+
}) => ReactNode);
|
|
250
|
+
onPress: () => void;
|
|
251
|
+
accessibilityLabel: string; // required
|
|
252
|
+
accessibilityHint?: string;
|
|
253
|
+
badgeCount?: number;
|
|
254
|
+
color?: string; // per-icon override of the theme color
|
|
255
|
+
size?: number; // per-icon override of theme.iconSize
|
|
256
|
+
style?: StyleProp<ViewStyle>; // the 36×36 round hit area
|
|
257
|
+
hidden?: boolean;
|
|
258
|
+
showInSticky?: boolean; // default true
|
|
259
|
+
testID?: string;
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
- **0 to 3 actions** are valid (`MAX_HEADER_ACTIONS = 3`). Only the first 3 visible actions render. Passing more logs a `__DEV__` warning.
|
|
264
|
+
- No icon library is bundled. Use `@expo/vector-icons`, `react-native-vector-icons`, SVG, or an `Image`.
|
|
265
|
+
- The function form receives the variant's color and size, so one icon definition works in both bars.
|
|
266
|
+
- A badge shows `9+` when the count is above 9.
|
|
267
|
+
|
|
268
|
+
## Theming
|
|
269
|
+
|
|
270
|
+
Pass a partial theme. It is deep-merged over the defaults, and props take priority over the theme.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
const theme: StickyHeaderThemeOverrides = {
|
|
274
|
+
colors: {
|
|
275
|
+
stickyBg: '#ffffff', // compact bar
|
|
276
|
+
headerBg: 'transparent', // header background when there is no image
|
|
277
|
+
iconOverlay: '#ffffff', // icons on the hero
|
|
278
|
+
iconSolid: '#1f2937', // icons on the compact bar
|
|
279
|
+
actionBgOverlay: 'transparent',
|
|
280
|
+
actionBgSolid: 'transparent',
|
|
281
|
+
badge: '#ef4444',
|
|
282
|
+
badgeText: '#ffffff',
|
|
283
|
+
badgeBorder: '#ffffff',
|
|
284
|
+
greeting: '#ffffff',
|
|
285
|
+
title: '#ffffff',
|
|
286
|
+
subtitle: 'rgba(255,255,255,0.85)',
|
|
287
|
+
stickyTitle: '#111827',
|
|
288
|
+
fade: ['transparent', '#ffffff'],
|
|
289
|
+
shadow: '#000000',
|
|
290
|
+
},
|
|
291
|
+
spacing: {
|
|
292
|
+
horizontal: 16,
|
|
293
|
+
actionGap: 8,
|
|
294
|
+
avatarGap: 12,
|
|
295
|
+
leadingGap: 8, // logo / avatar / title gap in the nav bar
|
|
296
|
+
navBar: {
|
|
297
|
+
overlay: { paddingHorizontal: 16, paddingVertical: 8 },
|
|
298
|
+
solid: { paddingHorizontal: 16, paddingVertical: 4 },
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
radius: { headerBottom: 0 },
|
|
302
|
+
pressedOpacity: 0.6,
|
|
303
|
+
iconSize: 22,
|
|
304
|
+
actionSize: 36,
|
|
305
|
+
avatarSize: 44,
|
|
306
|
+
badgeSize: 18,
|
|
307
|
+
logoSize: {
|
|
308
|
+
expanded: { width: 100, height: 75 },
|
|
309
|
+
compact: { width: 64, height: 48 },
|
|
310
|
+
},
|
|
311
|
+
typography: {
|
|
312
|
+
greetingSize: 14,
|
|
313
|
+
titleSize: 24,
|
|
314
|
+
subtitleSize: 14,
|
|
315
|
+
stickyTitleSize: 17,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`createTheme(...partials)` returns a fully resolved theme. Example brand theme (amber and white):
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
const beanBrewTheme = {
|
|
324
|
+
colors: { iconSolid: '#d97706', badge: '#f59e0b', greeting: '#fde7c7' },
|
|
325
|
+
};
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Recipes
|
|
329
|
+
|
|
330
|
+
### Customize the collapsed header
|
|
331
|
+
|
|
332
|
+
The collapsed header is the compact bar that appears once the expanded header scrolls away.
|
|
333
|
+
|
|
334
|
+
**Color:**
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
<CollapsibleStickyHeaderScreen
|
|
338
|
+
stickyBarBackground="#4f46e5" // or theme.colors.stickyBg
|
|
339
|
+
theme={{
|
|
340
|
+
colors: {
|
|
341
|
+
iconSolid: '#ffffff', // icons in the collapsed bar
|
|
342
|
+
actionBgSolid: 'rgba(255,255,255,0.16)',
|
|
343
|
+
stickyTitle: '#ffffff', // stickyTitle text
|
|
344
|
+
},
|
|
345
|
+
}}
|
|
346
|
+
stickyBarStyle={{ borderBottomWidth: 1, borderBottomColor: '#3730a3' }}
|
|
347
|
+
stickyShadow={false}
|
|
348
|
+
…
|
|
349
|
+
/>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Content:** choose the items and their order. Actions always sit on the right.
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
<CollapsibleStickyHeaderScreen
|
|
356
|
+
avatar={{ uri: user.photoUrl }}
|
|
357
|
+
stickyTitle="Ava's tasks"
|
|
358
|
+
stickyContent={['avatar', 'title', 'actions']} // no logo in the collapsed bar
|
|
359
|
+
stickyAvatarSize={32}
|
|
360
|
+
actions={[
|
|
361
|
+
search,
|
|
362
|
+
{ ...settings, showInSticky: false }, // expanded bar only
|
|
363
|
+
]}
|
|
364
|
+
…
|
|
365
|
+
/>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
To keep the same layout but drop the logo, use `stickyLogo={false}`. To show a smaller or different mark, pass `stickyLogo={require('./mark.png')}`.
|
|
369
|
+
|
|
370
|
+
**Gradient, image or blur background:**
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
<CollapsibleStickyHeaderScreen
|
|
374
|
+
renderStickyBackground={() => (
|
|
375
|
+
<LinearGradient colors={['#4f46e5', '#7c3aed']} style={StyleSheet.absoluteFill} />
|
|
376
|
+
)}
|
|
377
|
+
…
|
|
378
|
+
/>
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Animated color**, for example transparent → solid while the bar appears:
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
import { interpolateColor } from 'react-native-reanimated';
|
|
385
|
+
|
|
386
|
+
const colorIn = (p: number) => {
|
|
387
|
+
'worklet';
|
|
388
|
+
return {
|
|
389
|
+
opacity: p,
|
|
390
|
+
backgroundColor: interpolateColor(p, [0, 1], ['rgba(79,70,229,0)', '#4f46e5']),
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
<CollapsibleStickyHeaderScreen stickyAnimation={colorIn} … />
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Anything else:** `renderStickyNav={({ isSticky, theme }) => <MyBar />}` replaces the bar's content entirely. The bar container, safe-area padding, animation and accessibility handling stay in place.
|
|
398
|
+
|
|
399
|
+
### "Fade" vs "slide" sticky bar
|
|
400
|
+
|
|
401
|
+
The default sticky bar fades in. To slide it down from the top instead:
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
import { slideStickyAnimation } from 'react-native-collapsible-sticky-header';
|
|
405
|
+
|
|
406
|
+
<CollapsibleStickyHeaderScreen stickyAnimation={slideStickyAnimation(110)} … />
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Or write your own. **It must be a worklet**:
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
const scaleIn = (p: number) => {
|
|
413
|
+
'worklet';
|
|
414
|
+
return { opacity: p, transform: [{ scale: 0.95 + p * 0.05 }] };
|
|
415
|
+
};
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### No background image
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
<CollapsibleStickyHeaderScreen backgroundColor="#0f766e" title="Orders" … />
|
|
422
|
+
// or no background at all (transparent): omit both
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Rounded header bottom
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
<CollapsibleStickyHeaderScreen
|
|
429
|
+
backgroundColor="#4f46e5"
|
|
430
|
+
headerBottomRadius={24} // also disables the fade and the overhang by default
|
|
431
|
+
headerStyle={{ paddingBottom: 20 }}
|
|
432
|
+
…
|
|
433
|
+
/>
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Avatar + welcome text
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
<CollapsibleStickyHeaderScreen
|
|
440
|
+
greeting="Welcome back,"
|
|
441
|
+
title="Ava Thompson"
|
|
442
|
+
subtitle="You have 6 tasks today"
|
|
443
|
+
avatar={{ uri: user.photoUrl }} // or <Initials name="Ava" />
|
|
444
|
+
avatarPosition="left"
|
|
445
|
+
avatarSize={52}
|
|
446
|
+
onAvatarPress={openProfile}
|
|
447
|
+
…
|
|
448
|
+
/>
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Custom icons (up to 3)
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
actions={[
|
|
455
|
+
{ key: 'search', accessibilityLabel: 'Search', onPress: search,
|
|
456
|
+
icon: ({ color, size }) => <SearchIcon color={color} width={size} height={size} /> },
|
|
457
|
+
{ key: 'inbox', accessibilityLabel: 'Inbox', badgeCount: unread, onPress: openInbox,
|
|
458
|
+
icon: ({ color, size, variant }) =>
|
|
459
|
+
<Ionicons name={variant === 'overlay' ? 'mail' : 'mail-outline'} color={color} size={size} /> },
|
|
460
|
+
{ key: 'settings', accessibilityLabel: 'Settings', onPress: openSettings, showInSticky: false,
|
|
461
|
+
icon: <Image source={require('./gear.png')} style={{ width: 22, height: 22 }} /> },
|
|
462
|
+
]}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Custom collapsible content
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
|
|
469
|
+
|
|
470
|
+
function Stats({ progress }: { progress: SharedValue<number> }) {
|
|
471
|
+
const style = useAnimatedStyle(() => ({ transform: [{ scale: 1 - progress.value * 0.1 }] }));
|
|
472
|
+
return <Animated.View style={style}>{/* … */}</Animated.View>;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
<CollapsibleStickyHeaderScreen renderCollapsible={({ progress }) => <Stats progress={progress} />} … />
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Custom scrollable (FlashList)
|
|
479
|
+
|
|
480
|
+
```tsx
|
|
481
|
+
const AnimatedFlashList = Animated.createAnimatedComponent(FlashList);
|
|
482
|
+
|
|
483
|
+
<CollapsibleStickyHeaderScreen as={AnimatedFlashList} scrollProps={{ data, renderItem, estimatedItemSize: 72 }} … />
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Building blocks
|
|
487
|
+
|
|
488
|
+
To build your own screen, use these pieces directly:
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
import {
|
|
492
|
+
useStickyHeader,
|
|
493
|
+
HeaderNavBar,
|
|
494
|
+
HeaderGreeting,
|
|
495
|
+
Badge,
|
|
496
|
+
} from 'react-native-collapsible-sticky-header';
|
|
497
|
+
|
|
498
|
+
function MyScreen() {
|
|
499
|
+
const { scrollHandler, onNavLayout, collapseStyle, stickyStyle, isSticky } =
|
|
500
|
+
useStickyHeader({
|
|
501
|
+
thresholdOffset: 0,
|
|
502
|
+
stickyFadeDistance: 40,
|
|
503
|
+
stickyPointerOffset: 20,
|
|
504
|
+
onStickyChange: (s) => console.log(s),
|
|
505
|
+
});
|
|
506
|
+
return (
|
|
507
|
+
<View style={{ flex: 1 }}>
|
|
508
|
+
<Animated.ScrollView onScroll={scrollHandler} scrollEventThrottle={16}>
|
|
509
|
+
<View onLayout={onNavLayout}>
|
|
510
|
+
<HeaderNavBar variant="overlay" logo={logo} />
|
|
511
|
+
</View>
|
|
512
|
+
<Animated.View style={collapseStyle}>
|
|
513
|
+
<HeaderGreeting title="Hi" />
|
|
514
|
+
</Animated.View>
|
|
515
|
+
{/* … */}
|
|
516
|
+
</Animated.ScrollView>
|
|
517
|
+
<Animated.View
|
|
518
|
+
pointerEvents={isSticky ? 'auto' : 'none'}
|
|
519
|
+
style={[styles.bar, stickyStyle]}
|
|
520
|
+
>
|
|
521
|
+
<HeaderNavBar variant="solid" logo={logo} />
|
|
522
|
+
</Animated.View>
|
|
523
|
+
</View>
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
`useStickyHeader` returns `{ scrollHandler, scrollY, threshold, navBarHeight, collapseProgress, stickyProgress, collapseStyle, stickyStyle, isSticky, onNavLayout, updateScrollY, topInset, reducedMotion }`. `updateScrollY(y)` is a worklet that feeds offsets from a scrollable you drive yourself.
|
|
529
|
+
|
|
530
|
+
Also exported: `Badge` (`count`, `max = 9`, `color`, `borderColor`, `textColor`, `size`, `showZero`), `createTheme`, `defaultTheme`, `MAX_HEADER_ACTIONS`, the pure worklets `computeThreshold`, `getCollapseProgress`, `getStickyProgress` and `isStickyAt`, and the animation presets `fadeStickyAnimation` and `slideStickyAnimation`.
|
|
531
|
+
|
|
532
|
+
## Accessibility & performance
|
|
533
|
+
|
|
534
|
+
- Every action is a `Pressable` with `accessibilityRole="button"` and a **required** `accessibilityLabel`. A badge count is announced through `accessibilityValue`.
|
|
535
|
+
- While hidden, the sticky bar has `pointerEvents="none"`, `importantForAccessibility="no-hide-descendants"` (Android) and `accessibilityElementsHidden` (iOS).
|
|
536
|
+
- When reduced motion is enabled (`useReducedMotion()`), entering animations are skipped, the sticky bar appears instantly and the collapsible block only fades.
|
|
537
|
+
- Scroll is handled by `useAnimatedScrollHandler` on the UI thread. The component crosses to JS (`runOnJS`) only when the sticky state flips, so scrolling causes **no React re-renders**.
|
|
538
|
+
- Slots are memoized. If you pass inline `theme` objects or `actions` arrays, wrap them in `useMemo` to avoid needless header re-renders when the parent re-renders.
|
|
539
|
+
- Passing `scrollProps.onScroll` makes every scroll frame call into JS. Prefer `onStickyChange` or `useStickyHeader().scrollY`.
|
|
540
|
+
|
|
541
|
+
## Example app
|
|
542
|
+
|
|
543
|
+
`example/` is an Expo app with three screens:
|
|
544
|
+
|
|
545
|
+
1. **Bean Brew**: hero image, greeting, logo, bell and bag icons with a cart badge, and pull-to-refresh. Tap the compact logo to scroll to the top.
|
|
546
|
+
2. **Slide**: a `ScrollView` with a slide-down sticky bar.
|
|
547
|
+
3. **Rounded**: no image, a solid color, `headerBottomRadius={24}`, an avatar with welcome text, and 3 custom actions.
|
|
548
|
+
|
|
549
|
+
```sh
|
|
550
|
+
npm install
|
|
551
|
+
cd example && npm install
|
|
552
|
+
npx expo start # press i for iOS, a for Android
|
|
553
|
+
# or a native build:
|
|
554
|
+
npx expo run:ios
|
|
555
|
+
npx expo run:android
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The example resolves the library from `../src` through `metro.config.js`, so edits show up immediately.
|
|
559
|
+
|
|
560
|
+
## Contributing
|
|
561
|
+
|
|
562
|
+
Issues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
563
|
+
|
|
564
|
+
## Machine-readable summary
|
|
565
|
+
|
|
566
|
+
AI assistants and tools can read [`llms.txt`](llms.txt) for a compact API summary.
|
|
567
|
+
|
|
568
|
+
## License
|
|
569
|
+
|
|
570
|
+
MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Badge = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _theme = require("./theme.js");
|
|
10
|
+
var _utils = require("./utils.js");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
const Badge = exports.Badge = /*#__PURE__*/(0, _react.memo)(function Badge({
|
|
13
|
+
count,
|
|
14
|
+
max = _utils.DEFAULT_BADGE_MAX,
|
|
15
|
+
color = _theme.defaultTheme.colors.badge,
|
|
16
|
+
textColor = _theme.defaultTheme.colors.badgeText,
|
|
17
|
+
borderColor = _theme.defaultTheme.colors.badgeBorder,
|
|
18
|
+
borderWidth = 1.5,
|
|
19
|
+
size = _theme.defaultTheme.badgeSize,
|
|
20
|
+
showZero = false,
|
|
21
|
+
style,
|
|
22
|
+
textStyle,
|
|
23
|
+
testID = 'sticky-header-badge'
|
|
24
|
+
}) {
|
|
25
|
+
const whole = Math.floor(count);
|
|
26
|
+
if (!Number.isFinite(whole) || whole < 0 || whole === 0 && !showZero) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
30
|
+
testID: testID,
|
|
31
|
+
pointerEvents: "none",
|
|
32
|
+
importantForAccessibility: "no-hide-descendants",
|
|
33
|
+
accessibilityElementsHidden: true,
|
|
34
|
+
style: [styles.badge, {
|
|
35
|
+
backgroundColor: color,
|
|
36
|
+
borderColor,
|
|
37
|
+
borderWidth,
|
|
38
|
+
minWidth: size,
|
|
39
|
+
height: size,
|
|
40
|
+
borderRadius: size / 2
|
|
41
|
+
}, style],
|
|
42
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
43
|
+
numberOfLines: 1,
|
|
44
|
+
allowFontScaling: false,
|
|
45
|
+
style: [styles.text, {
|
|
46
|
+
color: textColor,
|
|
47
|
+
fontSize: Math.round(size * 0.55)
|
|
48
|
+
}, textStyle],
|
|
49
|
+
children: (0, _utils.formatBadgeCount)(whole, max)
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
const styles = _reactNative.StyleSheet.create({
|
|
54
|
+
badge: {
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
justifyContent: 'center',
|
|
57
|
+
paddingHorizontal: 4
|
|
58
|
+
},
|
|
59
|
+
text: {
|
|
60
|
+
fontWeight: '700',
|
|
61
|
+
includeFontPadding: false,
|
|
62
|
+
textAlign: 'center'
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=Badge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","require","_reactNative","_theme","_utils","_jsxRuntime","Badge","exports","memo","count","max","DEFAULT_BADGE_MAX","color","defaultTheme","colors","badge","textColor","badgeText","borderColor","badgeBorder","borderWidth","size","badgeSize","showZero","style","textStyle","testID","whole","Math","floor","Number","isFinite","jsx","View","pointerEvents","importantForAccessibility","accessibilityElementsHidden","styles","backgroundColor","minWidth","height","borderRadius","children","Text","numberOfLines","allowFontScaling","text","fontSize","round","formatBadgeCount","StyleSheet","create","alignItems","justifyContent","paddingHorizontal","fontWeight","includeFontPadding","textAlign"],"sourceRoot":"../../src","sources":["Badge.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAQA,IAAAE,MAAA,GAAAF,OAAA;AACA,IAAAG,MAAA,GAAAH,OAAA;AAA8D,IAAAI,WAAA,GAAAJ,OAAA;AAoBvD,MAAMK,KAAK,GAAAC,OAAA,CAAAD,KAAA,gBAAG,IAAAE,WAAI,EAAC,SAASF,KAAKA,CAAC;EACvCG,KAAK;EACLC,GAAG,GAAGC,wBAAiB;EACvBC,KAAK,GAAGC,mBAAY,CAACC,MAAM,CAACC,KAAK;EACjCC,SAAS,GAAGH,mBAAY,CAACC,MAAM,CAACG,SAAS;EACzCC,WAAW,GAAGL,mBAAY,CAACC,MAAM,CAACK,WAAW;EAC7CC,WAAW,GAAG,GAAG;EACjBC,IAAI,GAAGR,mBAAY,CAACS,SAAS;EAC7BC,QAAQ,GAAG,KAAK;EAChBC,KAAK;EACLC,SAAS;EACTC,MAAM,GAAG;AACC,CAAC,EAAE;EACb,MAAMC,KAAK,GAAGC,IAAI,CAACC,KAAK,CAACpB,KAAK,CAAC;EAC/B,IAAI,CAACqB,MAAM,CAACC,QAAQ,CAACJ,KAAK,CAAC,IAAIA,KAAK,GAAG,CAAC,IAAKA,KAAK,KAAK,CAAC,IAAI,CAACJ,QAAS,EAAE;IACtE,OAAO,IAAI;EACb;EACA,oBACE,IAAAlB,WAAA,CAAA2B,GAAA,EAAC9B,YAAA,CAAA+B,IAAI;IACHP,MAAM,EAAEA,MAAO;IACfQ,aAAa,EAAC,MAAM;IACpBC,yBAAyB,EAAC,qBAAqB;IAC/CC,2BAA2B;IAC3BZ,KAAK,EAAE,CACLa,MAAM,CAACtB,KAAK,EACZ;MACEuB,eAAe,EAAE1B,KAAK;MACtBM,WAAW;MACXE,WAAW;MACXmB,QAAQ,EAAElB,IAAI;MACdmB,MAAM,EAAEnB,IAAI;MACZoB,YAAY,EAAEpB,IAAI,GAAG;IACvB,CAAC,EACDG,KAAK,CACL;IAAAkB,QAAA,eAEF,IAAArC,WAAA,CAAA2B,GAAA,EAAC9B,YAAA,CAAAyC,IAAI;MACHC,aAAa,EAAE,CAAE;MACjBC,gBAAgB,EAAE,KAAM;MACxBrB,KAAK,EAAE,CACLa,MAAM,CAACS,IAAI,EACX;QAAElC,KAAK,EAAEI,SAAS;QAAE+B,QAAQ,EAAEnB,IAAI,CAACoB,KAAK,CAAC3B,IAAI,GAAG,IAAI;MAAE,CAAC,EACvDI,SAAS,CACT;MAAAiB,QAAA,EAED,IAAAO,uBAAgB,EAACtB,KAAK,EAAEjB,GAAG;IAAC,CACzB;EAAC,CACH,CAAC;AAEX,CAAC,CAAC;AAEF,MAAM2B,MAAM,GAAGa,uBAAU,CAACC,MAAM,CAAC;EAC/BpC,KAAK,EAAE;IACLqC,UAAU,EAAE,QAAQ;IACpBC,cAAc,EAAE,QAAQ;IACxBC,iBAAiB,EAAE;EACrB,CAAC;EACDR,IAAI,EAAE;IACJS,UAAU,EAAE,KAAK;IACjBC,kBAAkB,EAAE,KAAK;IACzBC,SAAS,EAAE;EACb;AACF,CAAC,CAAC","ignoreList":[]}
|