Skip to content

Commit 9b5b72a

Browse files
committed
chore: use ts files instead of json files
1 parent ca375da commit 9b5b72a

33 files changed

Lines changed: 976 additions & 591 deletions

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
blank_issues_enabled: false
22
contact_links:
33
- name: Feature Request 💡
4-
url: https://github.com/shobbak/react-native-auto-skeleton/discussions/new?category=ideas
4+
url: https://github.com/numandev1/react-native-auto-skeleton/discussions/new?category=ideas
55
about: If you have a feature request, please create a new discussion on GitHub.
66
- name: Discussions on GitHub 💬
7-
url: https://github.com/shobbak/react-native-auto-skeleton/discussions
7+
url: https://github.com/numandev1/react-native-auto-skeleton/discussions
88
about: If this library works as promised but you need help, please ask questions there.

README.md

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ Building skeleton screens is tedious. You eyeball pixel values, hardcode widths,
7171

7272
![Skeleton Inspector live capture](media/simulator_with_debugger.png)
7373

74+
<div align="center">
75+
76+
*Live capture —> simulator on the right, Skeleton Inspector DevTools panel on the left*
77+
78+
</div>
79+
7480
<br/>
7581

7682
```
@@ -101,20 +107,12 @@ Your app (simulator / device) React Native DevTools
101107

102108
1. Wrap your component with `<SkeletonCapture name="card">`
103109
2. Open Skeleton Inspector in React Native DevTools
104-
3. Click **Capture** — real skeleton geometry is measured instantly
110+
3. Click **Capture** — real skeleton geometry is measured instantly
105111
4. Remove any unwanted skeletons by clicking the trash icon
106-
5. Click **Save** `card.skeletons.json` is written to your project
107-
6. Import the JSON and pass it to `<Skeleton initialSkeletons={…}>`
112+
5. Click **Save Descriptor (.ts)** for a cross-platform `.ts` file, or **↓ Save skeletons.json** for a static JSON snapshot
113+
6. The inspector shows the exact import + `<Skeleton>` snippet — copy and paste it
108114
7. Ship. Done.
109115

110-
<div align="center">
111-
112-
![Skeleton Inspector live capture](media/simulator_with_debugger.png)
113-
114-
*Live capture — simulator on the left, Skeleton Inspector DevTools panel on the right*
115-
116-
</div>
117-
118116
---
119117

120118
## Installation
@@ -129,13 +127,85 @@ yarn add react-native-auto-shimmer
129127
130128
---
131129

130+
## Two approaches to skeleton data
131+
132+
There are two ways to provide skeleton data to `<Skeleton>`. Choose based on your needs:
133+
134+
| | **Descriptor** (TypeScript) ⭐ | **Capture** (Inspector → JSON) |
135+
|---|---|---|
136+
| How | Describe component structure in a `.ts` file | Inspector measures live layout, saves `.skeletons.json` |
137+
| Cross-platform | ✅ Perfect on every device & screen size | ⚠️ Vertical positions are device-specific |
138+
| Accuracy | Adapts at runtime to any container width | Pixel-perfect on the capture device |
139+
| Dynamic logic |`Platform.OS`, breakpoints, content length | ❌ Static JSON |
140+
| Auto-generate | ✅ Inspector generates a `.ts` starting point | ✅ Inspector saves directly |
141+
| Prop | `descriptor={myDescriptor}` | `initialSkeletons={data}` |
142+
143+
**Use Descriptor** when you target multiple screen sizes, need Android precision, or want live adaptability — **recommended**.
144+
**Use JSON Capture** when you want a quick pixel-perfect result on a single device or to verify layout visually.
145+
146+
---
147+
132148
## Quick start
133149

134-
### 1. Wrap your screen
150+
### Option A — Descriptor ⭐ (recommended, cross-platform)
151+
152+
The inspector can **auto-generate** a `.ts` descriptor from your live layout. Click **Save Descriptor (.ts)** after capturing — it writes a starting-point `.ts` file you can refine. Or write one by hand:
153+
154+
```tsx
155+
// card.skeletons.ts
156+
import type { SkeletonDescriptor } from 'react-native-auto-shimmer';
157+
158+
const cardDescriptor: SkeletonDescriptor = {
159+
display: 'flex',
160+
flexDirection: 'column',
161+
children: [
162+
{ aspectRatio: 16 / 9, borderRadius: 0 }, // hero image
163+
{
164+
display: 'flex', flexDirection: 'column',
165+
padding: 16, gap: 8,
166+
children: [
167+
{ text: 'Article Title Here', font: '700 18px Inter', lineHeight: 26 },
168+
{ text: 'Article body that may wrap across several lines of text.', font: '14px Inter', lineHeight: 20 },
169+
{
170+
display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 10,
171+
children: [
172+
{ width: 32, height: 32, borderRadius: '50%' }, // avatar
173+
{ text: 'Author Name', font: '600 14px Inter', lineHeight: 20 },
174+
],
175+
},
176+
],
177+
},
178+
],
179+
};
180+
181+
export default cardDescriptor;
182+
```
183+
184+
```tsx
185+
// ArticleScreen.tsx
186+
import { Skeleton } from 'react-native-auto-shimmer';
187+
import cardDescriptor from './skeletons/card.skeletons';
188+
189+
export function ArticleScreen() {
190+
const [loading, setLoading] = useState(true);
191+
192+
return (
193+
<Skeleton loading={loading} descriptor={cardDescriptor} style={styles.card}>
194+
<ArticleCard />
195+
</Skeleton>
196+
);
197+
}
198+
```
199+
200+
> `<Skeleton>` measures its own width via `onLayout` and runs `computeLayout()` automatically — no manual width tracking needed. Works identically on iOS and Android at any screen size.
201+
202+
### Option B — JSON Capture (pixel-perfect on capture device)
203+
204+
Run the [Skeleton Inspector](#capturing-skeletons-with-skeleton-inspector), click **Capture → Save skeletons.json**, then use the generated file:
135205

136206
```tsx
137207
import { Skeleton } from 'react-native-auto-shimmer';
138-
import cardSkeletons from './skeletons/card.skeletons.json'; // added after first capture
208+
import cardSkeletons from './skeletons/card.skeletons.json'; // generated by inspector
139209

