@honeypathkar/react-native-lq-bottom-bar 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 +341 -2
- package/android/build.gradle +78 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/honeypathkar/lqbottombar/BackdropBlur.kt +428 -0
- package/android/src/main/java/com/honeypathkar/lqbottombar/LQBottomBarPackage.kt +34 -0
- package/android/src/main/java/com/honeypathkar/lqbottombar/LQBottomBarView.kt +503 -0
- package/android/src/main/java/com/honeypathkar/lqbottombar/LQBottomBarViewManager.kt +119 -0
- package/android/src/main/java/com/honeypathkar/lqbottombar/TabSelectedEvent.kt +30 -0
- package/ios/LQBottomBarView.swift +287 -0
- package/ios/LQBottomBarViewManager.m +21 -0
- package/ios/LQBottomBarViewManager.swift +36 -0
- package/lib/commonjs/NativeBottomBar.d.ts +33 -0
- package/lib/commonjs/NativeBottomBar.d.ts.map +1 -0
- package/lib/commonjs/NativeBottomBar.js +9 -0
- package/lib/commonjs/NativeBottomBar.js.map +1 -0
- package/lib/commonjs/index.d.ts +6 -0
- package/lib/commonjs/index.d.ts.map +1 -0
- package/lib/commonjs/index.js +131 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/rasterise.d.ts +20 -0
- package/lib/commonjs/rasterise.d.ts.map +1 -0
- package/lib/commonjs/rasterise.js +398 -0
- package/lib/commonjs/rasterise.js.map +1 -0
- package/lib/commonjs/types.d.ts +119 -0
- package/lib/commonjs/types.d.ts.map +1 -0
- package/lib/commonjs/types.js +3 -0
- package/lib/commonjs/types.js.map +1 -0
- package/package.json +65 -4
- package/react-native-lq-bottom-bar.podspec +27 -0
- package/src/NativeBottomBar.ts +43 -0
- package/src/index.tsx +139 -0
- package/src/rasterise.tsx +444 -0
- package/src/types.ts +136 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 honeypathkar
|
|
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,342 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-native-lq-bottom-bar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The **real** iOS `UITabBar` — liquid glass on iOS 26, UIKit's own selection
|
|
4
|
+
animation, the system's accessibility — on **any** screen. Plus a matching
|
|
5
|
+
Android bar with a blurred backdrop.
|
|
6
|
+
|
|
7
|
+
Takes **React icons**, not only images.
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import LQBottomBar from '@honeypathkar/react-native-lq-bottom-bar';
|
|
11
|
+
import { House, Ticket, MessageCircle, User } from 'lucide-react-native';
|
|
12
|
+
|
|
13
|
+
<LQBottomBar
|
|
14
|
+
style={{ height: 49 + insets.bottom }}
|
|
15
|
+
selectedIndex={index}
|
|
16
|
+
onTabSelected={setIndex}
|
|
17
|
+
selectedColor="#4F8EF7"
|
|
18
|
+
items={[
|
|
19
|
+
{ key: 'home', title: 'Home', icon: <House /> },
|
|
20
|
+
{ key: 'events', title: 'Events', icon: <Ticket /> },
|
|
21
|
+
{ key: 'chat', title: 'Chats', icon: <MessageCircle /> },
|
|
22
|
+
{ key: 'profile', title: 'Profile', icon: <User /> },
|
|
23
|
+
]}
|
|
24
|
+
/>;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Why this exists
|
|
28
|
+
|
|
29
|
+
A native tab bar normally only exists inside a `UITabBarController`, and a
|
|
30
|
+
`UITabBarController` only draws its bar for its own child view controllers. So
|
|
31
|
+
a screen pushed **above** a tab navigator cannot have one, and every app ends
|
|
32
|
+
up hand-drawing a replacement that does not quite match.
|
|
33
|
+
|
|
34
|
+
That constraint is real for the **controller**. It is not real for the **bar**.
|
|
35
|
+
`UITabBar` is a plain `UIView`: it can be added to any hierarchy, given
|
|
36
|
+
`UITabBarItem`s, and told which one is selected. Doing that gets the genuine
|
|
37
|
+
control rather than a copy of it —
|
|
38
|
+
|
|
39
|
+
- On iOS 26 the system applies **liquid glass**, including the scroll-edge
|
|
40
|
+
treatment, because the appearance is resolved by the bar itself and not by
|
|
41
|
+
its controller.
|
|
42
|
+
- The selection animation, the icon bounce and the label crossfade are UIKit's
|
|
43
|
+
own.
|
|
44
|
+
- VoiceOver, Dynamic Type, Reduce Motion and the high-contrast variants all
|
|
45
|
+
come for free, because this **is** the system control.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npm install @honeypathkar/react-native-lq-bottom-bar react-native-svg
|
|
51
|
+
cd ios && pod install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Autolinked on both platforms. `react-native-svg` is a peer dependency, needed
|
|
55
|
+
only for React-element icons — which is the point of the package, so you almost
|
|
56
|
+
certainly want it.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Icons
|
|
61
|
+
|
|
62
|
+
`items[].icon` accepts any of:
|
|
63
|
+
|
|
64
|
+
| Form | Example | Platforms |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| React element | `<House />`, any `<Svg>` | iOS, Android |
|
|
67
|
+
| Render function | `({ selected, color, size }) => <Icon … />` | iOS, Android |
|
|
68
|
+
| Bundled asset | `require('./home.png')` | iOS |
|
|
69
|
+
| Asset catalogue | `{ name: 'home' }` | iOS |
|
|
70
|
+
| SF Symbol | `{ sfSymbol: 'house.fill' }` | iOS |
|
|
71
|
+
|
|
72
|
+
### ⚠️ Your icon must forward its ref to an `<Svg>`
|
|
73
|
+
|
|
74
|
+
**This is the single most common reason icons do not appear.** Read it before
|
|
75
|
+
anything else.
|
|
76
|
+
|
|
77
|
+
Both platforms draw their own tab items natively, so a React icon has to become
|
|
78
|
+
a PNG before it reaches the bar. The capture is done through react-native-svg's
|
|
79
|
+
`toDataURL()`, which needs a ref on the `<Svg>` itself. A plain function
|
|
80
|
+
component **silently drops the ref**:
|
|
81
|
+
|
|
82
|
+
```jsx
|
|
83
|
+
// ✗ Ref goes nowhere. Renders a title with no glyph.
|
|
84
|
+
const HomeIcon = ({ size }) => <Svg width={size} height={size}>…</Svg>;
|
|
85
|
+
|
|
86
|
+
// ✓ Ref reaches the Svg.
|
|
87
|
+
const HomeIcon = forwardRef(({ size }, ref) => (
|
|
88
|
+
<Svg ref={ref} width={size} height={size}>…</Svg>
|
|
89
|
+
));
|
|
90
|
+
HomeIcon.displayName = 'HomeIcon';
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Lucide icons already do this — `createLucideIcon` is built on `forwardRef` and
|
|
94
|
+
passes the ref straight to its `<Svg>` — so `lucide-react-native` works
|
|
95
|
+
unmodified. Your own icon components probably do not, unless you wrote them
|
|
96
|
+
that way.
|
|
97
|
+
|
|
98
|
+
The same applies to an icon wrapped in a `<View>`: the ref lands on the wrapper
|
|
99
|
+
and the SVG underneath is unreachable.
|
|
100
|
+
|
|
101
|
+
In development the package **warns** when a capture fails, naming the item:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
[lq-bottom-bar] Could not rasterise the icon for "home". The icon must be a
|
|
105
|
+
react-native-svg <Svg> element, or a component that forwards its ref to one …
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If you see a bar with labels and no glyphs, check your logs for that line.
|
|
109
|
+
|
|
110
|
+
### How a React element becomes a tab-bar icon
|
|
111
|
+
|
|
112
|
+
A `UITabBar` renders its items natively and accepts only `UIImage`. That is not
|
|
113
|
+
a limitation to argue with — it is precisely **why** the bar looks and behaves
|
|
114
|
+
like the system's own, since UIKit draws the items rather than compositing
|
|
115
|
+
someone else's views over them. The Android bar draws its own items too.
|
|
116
|
+
|
|
117
|
+
So the package mounts each icon off-screen once, captures it with `toDataURL()`,
|
|
118
|
+
hands the base64 to the native bar, and memoises the result by icon identity.
|
|
119
|
+
|
|
120
|
+
The alternative — overlaying JS icons on top of the native bar — accepts any
|
|
121
|
+
renderer and needs none of this, but forfeits UIKit's selection animation,
|
|
122
|
+
which is the thing the native bar is for. A one-off rasterisation is the
|
|
123
|
+
cheaper trade.
|
|
124
|
+
|
|
125
|
+
**What it costs.** The first frame for an uncached icon shows the title with no
|
|
126
|
+
glyph. Successful captures are cached at module scope, so that is one frame per
|
|
127
|
+
distinct icon per app launch, not per mount.
|
|
128
|
+
|
|
129
|
+
**Failures are not cached across mounts.** An icon that could not be captured is
|
|
130
|
+
retried next time the bar mounts, rather than being written off for the life of
|
|
131
|
+
the JS context — otherwise fixing a broken icon during a Fast Refresh session
|
|
132
|
+
would appear to do nothing, because the failure would outlive the fix.
|
|
133
|
+
|
|
134
|
+
### Tinting, and the solid face
|
|
135
|
+
|
|
136
|
+
The capture is handed to the platform as a **template**: its alpha channel is
|
|
137
|
+
the shape and the system picks the colour, selected against unselected. One
|
|
138
|
+
artwork covers both states and follows the platform's own treatment.
|
|
139
|
+
|
|
140
|
+
This means you want the **solid, opaque** face of a two-state icon, not the
|
|
141
|
+
outlined one. An outline variant wrapped in `<G opacity="0.5">` bakes
|
|
142
|
+
half-transparency into the alpha and comes out washed out in **both** states:
|
|
143
|
+
|
|
144
|
+
```jsx
|
|
145
|
+
// Rasterise the solid face. The system decides the colour from there.
|
|
146
|
+
icon: ({ size }) => <MyIcon isActive size={size} />;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Set `tintable: false` on an item whose own colours **are** the content — an
|
|
150
|
+
avatar, a brand mark. It will then look identical selected and not, which is
|
|
151
|
+
right for those and wrong for a glyph.
|
|
152
|
+
|
|
153
|
+
### Cache keys
|
|
154
|
+
|
|
155
|
+
Captures are memoised by icon identity. Pass a stable `key` per item whenever
|
|
156
|
+
the list is static, which it nearly always is:
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
{ key: 'home', title: 'Home', icon: <House /> }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Without it the key is derived from the icon's component type and a
|
|
163
|
+
serialisation of its props — correct, but it costs that serialisation on every
|
|
164
|
+
render, and it collapses any function-valued prop into a single bucket.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Props
|
|
169
|
+
|
|
170
|
+
| Prop | Type | Default | Notes |
|
|
171
|
+
| --- | --- | --- | --- |
|
|
172
|
+
| `items` | `LQBottomBarItem[]` | — | `{ title?, icon, key?, tintable?, accessibilityLabel? }` |
|
|
173
|
+
| `selectedIndex` | `number` | — | **You own this**; see below |
|
|
174
|
+
| `onTabSelected` | `(index: number) => void` | — | A report, not a commit |
|
|
175
|
+
| `selectedColor` | `string` | system | Tint for the selected item |
|
|
176
|
+
| `unselectedColor` | `string` | system | Tint for the rest |
|
|
177
|
+
| `labeled` | `boolean` | `true` | `false` hides titles and re-centres the glyphs |
|
|
178
|
+
| `iconSize` | `number` | `24` | Points |
|
|
179
|
+
| `style` | `ViewStyle` | — | The bar is laid out by Yoga; height and width are whatever this says |
|
|
180
|
+
| `pointerEvents` | `ViewProps['pointerEvents']` | — | `"box-none"` lets touches through the empty space around the items |
|
|
181
|
+
| `blurAmount` | `number` | `20` | **Android only.** `0` disables the blur entirely |
|
|
182
|
+
| `androidBackgroundColor` | `string` | `rgba(20,20,20,0.45)` | **Android only.** The material's tint, over the blur — keep it translucent |
|
|
183
|
+
|
|
184
|
+
### `selectedIndex` is yours, on purpose
|
|
185
|
+
|
|
186
|
+
`onTabSelected` **reports** a press; it does not commit to it. Both bars put
|
|
187
|
+
their highlight back where `selectedIndex` says it is after every press, so a
|
|
188
|
+
tap you decline to act on — one that opens a detail screen instead of switching
|
|
189
|
+
tabs — cannot leave the bar showing the wrong item.
|
|
190
|
+
|
|
191
|
+
```jsx
|
|
192
|
+
onTabSelected={index => {
|
|
193
|
+
if (index === HOME && activePremise) {
|
|
194
|
+
navigation.navigate('PremiseDetail'); // declined: the bar does not move
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
setIndex(index);
|
|
198
|
+
}}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Layout: in flow, or overlaid?
|
|
204
|
+
|
|
205
|
+
The bar is sized entirely by `style`. UIKit's standard body is **49pt**,
|
|
206
|
+
excluding the home-indicator inset:
|
|
207
|
+
|
|
208
|
+
```jsx
|
|
209
|
+
style={{ height: 49 + insets.bottom }}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`marginHorizontal` plus a `borderRadius` narrows it into a floating pill; the
|
|
213
|
+
bar spreads its items evenly across whatever width it ends up with.
|
|
214
|
+
|
|
215
|
+
**Decide early which of these two you are doing, and apply it consistently.**
|
|
216
|
+
Mixing them is the single biggest source of layout bugs with this component.
|
|
217
|
+
|
|
218
|
+
**In flow** — the bar is an ordinary flex child and claims its own band:
|
|
219
|
+
|
|
220
|
+
```jsx
|
|
221
|
+
<View style={{ flex: 1 }}>
|
|
222
|
+
<View style={{ flex: 1 }}>{screens}</View>
|
|
223
|
+
<LQBottomBar style={{ height: 49 + insets.bottom }} … />
|
|
224
|
+
</View>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Every screen's viewport ends where the bar begins, so a control pinned at
|
|
228
|
+
`bottom: 0` sits above it and **no screen should add clearance** — doing so
|
|
229
|
+
reserves the band twice and opens a strip of dead page. The cost is that a
|
|
230
|
+
translucent `UITabBar` has only your root view behind it, so the material
|
|
231
|
+
renders over whatever colour that is. Give the container a background you are
|
|
232
|
+
happy to see through glass.
|
|
233
|
+
|
|
234
|
+
**Overlaid** — the bar floats over the content:
|
|
235
|
+
|
|
236
|
+
```jsx
|
|
237
|
+
<LQBottomBar
|
|
238
|
+
pointerEvents="box-none"
|
|
239
|
+
style={{ position: 'absolute', left: 0, right: 0, bottom: 0,
|
|
240
|
+
height: 49 + insets.bottom }}
|
|
241
|
+
…
|
|
242
|
+
/>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This is what the material is designed for: the page runs underneath and the
|
|
246
|
+
glass refracts it. But then **every** screen must reserve the bar's height
|
|
247
|
+
itself — scroll views via `contentContainerStyle.paddingBottom`, pinned
|
|
248
|
+
controls via `bottom`. Any screen that forgets gets its bottom content covered.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Android
|
|
253
|
+
|
|
254
|
+
There is no system bottom bar to borrow — Material's `NavigationBarView` is a
|
|
255
|
+
different control with its own shape and opinions — so the Android bar is drawn
|
|
256
|
+
to match: same dimensions, same tint crossfade, same icon bounce on UIKit's own
|
|
257
|
+
timing curve, with a blurred backdrop standing in for the iOS material.
|
|
258
|
+
|
|
259
|
+
Styling comes from React Native's standard props, because the view subclasses
|
|
260
|
+
`ReactViewGroup`: `borderRadius`, `backgroundColor`, `overflow` and elevation
|
|
261
|
+
all work as they do on a `<View>`.
|
|
262
|
+
|
|
263
|
+
### The blur, and its one real limitation
|
|
264
|
+
|
|
265
|
+
Android has no API for blurring what is **behind** a view.
|
|
266
|
+
`RenderEffect.createBlurEffect` blurs a view's *own* content, and
|
|
267
|
+
`Window.setBackgroundBlurRadius` applies to a window, not a view inside one. So
|
|
268
|
+
the backdrop is produced the way every Android blur library produces it: draw
|
|
269
|
+
the root view into a small off-screen bitmap, blur that, and paint it under the
|
|
270
|
+
bar's contents. `RenderEffect` does the blur on API 31+; a three-pass box blur
|
|
271
|
+
does it below that.
|
|
272
|
+
|
|
273
|
+
That technique uses a **software** draw, and content the GPU owns exclusively
|
|
274
|
+
does not participate. A `SurfaceView` or `TextureView` — **video, a map, a
|
|
275
|
+
camera preview** — captures as a blank region, so the bar reads as frosted over
|
|
276
|
+
nothing there. There is no fix available from inside the view hierarchy. Where
|
|
277
|
+
that matters, pass `blurAmount={0}` and let the flat `androidBackgroundColor`
|
|
278
|
+
scrim carry the bar; the capture work and its per-frame cost stop entirely.
|
|
279
|
+
|
|
280
|
+
No JitPack dependency is involved, deliberately — the obvious off-the-shelf
|
|
281
|
+
choice would force every consuming app to add that repository to its own
|
|
282
|
+
`settings.gradle` before this package would resolve.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Troubleshooting
|
|
287
|
+
|
|
288
|
+
**Labels show but glyphs do not.** Your icon component is not forwarding its
|
|
289
|
+
ref to an `<Svg>`. See the warning section above — this is nearly always it.
|
|
290
|
+
|
|
291
|
+
**A white or mismatched strip under the bar.** The bar is in flow and the
|
|
292
|
+
translucent material has only your root view behind it. Give that container a
|
|
293
|
+
background colour, or move to the overlaid layout.
|
|
294
|
+
|
|
295
|
+
**Dead space above the bar.** Something is reserving the bar's height twice —
|
|
296
|
+
usually an in-flow bar plus a screen that also adds bottom clearance.
|
|
297
|
+
|
|
298
|
+
**`Unable to resolve module` when linked with `file:`.** Metro only serves
|
|
299
|
+
files under roots it knows about; add the package's real path to
|
|
300
|
+
`watchFolders`. Metro reads its config once at startup, so restart it.
|
|
301
|
+
|
|
302
|
+
**`Cannot read property 'useMemo' of null` when linked with `file:`.** Two
|
|
303
|
+
copies of React — the linked package has its own in `node_modules`. Pin the
|
|
304
|
+
singletons in `metro.config.js`; `extraNodeModules` will **not** do it, because
|
|
305
|
+
it is only a fallback for modules that fail to resolve, and these resolve fine
|
|
306
|
+
to the wrong copy:
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
const SINGLETONS = ['react', 'react-native', 'react-native-svg'];
|
|
310
|
+
|
|
311
|
+
resolver: {
|
|
312
|
+
resolveRequest: (context, moduleName, platform) =>
|
|
313
|
+
context.resolveRequest(
|
|
314
|
+
SINGLETONS.some(n => moduleName === n || moduleName.startsWith(`${n}/`))
|
|
315
|
+
? { ...context, originModulePath: path.join(__dirname, 'index.js') }
|
|
316
|
+
: context,
|
|
317
|
+
moduleName,
|
|
318
|
+
platform,
|
|
319
|
+
),
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Architecture notes
|
|
326
|
+
|
|
327
|
+
Both platforms use a **legacy view manager**, bridged through React Native's
|
|
328
|
+
interop layer, rather than a Fabric component. On iOS a Fabric component view
|
|
329
|
+
must be written in Objective-C++, which cannot host Swift directly and
|
|
330
|
+
interacts badly with `use_frameworks!` linkage; on Android it would need a
|
|
331
|
+
codegen spec kept in step for no behaviour this bar needs.
|
|
332
|
+
|
|
333
|
+
The podspec calls `install_modules_dependencies` rather than depending on
|
|
334
|
+
`React-Core` directly. With the new architecture on, a pod that touches React
|
|
335
|
+
headers needs the whole Fabric/JSI header surface and its flags; `React-Core`
|
|
336
|
+
alone leaves `jsi/jsi.h` off the search path, and the failure then surfaces
|
|
337
|
+
inside React's **own** targets — which looks like a broken app rather than a
|
|
338
|
+
bad podspec.
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
MIT
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext {
|
|
3
|
+
buildToolsVersion = "34.0.0"
|
|
4
|
+
minSdkVersion = 24
|
|
5
|
+
compileSdkVersion = 35
|
|
6
|
+
targetSdkVersion = 35
|
|
7
|
+
kotlinVersion = "1.9.24"
|
|
8
|
+
}
|
|
9
|
+
repositories {
|
|
10
|
+
google()
|
|
11
|
+
mavenCentral()
|
|
12
|
+
}
|
|
13
|
+
dependencies {
|
|
14
|
+
classpath "com.android.tools.build:gradle:8.2.1"
|
|
15
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
apply plugin: "com.android.library"
|
|
20
|
+
apply plugin: "kotlin-android"
|
|
21
|
+
|
|
22
|
+
android {
|
|
23
|
+
namespace "com.honeypathkar.lqbottombar"
|
|
24
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion", 35)
|
|
25
|
+
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion getExtOrDefault("minSdkVersion", 24)
|
|
28
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion", 35)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
buildTypes {
|
|
32
|
+
release {
|
|
33
|
+
minifyEnabled false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
lintOptions {
|
|
38
|
+
disable "GradleCompatible"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
compileOptions {
|
|
42
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
43
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
kotlinOptions {
|
|
47
|
+
jvmTarget = "17"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def getExtOrDefault(name, defaultValue) {
|
|
52
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : defaultValue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def getKotlinVersion() {
|
|
56
|
+
return rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : "1.9.24"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
repositories {
|
|
60
|
+
google()
|
|
61
|
+
mavenCentral()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
dependencies {
|
|
65
|
+
implementation "com.facebook.react:react-native:+"
|
|
66
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:${getKotlinVersion()}"
|
|
67
|
+
implementation "androidx.core:core-ktx:1.13.1"
|
|
68
|
+
|
|
69
|
+
// NO BLUR LIBRARY, deliberately.
|
|
70
|
+
//
|
|
71
|
+
// The obvious candidate is Dimezis/BlurView, but it is published on
|
|
72
|
+
// JitPack: every consuming app would have to add that repository to its
|
|
73
|
+
// own settings.gradle before this package would even resolve, which is a
|
|
74
|
+
// poor thing to impose from a dependency. The backdrop blur is instead
|
|
75
|
+
// implemented against the platform in BackdropBlur.kt — the same
|
|
76
|
+
// capture-downscale-blur technique, using RenderEffect where the platform
|
|
77
|
+
// has it and a stack blur below that.
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|