react-native-skelo 0.1.0-alpha.0 → 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 +1 -1
- package/README.md +147 -75
- package/lib/commonjs/animations/PulseAnimation.js +17 -42
- package/lib/commonjs/animations/PulseAnimation.js.map +1 -1
- package/lib/commonjs/animations/ShimmerAnimation.js +54 -65
- package/lib/commonjs/animations/ShimmerAnimation.js.map +1 -1
- package/lib/commonjs/components/Skeleton.js +37 -23
- package/lib/commonjs/components/Skeleton.js.map +1 -1
- package/lib/commonjs/components/SkeletonProvider.js +38 -0
- package/lib/commonjs/components/SkeletonProvider.js.map +1 -0
- package/lib/commonjs/components/withSkeleton.js +1 -0
- package/lib/commonjs/components/withSkeleton.js.map +1 -1
- package/lib/commonjs/core/deepRegistry.js +18 -0
- package/lib/commonjs/core/deepRegistry.js.map +1 -0
- package/lib/commonjs/core/parser/expandLists.js +67 -0
- package/lib/commonjs/core/parser/expandLists.js.map +1 -0
- package/lib/commonjs/deep.js +18 -0
- package/lib/commonjs/deep.js.map +1 -0
- package/lib/commonjs/index.js +8 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/animations/PulseAnimation.js +18 -43
- package/lib/module/animations/PulseAnimation.js.map +1 -1
- package/lib/module/animations/ShimmerAnimation.js +55 -66
- package/lib/module/animations/ShimmerAnimation.js.map +1 -1
- package/lib/module/components/Skeleton.js +37 -23
- package/lib/module/components/Skeleton.js.map +1 -1
- package/lib/module/components/SkeletonProvider.js +30 -0
- package/lib/module/components/SkeletonProvider.js.map +1 -0
- package/lib/module/components/withSkeleton.js +1 -0
- package/lib/module/components/withSkeleton.js.map +1 -1
- package/lib/module/core/deepRegistry.js +11 -0
- package/lib/module/core/deepRegistry.js.map +1 -0
- package/lib/module/core/parser/expandLists.js +61 -0
- package/lib/module/core/parser/expandLists.js.map +1 -0
- package/lib/module/deep.js +11 -0
- package/lib/module/deep.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/animations/PulseAnimation.d.ts +0 -44
- package/lib/typescript/animations/PulseAnimation.d.ts.map +1 -1
- package/lib/typescript/animations/ShimmerAnimation.d.ts +0 -44
- package/lib/typescript/animations/ShimmerAnimation.d.ts.map +1 -1
- package/lib/typescript/components/Skeleton.d.ts +1 -1
- package/lib/typescript/components/Skeleton.d.ts.map +1 -1
- package/lib/typescript/components/SkeletonProvider.d.ts +10 -0
- package/lib/typescript/components/SkeletonProvider.d.ts.map +1 -0
- package/lib/typescript/components/withSkeleton.d.ts.map +1 -1
- package/lib/typescript/core/deepRegistry.d.ts +7 -0
- package/lib/typescript/core/deepRegistry.d.ts.map +1 -0
- package/lib/typescript/core/parser/expandLists.d.ts +3 -0
- package/lib/typescript/core/parser/expandLists.d.ts.map +1 -0
- package/lib/typescript/deep.d.ts +2 -0
- package/lib/typescript/deep.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +3 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/skeleton.d.ts +8 -0
- package/lib/typescript/types/skeleton.d.ts.map +1 -1
- package/package.json +23 -12
- package/src/animations/PulseAnimation.tsx +17 -89
- package/src/animations/ShimmerAnimation.tsx +39 -118
- package/src/components/Skeleton.tsx +39 -23
- package/src/components/SkeletonProvider.tsx +40 -0
- package/src/components/withSkeleton.tsx +1 -0
- package/src/core/deepRegistry.ts +16 -0
- package/src/core/parser/expandLists.tsx +77 -0
- package/src/deep.ts +12 -0
- package/src/index.ts +6 -0
- package/src/types/skeleton.ts +9 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Shubhanshu
|
|
3
|
+
Copyright (c) 2026 Shubhanshu Barnwal <connect@shubhanshubb.dev> (https://shubhanshubb.dev)
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,119 +1,182 @@
|
|
|
1
|
-
|
|
1
|
+
# React Native Skelo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/react-native-skelo)
|
|
4
|
+
[](https://www.npmjs.com/package/react-native-skelo)
|
|
5
|
+
[](./LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Automatic skeleton loading for React Native, built from the UI you already have. Skelo turns your components and lists into matching loading placeholders, with built-in shimmer and pulse animations. Theme support lets you share colors and animation settings across your app, with nested themes and per-view overrides through `SkeletonProvider`.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
<img src="./assets/demo.gif" alt="Skelo skeleton demo" width="270" />
|
|
10
|
+
|
|
11
|
+
<img src="./assets/grid.gif" alt="Image-grid skeleton" width="270" />
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
> ⚠️ **Alpha.** The API may change before `1.0`. The `deep` engine is experimental — see [caveats](#deep-mode-caveats-experimental).
|
|
13
|
-
|
|
14
|
-
Skeleton loading screens usually mean building — and maintaining — a second copy of your UI. Skelo derives the skeleton from the UI you already wrote.
|
|
13
|
+
**Stop building and maintaining a second copy of your UI just for loading states.** Wrap your components in `<Skeleton>` and Skelo generates a matching skeleton — sized from the styles you already wrote.
|
|
15
14
|
|
|
16
15
|
```tsx
|
|
17
|
-
import { Skeleton } from 'react-native-skelo';
|
|
18
|
-
|
|
19
16
|
<Skeleton loading={isLoading}>
|
|
20
|
-
<
|
|
21
|
-
<Image style={styles.avatar} />
|
|
22
|
-
<Text style={styles.name}>{user.name}</Text>
|
|
23
|
-
</View>
|
|
17
|
+
<ProfileCard user={user} />
|
|
24
18
|
</Skeleton>
|
|
25
19
|
```
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
That's the whole API. No skeleton screens to hand-draw, no keeping two layouts in sync.
|
|
22
|
+
|
|
23
|
+
> **Stable release (`0.1.0`)** — Skelo now follows semantic versioning for its public API.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ✨ Features
|
|
28
|
+
|
|
29
|
+
- 🪄 **Zero-config** — wrap a component, get a skeleton. No setup, no separate loading screen.
|
|
30
|
+
- 🧠 **Sees inside your components** — introspects custom components automatically (no inlining).
|
|
31
|
+
- 📋 **Lists included** — `FlatList` / `SectionList` render skeleton rows while data loads.
|
|
32
|
+
- 🎨 **Shimmer · Pulse · None** — built-in animations on the native driver.
|
|
33
|
+
- 🌗 **Theme support (new in 0.1.0)** — use `SkeletonProvider` for shared colors and animation defaults, with nested themes and per-view overrides.
|
|
34
|
+
- 🪶 **No dependencies** — no `reanimated`, no gradient library. Just `react` + `react-native`.
|
|
35
|
+
- 📐 **Style-aware** — every skeleton is sized from your real element styles.
|
|
36
|
+
|
|
37
|
+
---
|
|
28
38
|
|
|
29
|
-
## Installation
|
|
39
|
+
## 📦 Installation
|
|
30
40
|
|
|
31
41
|
```bash
|
|
32
|
-
npm install react-native-skelo
|
|
42
|
+
npm install react-native-skelo
|
|
33
43
|
```
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
**That's it — no other dependencies.** Everything (including custom-component introspection) works out of the box.
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
> React 18 apps: add `"react-reconciler": "0.29.x"` to your deps (Skelo ships targeting React 19's `0.31`).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🚀 Usage
|
|
52
|
+
|
|
53
|
+
### Just wrap it
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { Skeleton } from 'react-native-skelo';
|
|
57
|
+
|
|
58
|
+
function Screen({ loading, user }) {
|
|
59
|
+
return (
|
|
60
|
+
<Skeleton loading={loading} animation="shimmer">
|
|
61
|
+
<ProfileCard user={user} />
|
|
62
|
+
</Skeleton>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
41
65
|
```
|
|
42
66
|
|
|
43
|
-
|
|
67
|
+
Skelo renders `ProfileCard`, reads its real `View`/`Text`/`Image` tree + styles, and draws a matching skeleton while `loading` is `true`. When it's `false`, your real UI renders.
|
|
44
68
|
|
|
45
|
-
###
|
|
69
|
+
### Theme support (new in 0.1.0)
|
|
46
70
|
|
|
47
|
-
|
|
71
|
+
Wrap your app or a group of views in `SkeletonProvider` to share colors and animation settings:
|
|
48
72
|
|
|
49
73
|
```tsx
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
import { Skeleton, SkeletonProvider } from 'react-native-skelo';
|
|
75
|
+
|
|
76
|
+
<SkeletonProvider
|
|
77
|
+
baseColor="#2A3F4F"
|
|
78
|
+
highlightColor="#3A5364"
|
|
79
|
+
animation="pulse"
|
|
80
|
+
duration={1500}
|
|
81
|
+
>
|
|
82
|
+
<App />
|
|
83
|
+
</SkeletonProvider>
|
|
56
84
|
```
|
|
57
85
|
|
|
58
|
-
|
|
86
|
+
Individual `Skeleton` props override the theme. Nested providers inherit settings
|
|
87
|
+
from their parent, so a section can override just its colors. Themes also apply to
|
|
88
|
+
`withSkeleton` and styles-driven skeletons. The provider accepts `animation`,
|
|
89
|
+
`duration`, `baseColor`, `highlightColor`, `borderRadius`, and `debug`; without a
|
|
90
|
+
provider, existing defaults are unchanged. Standalone primitives keep their own
|
|
91
|
+
explicit props and defaults.
|
|
59
92
|
|
|
60
|
-
|
|
93
|
+
#### Follow light and dark mode
|
|
61
94
|
|
|
62
|
-
|
|
95
|
+
Connect the provider to React Native's `useColorScheme` to update skeleton colors
|
|
96
|
+
when the system theme changes:
|
|
63
97
|
|
|
64
98
|
```tsx
|
|
65
|
-
import {
|
|
99
|
+
import { useColorScheme } from 'react-native';
|
|
100
|
+
import { SkeletonProvider } from 'react-native-skelo';
|
|
101
|
+
|
|
102
|
+
function ThemedApp() {
|
|
103
|
+
const isDark = useColorScheme() === 'dark';
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<SkeletonProvider
|
|
107
|
+
baseColor={isDark ? '#2A3F4F' : '#E1E9EE'}
|
|
108
|
+
highlightColor={isDark ? '#3A5364' : '#F0F4F8'}
|
|
109
|
+
animation="shimmer"
|
|
110
|
+
>
|
|
111
|
+
<App />
|
|
112
|
+
</SkeletonProvider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
66
116
|
|
|
67
|
-
|
|
68
|
-
<ProfileScreen />
|
|
69
|
-
</Skeleton>
|
|
117
|
+
#### Override a view's theme
|
|
70
118
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
119
|
+
Nest a provider to customize one section, or pass props to a single skeleton:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<SkeletonProvider animation="pulse" baseColor="#E1E9EE">
|
|
123
|
+
<Skeleton loading={loading}>
|
|
124
|
+
<ProfileCard user={user} />
|
|
125
|
+
</Skeleton>
|
|
126
|
+
|
|
127
|
+
<SkeletonProvider baseColor="#2A3F4F" highlightColor="#3A5364">
|
|
128
|
+
<Skeleton loading={loading} animation="none">
|
|
129
|
+
<StatsCard stats={stats} />
|
|
130
|
+
</Skeleton>
|
|
131
|
+
</SkeletonProvider>
|
|
132
|
+
</SkeletonProvider>
|
|
74
133
|
```
|
|
75
134
|
|
|
76
|
-
|
|
135
|
+
Here, the profile uses the shared pulse animation. The stats view uses the nested
|
|
136
|
+
dark colors and its own static animation.
|
|
77
137
|
|
|
78
|
-
###
|
|
138
|
+
### Lists
|
|
79
139
|
|
|
80
|
-
|
|
140
|
+
Wrap a `FlatList` — Skelo fills it with skeleton rows while the data loads:
|
|
81
141
|
|
|
82
142
|
```tsx
|
|
83
|
-
<Skeleton loading={loading}
|
|
143
|
+
<Skeleton loading={loading} count={6}>
|
|
144
|
+
<FlatList data={items} renderItem={renderItem} />
|
|
145
|
+
</Skeleton>
|
|
84
146
|
```
|
|
85
147
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### `Skeleton.Ignore` — keep something visible
|
|
148
|
+
### Keep something visible
|
|
89
149
|
|
|
90
150
|
```tsx
|
|
91
151
|
<Skeleton loading={loading}>
|
|
92
152
|
<Skeleton.Ignore>
|
|
93
|
-
<Text style={styles.heading}>Profile</Text>
|
|
153
|
+
<Text style={styles.heading}>Profile</Text> {/* stays real */}
|
|
94
154
|
</Skeleton.Ignore>
|
|
95
155
|
<ProfileBody />
|
|
96
156
|
</Skeleton>
|
|
97
157
|
```
|
|
98
158
|
|
|
99
|
-
###
|
|
159
|
+
### Generate from a StyleSheet
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
<Skeleton loading={loading} styles={styles} excludeStyles={['container']} />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Custom skeleton for a component
|
|
100
166
|
|
|
101
167
|
```tsx
|
|
102
168
|
import { Skelo } from 'react-native-skelo';
|
|
103
|
-
import { Avatar } from 'my-design-system';
|
|
104
169
|
|
|
105
170
|
Skelo.register({
|
|
106
171
|
name: 'Avatar',
|
|
107
172
|
component: Avatar,
|
|
108
|
-
strategy: (props, { primitives }) =>
|
|
109
|
-
<primitives.Circle size={props.size ?? 40} />
|
|
110
|
-
),
|
|
173
|
+
strategy: (props, { primitives }) => <primitives.Circle size={props.size ?? 40} />,
|
|
111
174
|
});
|
|
112
175
|
```
|
|
113
176
|
|
|
114
|
-
|
|
177
|
+
---
|
|
115
178
|
|
|
116
|
-
|
|
179
|
+
## ⚙️ `<Skeleton>` props
|
|
117
180
|
|
|
118
181
|
| Prop | Type | Default | Description |
|
|
119
182
|
|---|---|---|---|
|
|
@@ -122,33 +185,42 @@ Skelo.register({
|
|
|
122
185
|
| `duration` | `number` | `1200` | Animation duration (ms) |
|
|
123
186
|
| `baseColor` / `highlightColor` | `string` | theme | Skeleton colors |
|
|
124
187
|
| `borderRadius` | `number` | `4` | Default corner radius |
|
|
125
|
-
| `
|
|
188
|
+
| `count` | `number` | `6` | Placeholder rows for a `FlatList`/`SectionList` |
|
|
189
|
+
| `deep` | `boolean` | auto | Force component introspection on/off (auto when available) |
|
|
126
190
|
| `styles` | `StyleSheet \| style[]` | — | Generate from styles instead of children |
|
|
127
|
-
| `excludeStyles` | `string[]` | — | Style keys to skip in `styles` mode |
|
|
128
191
|
| `accessibilityLabel` | `string` | `'Loading content'` | Screen-reader label |
|
|
129
192
|
|
|
130
|
-
Also exported: `withSkeleton`, `SkeletonIgnore`, `StyleSkeleton`, the primitives (`SkeletonBox`, `SkeletonText`, `SkeletonImage`, `SkeletonCircle`), and `Skelo` (
|
|
193
|
+
Also exported: `withSkeleton`, `SkeletonIgnore`, `StyleSkeleton`, the primitives (`SkeletonBox`, `SkeletonText`, `SkeletonImage`, `SkeletonCircle`), and `Skelo` (`register` / `fromStyles`).
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🧩 How it works
|
|
198
|
+
|
|
199
|
+
- **Host elements** (`View` / `Text` / `Image`) are read directly (`React.Children` + `StyleSheet.flatten`).
|
|
200
|
+
- **Custom components** are expanded by a tiny JS-only `react-reconciler` renderer (bundled) that renders them off-screen to recover their real host tree + styles — then feeds the same skeleton engine.
|
|
201
|
+
- Anything Skelo can't introspect (native content) is measured and shown as one size-matched block.
|
|
202
|
+
|
|
203
|
+
---
|
|
131
204
|
|
|
132
|
-
##
|
|
205
|
+
## ⚠️ Good to know
|
|
133
206
|
|
|
134
|
-
|
|
135
|
-
- **`deep` mode** — a JS-only `react-reconciler` renderer renders the component off-screen to recover its real host tree + styles, then feeds the same skeleton renderer.
|
|
136
|
-
- Anything Skelo can't introspect (a custom component without `deep`, or native content) is measured and shown as a single size-matched block.
|
|
207
|
+
Skelo skeletonizes **what your component renders during loading**:
|
|
137
208
|
|
|
138
|
-
|
|
209
|
+
- Introspection **runs mount effects** — a screen that fetches on mount may fetch twice. Prefer it for presentational trees; register a plugin for effect-heavy screens.
|
|
210
|
+
- It renders **outside your providers** — components needing navigation/theme/redux context fall back to a measured block.
|
|
211
|
+
- **Native content** (`WebView`, `MapView`, `Video`, …) lives outside React, so it becomes a single sized block.
|
|
212
|
+
- A **data-driven list/grid** that renders nothing while loading has nothing to skeletonize — keep placeholder items rendered (see the [example](./example)).
|
|
139
213
|
|
|
140
|
-
|
|
214
|
+
---
|
|
141
215
|
|
|
142
|
-
|
|
143
|
-
- **Renders outside your providers** — components that require navigation/theme/redux context may fail to expand; Skelo catches this and falls back to a measured block.
|
|
144
|
-
- Only the **React host tree** is visible. `WebView`, `MapView`, `Video`, etc. render their content natively (outside React), so they can only become a single sized block.
|
|
216
|
+
## 📋 Requirements
|
|
145
217
|
|
|
146
|
-
|
|
218
|
+
- React Native (New Architecture recommended)
|
|
219
|
+
- React 19 (React 18 → override `react-reconciler` to `0.29`)
|
|
220
|
+
- No other dependencies
|
|
147
221
|
|
|
148
|
-
|
|
149
|
-
- `react-native-reanimated` ≥ 3
|
|
150
|
-
- `react-reconciler` (optional, for `deep`) — must match your React version
|
|
222
|
+
---
|
|
151
223
|
|
|
152
|
-
## License
|
|
224
|
+
## 📄 License
|
|
153
225
|
|
|
154
226
|
MIT © [Shubhanshu Barnwal](https://shubhanshubb.dev)
|
|
@@ -6,25 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.PulseAnimation = PulseAnimation;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
-
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
10
9
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
11
|
-
|
|
12
|
-
* Pulse animation component using Reanimated 3
|
|
13
|
-
*
|
|
14
|
-
* Creates a subtle breathing/pulsing effect using:
|
|
15
|
-
* - Shared values for performance
|
|
16
|
-
* - Worklets for 60 FPS on UI thread
|
|
17
|
-
* - Opacity animation for pulse effect
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <PulseAnimation
|
|
22
|
-
* width={200}
|
|
23
|
-
* height={20}
|
|
24
|
-
* borderRadius={4}
|
|
25
|
-
* />
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
10
|
+
// Breathing opacity loop using React Native's built-in Animated (native driver).
|
|
28
11
|
function PulseAnimation({
|
|
29
12
|
width,
|
|
30
13
|
height,
|
|
@@ -34,38 +17,30 @@ function PulseAnimation({
|
|
|
34
17
|
style,
|
|
35
18
|
children
|
|
36
19
|
}) {
|
|
37
|
-
|
|
38
|
-
const opacity = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
20
|
+
const opacity = (0, _react.useRef)(new _reactNative.Animated.Value(1)).current;
|
|
39
21
|
(0, _react.useEffect)(() => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
opacity.value = (0, _reactNativeReanimated.withRepeat)((0, _reactNativeReanimated.withSequence)((0, _reactNativeReanimated.withTiming)(0.5, {
|
|
22
|
+
const loop = _reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(opacity, {
|
|
23
|
+
toValue: 0.5,
|
|
43
24
|
duration: duration / 2,
|
|
44
|
-
easing:
|
|
45
|
-
|
|
25
|
+
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
26
|
+
useNativeDriver: true
|
|
27
|
+
}), _reactNative.Animated.timing(opacity, {
|
|
28
|
+
toValue: 1,
|
|
46
29
|
duration: duration / 2,
|
|
47
|
-
easing:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
30
|
+
easing: _reactNative.Easing.inOut(_reactNative.Easing.ease),
|
|
31
|
+
useNativeDriver: true
|
|
32
|
+
})]));
|
|
33
|
+
loop.start();
|
|
34
|
+
return () => loop.stop();
|
|
52
35
|
}, [duration, opacity]);
|
|
53
|
-
|
|
54
|
-
// Animated style for pulse effect
|
|
55
|
-
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
|
|
56
|
-
'worklet';
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
opacity: opacity.value
|
|
60
|
-
};
|
|
61
|
-
}, []);
|
|
62
|
-
return /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
|
|
36
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
|
|
63
37
|
style: [styles.container, {
|
|
64
38
|
width,
|
|
65
39
|
height,
|
|
66
40
|
borderRadius,
|
|
67
|
-
backgroundColor: baseColor
|
|
68
|
-
|
|
41
|
+
backgroundColor: baseColor,
|
|
42
|
+
opacity
|
|
43
|
+
}, style]
|
|
69
44
|
}, children);
|
|
70
45
|
}
|
|
71
46
|
const styles = _reactNative.StyleSheet.create({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","PulseAnimation","width","height","baseColor","duration","borderRadius","style","children","opacity","useRef","Animated","Value","current","useEffect","loop","sequence","timing","toValue","easing","Easing","inOut","ease","useNativeDriver","start","stop","createElement","View","styles","container","backgroundColor","StyleSheet","create","overflow"],"sourceRoot":"../../../src","sources":["animations/PulseAnimation.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,26 +6,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.ShimmerAnimation = ShimmerAnimation;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
-
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
10
|
-
var _reactNativeLinearGradient = require("react-native-linear-gradient");
|
|
11
9
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* Creates a smooth left-to-right shimmer effect using:
|
|
16
|
-
* - Shared values for performance
|
|
17
|
-
* - Worklets for 60 FPS on UI thread
|
|
18
|
-
* - Linear gradient for shimmer effect
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```tsx
|
|
22
|
-
* <ShimmerAnimation
|
|
23
|
-
* width={200}
|
|
24
|
-
* height={20}
|
|
25
|
-
* borderRadius={4}
|
|
26
|
-
* />
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
10
|
+
// Zero-dependency shimmer: a highlight band (dim/bright/dim segments to fake a
|
|
11
|
+
// soft gradient) sweeps across the base, driven by React Native's built-in
|
|
12
|
+
// Animated native driver. Plain Views only — guaranteed to render and animate.
|
|
29
13
|
function ShimmerAnimation({
|
|
30
14
|
width,
|
|
31
15
|
height,
|
|
@@ -36,68 +20,73 @@ function ShimmerAnimation({
|
|
|
36
20
|
style,
|
|
37
21
|
children
|
|
38
22
|
}) {
|
|
39
|
-
|
|
40
|
-
const
|
|
23
|
+
const progress = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
24
|
+
const [measuredWidth, setMeasuredWidth] = (0, _react.useState)(0);
|
|
41
25
|
(0, _react.useEffect)(() => {
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
const loop = _reactNative.Animated.loop(_reactNative.Animated.timing(progress, {
|
|
27
|
+
toValue: 1,
|
|
44
28
|
duration,
|
|
45
|
-
easing:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
29
|
+
easing: _reactNative.Easing.linear,
|
|
30
|
+
useNativeDriver: true
|
|
31
|
+
}));
|
|
32
|
+
loop.start();
|
|
33
|
+
return () => loop.stop();
|
|
50
34
|
}, [duration, progress]);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
translateX: translateX * (typeof width === 'number' ? width : 200)
|
|
62
|
-
}]
|
|
63
|
-
};
|
|
64
|
-
}, [width]);
|
|
35
|
+
const onLayout = event => {
|
|
36
|
+
const w = event.nativeEvent.layout.width;
|
|
37
|
+
setMeasuredWidth(prev => prev === w ? prev : w);
|
|
38
|
+
};
|
|
39
|
+
const containerWidth = typeof width === 'number' ? width : measuredWidth || 200;
|
|
40
|
+
const bandWidth = Math.max(containerWidth * 0.6, 80);
|
|
41
|
+
const translateX = progress.interpolate({
|
|
42
|
+
inputRange: [0, 1],
|
|
43
|
+
outputRange: [-bandWidth, containerWidth]
|
|
44
|
+
});
|
|
65
45
|
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
46
|
+
onLayout: onLayout,
|
|
66
47
|
style: [styles.container, {
|
|
67
48
|
width,
|
|
68
49
|
height,
|
|
69
50
|
borderRadius,
|
|
70
51
|
backgroundColor: baseColor
|
|
71
52
|
}, style]
|
|
72
|
-
}, /*#__PURE__*/_react.default.createElement(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
|
|
54
|
+
pointerEvents: "none",
|
|
55
|
+
style: [styles.band, {
|
|
56
|
+
width: bandWidth,
|
|
57
|
+
transform: [{
|
|
58
|
+
translateX
|
|
59
|
+
}]
|
|
60
|
+
}]
|
|
61
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
62
|
+
style: [styles.segment, {
|
|
63
|
+
backgroundColor: highlightColor,
|
|
64
|
+
opacity: 0.25
|
|
65
|
+
}]
|
|
66
|
+
}), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
67
|
+
style: [styles.segment, {
|
|
68
|
+
backgroundColor: highlightColor,
|
|
69
|
+
opacity: 0.9
|
|
76
70
|
}]
|
|
77
|
-
}, /*#__PURE__*/_react.default.createElement(
|
|
78
|
-
style: [styles.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
colors: [baseColor, highlightColor, highlightColor, baseColor],
|
|
84
|
-
start: {
|
|
85
|
-
x: 0,
|
|
86
|
-
y: 0
|
|
87
|
-
},
|
|
88
|
-
end: {
|
|
89
|
-
x: 1,
|
|
90
|
-
y: 0
|
|
91
|
-
},
|
|
92
|
-
style: _reactNative.StyleSheet.absoluteFill
|
|
93
|
-
}))), children);
|
|
71
|
+
}), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
72
|
+
style: [styles.segment, {
|
|
73
|
+
backgroundColor: highlightColor,
|
|
74
|
+
opacity: 0.25
|
|
75
|
+
}]
|
|
76
|
+
})), children);
|
|
94
77
|
}
|
|
95
78
|
const styles = _reactNative.StyleSheet.create({
|
|
96
79
|
container: {
|
|
97
80
|
overflow: 'hidden'
|
|
98
81
|
},
|
|
99
|
-
|
|
100
|
-
position: 'absolute'
|
|
82
|
+
band: {
|
|
83
|
+
position: 'absolute',
|
|
84
|
+
top: 0,
|
|
85
|
+
bottom: 0,
|
|
86
|
+
flexDirection: 'row'
|
|
87
|
+
},
|
|
88
|
+
segment: {
|
|
89
|
+
flex: 1
|
|
101
90
|
}
|
|
102
91
|
});
|
|
103
92
|
//# sourceMappingURL=ShimmerAnimation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ShimmerAnimation","width","height","baseColor","highlightColor","duration","borderRadius","style","children","progress","useRef","Animated","Value","current","measuredWidth","setMeasuredWidth","useState","useEffect","loop","timing","toValue","easing","Easing","linear","useNativeDriver","start","stop","onLayout","event","w","nativeEvent","layout","prev","containerWidth","bandWidth","Math","max","translateX","interpolate","inputRange","outputRange","createElement","View","styles","container","backgroundColor","pointerEvents","band","transform","segment","opacity","StyleSheet","create","overflow","position","top","bottom","flexDirection","flex"],"sourceRoot":"../../../src","sources":["animations/ShimmerAnimation.tsx"],"mappings":"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","ignoreList":[]}
|