140210
export function ArticleScreen() {
141211
const [loading, setLoading] = useState(true);
@@ -148,7 +218,7 @@ export function ArticleScreen() {
148218
}
149219
```
150220

151-
Before you've captured skeletons, `<Skeleton>` shows a blank fallback. The next section walks through capturing the real geometry.
221+
Before you've set up skeleton data, `<Skeleton>` shows a blank fallback. The next section walks through the inspector capture workflow.
152222

153223
---
154224

@@ -246,12 +316,22 @@ The panel draws every skeleton with a numbered colour overlay and a data table b
246316

247317
### Step 9 — Save
248318

249-
Click **↓ Save skeleton.json**. The file lands in the Output Directory shown in the sidebar (default `src/skeletons`):
319+
Two buttons appear after capture:
320+
321+
| Button | Output | Best for |
322+
|---|---|---|
323+
| **↓ Save Descriptor (.ts)**| `card.skeletons.ts` | Cross-platform, all Android sizes |
324+
| **↓ Save skeletons.json** | `card.skeletons.json` | Quick snapshot, single device |
325+
326+
Files land in the Output Directory shown in the sidebar (default `src/skeletons`):
250327

251328
```
252-
src/skeletons/card.skeletons.json ✓
329+
src/skeletons/card.skeletons.ts ✓ ← descriptor (recommended)
330+
src/skeletons/card.skeletons.json ✓ ← json snapshot
253331
```
254332

333+
After saving, the inspector shows a **ready-to-paste code snippet** with the exact import line and `<Skeleton>` usage — just copy and drop it into your screen file.
334+
255335
---
256336

257337
## Using saved skeletons
@@ -558,7 +638,7 @@ We welcome contributions of all sizes — bug fixes, new features, docs improvem
558638

559639
## License
560640

561-
MIT © [Numan](https://github.com/shobbak)
641+
MIT © [Numan](https://github.com/numandev1)
562642

563643
---
564644

example/metro.config.js

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function skeletonSaveMiddleware(middleware) {
3636
req.on('data', (chunk) => (body += chunk));
3737
req.on('end', () => {
3838
try {
39-
const { name, outDir, viewportWidth, height, skeletons } = JSON.parse(body);
39+
const { name, outDir, viewportWidth, height, skeletons, asDescriptor, descriptorSource } = JSON.parse(body);
4040

4141
if (!name || !Array.isArray(skeletons) || !viewportWidth || !outDir) {
4242
res.writeHead(400);
@@ -49,30 +49,40 @@ function skeletonSaveMiddleware(middleware) {
4949
: path.resolve(__dirname, outDir);
5050
fs.mkdirSync(absOut, { recursive: true });
5151

52-
const jsonFile = path.join(absOut, `${name}.skeletons.json`);
53-
54-
let existing = { breakpoints: {} };
55-
if (fs.existsSync(jsonFile)) {
56-
try { existing = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); }
57-
catch { existing = { breakpoints: {} }; }
52+
if (asDescriptor && descriptorSource) {
53+
// ── Save as .ts (ResponsiveSkeletons) ──────────────────────────
54+
const tsFile = path.join(absOut, `${name}.skeletons.ts`);
55+
fs.writeFileSync(tsFile, descriptorSource, 'utf8');
56+
const relFile = path.relative(__dirname, tsFile);
57+
console.log(`\n [Skeleton Inspector] ✓ ${name}${skeletons.length} skeletons → ${relFile}\n`);
58+
res.writeHead(200);
59+
res.end(JSON.stringify({ ok: true, file: relFile, skeletons: skeletons.length }));
60+
} else {
61+
// ── Save as .json (raw breakpoints) ────────────────────────────
62+
const jsonFile = path.join(absOut, `${name}.skeletons.json`);
63+
64+
let existing = { breakpoints: {} };
65+
if (fs.existsSync(jsonFile)) {
66+
try { existing = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); }
67+
catch { existing = { breakpoints: {} }; }
68+
}
69+
70+
const key = String(Math.round(viewportWidth));
71+
existing.breakpoints[key] = {
72+
name,
73+
viewportWidth: Math.round(viewportWidth),
74+
width: Math.round(viewportWidth),
75+
height: Math.round(height || 0),
76+
skeletons,
77+
};
78+
79+
fs.writeFileSync(jsonFile, JSON.stringify(existing, null, 2) + '\n');
80+
81+
const relFile = path.relative(__dirname, jsonFile);
82+
console.log(`\n [Skeleton Inspector] ✓ ${name}${skeletons.length} skeletons → ${relFile}\n`);
83+
res.writeHead(200);
84+
res.end(JSON.stringify({ ok: true, file: relFile, skeletons: skeletons.length }));
5885
}
59-
60-
const key = String(Math.round(viewportWidth));
61-
existing.breakpoints[key] = {
62-
name,
63-
viewportWidth: Math.round(viewportWidth),
64-
width: Math.round(viewportWidth),
65-
height: Math.round(height || 0),
66-
skeletons,
67-
};
68-
69-
fs.writeFileSync(jsonFile, JSON.stringify(existing, null, 2) + '\n');
70-
71-
const relFile = path.relative(__dirname, jsonFile);
72-
console.log(`\n [Skeleton Inspector] ✓ ${name}${skeletons.length} skeletons → ${relFile}\n`);
73-
74-
res.writeHead(200);
75-
res.end(JSON.stringify({ ok: true, file: relFile, skeletons: skeletons.length }));
7686
} catch (e) {
7787
console.error('[skeleton-save] Error:', e.message);
7888
res.writeHead(500);

example/src/screens/CardScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
type AnimationStyle,
1515
} from 'react-native-auto-shimmer';
1616
import { ScreenShell } from '../navigation';
17-
import cardSkeletons from '../skeletons/card.skeletons.json';
17+
import cardSkeletons from '../skeletons/card.skeletons';
1818

1919
const PAD = 16;
2020

example/src/screens/FeedScreen.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {
1414
type AnimationStyle,
1515
} from 'react-native-auto-shimmer';
1616
import { ScreenShell } from '../navigation';
17-
import feedPostSkeletons from '../skeletons/feedPost.skeletons.json';
18-
import feedPostTextSkeletons from '../skeletons/feedPostText.skeletons.json';
17+
import feedPostSkeletons from '../skeletons/feedPost.skeletons';
18+
import feedPostTextSkeletons from '../skeletons/feedPostText.skeletons';
1919

2020
const PAD = 16;
2121

example/src/screens/KitchenSinkScreen.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ import {
2323
} from 'react-native-auto-shimmer';
2424
import { ScreenShell } from '../navigation';
2525

26-
// ── Skeletons ──────────────────────────────────────────────────────────────────────
27-
import cardSkeletons from '../skeletons/card.skeletons.json';
28-
import feedPostSkeletons from '../skeletons/feedPost.skeletons.json';
29-
import feedPostTextSkeletons from '../skeletons/feedPostText.skeletons.json';
30-
import profileSkeletons from '../skeletons/profile.skeletons.json';
31-
import productItemSkeletons from '../skeletons/productItem.skeletons.json';
32-
import notificationSkeletons from '../skeletons/notification.skeletons.json';
26+
// ── Skeletons (descriptor-driven — responsive on every device) ─────────────────────
27+
import cardSkeletons from '../skeletons/card.skeletons';
28+
import feedPostSkeletons from '../skeletons/feedPost.skeletons';
29+
import feedPostTextSkeletons from '../skeletons/feedPostText.skeletons';
30+
import profileSkeletons from '../skeletons/profile.skeletons';
31+
import productItemSkeletons from '../skeletons/productItem.skeletons';
32+
import notificationSkeletons from '../skeletons/notification.skeletons';
3333

3434
// ── Layout constants ───────────────────────────────────────────────────────────
3535
const { width: SCREEN_W } = Dimensions.get('window');

example/src/screens/NotificationsScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
type AnimationStyle,
1414
} from 'react-native-auto-shimmer';
1515
import { ScreenShell } from '../navigation';
16-
import notificationSkeletons from '../skeletons/notification.skeletons.json';
16+
import notificationSkeletons from '../skeletons/notification.skeletons';
1717

1818
const PAD = 16;
1919

example/src/screens/ProductGridScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
type AnimationStyle,
1616
} from 'react-native-auto-shimmer';
1717
import { ScreenShell } from '../navigation';
18-
import productItemSkeletons from '../skeletons/productItem.skeletons.json';
18+
import productItemSkeletons from '../skeletons/productItem.skeletons';
1919

2020
const W = Dimensions.get('window').width;
2121
const PAD = 16;

example/src/screens/ProfileScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
type AnimationStyle,
1515
} from 'react-native-auto-shimmer';
1616
import { ScreenShell } from '../navigation';
17-
import profileSkeletons from '../skeletons/profile.skeletons.json';
17+
import profileSkeletons from '../skeletons/profile.skeletons';
1818

1919
const PAD = 16;
2020

example/src/skeletons/card.skeletons.json

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)