Skip to content

Commit 9a26d4b

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

33 files changed

Lines changed: 1004 additions & 594 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: 125 additions & 20 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 .ts (Responsive)** for a TypeScript file that scales on every device, 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,98 @@ 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+
| | **TypeScript `.ts`**| **JSON `.json`** |
135+
|---|---|---|
136+
| How | Inspector auto-generates a `.ts` file with live measurements | Inspector saves raw captured data as `.json` |
137+
| Horizontal |`x`/`w` stored as percentages → scale to any screen width | ⚠️ All values are dp — may shift on different device widths |
138+
| Vertical |`y`/`h` are exact dp from live layout |`y`/`h` are exact dp from live layout |
139+
| Editable | ✅ Plain TypeScript — add breakpoints, conditions, comments | ❌ Static data — requires re-capture to change |
140+
| Auto-generate | ✅ Click **Save .ts** in the inspector | ✅ Click **Save .json** in the inspector |
141+
| Prop | `initialSkeletons={data}` | `initialSkeletons={data}` |
142+
143+
**Use `.ts`** when you target multiple screen sizes or want easy future refinement — **recommended**.
144+
**Use `.json`** when you want the fastest possible save-and-ship flow on a single target device.
145+
146+
---
147+
132148
## Quick start
133149

134-
### 1. Wrap your screen
150+
### Option A — TypeScript `.ts` ⭐ (recommended, responsive)
151+
152+
In the inspector, click **↓ Save .ts (Responsive)** after capturing. The inspector writes a ready-to-use `.ts` file like this to your `src/skeletons/` folder:
153+
154+
```ts
155+
// card.skeletons.ts — auto-generated by Skeleton Inspector
156+
import type { ResponsiveSkeletons } from 'react-native-auto-shimmer';
157+
158+
/**
159+
* Auto-generated by Skeleton Inspector — 2025-01-15
160+
* Component : card
161+
* Captured : 390dp wide · 280dp tall · 6 pieces
162+
*
163+
* ✅ Pixel-perfect: x/w are percentages so widths scale on any screen.
164+
* y/h are exact dp values measured from the live layout.
165+
*
166+
* 💡 Add more breakpoints (capture on other devices) for more precision,
167+
* or refine values manually in this file.
168+
*/
169+
const cardSkeletons: ResponsiveSkeletons = {
170+
breakpoints: {
171+
390: {
172+
name: 'card',
173+
viewportWidth: 390,
174+
width: 390,
175+
height: 280,
176+
skeletons: [
177+
// piece 0 — hero image / banner
178+
{ x: 0, y: 0, w: 100, h: 180, r: 0 },
179+
// piece 1 — text line (full width)
180+
{ x: 4.1, y: 196, w: 91.8, h: 22, r: 6 },
181+
// piece 2 — text line
182+
{ x: 4.1, y: 226, w: 61.5, h: 16, r: 6 },
183+
// piece 3 — avatar circle
184+
{ x: 4.1, y: 252, w: 8.2, h: 32, r: '50%' },
185+
// piece 4 — text line
186+
{ x: 14.4, y: 258, w: 35.9, h: 18, r: 6 },
187+
],
188+
},
189+
},
190+
};
191+
192+
export default cardSkeletons;
193+
```
194+
195+
Then import and use it — same `initialSkeletons` prop as JSON:
196+
197+
```tsx
198+
// ArticleScreen.tsx
199+
import { Skeleton } from 'react-native-auto-shimmer';
200+
import cardSkeletons from './skeletons/card.skeletons';
201+
202+
export function ArticleScreen() {
203+
const [loading, setLoading] = useState(true);
204+
205+
return (
206+
<Skeleton loading={loading} initialSkeletons={cardSkeletons} style={styles.card}>
207+
<ArticleCard />
208+
</Skeleton>
209+
);
210+
}
211+
```
212+
213+
> **Why `.ts` over `.json`?** The `x`/`w` values are percentages of the container width, so the skeleton automatically scales to the right proportions on any device. `y`/`h` are exact dp from the live layout measurement. Capture once on any device — it looks right everywhere.
214+
215+
### Option B — JSON `.json` (quickest, single device)
216+
217+
Run the [Skeleton Inspector](#capturing-skeletons-with-skeleton-inspector), click **↓ Save skeletons.json**, then use the generated file:
135218

136219
```tsx
137220
import { Skeleton } from 'react-native-auto-shimmer';
138-
import cardSkeletons from './skeletons/card.skeletons.json'; // added after first capture
221+
import cardSkeletons from './skeletons/card.skeletons.json'; // generated by inspector
139222

140223
export function ArticleScreen() {
141224
const [loading, setLoading] = useState(true);
@@ -148,7 +231,7 @@ export function ArticleScreen() {
148231
}
149232
```
150233

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

153236
---
154237

@@ -246,18 +329,40 @@ The panel draws every skeleton with a numbered colour overlay and a data table b
246329

247330
### Step 9 — Save
248331

249-
Click **↓ Save skeleton.json**. The file lands in the Output Directory shown in the sidebar (default `src/skeletons`):
332+
Two buttons appear after capture:
333+
334+
| Button | Output | Best for |
335+
|---|---|---|
336+
| **↓ Save .ts (Responsive)**| `card.skeletons.ts` | Every device — x/w scale as percentages |
337+
| **↓ Save skeletons.json** | `card.skeletons.json` | Quick snapshot, single device |
338+
339+
Files land in the Output Directory shown in the sidebar (default `src/skeletons`):
250340

251341
```
252-
src/skeletons/card.skeletons.json ✓
342+
src/skeletons/card.skeletons.ts ✓ ← responsive TypeScript (recommended)
343+
src/skeletons/card.skeletons.json ✓ ← static JSON snapshot
253344
```
254345

346+
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.
347+
255348
---
256349

257350
## Using saved skeletons
258351

259352
### Import directly
260353

354+
**From the auto-generated `.ts` file (recommended):**
355+
356+
```tsx
357+
import cardSkeletons from './skeletons/card.skeletons'; // ← .ts, no extension needed
358+
359+
<Skeleton loading={loading} initialSkeletons={cardSkeletons} style={styles.card}>
360+
<ArticleCard />
361+
</Skeleton>
362+
```
363+
364+
**Or from a JSON snapshot:**
365+
261366
```tsx
262367
import cardSkeletons from './skeletons/card.skeletons.json';
263368

@@ -273,9 +378,9 @@ Register all your skeletons at app startup so every `<Skeleton>` can reference t
273378
```ts
274379
// App.tsx
275380
import { registerSkeletons } from 'react-native-auto-shimmer';
276-
import cardSkeletons from './skeletons/card.skeletons.json';
277-
import profileSkeletons from './skeletons/profile.skeletons.json';
278-
import feedPostSkeletons from './skeletons/feedPost.skeletons.json';
381+
import cardSkeletons from './skeletons/card.skeletons'; // .ts (responsive)
382+
import profileSkeletons from './skeletons/profile.skeletons'; // .ts (responsive)
383+
import feedPostSkeletons from './skeletons/feedPost.skeletons.json'; // .json also works
279384

280385
registerSkeletons({
281386
card: cardSkeletons,
@@ -558,7 +663,7 @@ We welcome contributions of all sizes — bug fixes, new features, docs improvem
558663

559664
## License
560665

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

563668
---
564669

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

0 commit comments

Comments
 (0)