shimmer-trace 1.1.4 → 1.2.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/AGENTS.md +54 -1
- package/README.md +50 -5
- package/dist/{index.js → index.cjs} +26 -10
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +25 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/dist/index.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
## What This Library Does (One Paragraph)
|
|
10
10
|
|
|
11
|
-
`shimmer-trace` is a React skeleton loading library. It renders your real component
|
|
11
|
+
`shimmer-trace` is a React skeleton loading library. It renders your real component, walks the live DOM to find every visible leaf element (headings, paragraphs, images, inputs, buttons, etc.), measures each one's exact position and size via `getBoundingClientRect()`, then paints an absolutely-positioned shimmer overlay on top of those measured rects. The skeleton is therefore pixel-perfect and auto-updates via `ResizeObserver`. You never write a manual skeleton. You wrap your component and pass `loading={true}`.
|
|
12
|
+
|
|
13
|
+
The library is **client-only** by nature — measurement requires a real DOM — but its bundled output is marked with `'use client'`, so it can be **imported directly from React Server Components / Next.js App Router pages** without manual wrapping.
|
|
12
14
|
|
|
13
15
|
---
|
|
14
16
|
|
|
@@ -20,6 +22,13 @@ npm install shimmer-trace
|
|
|
20
22
|
|
|
21
23
|
**Peer deps required:** `react >= 18.0.0`, `react-dom >= 18.0.0`
|
|
22
24
|
|
|
25
|
+
### Server vs Client Components (Next.js App Router)
|
|
26
|
+
|
|
27
|
+
- The package's bundled `dist/index.mjs` and `dist/index.js` both start with `'use client';`. Every exported component (`Shimmer`, `ShimmerSuspense`, `createShimmer`) and hook (`useIsShimmering`, `useShimmerContext`) crosses the client boundary automatically.
|
|
28
|
+
- **You can import the package from a Server Component file.** Next.js will treat the imported subgraph as client code. You do NOT need to add `'use client'` at the top of a page just because it imports `Shimmer`.
|
|
29
|
+
- **Children passed to `<Shimmer>` follow normal RSC rules.** A Server Component child that produces DOM synchronously will render and be measured fine. A Server Component child that suspends needs `<ShimmerSuspense>` instead — otherwise it produces no DOM during the fallback and the skeleton is empty.
|
|
30
|
+
- All measurement uses `window`, `document`, `getBoundingClientRect`, and `ResizeObserver`. There is no SSR measurement pass; the skeleton paints after hydration.
|
|
31
|
+
|
|
23
32
|
---
|
|
24
33
|
|
|
25
34
|
## Public API — Full Export List
|
|
@@ -411,8 +420,25 @@ Use when: nested Shimmer should NOT bubble rects to parent — it manages its ow
|
|
|
411
420
|
<span data-shimmer-ignore>
|
|
412
421
|
<DecorativeIcon />
|
|
413
422
|
</span>
|
|
423
|
+
|
|
424
|
+
// Force trace a position:fixed / position:sticky element (overrides
|
|
425
|
+
// the auto-skip described in "Known Limitations"). The block will
|
|
426
|
+
// drift on scroll — only use this on non-scrolling pages or when the
|
|
427
|
+
// fixed element is short-lived.
|
|
428
|
+
<div data-shimmer style={{ position: 'fixed', top: 0 }}>
|
|
429
|
+
<Banner />
|
|
430
|
+
</div>
|
|
414
431
|
```
|
|
415
432
|
|
|
433
|
+
**Precedence** (matches source order in `useTrace.ts`):
|
|
434
|
+
|
|
435
|
+
1. `data-shimmer-ignore` → never traced, subtree skipped.
|
|
436
|
+
2. `data-shimmer-reporter` → internal, skipped by walk.
|
|
437
|
+
3. `data-shimmer` on a `position: fixed` / `sticky` element → trace it anyway (override).
|
|
438
|
+
4. No override + `fixed` / `sticky` position → skip subtree, warn once per Master.
|
|
439
|
+
5. `data-shimmer` on a normal element → force-trace.
|
|
440
|
+
6. Default: tag-whitelist or visible-leaf heuristic.
|
|
441
|
+
|
|
416
442
|
---
|
|
417
443
|
|
|
418
444
|
## Config Inheritance Chain
|
|
@@ -569,6 +595,33 @@ function UserCard() {
|
|
|
569
595
|
|
|
570
596
|
Library injects one `<style>` tag with id `shimmer-trace-styles` into `document.head` on first render. Contains only `@keyframes` definitions and `preserveBackground` CSS attribute selectors. Safe to SSR — guard is `typeof document === 'undefined'`.
|
|
571
597
|
|
|
598
|
+
Injection runs inside `useInsertionEffect`, not `useEffect`. This means the keyframes are present in the document **before the first paint** of any `<Shimmer>` subtree, so there is no first-frame FOUC (no momentary static gray blocks before the wave starts). Agents should not "fix" this back to `useEffect` — the timing is intentional.
|
|
599
|
+
|
|
600
|
+
## Clone Keys & Hydration
|
|
601
|
+
|
|
602
|
+
When `dummyLength` or `dummyData` causes Shimmer to clone children, each clone receives a key of the form `${parentShimmerId}-${slot}-${index}`, where `parentShimmerId` comes from React's `useId()` (stable across SSR and hydration).
|
|
603
|
+
|
|
604
|
+
Keys are deterministic per render slot. Agents should:
|
|
605
|
+
|
|
606
|
+
- **Not** assume keys change between renders.
|
|
607
|
+
- **Not** introduce a module-scope counter into `utils.ts` or anywhere else — the previous implementation did this and it caused (a) hydration mismatch warnings when SSR and client incremented the counter differently and (b) full remount of every cloned child on every parent re-render.
|
|
608
|
+
- Rely on `useId()` already being called inside `<Shimmer>` for any new keying logic.
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## Known Limitations (Agents: do not invent fixes that aren't in source)
|
|
613
|
+
|
|
614
|
+
These are real gaps in the current implementation. If a user asks why their setup misbehaves, match the symptom to one of these before debugging deeper.
|
|
615
|
+
|
|
616
|
+
- **Portals are silently skipped.** `collectTraceableElements` walks the descendants of the Master container ref. Anything mounted via `ReactDOM.createPortal` lives elsewhere in the DOM and never enters the walk. No console warning. Workaround: put a separate `<Shimmer>` *inside* the portal target.
|
|
617
|
+
- **`position: fixed` / `position: sticky` children are detected and skipped.** The rect math is `el.getBoundingClientRect() - container.getBoundingClientRect()`. For fixed/sticky elements that coordinate space diverges from the container as soon as the page scrolls, so any overlay block we generated would drift. `collectTraceableElements` calls `getComputedStyle(el).position` during the walk; if it's `fixed` or `sticky`, the entire subtree is skipped and one `console.warn` is emitted per Master container (deduped via a `WeakSet`, so resize-driven re-traces never re-warn). The warning fires in production too — it's intentional. Two opt-ins exist:
|
|
618
|
+
- Add `data-shimmer` to the fixed element to force-trace it (user accepts the scroll drift). When `data-shimmer` is present, the position check is bypassed.
|
|
619
|
+
- Recommended: render a separate `<Shimmer>` *inside* the fixed element. Master/Reporter detection handles the nesting, and the inner overlay sits inside the fixed coordinate space, so alignment works.
|
|
620
|
+
- **Transformed ancestors.** The overlay sits inside the Master container, so it inherits the same transform stack — this usually renders correctly. A transform applied *between* the Master container and a traced descendant can desynchronize the overlay block from its source element.
|
|
621
|
+
- **Suspending Server Components inside plain `<Shimmer>`.** Without `<ShimmerSuspense>`, a suspending child produces no DOM during the fallback, so the trace returns zero rects and the skeleton is empty. Always recommend `ShimmerSuspense` for suspending children.
|
|
622
|
+
- **Server-side measurement is not supported.** There is no DOM on the server. The first paint after hydration is when measurements happen. Agents should not promise SSR-rendered skeletons — only that hydration is clean.
|
|
623
|
+
- **`getBoundingClientRect` is called twice per leaf** (once in `isTraceable` for the dimension check, once in `measureElement`). This is a known perf bottleneck on very large trees and is on the roadmap; do not claim it's already optimized.
|
|
624
|
+
|
|
572
625
|
---
|
|
573
626
|
|
|
574
627
|
## TypeScript Usage
|
package/README.md
CHANGED
|
@@ -43,6 +43,9 @@ It renders your real component invisibly, traces every element's exact position
|
|
|
43
43
|
|
|
44
44
|
- **Auto-tracing** — Measures real DOM layout. No manual skeleton code.
|
|
45
45
|
- **Zero CLS** — Container layout preserved. Default `preserveBackground` keeps card backgrounds, borders, and padding visible underneath the shimmer.
|
|
46
|
+
- **Next.js App Router / RSC ready** — Ships with `'use client'` directive baked into the bundled output. Import directly from a Server Component without a wrapper.
|
|
47
|
+
- **SSR-safe** — Deterministic clone keys across server and client. No hydration mismatch warnings, no remount of cloned children on every render.
|
|
48
|
+
- **No first-frame FOUC** — Animation styles are injected via `useInsertionEffect` so keyframes land before the first paint.
|
|
46
49
|
- **Synchronized animation** — One overlay, one wave. All skeletons animate in perfect sync.
|
|
47
50
|
- **5 animation styles** — `wave`, `pulse`, `shine`, `glow`, `gradient`.
|
|
48
51
|
- **Dummy data injection** — `dummyData` clones children with template props so skeletons render with realistic shape, no `data || fallback` ternaries in JSX.
|
|
@@ -71,6 +74,31 @@ pnpm add shimmer-trace
|
|
|
71
74
|
|
|
72
75
|
---
|
|
73
76
|
|
|
77
|
+
## Next.js App Router (RSC) usage
|
|
78
|
+
|
|
79
|
+
`shimmer-trace` ships its bundled output with a top-of-file `'use client'` directive, so you can import `Shimmer` (or `ShimmerSuspense`, `createShimmer`) directly from a Server Component without crashing the build.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
// app/profile/page.tsx — Server Component, no 'use client' at the top
|
|
83
|
+
import { Shimmer } from "shimmer-trace";
|
|
84
|
+
|
|
85
|
+
export default function ProfilePage() {
|
|
86
|
+
return (
|
|
87
|
+
<Shimmer loading>
|
|
88
|
+
<UserCard />
|
|
89
|
+
</Shimmer>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Notes:
|
|
95
|
+
|
|
96
|
+
- The `<Shimmer>` boundary itself is a Client Component. Children you pass through it must also be renderable as Client Components if they use state, effects, or browser APIs.
|
|
97
|
+
- The library uses `getBoundingClientRect`, `ResizeObserver`, and `useInsertionEffect`, all of which only run in the browser. There is no server-side measurement step — the skeleton appears after hydration.
|
|
98
|
+
- Server Component children that suspend should be wrapped with `ShimmerSuspense` (see [Suspense](#shimmersuspense--suspense-native-loading) below).
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
74
102
|
## Quick Start
|
|
75
103
|
|
|
76
104
|
```tsx
|
|
@@ -435,19 +463,21 @@ Fine-tune what gets traced with data attributes:
|
|
|
435
463
|
<div data-shimmer-ignore>Never shimmer this</div>
|
|
436
464
|
```
|
|
437
465
|
|
|
466
|
+
`data-shimmer` is also the explicit opt-in for tracing `position: fixed` / `position: sticky` elements — the library skips those by default to avoid scroll drift. See [Known Limitations](#known-limitations) for the trade-off.
|
|
467
|
+
|
|
438
468
|
---
|
|
439
469
|
|
|
440
470
|
## How It Works
|
|
441
471
|
|
|
442
|
-
1. **
|
|
472
|
+
1. **Inject keyframes pre-paint** — On first mount, the library inserts its `@keyframes` and `preserveBackground` CSS rules into `document.head` via `useInsertionEffect`. This runs before any layout effect or paint, so the wave animation is live on the very first frame — no flash of unstyled (static) shimmer blocks.
|
|
443
473
|
|
|
444
|
-
2. **
|
|
474
|
+
2. **Render real DOM** — `Shimmer` renders children normally. With `preserveBackground` (default), CSS rules hide text (`color: transparent`) and media (`opacity: 0`) on leaf elements while keeping container backgrounds, borders, and padding fully visible. Layout stays identical — zero CLS.
|
|
445
475
|
|
|
446
|
-
3. **
|
|
476
|
+
3. **Walk the DOM** — `useTrace` recursively traverses the container, collecting every traceable element: headings, paragraphs, images, inputs, buttons, and leaf nodes with visible dimensions.
|
|
447
477
|
|
|
448
|
-
4. **
|
|
478
|
+
4. **Measure everything** — Each element is measured with `getBoundingClientRect()` relative to the master container, capturing position, size, and computed `border-radius`.
|
|
449
479
|
|
|
450
|
-
5. **
|
|
480
|
+
5. **Build the overlay** — One absolutely-positioned `<div>` is rendered per traced rect, sized and positioned to match exactly. For sweep animations (`wave`, `shine`), each block also gets a gradient layer that spans the full container width — the highlight sweeps across all blocks in perfect sync.
|
|
451
481
|
|
|
452
482
|
6. **Re-trace on resize** — `ResizeObserver` watches the container and re-measures on every resize, keeping skeletons accurate at any screen size.
|
|
453
483
|
|
|
@@ -482,6 +512,21 @@ import {
|
|
|
482
512
|
|
|
483
513
|
---
|
|
484
514
|
|
|
515
|
+
## Known Limitations
|
|
516
|
+
|
|
517
|
+
These are real edge cases the library does **not** currently handle gracefully. Worth knowing before you wrap something complex.
|
|
518
|
+
|
|
519
|
+
- **React Portals are skipped silently.** `useTrace` walks the subtree of the Master container; anything rendered into a portal (e.g. `createPortal(<Modal/>, document.body)`) is mounted outside that subtree, so it never gets measured and never gets a shimmer block. Use `<Shimmer>` *inside* the portal target if you need it shimmered there.
|
|
520
|
+
- **`position: fixed` / `position: sticky` children are auto-skipped.** Their coordinate space cannot follow the Master container during scroll, so the overlay block would drift. The library detects them during the trace walk and silently skips the entire subtree, emitting one `console.warn` per Master container (deduped via a `WeakSet`, so resize-driven re-traces never re-warn). The warning fires in production too — it's an actionable signal, not log noise. Two workarounds:
|
|
521
|
+
- **Recommended:** put a nested `<Shimmer>` *inside* the fixed element. The inner Master sits in the fixed coordinate space and aligns correctly.
|
|
522
|
+
- **Override:** add `data-shimmer` to the fixed element to force-trace it. The block will drift on scroll — you accept the trade-off.
|
|
523
|
+
- **Heavily-transformed ancestors.** If an ancestor of the Master container has a CSS `transform`, the overlay sits inside the same transformed space and usually renders correctly. If a *descendant* has a transform that the rect math doesn't expect, the overlay block may not line up.
|
|
524
|
+
- **Suspending Server Component children.** A Server Component that suspends inside `<Shimmer>` (without `ShimmerSuspense`) won't produce DOM for the library to measure, so the skeleton appears empty. Use `ShimmerSuspense` for the suspending boundary, or pass template data via `dummyData`.
|
|
525
|
+
|
|
526
|
+
If you hit one of these in a real app, please open an issue with a reproduction.
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
485
530
|
## Comparison
|
|
486
531
|
|
|
487
532
|
| | shimmer-trace | react-loading-skeleton | MUI Skeleton |
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
var React2 = require('react');
|
|
@@ -133,9 +134,8 @@ var ShimmerOverlay = ({
|
|
|
133
134
|
};
|
|
134
135
|
|
|
135
136
|
// src/utils.ts
|
|
136
|
-
var counter = 0;
|
|
137
137
|
function generateShimmerKey(prefix = "shimmer") {
|
|
138
|
-
return
|
|
138
|
+
return prefix;
|
|
139
139
|
}
|
|
140
140
|
var FALLBACK_DIMENSIONS = {
|
|
141
141
|
INPUT: { width: 200, height: 36 },
|
|
@@ -196,13 +196,23 @@ function isTraceable(el) {
|
|
|
196
196
|
}
|
|
197
197
|
return false;
|
|
198
198
|
}
|
|
199
|
+
function isViewportLocked(el) {
|
|
200
|
+
const pos = window.getComputedStyle(el).position;
|
|
201
|
+
return pos === "fixed" || pos === "sticky";
|
|
202
|
+
}
|
|
203
|
+
var warnedContainers = /* @__PURE__ */ new WeakSet();
|
|
199
204
|
function collectTraceableElements(root) {
|
|
200
|
-
const
|
|
205
|
+
const traced = [];
|
|
206
|
+
let skippedFixed = 0;
|
|
201
207
|
function walk(el) {
|
|
202
208
|
if (el.hasAttribute("data-shimmer-ignore")) return;
|
|
203
209
|
if (el.hasAttribute("data-shimmer-reporter")) return;
|
|
210
|
+
if (!el.hasAttribute("data-shimmer") && isViewportLocked(el)) {
|
|
211
|
+
skippedFixed += 1;
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
204
214
|
if (isTraceable(el)) {
|
|
205
|
-
|
|
215
|
+
traced.push(el);
|
|
206
216
|
return;
|
|
207
217
|
}
|
|
208
218
|
for (let i = 0; i < el.children.length; i++) {
|
|
@@ -212,7 +222,7 @@ function collectTraceableElements(root) {
|
|
|
212
222
|
for (let i = 0; i < root.children.length; i++) {
|
|
213
223
|
walk(root.children[i]);
|
|
214
224
|
}
|
|
215
|
-
return
|
|
225
|
+
return { traced, skippedFixed };
|
|
216
226
|
}
|
|
217
227
|
function measureElement(el, containerRect, globalBorderRadius) {
|
|
218
228
|
const elRect = el.getBoundingClientRect();
|
|
@@ -245,8 +255,14 @@ function measureElement(el, containerRect, globalBorderRadius) {
|
|
|
245
255
|
function performTrace(container, globalBorderRadius, anchorRef) {
|
|
246
256
|
const anchor = anchorRef?.current ?? container;
|
|
247
257
|
const anchorRect = anchor.getBoundingClientRect();
|
|
248
|
-
const
|
|
249
|
-
|
|
258
|
+
const { traced, skippedFixed } = collectTraceableElements(container);
|
|
259
|
+
if (skippedFixed > 0 && !warnedContainers.has(container)) {
|
|
260
|
+
warnedContainers.add(container);
|
|
261
|
+
console.warn(
|
|
262
|
+
`[shimmer-trace] Skipped ${skippedFixed} element(s) with position:fixed or position:sticky. Their coordinate space cannot follow the Master container during scroll, so the overlay block would drift. Render a nested <Shimmer> inside the fixed/sticky element if you need a skeleton there, or add data-shimmer to opt in (positioning may still drift on scroll).`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
return traced.map((el) => measureElement(el, anchorRect, globalBorderRadius)).filter((r) => r !== null && r.width > 0 && r.height > 0);
|
|
250
266
|
}
|
|
251
267
|
function useTrace(containerRef, loading, globalBorderRadius, anchorRef) {
|
|
252
268
|
const [rects, setRects] = React2.useState([]);
|
|
@@ -414,7 +430,7 @@ function MasterShimmer({
|
|
|
414
430
|
return next;
|
|
415
431
|
});
|
|
416
432
|
}, []);
|
|
417
|
-
|
|
433
|
+
React2.useInsertionEffect(() => {
|
|
418
434
|
injectStyles();
|
|
419
435
|
}, []);
|
|
420
436
|
const tracedRects = useTrace(
|
|
@@ -597,5 +613,5 @@ exports.ShimmerSuspense = ShimmerSuspense;
|
|
|
597
613
|
exports.createShimmer = createShimmer;
|
|
598
614
|
exports.useIsShimmering = useIsShimmering;
|
|
599
615
|
exports.useShimmerContext = useShimmerContext;
|
|
600
|
-
//# sourceMappingURL=index.
|
|
601
|
-
//# sourceMappingURL=index.
|
|
616
|
+
//# sourceMappingURL=index.cjs.map
|
|
617
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/ShimmerContext.tsx","../src/ShimmerOverlay.tsx","../src/utils.ts","../src/useTrace.ts","../src/styles.ts","../src/Shimmer.tsx","../src/createShimmer.tsx","../src/ShimmerSuspense.tsx"],"names":["createContext","useContext","jsx","React","useState","useCallback","useLayoutEffect","useId","useMemo","useRef","useInsertionEffect","jsxs","createElement"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { ReactNode } from 'react';\r\n\r\n/**\r\n * Represents a measured rectangle of a traced DOM element,\r\n * positioned relative to the Master Shimmer container.\r\n */\r\nexport interface ShimmerRect {\r\n x: number;\r\n y: number;\r\n width: number;\r\n height: number;\r\n borderRadius: string;\r\n}\r\n\r\n/** Available animation types for the shimmer effect. */\r\nexport type AnimationType =\r\n | 'wave'\r\n | 'pulse'\r\n | 'shine'\r\n | 'glow'\r\n | 'gradient';\r\n\r\n/** Configuration options for the shimmer effect (all optional). */\r\nexport interface ShimmerConfig {\r\n /** Animation style. Defaults to 'wave'. */\r\n animation?: AnimationType;\r\n /** Base color of the shimmer blocks. Defaults to '#e0e0e0'. */\r\n baseColor?: string;\r\n /** Highlight color of the shimmer animation. Defaults to '#f5f5f5'. */\r\n highlightColor?: string;\r\n /** Animation duration in seconds. Defaults to 1.5. */\r\n speed?: number;\r\n /** Global border-radius override. If omitted, auto-detected from each element (defaults to 4px if detection is 0px). */\r\n borderRadius?: string;\r\n /**\r\n * Keep container backgrounds, borders, and padding visible while loading.\r\n * When `true` (default), only text and media leaves are hidden via\r\n * `color:transparent` / `opacity:0` so card backgrounds, borders, and\r\n * spacing remain visible underneath the shimmer overlay.\r\n *\r\n * Set `false` for legacy behavior (`visibility:hidden` on whole tree).\r\n */\r\n preserveBackground?: boolean;\r\n}\r\n\r\n/** Props for the Shimmer component. */\r\nexport interface ShimmerProps extends ShimmerConfig {\r\n /** Whether the loading state is active. */\r\n loading?: boolean;\r\n /** The children to trace and render shimmer over. */\r\n children: ReactNode;\r\n /**\r\n * Number of placeholder clones to generate for list-like loading states.\r\n *\r\n * When `loading=true` and `dummyLength` is set, Shimmer grabs the first\r\n * available child (or a cached template from the last loaded render) and\r\n * clones it `dummyLength` times to produce skeleton placeholders.\r\n *\r\n * When `loading=false`, children are rendered as-is.\r\n */\r\n dummyLength?: number;\r\n /**\r\n * Props injected into each child element while `loading=true` so the\r\n * skeleton renders with realistic shape without requiring real data.\r\n *\r\n * Example:\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyData={{ user: { name: 'Loading...', role: '...', avatar: '' } }}\r\n * >\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * While loading, each direct child is cloned with these props merged on top\r\n * of its own props. Ignored when `loading=false`.\r\n */\r\n dummyData?: Record<string, any>;\r\n /**\r\n * Component used to auto-generate skeleton elements while `loading=true`.\r\n *\r\n * When set, Shimmer ignores `children` during loading and renders\r\n * `dummyLength` (defaults to 1) instances of `<as {...dummyData} />`\r\n * to derive shape. Real children render once `loading=false`.\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * as={MovieCard}\r\n * dummyData={{ movie: movieTemplate }}\r\n * dummyLength={10}\r\n * >\r\n * {movies.map((m) => <MovieCard movie={m} key={m.id} />)}\r\n * </Shimmer>\r\n * ```\r\n */\r\n as?: React.ComponentType<any>;\r\n /** Force this Shimmer to be a Master renderer even if nested inside another Shimmer. */\r\n stopPropagation?: boolean;\r\n /**\r\n * className applied to the Master container div.\r\n * Use to control layout (e.g. display:flex) without losing position:relative.\r\n */\r\n className?: string;\r\n /**\r\n * Inline styles merged into the Master container div.\r\n * position:relative is always applied; everything else is overridable.\r\n */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n/** Default configuration values. */\r\nexport const DEFAULTS: Required<ShimmerConfig> = {\r\n animation: 'wave',\r\n baseColor: '#e0e0e0',\r\n highlightColor: '#f5f5f5',\r\n speed: 1.5,\r\n borderRadius: '',\r\n preserveBackground: true,\r\n};\r\n","\"use client\";\r\n\r\nimport { createContext, useContext, RefObject } from \"react\";\r\nimport { ShimmerRect, ShimmerConfig } from \"./types\";\r\n\r\nexport interface ShimmerContextValue {\r\n\tregister: (id: string, rects: ShimmerRect[]) => void;\r\n\tunregister: (id: string) => void;\r\n\t/** Ref object (not .current) so Reporters always read a fresh value. */\r\n\tmasterRef: RefObject<HTMLElement | null>;\r\n\tloading: boolean;\r\n\tconfig: Required<ShimmerConfig>;\r\n}\r\n\r\nexport const ShimmerContext = createContext<ShimmerContextValue | null>(null);\r\n\r\nexport function useShimmerContext(): ShimmerContextValue | null {\r\n\treturn useContext(ShimmerContext);\r\n}\r\n\r\n/**\r\n * True when rendered inside a ShimmerSuspense fallback (Option B).\r\n * Components use this to skip data fetching and return an empty shape.\r\n */\r\nexport const IsShimmeringContext = createContext<boolean>(false);\r\n\r\nexport function useIsShimmering(): boolean {\r\n\treturn useContext(IsShimmeringContext);\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { ShimmerRect, AnimationType } from './types';\r\n\r\ninterface ShimmerOverlayProps {\r\n rects: ShimmerRect[];\r\n animation: AnimationType;\r\n baseColor: string;\r\n highlightColor: string;\r\n speed: number;\r\n}\r\n\r\n/**\r\n * Returns animation-specific inline styles for each shimmer block.\r\n * For sweep-style animations (wave, shine), the colored gradient is rendered\r\n * as a child layer so the sweep can extend across the container in sync.\r\n */\r\nfunction getBlockStyles(\r\n animation: AnimationType,\r\n baseColor: string,\r\n highlightColor: string,\r\n speed: number,\r\n rect: ShimmerRect,\r\n): React.CSSProperties {\r\n const base: React.CSSProperties = {\r\n position: 'absolute',\r\n top: rect.y,\r\n left: rect.x,\r\n width: rect.width,\r\n height: rect.height,\r\n borderRadius: rect.borderRadius,\r\n overflow: 'hidden',\r\n };\r\n\r\n switch (animation) {\r\n case 'wave':\r\n case 'shine':\r\n return { ...base, background: baseColor };\r\n case 'pulse':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-pulse ${speed}s ease-in-out infinite`,\r\n };\r\n case 'glow':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-glow ${speed}s ease-in-out infinite`,\r\n };\r\n case 'gradient':\r\n return {\r\n ...base,\r\n backgroundImage: `linear-gradient(90deg, ${baseColor}, ${highlightColor}, ${baseColor})`,\r\n backgroundSize: '200% 100%',\r\n animation: `shimmer-gradient ${speed * 1.5}s ease-in-out infinite`,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Sweeping shine layer used by `wave` and `shine` animations.\r\n * Spans the full container width so the highlight sweeps in sync across all blocks.\r\n */\r\nconst SweepLayer: React.FC<{\r\n rect: ShimmerRect;\r\n highlightColor: string;\r\n speed: number;\r\n containerWidth: number;\r\n variant: 'wave' | 'shine';\r\n}> = ({ rect, highlightColor, speed, containerWidth, variant }) => {\r\n const isShine = variant === 'shine';\r\n return (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: -rect.x,\r\n width: containerWidth > 0 ? containerWidth : '100vw',\r\n height: '100%',\r\n background: isShine\r\n ? `linear-gradient(115deg, transparent 30%, ${highlightColor} 50%, transparent 70%)`\r\n : `linear-gradient(90deg, transparent 0%, ${highlightColor} 50%, transparent 100%)`,\r\n animation: `${isShine ? 'shimmer-shine' : 'shimmer-wave'} ${speed}s ease-in-out infinite`,\r\n }}\r\n />\r\n );\r\n};\r\n\r\n/**\r\n * The overlay component rendered by the Master Shimmer.\r\n *\r\n * Renders one absolutely-positioned div per traced rect. Sweep-style\r\n * animations (`wave`, `shine`) get an additional gradient layer that spans\r\n * the container so the highlight passes across all blocks in sync.\r\n */\r\nexport const ShimmerOverlay: React.FC<ShimmerOverlayProps> = ({\r\n rects,\r\n animation,\r\n baseColor,\r\n highlightColor,\r\n speed,\r\n}) => {\r\n const overlayRef = React.useRef<HTMLDivElement>(null);\r\n const [containerWidth, setContainerWidth] = React.useState(0);\r\n\r\n React.useLayoutEffect(() => {\r\n if (!overlayRef.current?.parentElement) return;\r\n setContainerWidth(overlayRef.current.parentElement.offsetWidth);\r\n }, [rects]);\r\n\r\n if (rects.length === 0) return null;\r\n\r\n const isSweep = animation === 'wave' || animation === 'shine';\r\n\r\n return (\r\n <div\r\n ref={overlayRef}\r\n role=\"status\"\r\n aria-busy=\"true\"\r\n aria-label=\"Loading content\"\r\n data-shimmer-ignore=\"true\"\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n zIndex: 1,\r\n pointerEvents: 'none',\r\n visibility: 'visible',\r\n }}\r\n >\r\n {rects.map((rect, i) => (\r\n <div\r\n key={i}\r\n style={getBlockStyles(animation, baseColor, highlightColor, speed, rect)}\r\n >\r\n {isSweep && (\r\n <SweepLayer\r\n rect={rect}\r\n highlightColor={highlightColor}\r\n speed={speed}\r\n containerWidth={containerWidth}\r\n variant={animation as 'wave' | 'shine'}\r\n />\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n};\r\n","/**\r\n * Generates a deterministic key for cloned elements.\r\n *\r\n * Prefix already includes the parent Shimmer's `useId()` plus a positional\r\n * index from the caller, so it is unique per render slot and stable across\r\n * SSR + client hydration. No module-scope counter — that caused hydration\r\n * mismatches and forced React to remount cloned children every render.\r\n */\r\nexport function generateShimmerKey(prefix: string = 'shimmer'): string {\r\n return prefix;\r\n}\r\n\r\n/**\r\n * Default fallback dimensions for common elements when their\r\n * measured dimensions are 0px (e.g., empty inputs, images not yet loaded).\r\n */\r\nexport const FALLBACK_DIMENSIONS: Record<string, { width: number; height: number }> = {\r\n INPUT: { width: 200, height: 36 },\r\n BUTTON: { width: 120, height: 36 },\r\n TEXTAREA: { width: 300, height: 80 },\r\n SELECT: { width: 200, height: 36 },\r\n IMG: { width: 100, height: 100 },\r\n H1: { width: 300, height: 36 },\r\n H2: { width: 260, height: 30 },\r\n H3: { width: 220, height: 26 },\r\n H4: { width: 200, height: 22 },\r\n H5: { width: 180, height: 20 },\r\n H6: { width: 160, height: 18 },\r\n P: { width: 250, height: 16 },\r\n SPAN: { width: 100, height: 16 },\r\n};\r\n","'use client';\r\n\r\nimport { useLayoutEffect, useState, RefObject, useCallback } from 'react';\r\nimport { ShimmerRect } from './types';\r\nimport { FALLBACK_DIMENSIONS } from './utils';\r\n\r\n/**\r\n * Tags that are always considered \"traceable\" leaf elements\r\n * whose dimensions should be captured for the shimmer overlay.\r\n */\r\nconst TRACEABLE_TAGS = new Set([\r\n // Text\r\n 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'SPAN', 'A', 'LI',\r\n 'LABEL', 'TD', 'TH', 'BLOCKQUOTE', 'CODE', 'PRE',\r\n // Media\r\n 'IMG', 'VIDEO', 'SVG', 'CANVAS', 'PICTURE',\r\n // Form\r\n 'INPUT', 'TEXTAREA', 'SELECT', 'BUTTON',\r\n // Misc\r\n 'HR',\r\n]);\r\n\r\n/**\r\n * Determines if an element should be traced.\r\n * Explicit data attributes override automatic detection.\r\n */\r\nfunction isTraceable(el: Element): boolean {\r\n if (el.hasAttribute('data-shimmer-ignore')) return false;\r\n if (el.hasAttribute('data-shimmer')) return true;\r\n if (TRACEABLE_TAGS.has(el.tagName)) return true;\r\n\r\n // Leaf element with visible dimensions → trace it\r\n if (el.children.length === 0) {\r\n const rect = el.getBoundingClientRect();\r\n return rect.width > 0 && rect.height > 0;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Detects elements whose coordinate space cannot follow the Master\r\n * container during scroll. `position: fixed` and `position: sticky`\r\n * both live in a different scroll context (viewport / nearest scroll\r\n * container) than the Master, so any container-relative rect we compute\r\n * for them becomes stale on the first scroll.\r\n *\r\n * Returning `true` here causes the entire subtree under `el` to be\r\n * skipped, since descendants of a fixed/sticky element inherit the same\r\n * broken coordinate space.\r\n */\r\nfunction isViewportLocked(el: Element): boolean {\r\n const pos = window.getComputedStyle(el).position;\r\n return pos === 'fixed' || pos === 'sticky';\r\n}\r\n\r\n/**\r\n * Tracks Master containers we've already warned about, so the\r\n * console.warn fires once per container instead of on every resize-driven\r\n * re-trace. WeakSet keeps the dedupe noise-free across container churn —\r\n * a fresh Master gets one warning, period.\r\n */\r\nconst warnedContainers = new WeakSet<Element>();\r\n\r\ninterface CollectResult {\r\n traced: Element[];\r\n skippedFixed: number;\r\n}\r\n\r\n/**\r\n * Recursively walks the DOM tree and collects all traceable elements.\r\n *\r\n * Also reports how many elements were silently skipped because they used\r\n * `position: fixed` / `position: sticky`. Users can override this skip\r\n * by adding `data-shimmer` to the element, but the resulting overlay\r\n * block will drift on scroll — that's their choice to make.\r\n */\r\nfunction collectTraceableElements(root: Element): CollectResult {\r\n const traced: Element[] = [];\r\n let skippedFixed = 0;\r\n\r\n function walk(el: Element) {\r\n if (el.hasAttribute('data-shimmer-ignore')) return;\r\n if (el.hasAttribute('data-shimmer-reporter')) return; // Ignore nested reporters, they report their own rects\r\n\r\n // `data-shimmer` is an explicit opt-in — trust the user and trace.\r\n // Without it, fixed/sticky elements are skipped entirely (including\r\n // their descendants, which share the broken coordinate space).\r\n if (!el.hasAttribute('data-shimmer') && isViewportLocked(el)) {\r\n skippedFixed += 1;\r\n return;\r\n }\r\n\r\n if (isTraceable(el)) {\r\n traced.push(el);\r\n return; // Don't recurse into traced elements\r\n }\r\n\r\n for (let i = 0; i < el.children.length; i++) {\r\n walk(el.children[i]);\r\n }\r\n }\r\n\r\n for (let i = 0; i < root.children.length; i++) {\r\n walk(root.children[i]);\r\n }\r\n\r\n return { traced, skippedFixed };\r\n}\r\n\r\n/**\r\n * Measures an element and returns its ShimmerRect relative to the container.\r\n */\r\nfunction measureElement(\r\n el: Element,\r\n containerRect: DOMRect,\r\n globalBorderRadius?: string,\r\n): ShimmerRect | null {\r\n const elRect = el.getBoundingClientRect();\r\n\r\n // If the element is display: none or detached, getBoundingClientRect returns all zeros.\r\n // We must not trace it, otherwise it ends up at the left edge of the screen.\r\n if (elRect.width === 0 && elRect.height === 0 && elRect.left === 0 && elRect.top === 0) {\r\n return null;\r\n }\r\n\r\n const computedStyle = window.getComputedStyle(el);\r\n let borderRadius = globalBorderRadius || computedStyle.borderRadius;\r\n\r\n // If the element has no border radius (common for text tags),\r\n // apply a small default to avoid sharp edges in the shimmer.\r\n const isZero = !borderRadius || borderRadius === 'none' || borderRadius.split(' ').every(v => v === '0' || v === '0px' || v === '0%');\r\n if (isZero) {\r\n borderRadius = '4px';\r\n }\r\n\r\n let width = elRect.width;\r\n let height = elRect.height;\r\n\r\n // Apply fallback dimensions if element has 0 size but is actually visible (e.g. empty inline element)\r\n if (width === 0 || height === 0) {\r\n const fallback = FALLBACK_DIMENSIONS[el.tagName];\r\n if (fallback) {\r\n width = width || fallback.width;\r\n height = height || fallback.height;\r\n }\r\n }\r\n\r\n return {\r\n x: elRect.left - containerRect.left,\r\n y: elRect.top - containerRect.top,\r\n width,\r\n height,\r\n borderRadius,\r\n };\r\n}\r\n\r\n/**\r\n * Performs a full trace of all traceable elements within the container.\r\n *\r\n * @param anchorRef - When provided (Reporter mode), elements are measured\r\n * relative to this element instead of the container. Allows Reporter's\r\n * wrapper to use display:contents without breaking measurement.\r\n */\r\nfunction performTrace(\r\n container: HTMLElement,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const anchor = anchorRef?.current ?? container;\r\n const anchorRect = anchor.getBoundingClientRect();\r\n const { traced, skippedFixed } = collectTraceableElements(container);\r\n\r\n if (skippedFixed > 0 && !warnedContainers.has(container)) {\r\n warnedContainers.add(container);\r\n // eslint-disable-next-line no-console\r\n console.warn(\r\n `[shimmer-trace] Skipped ${skippedFixed} element(s) with ` +\r\n `position:fixed or position:sticky. Their coordinate space ` +\r\n `cannot follow the Master container during scroll, so the overlay ` +\r\n `block would drift. Render a nested <Shimmer> inside the fixed/sticky ` +\r\n `element if you need a skeleton there, or add data-shimmer to opt in ` +\r\n `(positioning may still drift on scroll).`,\r\n );\r\n }\r\n\r\n return traced\r\n .map((el) => measureElement(el, anchorRect, globalBorderRadius))\r\n .filter((r): r is ShimmerRect => r !== null && r.width > 0 && r.height > 0);\r\n}\r\n\r\n/**\r\n * Hook that traces all visible leaf DOM elements within a container\r\n * and returns their measured ShimmerRects.\r\n *\r\n * Uses ResizeObserver to re-trace on container resize.\r\n *\r\n * @param anchorRef - When set, rects are relative to this element (Master).\r\n * Used by Reporter so its display:contents wrapper doesn't break measurement.\r\n */\r\nexport function useTrace(\r\n containerRef: RefObject<HTMLElement | null>,\r\n loading: boolean,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const [rects, setRects] = useState<ShimmerRect[]>([]);\r\n\r\n const trace = useCallback(() => {\r\n if (!containerRef.current) return;\r\n const traced = performTrace(containerRef.current, globalBorderRadius, anchorRef);\r\n setRects(traced);\r\n }, [containerRef, globalBorderRadius, anchorRef]);\r\n\r\n useLayoutEffect(() => {\r\n if (!loading || !containerRef.current) {\r\n setRects([]);\r\n return;\r\n }\r\n\r\n // Initial trace\r\n trace();\r\n\r\n // Re-trace on resize\r\n const observer = new ResizeObserver(() => {\r\n trace();\r\n });\r\n observer.observe(containerRef.current);\r\n\r\n return () => observer.disconnect();\r\n }, [loading, trace]);\r\n\r\n return rects;\r\n}\r\n","const SHIMMER_STYLES_ID = 'shimmer-trace-styles';\r\n\r\nconst CSS = `\r\n@keyframes shimmer-wave {\r\n 0% { transform: translateX(-100%); }\r\n 100% { transform: translateX(100%); }\r\n}\r\n\r\n@keyframes shimmer-pulse {\r\n 0%, 100% { opacity: 0.4; }\r\n 50% { opacity: 1; }\r\n}\r\n\r\n@keyframes shimmer-shine {\r\n 0% { transform: translateX(-150%) skewX(-20deg); }\r\n 100% { transform: translateX(150%) skewX(-20deg); }\r\n}\r\n\r\n@keyframes shimmer-glow {\r\n 0%, 100% { filter: brightness(1); }\r\n 50% { filter: brightness(1.35); }\r\n}\r\n\r\n@keyframes shimmer-gradient {\r\n 0% { background-position: 0% 50%; }\r\n 50% { background-position: 100% 50%; }\r\n 100% { background-position: 0% 50%; }\r\n}\r\n\r\n/* preserveBackground mode: hide text + media but keep container styles */\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(h1,h2,h3,h4,h5,h6,p,span,a,li,label,td,th,blockquote,code,pre,strong,em,small) {\r\n color: transparent !important;\r\n text-shadow: none !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(img,video,svg,canvas,picture) {\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(input,textarea,select,button) {\r\n color: transparent !important;\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] {\r\n pointer-events: none !important;\r\n user-select: none !important;\r\n}\r\n`;\r\n\r\n/**\r\n * Injects the shimmer keyframe animations into the document head.\r\n * Safe to call multiple times — only injects once.\r\n */\r\nexport function injectStyles(): void {\r\n if (typeof document === 'undefined') return;\r\n if (document.getElementById(SHIMMER_STYLES_ID)) return;\r\n\r\n const style = document.createElement('style');\r\n style.id = SHIMMER_STYLES_ID;\r\n style.textContent = CSS;\r\n document.head.appendChild(style);\r\n}\r\n","'use client';\r\n\r\nimport React, { useRef, useCallback, useState, useId, useMemo, useInsertionEffect } from \"react\";\r\nimport { ShimmerProps, ShimmerRect, DEFAULTS } from \"./types\";\r\nimport { ShimmerContext, useShimmerContext } from \"./ShimmerContext\";\r\nimport { ShimmerOverlay } from \"./ShimmerOverlay\";\r\nimport { useTrace } from \"./useTrace\";\r\nimport { injectStyles } from \"./styles\";\r\nimport { generateShimmerKey } from \"./utils\";\r\n\r\n/**\r\n * The main Shimmer component.\r\n *\r\n * Auto-detects **Master** (no parent Shimmer) vs **Reporter** (nested).\r\n * - Master: renders children hidden, traces DOM, paints overlay.\r\n * - Reporter: measures own rects, reports to parent Master.\r\n *\r\n * ### Skeleton shape via `dummyData`\r\n *\r\n * Pass `dummyData` so children render with realistic data while loading.\r\n * No render-prop, no manual `data || fallback` in JSX.\r\n *\r\n * ```tsx\r\n * const userTemplate = { name: 'Loading...', role: '...', avatar: '' };\r\n *\r\n * <Shimmer loading={loading} dummyData={{ user: userTemplate }}>\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * ### List mode (`dummyLength`)\r\n *\r\n * Combined with `dummyData`, clones the first child N times with\r\n * template props merged in:\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyLength={5}\r\n * dummyData={{ fruit: { name: 'xxxxx', price: '$0.00' } }}\r\n * >\r\n * <FruitCard fruit={undefined as any} />\r\n * </Shimmer>\r\n * ```\r\n */\r\nexport function Shimmer({\r\n\tloading = false,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tstopPropagation = false,\r\n\tanimation,\r\n\tbaseColor,\r\n\thighlightColor,\r\n\tspeed,\r\n\tborderRadius,\r\n\tpreserveBackground,\r\n\tclassName,\r\n\tstyle,\r\n}: ShimmerProps) {\r\n\tconst parentContext = useShimmerContext();\r\n\tconst isMaster = !parentContext || stopPropagation;\r\n\tconst id = useId();\r\n\r\n\tconst config = useMemo(\r\n\t\t() => ({\r\n\t\t\tanimation:\r\n\t\t\t\tanimation ?? parentContext?.config.animation ?? DEFAULTS.animation,\r\n\t\t\tbaseColor:\r\n\t\t\t\tbaseColor ?? parentContext?.config.baseColor ?? DEFAULTS.baseColor,\r\n\t\t\thighlightColor:\r\n\t\t\t\thighlightColor ??\r\n\t\t\t\tparentContext?.config.highlightColor ??\r\n\t\t\t\tDEFAULTS.highlightColor,\r\n\t\t\tspeed: speed ?? parentContext?.config.speed ?? DEFAULTS.speed,\r\n\t\t\tborderRadius:\r\n\t\t\t\tborderRadius ??\r\n\t\t\t\tparentContext?.config.borderRadius ??\r\n\t\t\t\tDEFAULTS.borderRadius,\r\n\t\t\tpreserveBackground:\r\n\t\t\t\tpreserveBackground ??\r\n\t\t\t\tparentContext?.config.preserveBackground ??\r\n\t\t\t\tDEFAULTS.preserveBackground,\r\n\t\t}),\r\n\t\t[\r\n\t\t\tanimation,\r\n\t\t\tbaseColor,\r\n\t\t\thighlightColor,\r\n\t\t\tspeed,\r\n\t\t\tborderRadius,\r\n\t\t\tpreserveBackground,\r\n\t\t\tparentContext?.config,\r\n\t\t],\r\n\t);\r\n\r\n\tif (isMaster) {\r\n\t\treturn (\r\n\t\t\t<MasterShimmer\r\n\t\t\t\tid={id}\r\n\t\t\t\tloading={loading}\r\n\t\t\t\tconfig={config}\r\n\t\t\t\tdummyLength={dummyLength}\r\n\t\t\t\tdummyData={dummyData}\r\n\t\t\t\tas={as}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={style}\r\n\t\t\t>\r\n\t\t\t\t{children}\r\n\t\t\t</MasterShimmer>\r\n\t\t);\r\n\t}\r\n\r\n\treturn (\r\n\t\t<ReporterShimmer\r\n\t\t\tid={id}\r\n\t\t\tparentContext={parentContext!}\r\n\t\t\tconfig={config}\r\n\t\t\tdummyLength={dummyLength}\r\n\t\t\tdummyData={dummyData}\r\n\t\t\tas={as}\r\n\t\t>\r\n\t\t\t{children}\r\n\t\t</ReporterShimmer>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Master ─────────────────── */\r\n\r\ninterface MasterShimmerProps {\r\n\tid: string;\r\n\tloading: boolean;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\nfunction MasterShimmer({\r\n\tid,\r\n\tloading,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tclassName,\r\n\tstyle,\r\n}: MasterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst [reporterRects, setReporterRects] = useState<\r\n\t\tRecord<string, ShimmerRect[]>\r\n\t>({});\r\n\r\n\tconst register = useCallback((rid: string, rects: ShimmerRect[]) => {\r\n\t\tsetReporterRects((prev) => ({ ...prev, [rid]: rects }));\r\n\t}, []);\r\n\r\n\tconst unregister = useCallback((rid: string) => {\r\n\t\tsetReporterRects((prev) => {\r\n\t\t\tconst next = { ...prev };\r\n\t\t\tdelete next[rid];\r\n\t\t\treturn next;\r\n\t\t});\r\n\t}, []);\r\n\r\n\tuseInsertionEffect(() => {\r\n\t\tinjectStyles();\r\n\t}, []);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tloading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t);\r\n\r\n\tconst allRects = useMemo(() => {\r\n\t\tconst reported = Object.values(reporterRects).flat();\r\n\t\treturn [...tracedRects, ...reported];\r\n\t}, [tracedRects, reporterRects]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\tconst contextValue = useMemo(\r\n\t\t() => ({\r\n\t\t\tregister,\r\n\t\t\tunregister,\r\n\t\t\tmasterRef: containerRef,\r\n\t\t\tloading,\r\n\t\t\tconfig,\r\n\t\t}),\r\n\t\t[register, unregister, loading, config],\r\n\t);\r\n\r\n\treturn (\r\n\t\t<ShimmerContext.Provider value={contextValue}>\r\n\t\t\t<div\r\n\t\t\t\tref={containerRef}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={{\r\n\t\t\t\t\tposition: \"relative\",\r\n\t\t\t\t\tvisibility:\r\n\t\t\t\t\t\tloading && !config.preserveBackground ? \"hidden\" : undefined,\r\n\t\t\t\t\t...style,\r\n\t\t\t\t}}\r\n\t\t\t\taria-hidden={loading || undefined}\r\n\t\t\t\tdata-shimmer-master\r\n\t\t\t\tdata-shimmer-preserve-bg={\r\n\t\t\t\t\tloading && config.preserveBackground ? \"true\" : undefined\r\n\t\t\t\t}\r\n\t\t\t>\r\n\t\t\t\t{renderedChildren}\r\n\r\n\t\t\t\t{loading && (\r\n\t\t\t\t\t<ShimmerOverlay\r\n\t\t\t\t\t\trects={allRects}\r\n\t\t\t\t\t\tanimation={config.animation}\r\n\t\t\t\t\t\tbaseColor={config.baseColor}\r\n\t\t\t\t\t\thighlightColor={config.highlightColor}\r\n\t\t\t\t\t\tspeed={config.speed}\r\n\t\t\t\t\t/>\r\n\t\t\t\t)}\r\n\t\t\t</div>\r\n\t\t</ShimmerContext.Provider>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Reporter ─────────────────── */\r\n\r\ninterface ReporterShimmerProps {\r\n\tid: string;\r\n\tparentContext: NonNullable<ReturnType<typeof useShimmerContext>>;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n}\r\n\r\nfunction ReporterShimmer({\r\n\tid,\r\n\tparentContext,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n}: ReporterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tparentContext.loading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t\tparentContext.masterRef,\r\n\t);\r\n\r\n\tReact.useLayoutEffect(() => {\r\n\t\tif (!parentContext.loading || tracedRects.length === 0) {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tparentContext.register(id, tracedRects);\r\n\t\treturn () => {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t};\r\n\t}, [tracedRects, parentContext, id]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading: parentContext.loading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\treturn (\r\n\t\t<div\r\n\t\t\tref={containerRef}\r\n\t\t\tdata-shimmer-reporter\r\n\t\t\tstyle={{ display: \"contents\" }}\r\n\t\t>\r\n\t\t\t{renderedChildren}\r\n\t\t</div>\r\n\t);\r\n}\r\n\r\n/* ─────────────── Skeleton Children ─────────────── */\r\n\r\ninterface UseSkeletonChildrenParams {\r\n\tloading: boolean;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tid: string;\r\n}\r\n\r\n/**\r\n * Build the rendered children tree.\r\n *\r\n * Priority during `loading=true`:\r\n * 1. `as` set → render `dummyLength` (or 1) instances of `<as {...dummyData} />`.\r\n * Children ignored. Cold-start safe.\r\n * 2. `dummyData` set + children present → clone each child merging\r\n * dummyData over its props. If `dummyLength` set, clone first\r\n * templated child N times.\r\n * 3. None → pass children through (e.g. `useIsShimmering` flow).\r\n *\r\n * `loading=false` → children untouched.\r\n */\r\nfunction useSkeletonChildren({\r\n\tloading,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tid,\r\n}: UseSkeletonChildrenParams): React.ReactNode {\r\n\tif (!loading) return children;\r\n\r\n\tif (as) {\r\n\t\tconst count = dummyLength && dummyLength > 0 ? dummyLength : 1;\r\n\t\tconst Component = as;\r\n\t\treturn Array.from({ length: count }, (_, i) => (\r\n\t\t\t<Component\r\n\t\t\t\t{...(dummyData || {})}\r\n\t\t\t\tkey={generateShimmerKey(`${id}-as-${i}`)}\r\n\t\t\t/>\r\n\t\t));\r\n\t}\r\n\r\n\tconst childArray = React.Children.toArray(children);\r\n\r\n\tconst templated = childArray.map((c, i) => {\r\n\t\tif (!React.isValidElement(c)) return c;\r\n\t\tconst key = generateShimmerKey(`${id}-tpl-${i}`);\r\n\t\tconst props = dummyData ? { ...dummyData, key } : { key };\r\n\t\treturn React.cloneElement(c as React.ReactElement, props as any);\r\n\t});\r\n\r\n\tif (dummyLength && dummyLength > 0) {\r\n\t\tconst first = templated.find((c) => React.isValidElement(c)) as\r\n\t\t\t| React.ReactElement\r\n\t\t\t| undefined;\r\n\t\tif (!first) return null;\r\n\t\treturn Array.from({ length: dummyLength }, (_, i) =>\r\n\t\t\tReact.cloneElement(first, {\r\n\t\t\t\tkey: generateShimmerKey(`${id}-clone-${i}`),\r\n\t\t\t} as any),\r\n\t\t);\r\n\t}\r\n\r\n\treturn templated;\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { ShimmerConfig, ShimmerProps, DEFAULTS } from './types';\r\n\r\n/**\r\n * Factory function to create a pre-configured Shimmer component.\r\n * Avoids \"Provider Hell\" by baking config into the returned component.\r\n *\r\n * All config properties are optional — defaults are used for anything\r\n * not specified.\r\n *\r\n * @example\r\n * ```tsx\r\n * const AppShimmer = createShimmer({\r\n * animation: 'pulse',\r\n * baseColor: '#1a1a2e',\r\n * highlightColor: '#16213e',\r\n * speed: 2,\r\n * });\r\n *\r\n * <AppShimmer loading={isLoading}>\r\n * <MyComponent />\r\n * </AppShimmer>\r\n * ```\r\n */\r\nexport function createShimmer(config: ShimmerConfig = {}) {\r\n const mergedConfig: Required<ShimmerConfig> = {\r\n animation: config.animation ?? DEFAULTS.animation,\r\n baseColor: config.baseColor ?? DEFAULTS.baseColor,\r\n highlightColor: config.highlightColor ?? DEFAULTS.highlightColor,\r\n speed: config.speed ?? DEFAULTS.speed,\r\n borderRadius: config.borderRadius ?? DEFAULTS.borderRadius,\r\n preserveBackground: config.preserveBackground ?? DEFAULTS.preserveBackground,\r\n };\r\n\r\n function ConfiguredShimmer(props: Omit<ShimmerProps, keyof ShimmerConfig> & Partial<ShimmerConfig>) {\r\n return (\r\n <Shimmer\r\n {...mergedConfig}\r\n {...props}\r\n />\r\n );\r\n }\r\n\r\n // Removed ConfiguredShimmer.displayName\r\n return ConfiguredShimmer;\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { IsShimmeringContext } from './ShimmerContext';\r\nimport { ShimmerConfig } from './types';\r\n\r\nexport interface ShimmerSuspenseProps extends ShimmerConfig {\r\n children: React.ReactNode;\r\n /**\r\n * Explicit skeleton template. Rendered hidden and traced for shimmer shape.\r\n *\r\n * Preferred — pass the same component with no data props:\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * If omitted, falls back to Option B: children are re-rendered with\r\n * `useIsShimmering()=true` so they can return an empty shape themselves.\r\n */\r\n template?: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Suspense boundary that automatically shows a shimmer skeleton while\r\n * children are suspended (e.g. useSuspenseQuery, use(promise), etc).\r\n *\r\n * **Option A — explicit template (preferred):**\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * **Option B — useIsShimmering hook (no template):**\r\n * ```tsx\r\n * function UserCard() {\r\n * const isShimmering = useIsShimmering();\r\n * const data = isShimmering ? null : useSuspenseQuery(...);\r\n * return <div><h3>{data?.name}</h3></div>;\r\n * }\r\n *\r\n * <ShimmerSuspense>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n * Components must use `useIsShimmering()` to skip data fetching in shimmer mode,\r\n * otherwise they will also suspend inside the fallback (causing an empty skeleton).\r\n */\r\nexport function ShimmerSuspense({\r\n children,\r\n template,\r\n ...shimmerConfig\r\n}: ShimmerSuspenseProps) {\r\n const skeletonContent =\r\n template !== undefined ? (\r\n template\r\n ) : (\r\n <IsShimmeringContext.Provider value={true}>\r\n {children}\r\n </IsShimmeringContext.Provider>\r\n );\r\n\r\n return (\r\n <React.Suspense\r\n fallback={\r\n <Shimmer loading={true} {...shimmerConfig}>\r\n {skeletonContent}\r\n </Shimmer>\r\n }\r\n >\r\n {children}\r\n </React.Suspense>\r\n );\r\n}\r\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
import React2, { createContext, useContext, useId, useMemo, useRef, useState, useCallback, useInsertionEffect, createElement, useLayoutEffect } from 'react';
|
|
2
3
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
4
|
|
|
4
5
|
// src/Shimmer.tsx
|
|
@@ -127,9 +128,8 @@ var ShimmerOverlay = ({
|
|
|
127
128
|
};
|
|
128
129
|
|
|
129
130
|
// src/utils.ts
|
|
130
|
-
var counter = 0;
|
|
131
131
|
function generateShimmerKey(prefix = "shimmer") {
|
|
132
|
-
return
|
|
132
|
+
return prefix;
|
|
133
133
|
}
|
|
134
134
|
var FALLBACK_DIMENSIONS = {
|
|
135
135
|
INPUT: { width: 200, height: 36 },
|
|
@@ -190,13 +190,23 @@ function isTraceable(el) {
|
|
|
190
190
|
}
|
|
191
191
|
return false;
|
|
192
192
|
}
|
|
193
|
+
function isViewportLocked(el) {
|
|
194
|
+
const pos = window.getComputedStyle(el).position;
|
|
195
|
+
return pos === "fixed" || pos === "sticky";
|
|
196
|
+
}
|
|
197
|
+
var warnedContainers = /* @__PURE__ */ new WeakSet();
|
|
193
198
|
function collectTraceableElements(root) {
|
|
194
|
-
const
|
|
199
|
+
const traced = [];
|
|
200
|
+
let skippedFixed = 0;
|
|
195
201
|
function walk(el) {
|
|
196
202
|
if (el.hasAttribute("data-shimmer-ignore")) return;
|
|
197
203
|
if (el.hasAttribute("data-shimmer-reporter")) return;
|
|
204
|
+
if (!el.hasAttribute("data-shimmer") && isViewportLocked(el)) {
|
|
205
|
+
skippedFixed += 1;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
198
208
|
if (isTraceable(el)) {
|
|
199
|
-
|
|
209
|
+
traced.push(el);
|
|
200
210
|
return;
|
|
201
211
|
}
|
|
202
212
|
for (let i = 0; i < el.children.length; i++) {
|
|
@@ -206,7 +216,7 @@ function collectTraceableElements(root) {
|
|
|
206
216
|
for (let i = 0; i < root.children.length; i++) {
|
|
207
217
|
walk(root.children[i]);
|
|
208
218
|
}
|
|
209
|
-
return
|
|
219
|
+
return { traced, skippedFixed };
|
|
210
220
|
}
|
|
211
221
|
function measureElement(el, containerRect, globalBorderRadius) {
|
|
212
222
|
const elRect = el.getBoundingClientRect();
|
|
@@ -239,8 +249,14 @@ function measureElement(el, containerRect, globalBorderRadius) {
|
|
|
239
249
|
function performTrace(container, globalBorderRadius, anchorRef) {
|
|
240
250
|
const anchor = anchorRef?.current ?? container;
|
|
241
251
|
const anchorRect = anchor.getBoundingClientRect();
|
|
242
|
-
const
|
|
243
|
-
|
|
252
|
+
const { traced, skippedFixed } = collectTraceableElements(container);
|
|
253
|
+
if (skippedFixed > 0 && !warnedContainers.has(container)) {
|
|
254
|
+
warnedContainers.add(container);
|
|
255
|
+
console.warn(
|
|
256
|
+
`[shimmer-trace] Skipped ${skippedFixed} element(s) with position:fixed or position:sticky. Their coordinate space cannot follow the Master container during scroll, so the overlay block would drift. Render a nested <Shimmer> inside the fixed/sticky element if you need a skeleton there, or add data-shimmer to opt in (positioning may still drift on scroll).`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return traced.map((el) => measureElement(el, anchorRect, globalBorderRadius)).filter((r) => r !== null && r.width > 0 && r.height > 0);
|
|
244
260
|
}
|
|
245
261
|
function useTrace(containerRef, loading, globalBorderRadius, anchorRef) {
|
|
246
262
|
const [rects, setRects] = useState([]);
|
|
@@ -408,7 +424,7 @@ function MasterShimmer({
|
|
|
408
424
|
return next;
|
|
409
425
|
});
|
|
410
426
|
}, []);
|
|
411
|
-
|
|
427
|
+
useInsertionEffect(() => {
|
|
412
428
|
injectStyles();
|
|
413
429
|
}, []);
|
|
414
430
|
const tracedRects = useTrace(
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/ShimmerContext.tsx","../src/ShimmerOverlay.tsx","../src/utils.ts","../src/useTrace.ts","../src/styles.ts","../src/Shimmer.tsx","../src/createShimmer.tsx","../src/ShimmerSuspense.tsx"],"names":["React","jsx","useState","useCallback"],"mappings":"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","file":"index.mjs","sourcesContent":["import React, { ReactNode } from 'react';\r\n\r\n/**\r\n * Represents a measured rectangle of a traced DOM element,\r\n * positioned relative to the Master Shimmer container.\r\n */\r\nexport interface ShimmerRect {\r\n x: number;\r\n y: number;\r\n width: number;\r\n height: number;\r\n borderRadius: string;\r\n}\r\n\r\n/** Available animation types for the shimmer effect. */\r\nexport type AnimationType =\r\n | 'wave'\r\n | 'pulse'\r\n | 'shine'\r\n | 'glow'\r\n | 'gradient';\r\n\r\n/** Configuration options for the shimmer effect (all optional). */\r\nexport interface ShimmerConfig {\r\n /** Animation style. Defaults to 'wave'. */\r\n animation?: AnimationType;\r\n /** Base color of the shimmer blocks. Defaults to '#e0e0e0'. */\r\n baseColor?: string;\r\n /** Highlight color of the shimmer animation. Defaults to '#f5f5f5'. */\r\n highlightColor?: string;\r\n /** Animation duration in seconds. Defaults to 1.5. */\r\n speed?: number;\r\n /** Global border-radius override. If omitted, auto-detected from each element (defaults to 4px if detection is 0px). */\r\n borderRadius?: string;\r\n /**\r\n * Keep container backgrounds, borders, and padding visible while loading.\r\n * When `true` (default), only text and media leaves are hidden via\r\n * `color:transparent` / `opacity:0` so card backgrounds, borders, and\r\n * spacing remain visible underneath the shimmer overlay.\r\n *\r\n * Set `false` for legacy behavior (`visibility:hidden` on whole tree).\r\n */\r\n preserveBackground?: boolean;\r\n}\r\n\r\n/** Props for the Shimmer component. */\r\nexport interface ShimmerProps extends ShimmerConfig {\r\n /** Whether the loading state is active. */\r\n loading?: boolean;\r\n /** The children to trace and render shimmer over. */\r\n children: ReactNode;\r\n /**\r\n * Number of placeholder clones to generate for list-like loading states.\r\n *\r\n * When `loading=true` and `dummyLength` is set, Shimmer grabs the first\r\n * available child (or a cached template from the last loaded render) and\r\n * clones it `dummyLength` times to produce skeleton placeholders.\r\n *\r\n * When `loading=false`, children are rendered as-is.\r\n */\r\n dummyLength?: number;\r\n /**\r\n * Props injected into each child element while `loading=true` so the\r\n * skeleton renders with realistic shape without requiring real data.\r\n *\r\n * Example:\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyData={{ user: { name: 'Loading...', role: '...', avatar: '' } }}\r\n * >\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * While loading, each direct child is cloned with these props merged on top\r\n * of its own props. Ignored when `loading=false`.\r\n */\r\n dummyData?: Record<string, any>;\r\n /**\r\n * Component used to auto-generate skeleton elements while `loading=true`.\r\n *\r\n * When set, Shimmer ignores `children` during loading and renders\r\n * `dummyLength` (defaults to 1) instances of `<as {...dummyData} />`\r\n * to derive shape. Real children render once `loading=false`.\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * as={MovieCard}\r\n * dummyData={{ movie: movieTemplate }}\r\n * dummyLength={10}\r\n * >\r\n * {movies.map((m) => <MovieCard movie={m} key={m.id} />)}\r\n * </Shimmer>\r\n * ```\r\n */\r\n as?: React.ComponentType<any>;\r\n /** Force this Shimmer to be a Master renderer even if nested inside another Shimmer. */\r\n stopPropagation?: boolean;\r\n /**\r\n * className applied to the Master container div.\r\n * Use to control layout (e.g. display:flex) without losing position:relative.\r\n */\r\n className?: string;\r\n /**\r\n * Inline styles merged into the Master container div.\r\n * position:relative is always applied; everything else is overridable.\r\n */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n/** Default configuration values. */\r\nexport const DEFAULTS: Required<ShimmerConfig> = {\r\n animation: 'wave',\r\n baseColor: '#e0e0e0',\r\n highlightColor: '#f5f5f5',\r\n speed: 1.5,\r\n borderRadius: '',\r\n preserveBackground: true,\r\n};\r\n","import { createContext, useContext, RefObject } from 'react';\r\nimport { ShimmerRect, ShimmerConfig } from './types';\r\n\r\nexport interface ShimmerContextValue {\r\n register: (id: string, rects: ShimmerRect[]) => void;\r\n unregister: (id: string) => void;\r\n /** Ref object (not .current) so Reporters always read a fresh value. */\r\n masterRef: RefObject<HTMLElement | null>;\r\n loading: boolean;\r\n config: Required<ShimmerConfig>;\r\n}\r\n\r\nexport const ShimmerContext = createContext<ShimmerContextValue | null>(null);\r\n\r\nexport function useShimmerContext(): ShimmerContextValue | null {\r\n return useContext(ShimmerContext);\r\n}\r\n\r\n/**\r\n * True when rendered inside a ShimmerSuspense fallback (Option B).\r\n * Components use this to skip data fetching and return an empty shape.\r\n */\r\nexport const IsShimmeringContext = createContext<boolean>(false);\r\n\r\nexport function useIsShimmering(): boolean {\r\n return useContext(IsShimmeringContext);\r\n}\r\n","import React from 'react';\r\nimport { ShimmerRect, AnimationType } from './types';\r\n\r\ninterface ShimmerOverlayProps {\r\n rects: ShimmerRect[];\r\n animation: AnimationType;\r\n baseColor: string;\r\n highlightColor: string;\r\n speed: number;\r\n}\r\n\r\n/**\r\n * Returns animation-specific inline styles for each shimmer block.\r\n * For sweep-style animations (wave, shine), the colored gradient is rendered\r\n * as a child layer so the sweep can extend across the container in sync.\r\n */\r\nfunction getBlockStyles(\r\n animation: AnimationType,\r\n baseColor: string,\r\n highlightColor: string,\r\n speed: number,\r\n rect: ShimmerRect,\r\n): React.CSSProperties {\r\n const base: React.CSSProperties = {\r\n position: 'absolute',\r\n top: rect.y,\r\n left: rect.x,\r\n width: rect.width,\r\n height: rect.height,\r\n borderRadius: rect.borderRadius,\r\n overflow: 'hidden',\r\n };\r\n\r\n switch (animation) {\r\n case 'wave':\r\n case 'shine':\r\n return { ...base, background: baseColor };\r\n case 'pulse':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-pulse ${speed}s ease-in-out infinite`,\r\n };\r\n case 'glow':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-glow ${speed}s ease-in-out infinite`,\r\n };\r\n case 'gradient':\r\n return {\r\n ...base,\r\n backgroundImage: `linear-gradient(90deg, ${baseColor}, ${highlightColor}, ${baseColor})`,\r\n backgroundSize: '200% 100%',\r\n animation: `shimmer-gradient ${speed * 1.5}s ease-in-out infinite`,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Sweeping shine layer used by `wave` and `shine` animations.\r\n * Spans the full container width so the highlight sweeps in sync across all blocks.\r\n */\r\nconst SweepLayer: React.FC<{\r\n rect: ShimmerRect;\r\n highlightColor: string;\r\n speed: number;\r\n containerWidth: number;\r\n variant: 'wave' | 'shine';\r\n}> = ({ rect, highlightColor, speed, containerWidth, variant }) => {\r\n const isShine = variant === 'shine';\r\n return (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: -rect.x,\r\n width: containerWidth > 0 ? containerWidth : '100vw',\r\n height: '100%',\r\n background: isShine\r\n ? `linear-gradient(115deg, transparent 30%, ${highlightColor} 50%, transparent 70%)`\r\n : `linear-gradient(90deg, transparent 0%, ${highlightColor} 50%, transparent 100%)`,\r\n animation: `${isShine ? 'shimmer-shine' : 'shimmer-wave'} ${speed}s ease-in-out infinite`,\r\n }}\r\n />\r\n );\r\n};\r\n\r\n/**\r\n * The overlay component rendered by the Master Shimmer.\r\n *\r\n * Renders one absolutely-positioned div per traced rect. Sweep-style\r\n * animations (`wave`, `shine`) get an additional gradient layer that spans\r\n * the container so the highlight passes across all blocks in sync.\r\n */\r\nexport const ShimmerOverlay: React.FC<ShimmerOverlayProps> = ({\r\n rects,\r\n animation,\r\n baseColor,\r\n highlightColor,\r\n speed,\r\n}) => {\r\n const overlayRef = React.useRef<HTMLDivElement>(null);\r\n const [containerWidth, setContainerWidth] = React.useState(0);\r\n\r\n React.useLayoutEffect(() => {\r\n if (!overlayRef.current?.parentElement) return;\r\n setContainerWidth(overlayRef.current.parentElement.offsetWidth);\r\n }, [rects]);\r\n\r\n if (rects.length === 0) return null;\r\n\r\n const isSweep = animation === 'wave' || animation === 'shine';\r\n\r\n return (\r\n <div\r\n ref={overlayRef}\r\n role=\"status\"\r\n aria-busy=\"true\"\r\n aria-label=\"Loading content\"\r\n data-shimmer-ignore=\"true\"\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n zIndex: 1,\r\n pointerEvents: 'none',\r\n visibility: 'visible',\r\n }}\r\n >\r\n {rects.map((rect, i) => (\r\n <div\r\n key={i}\r\n style={getBlockStyles(animation, baseColor, highlightColor, speed, rect)}\r\n >\r\n {isSweep && (\r\n <SweepLayer\r\n rect={rect}\r\n highlightColor={highlightColor}\r\n speed={speed}\r\n containerWidth={containerWidth}\r\n variant={animation as 'wave' | 'shine'}\r\n />\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n};\r\n","/**\r\n * Generates a unique key for cloned elements to prevent\r\n * React \"missing key\" warnings during loading state.\r\n */\r\nlet counter = 0;\r\nexport function generateShimmerKey(prefix: string = 'shimmer'): string {\r\n return `${prefix}-clone-${++counter}`;\r\n}\r\n\r\n/**\r\n * Default fallback dimensions for common elements when their\r\n * measured dimensions are 0px (e.g., empty inputs, images not yet loaded).\r\n */\r\nexport const FALLBACK_DIMENSIONS: Record<string, { width: number; height: number }> = {\r\n INPUT: { width: 200, height: 36 },\r\n BUTTON: { width: 120, height: 36 },\r\n TEXTAREA: { width: 300, height: 80 },\r\n SELECT: { width: 200, height: 36 },\r\n IMG: { width: 100, height: 100 },\r\n H1: { width: 300, height: 36 },\r\n H2: { width: 260, height: 30 },\r\n H3: { width: 220, height: 26 },\r\n H4: { width: 200, height: 22 },\r\n H5: { width: 180, height: 20 },\r\n H6: { width: 160, height: 18 },\r\n P: { width: 250, height: 16 },\r\n SPAN: { width: 100, height: 16 },\r\n};\r\n","import { useLayoutEffect, useState, RefObject, useCallback } from 'react';\r\nimport { ShimmerRect } from './types';\r\nimport { FALLBACK_DIMENSIONS } from './utils';\r\n\r\n/**\r\n * Tags that are always considered \"traceable\" leaf elements\r\n * whose dimensions should be captured for the shimmer overlay.\r\n */\r\nconst TRACEABLE_TAGS = new Set([\r\n // Text\r\n 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'SPAN', 'A', 'LI',\r\n 'LABEL', 'TD', 'TH', 'BLOCKQUOTE', 'CODE', 'PRE',\r\n // Media\r\n 'IMG', 'VIDEO', 'SVG', 'CANVAS', 'PICTURE',\r\n // Form\r\n 'INPUT', 'TEXTAREA', 'SELECT', 'BUTTON',\r\n // Misc\r\n 'HR',\r\n]);\r\n\r\n/**\r\n * Determines if an element should be traced.\r\n * Explicit data attributes override automatic detection.\r\n */\r\nfunction isTraceable(el: Element): boolean {\r\n if (el.hasAttribute('data-shimmer-ignore')) return false;\r\n if (el.hasAttribute('data-shimmer')) return true;\r\n if (TRACEABLE_TAGS.has(el.tagName)) return true;\r\n\r\n // Leaf element with visible dimensions → trace it\r\n if (el.children.length === 0) {\r\n const rect = el.getBoundingClientRect();\r\n return rect.width > 0 && rect.height > 0;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Recursively walks the DOM tree and collects all traceable elements.\r\n */\r\nfunction collectTraceableElements(root: Element): Element[] {\r\n const result: Element[] = [];\r\n\r\n function walk(el: Element) {\r\n if (el.hasAttribute('data-shimmer-ignore')) return;\r\n if (el.hasAttribute('data-shimmer-reporter')) return; // Ignore nested reporters, they report their own rects\r\n\r\n if (isTraceable(el)) {\r\n result.push(el);\r\n return; // Don't recurse into traced elements\r\n }\r\n\r\n for (let i = 0; i < el.children.length; i++) {\r\n walk(el.children[i]);\r\n }\r\n }\r\n\r\n for (let i = 0; i < root.children.length; i++) {\r\n walk(root.children[i]);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Measures an element and returns its ShimmerRect relative to the container.\r\n */\r\nfunction measureElement(\r\n el: Element,\r\n containerRect: DOMRect,\r\n globalBorderRadius?: string,\r\n): ShimmerRect | null {\r\n const elRect = el.getBoundingClientRect();\r\n\r\n // If the element is display: none or detached, getBoundingClientRect returns all zeros.\r\n // We must not trace it, otherwise it ends up at the left edge of the screen.\r\n if (elRect.width === 0 && elRect.height === 0 && elRect.left === 0 && elRect.top === 0) {\r\n return null;\r\n }\r\n\r\n const computedStyle = window.getComputedStyle(el);\r\n let borderRadius = globalBorderRadius || computedStyle.borderRadius;\r\n\r\n // If the element has no border radius (common for text tags),\r\n // apply a small default to avoid sharp edges in the shimmer.\r\n const isZero = !borderRadius || borderRadius === 'none' || borderRadius.split(' ').every(v => v === '0' || v === '0px' || v === '0%');\r\n if (isZero) {\r\n borderRadius = '4px';\r\n }\r\n\r\n let width = elRect.width;\r\n let height = elRect.height;\r\n\r\n // Apply fallback dimensions if element has 0 size but is actually visible (e.g. empty inline element)\r\n if (width === 0 || height === 0) {\r\n const fallback = FALLBACK_DIMENSIONS[el.tagName];\r\n if (fallback) {\r\n width = width || fallback.width;\r\n height = height || fallback.height;\r\n }\r\n }\r\n\r\n return {\r\n x: elRect.left - containerRect.left,\r\n y: elRect.top - containerRect.top,\r\n width,\r\n height,\r\n borderRadius,\r\n };\r\n}\r\n\r\n/**\r\n * Performs a full trace of all traceable elements within the container.\r\n *\r\n * @param anchorRef - When provided (Reporter mode), elements are measured\r\n * relative to this element instead of the container. Allows Reporter's\r\n * wrapper to use display:contents without breaking measurement.\r\n */\r\nfunction performTrace(\r\n container: HTMLElement,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const anchor = anchorRef?.current ?? container;\r\n const anchorRect = anchor.getBoundingClientRect();\r\n const elements = collectTraceableElements(container);\r\n\r\n return elements\r\n .map((el) => measureElement(el, anchorRect, globalBorderRadius))\r\n .filter((r): r is ShimmerRect => r !== null && r.width > 0 && r.height > 0);\r\n}\r\n\r\n/**\r\n * Hook that traces all visible leaf DOM elements within a container\r\n * and returns their measured ShimmerRects.\r\n *\r\n * Uses ResizeObserver to re-trace on container resize.\r\n *\r\n * @param anchorRef - When set, rects are relative to this element (Master).\r\n * Used by Reporter so its display:contents wrapper doesn't break measurement.\r\n */\r\nexport function useTrace(\r\n containerRef: RefObject<HTMLElement | null>,\r\n loading: boolean,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const [rects, setRects] = useState<ShimmerRect[]>([]);\r\n\r\n const trace = useCallback(() => {\r\n if (!containerRef.current) return;\r\n const traced = performTrace(containerRef.current, globalBorderRadius, anchorRef);\r\n setRects(traced);\r\n }, [containerRef, globalBorderRadius, anchorRef]);\r\n\r\n useLayoutEffect(() => {\r\n if (!loading || !containerRef.current) {\r\n setRects([]);\r\n return;\r\n }\r\n\r\n // Initial trace\r\n trace();\r\n\r\n // Re-trace on resize\r\n const observer = new ResizeObserver(() => {\r\n trace();\r\n });\r\n observer.observe(containerRef.current);\r\n\r\n return () => observer.disconnect();\r\n }, [loading, trace]);\r\n\r\n return rects;\r\n}\r\n","const SHIMMER_STYLES_ID = 'shimmer-trace-styles';\r\n\r\nconst CSS = `\r\n@keyframes shimmer-wave {\r\n 0% { transform: translateX(-100%); }\r\n 100% { transform: translateX(100%); }\r\n}\r\n\r\n@keyframes shimmer-pulse {\r\n 0%, 100% { opacity: 0.4; }\r\n 50% { opacity: 1; }\r\n}\r\n\r\n@keyframes shimmer-shine {\r\n 0% { transform: translateX(-150%) skewX(-20deg); }\r\n 100% { transform: translateX(150%) skewX(-20deg); }\r\n}\r\n\r\n@keyframes shimmer-glow {\r\n 0%, 100% { filter: brightness(1); }\r\n 50% { filter: brightness(1.35); }\r\n}\r\n\r\n@keyframes shimmer-gradient {\r\n 0% { background-position: 0% 50%; }\r\n 50% { background-position: 100% 50%; }\r\n 100% { background-position: 0% 50%; }\r\n}\r\n\r\n/* preserveBackground mode: hide text + media but keep container styles */\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(h1,h2,h3,h4,h5,h6,p,span,a,li,label,td,th,blockquote,code,pre,strong,em,small) {\r\n color: transparent !important;\r\n text-shadow: none !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(img,video,svg,canvas,picture) {\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(input,textarea,select,button) {\r\n color: transparent !important;\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] {\r\n pointer-events: none !important;\r\n user-select: none !important;\r\n}\r\n`;\r\n\r\n/**\r\n * Injects the shimmer keyframe animations into the document head.\r\n * Safe to call multiple times — only injects once.\r\n */\r\nexport function injectStyles(): void {\r\n if (typeof document === 'undefined') return;\r\n if (document.getElementById(SHIMMER_STYLES_ID)) return;\r\n\r\n const style = document.createElement('style');\r\n style.id = SHIMMER_STYLES_ID;\r\n style.textContent = CSS;\r\n document.head.appendChild(style);\r\n}\r\n","import React, { useRef, useCallback, useState, useId, useMemo } from \"react\";\r\nimport { ShimmerProps, ShimmerRect, DEFAULTS } from \"./types\";\r\nimport { ShimmerContext, useShimmerContext } from \"./ShimmerContext\";\r\nimport { ShimmerOverlay } from \"./ShimmerOverlay\";\r\nimport { useTrace } from \"./useTrace\";\r\nimport { injectStyles } from \"./styles\";\r\nimport { generateShimmerKey } from \"./utils\";\r\n\r\n/**\r\n * The main Shimmer component.\r\n *\r\n * Auto-detects **Master** (no parent Shimmer) vs **Reporter** (nested).\r\n * - Master: renders children hidden, traces DOM, paints overlay.\r\n * - Reporter: measures own rects, reports to parent Master.\r\n *\r\n * ### Skeleton shape via `dummyData`\r\n *\r\n * Pass `dummyData` so children render with realistic data while loading.\r\n * No render-prop, no manual `data || fallback` in JSX.\r\n *\r\n * ```tsx\r\n * const userTemplate = { name: 'Loading...', role: '...', avatar: '' };\r\n *\r\n * <Shimmer loading={loading} dummyData={{ user: userTemplate }}>\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * ### List mode (`dummyLength`)\r\n *\r\n * Combined with `dummyData`, clones the first child N times with\r\n * template props merged in:\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyLength={5}\r\n * dummyData={{ fruit: { name: 'xxxxx', price: '$0.00' } }}\r\n * >\r\n * <FruitCard fruit={undefined as any} />\r\n * </Shimmer>\r\n * ```\r\n */\r\nexport function Shimmer({\r\n\tloading = false,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tstopPropagation = false,\r\n\tanimation,\r\n\tbaseColor,\r\n\thighlightColor,\r\n\tspeed,\r\n\tborderRadius,\r\n\tpreserveBackground,\r\n\tclassName,\r\n\tstyle,\r\n}: ShimmerProps) {\r\n\tconst parentContext = useShimmerContext();\r\n\tconst isMaster = !parentContext || stopPropagation;\r\n\tconst id = useId();\r\n\r\n\tconst config = useMemo(\r\n\t\t() => ({\r\n\t\t\tanimation:\r\n\t\t\t\tanimation ?? parentContext?.config.animation ?? DEFAULTS.animation,\r\n\t\t\tbaseColor:\r\n\t\t\t\tbaseColor ?? parentContext?.config.baseColor ?? DEFAULTS.baseColor,\r\n\t\t\thighlightColor:\r\n\t\t\t\thighlightColor ??\r\n\t\t\t\tparentContext?.config.highlightColor ??\r\n\t\t\t\tDEFAULTS.highlightColor,\r\n\t\t\tspeed: speed ?? parentContext?.config.speed ?? DEFAULTS.speed,\r\n\t\t\tborderRadius:\r\n\t\t\t\tborderRadius ??\r\n\t\t\t\tparentContext?.config.borderRadius ??\r\n\t\t\t\tDEFAULTS.borderRadius,\r\n\t\t\tpreserveBackground:\r\n\t\t\t\tpreserveBackground ??\r\n\t\t\t\tparentContext?.config.preserveBackground ??\r\n\t\t\t\tDEFAULTS.preserveBackground,\r\n\t\t}),\r\n\t\t[\r\n\t\t\tanimation,\r\n\t\t\tbaseColor,\r\n\t\t\thighlightColor,\r\n\t\t\tspeed,\r\n\t\t\tborderRadius,\r\n\t\t\tpreserveBackground,\r\n\t\t\tparentContext?.config,\r\n\t\t],\r\n\t);\r\n\r\n\tif (isMaster) {\r\n\t\treturn (\r\n\t\t\t<MasterShimmer\r\n\t\t\t\tid={id}\r\n\t\t\t\tloading={loading}\r\n\t\t\t\tconfig={config}\r\n\t\t\t\tdummyLength={dummyLength}\r\n\t\t\t\tdummyData={dummyData}\r\n\t\t\t\tas={as}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={style}\r\n\t\t\t>\r\n\t\t\t\t{children}\r\n\t\t\t</MasterShimmer>\r\n\t\t);\r\n\t}\r\n\r\n\treturn (\r\n\t\t<ReporterShimmer\r\n\t\t\tid={id}\r\n\t\t\tparentContext={parentContext!}\r\n\t\t\tconfig={config}\r\n\t\t\tdummyLength={dummyLength}\r\n\t\t\tdummyData={dummyData}\r\n\t\t\tas={as}\r\n\t\t>\r\n\t\t\t{children}\r\n\t\t</ReporterShimmer>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Master ─────────────────── */\r\n\r\ninterface MasterShimmerProps {\r\n\tid: string;\r\n\tloading: boolean;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\nfunction MasterShimmer({\r\n\tid,\r\n\tloading,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tclassName,\r\n\tstyle,\r\n}: MasterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst [reporterRects, setReporterRects] = useState<\r\n\t\tRecord<string, ShimmerRect[]>\r\n\t>({});\r\n\r\n\tconst register = useCallback((rid: string, rects: ShimmerRect[]) => {\r\n\t\tsetReporterRects((prev) => ({ ...prev, [rid]: rects }));\r\n\t}, []);\r\n\r\n\tconst unregister = useCallback((rid: string) => {\r\n\t\tsetReporterRects((prev) => {\r\n\t\t\tconst next = { ...prev };\r\n\t\t\tdelete next[rid];\r\n\t\t\treturn next;\r\n\t\t});\r\n\t}, []);\r\n\r\n\tReact.useEffect(() => {\r\n\t\tinjectStyles();\r\n\t}, []);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tloading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t);\r\n\r\n\tconst allRects = useMemo(() => {\r\n\t\tconst reported = Object.values(reporterRects).flat();\r\n\t\treturn [...tracedRects, ...reported];\r\n\t}, [tracedRects, reporterRects]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\tconst contextValue = useMemo(\r\n\t\t() => ({\r\n\t\t\tregister,\r\n\t\t\tunregister,\r\n\t\t\tmasterRef: containerRef,\r\n\t\t\tloading,\r\n\t\t\tconfig,\r\n\t\t}),\r\n\t\t[register, unregister, loading, config],\r\n\t);\r\n\r\n\treturn (\r\n\t\t<ShimmerContext.Provider value={contextValue}>\r\n\t\t\t<div\r\n\t\t\t\tref={containerRef}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={{\r\n\t\t\t\t\tposition: \"relative\",\r\n\t\t\t\t\tvisibility:\r\n\t\t\t\t\t\tloading && !config.preserveBackground ? \"hidden\" : undefined,\r\n\t\t\t\t\t...style,\r\n\t\t\t\t}}\r\n\t\t\t\taria-hidden={loading || undefined}\r\n\t\t\t\tdata-shimmer-master\r\n\t\t\t\tdata-shimmer-preserve-bg={\r\n\t\t\t\t\tloading && config.preserveBackground ? \"true\" : undefined\r\n\t\t\t\t}\r\n\t\t\t>\r\n\t\t\t\t{renderedChildren}\r\n\r\n\t\t\t\t{loading && (\r\n\t\t\t\t\t<ShimmerOverlay\r\n\t\t\t\t\t\trects={allRects}\r\n\t\t\t\t\t\tanimation={config.animation}\r\n\t\t\t\t\t\tbaseColor={config.baseColor}\r\n\t\t\t\t\t\thighlightColor={config.highlightColor}\r\n\t\t\t\t\t\tspeed={config.speed}\r\n\t\t\t\t\t/>\r\n\t\t\t\t)}\r\n\t\t\t</div>\r\n\t\t</ShimmerContext.Provider>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Reporter ─────────────────── */\r\n\r\ninterface ReporterShimmerProps {\r\n\tid: string;\r\n\tparentContext: NonNullable<ReturnType<typeof useShimmerContext>>;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n}\r\n\r\nfunction ReporterShimmer({\r\n\tid,\r\n\tparentContext,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n}: ReporterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tparentContext.loading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t\tparentContext.masterRef,\r\n\t);\r\n\r\n\tReact.useLayoutEffect(() => {\r\n\t\tif (!parentContext.loading || tracedRects.length === 0) {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tparentContext.register(id, tracedRects);\r\n\t\treturn () => {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t};\r\n\t}, [tracedRects, parentContext, id]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading: parentContext.loading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\treturn (\r\n\t\t<div\r\n\t\t\tref={containerRef}\r\n\t\t\tdata-shimmer-reporter\r\n\t\t\tstyle={{ display: \"contents\" }}\r\n\t\t>\r\n\t\t\t{renderedChildren}\r\n\t\t</div>\r\n\t);\r\n}\r\n\r\n/* ─────────────── Skeleton Children ─────────────── */\r\n\r\ninterface UseSkeletonChildrenParams {\r\n\tloading: boolean;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tid: string;\r\n}\r\n\r\n/**\r\n * Build the rendered children tree.\r\n *\r\n * Priority during `loading=true`:\r\n * 1. `as` set → render `dummyLength` (or 1) instances of `<as {...dummyData} />`.\r\n * Children ignored. Cold-start safe.\r\n * 2. `dummyData` set + children present → clone each child merging\r\n * dummyData over its props. If `dummyLength` set, clone first\r\n * templated child N times.\r\n * 3. None → pass children through (e.g. `useIsShimmering` flow).\r\n *\r\n * `loading=false` → children untouched.\r\n */\r\nfunction useSkeletonChildren({\r\n\tloading,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tid,\r\n}: UseSkeletonChildrenParams): React.ReactNode {\r\n\tif (!loading) return children;\r\n\r\n\tif (as) {\r\n\t\tconst count = dummyLength && dummyLength > 0 ? dummyLength : 1;\r\n\t\tconst Component = as;\r\n\t\treturn Array.from({ length: count }, (_, i) => (\r\n\t\t\t<Component\r\n\t\t\t\t{...(dummyData || {})}\r\n\t\t\t\tkey={generateShimmerKey(`${id}-as-${i}`)}\r\n\t\t\t/>\r\n\t\t));\r\n\t}\r\n\r\n\tconst childArray = React.Children.toArray(children);\r\n\r\n\tconst templated = childArray.map((c, i) => {\r\n\t\tif (!React.isValidElement(c)) return c;\r\n\t\tconst key = generateShimmerKey(`${id}-tpl-${i}`);\r\n\t\tconst props = dummyData ? { ...dummyData, key } : { key };\r\n\t\treturn React.cloneElement(c as React.ReactElement, props as any);\r\n\t});\r\n\r\n\tif (dummyLength && dummyLength > 0) {\r\n\t\tconst first = templated.find((c) => React.isValidElement(c)) as\r\n\t\t\t| React.ReactElement\r\n\t\t\t| undefined;\r\n\t\tif (!first) return null;\r\n\t\treturn Array.from({ length: dummyLength }, (_, i) =>\r\n\t\t\tReact.cloneElement(first, {\r\n\t\t\t\tkey: generateShimmerKey(`${id}-clone-${i}`),\r\n\t\t\t} as any),\r\n\t\t);\r\n\t}\r\n\r\n\treturn templated;\r\n}\r\n","import React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { ShimmerConfig, ShimmerProps, DEFAULTS } from './types';\r\n\r\n/**\r\n * Factory function to create a pre-configured Shimmer component.\r\n * Avoids \"Provider Hell\" by baking config into the returned component.\r\n *\r\n * All config properties are optional — defaults are used for anything\r\n * not specified.\r\n *\r\n * @example\r\n * ```tsx\r\n * const AppShimmer = createShimmer({\r\n * animation: 'pulse',\r\n * baseColor: '#1a1a2e',\r\n * highlightColor: '#16213e',\r\n * speed: 2,\r\n * });\r\n *\r\n * <AppShimmer loading={isLoading}>\r\n * <MyComponent />\r\n * </AppShimmer>\r\n * ```\r\n */\r\nexport function createShimmer(config: ShimmerConfig = {}) {\r\n const mergedConfig: Required<ShimmerConfig> = {\r\n animation: config.animation ?? DEFAULTS.animation,\r\n baseColor: config.baseColor ?? DEFAULTS.baseColor,\r\n highlightColor: config.highlightColor ?? DEFAULTS.highlightColor,\r\n speed: config.speed ?? DEFAULTS.speed,\r\n borderRadius: config.borderRadius ?? DEFAULTS.borderRadius,\r\n preserveBackground: config.preserveBackground ?? DEFAULTS.preserveBackground,\r\n };\r\n\r\n function ConfiguredShimmer(props: Omit<ShimmerProps, keyof ShimmerConfig> & Partial<ShimmerConfig>) {\r\n return (\r\n <Shimmer\r\n {...mergedConfig}\r\n {...props}\r\n />\r\n );\r\n }\r\n\r\n // Removed ConfiguredShimmer.displayName\r\n return ConfiguredShimmer;\r\n}\r\n","import React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { IsShimmeringContext } from './ShimmerContext';\r\nimport { ShimmerConfig } from './types';\r\n\r\nexport interface ShimmerSuspenseProps extends ShimmerConfig {\r\n children: React.ReactNode;\r\n /**\r\n * Explicit skeleton template. Rendered hidden and traced for shimmer shape.\r\n *\r\n * Preferred — pass the same component with no data props:\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * If omitted, falls back to Option B: children are re-rendered with\r\n * `useIsShimmering()=true` so they can return an empty shape themselves.\r\n */\r\n template?: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Suspense boundary that automatically shows a shimmer skeleton while\r\n * children are suspended (e.g. useSuspenseQuery, use(promise), etc).\r\n *\r\n * **Option A — explicit template (preferred):**\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * **Option B — useIsShimmering hook (no template):**\r\n * ```tsx\r\n * function UserCard() {\r\n * const isShimmering = useIsShimmering();\r\n * const data = isShimmering ? null : useSuspenseQuery(...);\r\n * return <div><h3>{data?.name}</h3></div>;\r\n * }\r\n *\r\n * <ShimmerSuspense>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n * Components must use `useIsShimmering()` to skip data fetching in shimmer mode,\r\n * otherwise they will also suspend inside the fallback (causing an empty skeleton).\r\n */\r\nexport function ShimmerSuspense({\r\n children,\r\n template,\r\n ...shimmerConfig\r\n}: ShimmerSuspenseProps) {\r\n const skeletonContent =\r\n template !== undefined ? (\r\n template\r\n ) : (\r\n <IsShimmeringContext.Provider value={true}>\r\n {children}\r\n </IsShimmeringContext.Provider>\r\n );\r\n\r\n return (\r\n <React.Suspense\r\n fallback={\r\n <Shimmer loading={true} {...shimmerConfig}>\r\n {skeletonContent}\r\n </Shimmer>\r\n }\r\n >\r\n {children}\r\n </React.Suspense>\r\n );\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/ShimmerContext.tsx","../src/ShimmerOverlay.tsx","../src/utils.ts","../src/useTrace.ts","../src/styles.ts","../src/Shimmer.tsx","../src/createShimmer.tsx","../src/ShimmerSuspense.tsx"],"names":["React","jsx","useState","useCallback"],"mappings":";;;;;;AAiHO,IAAM,QAAA,GAAoC;AAAA,EAC/C,SAAA,EAAW,MAAA;AAAA,EACX,SAAA,EAAW,SAAA;AAAA,EACX,cAAA,EAAgB,SAAA;AAAA,EAChB,KAAA,EAAO,GAAA;AAAA,EACP,YAAA,EAAc,EAAA;AAAA,EACd,kBAAA,EAAoB;AACtB,CAAA;AC1GO,IAAM,cAAA,GAAiB,cAA0C,IAAI;AAErE,SAAS,iBAAA,GAAgD;AAC/D,EAAA,OAAO,WAAW,cAAc,CAAA;AACjC;AAMO,IAAM,mBAAA,GAAsB,cAAuB,KAAK,CAAA;AAExD,SAAS,eAAA,GAA2B;AAC1C,EAAA,OAAO,WAAW,mBAAmB,CAAA;AACtC;ACVA,SAAS,cAAA,CACP,SAAA,EACA,SAAA,EACA,cAAA,EACA,OACA,IAAA,EACqB;AACrB,EAAA,MAAM,IAAA,GAA4B;AAAA,IAChC,QAAA,EAAU,UAAA;AAAA,IACV,KAAK,IAAA,CAAK,CAAA;AAAA,IACV,MAAM,IAAA,CAAK,CAAA;AAAA,IACX,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,cAAc,IAAA,CAAK,YAAA;AAAA,IACnB,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,QAAQ,SAAA;AAAW,IACjB,KAAK,MAAA;AAAA,IACL,KAAK,OAAA;AACH,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,UAAA,EAAY,SAAA,EAAU;AAAA,IAC1C,KAAK,OAAA;AACH,MAAA,OAAO;AAAA,QACL,GAAG,IAAA;AAAA,QACH,UAAA,EAAY,SAAA;AAAA,QACZ,SAAA,EAAW,iBAAiB,KAAK,CAAA,sBAAA;AAAA,OACnC;AAAA,IACF,KAAK,MAAA;AACH,MAAA,OAAO;AAAA,QACL,GAAG,IAAA;AAAA,QACH,UAAA,EAAY,SAAA;AAAA,QACZ,SAAA,EAAW,gBAAgB,KAAK,CAAA,sBAAA;AAAA,OAClC;AAAA,IACF,KAAK,UAAA;AACH,MAAA,OAAO;AAAA,QACL,GAAG,IAAA;AAAA,QACH,iBAAiB,CAAA,uBAAA,EAA0B,SAAS,CAAA,EAAA,EAAK,cAAc,KAAK,SAAS,CAAA,CAAA,CAAA;AAAA,QACrF,cAAA,EAAgB,WAAA;AAAA,QAChB,SAAA,EAAW,CAAA,iBAAA,EAAoB,KAAA,GAAQ,GAAG,CAAA,sBAAA;AAAA,OAC5C;AAAA;AAEN;AAMA,IAAM,UAAA,GAMD,CAAC,EAAE,IAAA,EAAM,gBAAgB,KAAA,EAAO,cAAA,EAAgB,SAAQ,KAAM;AACjE,EAAA,MAAM,UAAU,OAAA,KAAY,OAAA;AAC5B,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO;AAAA,QACL,QAAA,EAAU,UAAA;AAAA,QACV,GAAA,EAAK,CAAA;AAAA,QACL,IAAA,EAAM,CAAC,IAAA,CAAK,CAAA;AAAA,QACZ,KAAA,EAAO,cAAA,GAAiB,CAAA,GAAI,cAAA,GAAiB,OAAA;AAAA,QAC7C,MAAA,EAAQ,MAAA;AAAA,QACR,YAAY,OAAA,GACR,CAAA,yCAAA,EAA4C,cAAc,CAAA,sBAAA,CAAA,GAC1D,0CAA0C,cAAc,CAAA,uBAAA,CAAA;AAAA,QAC5D,WAAW,CAAA,EAAG,OAAA,GAAU,eAAA,GAAkB,cAAc,IAAI,KAAK,CAAA,sBAAA;AAAA;AACnE;AAAA,GACF;AAEJ,CAAA;AASO,IAAM,iBAAgD,CAAC;AAAA,EAC5D,KAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF,CAAA,KAAM;AACJ,EAAA,MAAM,UAAA,GAAaA,MAAA,CAAM,MAAA,CAAuB,IAAI,CAAA;AACpD,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIA,MAAA,CAAM,SAAS,CAAC,CAAA;AAE5D,EAAAA,MAAA,CAAM,gBAAgB,MAAM;AAC1B,IAAA,IAAI,CAAC,UAAA,CAAW,OAAA,EAAS,aAAA,EAAe;AACxC,IAAA,iBAAA,CAAkB,UAAA,CAAW,OAAA,CAAQ,aAAA,CAAc,WAAW,CAAA;AAAA,EAChE,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAE/B,EAAA,MAAM,OAAA,GAAU,SAAA,KAAc,MAAA,IAAU,SAAA,KAAc,OAAA;AAEtD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,UAAA;AAAA,MACL,IAAA,EAAK,QAAA;AAAA,MACL,WAAA,EAAU,MAAA;AAAA,MACV,YAAA,EAAW,iBAAA;AAAA,MACX,qBAAA,EAAoB,MAAA;AAAA,MACpB,KAAA,EAAO;AAAA,QACL,QAAA,EAAU,UAAA;AAAA,QACV,GAAA,EAAK,CAAA;AAAA,QACL,IAAA,EAAM,CAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,MAAA,EAAQ,MAAA;AAAA,QACR,MAAA,EAAQ,CAAA;AAAA,QACR,aAAA,EAAe,MAAA;AAAA,QACf,UAAA,EAAY;AAAA,OACd;AAAA,MAEC,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,qBAChB,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UAEC,OAAO,cAAA,CAAe,SAAA,EAAW,SAAA,EAAW,cAAA,EAAgB,OAAO,IAAI,CAAA;AAAA,UAEtE,QAAA,EAAA,OAAA,oBACC,GAAA;AAAA,YAAC,UAAA;AAAA,YAAA;AAAA,cACC,IAAA;AAAA,cACA,cAAA;AAAA,cACA,KAAA;AAAA,cACA,cAAA;AAAA,cACA,OAAA,EAAS;AAAA;AAAA;AACX,SAAA;AAAA,QAVG;AAAA,OAaR;AAAA;AAAA,GACH;AAEJ,CAAA;;;AChJO,SAAS,kBAAA,CAAmB,SAAiB,SAAA,EAAmB;AACrE,EAAA,OAAO,MAAA;AACT;AAMO,IAAM,mBAAA,GAAyE;AAAA,EACpF,KAAA,EAAO,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAChC,MAAA,EAAQ,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EACjC,QAAA,EAAU,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EACnC,MAAA,EAAQ,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EACjC,GAAA,EAAK,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,GAAA,EAAI;AAAA,EAC/B,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC7B,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC7B,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC7B,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC7B,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC7B,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC7B,CAAA,EAAG,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA,EAAG;AAAA,EAC5B,IAAA,EAAM,EAAE,KAAA,EAAO,GAAA,EAAK,QAAQ,EAAA;AAC9B,CAAA;;;ACpBA,IAAM,cAAA,uBAAqB,GAAA,CAAI;AAAA;AAAA,EAE7B,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,GAAA;AAAA,EAAK,MAAA;AAAA,EAAQ,GAAA;AAAA,EAAK,IAAA;AAAA,EACtD,OAAA;AAAA,EAAS,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,YAAA;AAAA,EAAc,MAAA;AAAA,EAAQ,KAAA;AAAA;AAAA,EAE3C,KAAA;AAAA,EAAO,OAAA;AAAA,EAAS,KAAA;AAAA,EAAO,QAAA;AAAA,EAAU,SAAA;AAAA;AAAA,EAEjC,OAAA;AAAA,EAAS,UAAA;AAAA,EAAY,QAAA;AAAA,EAAU,QAAA;AAAA;AAAA,EAE/B;AACF,CAAC,CAAA;AAMD,SAAS,YAAY,EAAA,EAAsB;AACzC,EAAA,IAAI,EAAA,CAAG,YAAA,CAAa,qBAAqB,CAAA,EAAG,OAAO,KAAA;AACnD,EAAA,IAAI,EAAA,CAAG,YAAA,CAAa,cAAc,CAAA,EAAG,OAAO,IAAA;AAC5C,EAAA,IAAI,cAAA,CAAe,GAAA,CAAI,EAAA,CAAG,OAAO,GAAG,OAAO,IAAA;AAG3C,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG;AAC5B,IAAA,MAAM,IAAA,GAAO,GAAG,qBAAA,EAAsB;AACtC,IAAA,OAAO,IAAA,CAAK,KAAA,GAAQ,CAAA,IAAK,IAAA,CAAK,MAAA,GAAS,CAAA;AAAA,EACzC;AAEA,EAAA,OAAO,KAAA;AACT;AAaA,SAAS,iBAAiB,EAAA,EAAsB;AAC9C,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,gBAAA,CAAiB,EAAE,CAAA,CAAE,QAAA;AACxC,EAAA,OAAO,GAAA,KAAQ,WAAW,GAAA,KAAQ,QAAA;AACpC;AAQA,IAAM,gBAAA,uBAAuB,OAAA,EAAiB;AAe9C,SAAS,yBAAyB,IAAA,EAA8B;AAC9D,EAAA,MAAM,SAAoB,EAAC;AAC3B,EAAA,IAAI,YAAA,GAAe,CAAA;AAEnB,EAAA,SAAS,KAAK,EAAA,EAAa;AACzB,IAAA,IAAI,EAAA,CAAG,YAAA,CAAa,qBAAqB,CAAA,EAAG;AAC5C,IAAA,IAAI,EAAA,CAAG,YAAA,CAAa,uBAAuB,CAAA,EAAG;AAK9C,IAAA,IAAI,CAAC,EAAA,CAAG,YAAA,CAAa,cAAc,CAAA,IAAK,gBAAA,CAAiB,EAAE,CAAA,EAAG;AAC5D,MAAA,YAAA,IAAgB,CAAA;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,WAAA,CAAY,EAAE,CAAA,EAAG;AACnB,MAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AACd,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AAC3C,MAAA,IAAA,CAAK,EAAA,CAAG,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,IACrB;AAAA,EACF;AAEA,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AAC7C,IAAA,IAAA,CAAK,IAAA,CAAK,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,EACvB;AAEA,EAAA,OAAO,EAAE,QAAQ,YAAA,EAAa;AAChC;AAKA,SAAS,cAAA,CACP,EAAA,EACA,aAAA,EACA,kBAAA,EACoB;AACpB,EAAA,MAAM,MAAA,GAAS,GAAG,qBAAA,EAAsB;AAIxC,EAAA,IAAI,MAAA,CAAO,KAAA,KAAU,CAAA,IAAK,MAAA,CAAO,MAAA,KAAW,CAAA,IAAK,MAAA,CAAO,IAAA,KAAS,CAAA,IAAK,MAAA,CAAO,GAAA,KAAQ,CAAA,EAAG;AACtF,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,gBAAA,CAAiB,EAAE,CAAA;AAChD,EAAA,IAAI,YAAA,GAAe,sBAAsB,aAAA,CAAc,YAAA;AAIvD,EAAA,MAAM,SAAS,CAAC,YAAA,IAAgB,YAAA,KAAiB,MAAA,IAAU,aAAa,KAAA,CAAM,GAAG,CAAA,CAAE,KAAA,CAAM,OAAK,CAAA,KAAM,GAAA,IAAO,CAAA,KAAM,KAAA,IAAS,MAAM,IAAI,CAAA;AACpI,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,YAAA,GAAe,KAAA;AAAA,EACjB;AAEA,EAAA,IAAI,QAAQ,MAAA,CAAO,KAAA;AACnB,EAAA,IAAI,SAAS,MAAA,CAAO,MAAA;AAGpB,EAAA,IAAI,KAAA,KAAU,CAAA,IAAK,MAAA,KAAW,CAAA,EAAG;AAC/B,IAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,EAAA,CAAG,OAAO,CAAA;AAC/C,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,KAAA,GAAQ,SAAS,QAAA,CAAS,KAAA;AAC1B,MAAA,MAAA,GAAS,UAAU,QAAA,CAAS,MAAA;AAAA,IAC9B;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,CAAA,EAAG,MAAA,CAAO,IAAA,GAAO,aAAA,CAAc,IAAA;AAAA,IAC/B,CAAA,EAAG,MAAA,CAAO,GAAA,GAAM,aAAA,CAAc,GAAA;AAAA,IAC9B,KAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;AASA,SAAS,YAAA,CACP,SAAA,EACA,kBAAA,EACA,SAAA,EACe;AACf,EAAA,MAAM,MAAA,GAAS,WAAW,OAAA,IAAW,SAAA;AACrC,EAAA,MAAM,UAAA,GAAa,OAAO,qBAAA,EAAsB;AAChD,EAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAa,GAAI,yBAAyB,SAAS,CAAA;AAEnE,EAAA,IAAI,eAAe,CAAA,IAAK,CAAC,gBAAA,CAAiB,GAAA,CAAI,SAAS,CAAA,EAAG;AACxD,IAAA,gBAAA,CAAiB,IAAI,SAAS,CAAA;AAE9B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,2BAA2B,YAAY,CAAA,6TAAA;AAAA,KAMzC;AAAA,EACF;AAEA,EAAA,OAAO,MAAA,CACJ,IAAI,CAAC,EAAA,KAAO,eAAe,EAAA,EAAI,UAAA,EAAY,kBAAkB,CAAC,CAAA,CAC9D,OAAO,CAAC,CAAA,KAAwB,MAAM,IAAA,IAAQ,CAAA,CAAE,QAAQ,CAAA,IAAK,CAAA,CAAE,SAAS,CAAC,CAAA;AAC9E;AAWO,SAAS,QAAA,CACd,YAAA,EACA,OAAA,EACA,kBAAA,EACA,SAAA,EACe;AACf,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,CAAwB,EAAE,CAAA;AAEpD,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,IAAA,MAAM,MAAA,GAAS,YAAA,CAAa,YAAA,CAAa,OAAA,EAAS,oBAAoB,SAAS,CAAA;AAC/E,IAAA,QAAA,CAAS,MAAM,CAAA;AAAA,EACjB,CAAA,EAAG,CAAC,YAAA,EAAc,kBAAA,EAAoB,SAAS,CAAC,CAAA;AAEhD,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,YAAA,CAAa,OAAA,EAAS;AACrC,MAAA,QAAA,CAAS,EAAE,CAAA;AACX,MAAA;AAAA,IACF;AAGA,IAAA,KAAA,EAAM;AAGN,IAAA,MAAM,QAAA,GAAW,IAAI,cAAA,CAAe,MAAM;AACxC,MAAA,KAAA,EAAM;AAAA,IACR,CAAC,CAAA;AACD,IAAA,QAAA,CAAS,OAAA,CAAQ,aAAa,OAAO,CAAA;AAErC,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,CAAA,EAAG,CAAC,OAAA,EAAS,KAAK,CAAC,CAAA;AAEnB,EAAA,OAAO,KAAA;AACT;;;ACzOA,IAAM,iBAAA,GAAoB,sBAAA;AAE1B,IAAM,GAAA,GAAM;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAiDL,SAAS,YAAA,GAAqB;AACnC,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,iBAAiB,CAAA,EAAG;AAEhD,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,EAAA,GAAK,iBAAA;AACX,EAAA,KAAA,CAAM,WAAA,GAAc,GAAA;AACpB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,KAAK,CAAA;AACjC;ACdO,SAAS,OAAA,CAAQ;AAAA,EACvB,OAAA,GAAU,KAAA;AAAA,EACV,QAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,EAAA;AAAA,EACA,eAAA,GAAkB,KAAA;AAAA,EAClB,SAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA;AAAA,EACA,kBAAA;AAAA,EACA,SAAA;AAAA,EACA;AACD,CAAA,EAAiB;AAChB,EAAA,MAAM,gBAAgB,iBAAA,EAAkB;AACxC,EAAA,MAAM,QAAA,GAAW,CAAC,aAAA,IAAiB,eAAA;AACnC,EAAA,MAAM,KAAK,KAAA,EAAM;AAEjB,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACd,OAAO;AAAA,MACN,SAAA,EACC,SAAA,IAAa,aAAA,EAAe,MAAA,CAAO,aAAa,QAAA,CAAS,SAAA;AAAA,MAC1D,SAAA,EACC,SAAA,IAAa,aAAA,EAAe,MAAA,CAAO,aAAa,QAAA,CAAS,SAAA;AAAA,MAC1D,cAAA,EACC,cAAA,IACA,aAAA,EAAe,MAAA,CAAO,kBACtB,QAAA,CAAS,cAAA;AAAA,MACV,KAAA,EAAO,KAAA,IAAS,aAAA,EAAe,MAAA,CAAO,SAAS,QAAA,CAAS,KAAA;AAAA,MACxD,YAAA,EACC,YAAA,IACA,aAAA,EAAe,MAAA,CAAO,gBACtB,QAAA,CAAS,YAAA;AAAA,MACV,kBAAA,EACC,kBAAA,IACA,aAAA,EAAe,MAAA,CAAO,sBACtB,QAAA,CAAS;AAAA,KACX,CAAA;AAAA,IACA;AAAA,MACC,SAAA;AAAA,MACA,SAAA;AAAA,MACA,cAAA;AAAA,MACA,KAAA;AAAA,MACA,YAAA;AAAA,MACA,kBAAA;AAAA,MACA,aAAA,EAAe;AAAA;AAChB,GACD;AAEA,EAAA,IAAI,QAAA,EAAU;AACb,IAAA,uBACCC,GAAAA;AAAA,MAAC,aAAA;AAAA,MAAA;AAAA,QACA,EAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAA;AAAA,QACA,WAAA;AAAA,QACA,SAAA;AAAA,QACA,EAAA;AAAA,QACA,SAAA;AAAA,QACA,KAAA;AAAA,QAEC;AAAA;AAAA,KACF;AAAA,EAEF;AAEA,EAAA,uBACCA,GAAAA;AAAA,IAAC,eAAA;AAAA,IAAA;AAAA,MACA,EAAA;AAAA,MACA,aAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,SAAA;AAAA,MACA,EAAA;AAAA,MAEC;AAAA;AAAA,GACF;AAEF;AAgBA,SAAS,aAAA,CAAc;AAAA,EACtB,EAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,EAAA;AAAA,EACA,SAAA;AAAA,EACA;AACD,CAAA,EAAuB;AACtB,EAAA,MAAM,YAAA,GAAe,OAAuB,IAAI,CAAA;AAEhD,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAIC,QAAAA,CAExC,EAAE,CAAA;AAEJ,EAAA,MAAM,QAAA,GAAWC,WAAAA,CAAY,CAAC,GAAA,EAAa,KAAA,KAAyB;AACnE,IAAA,gBAAA,CAAiB,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,GAAG,GAAG,KAAA,EAAM,CAAE,CAAA;AAAA,EACvD,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAaA,WAAAA,CAAY,CAAC,GAAA,KAAgB;AAC/C,IAAA,gBAAA,CAAiB,CAAC,IAAA,KAAS;AAC1B,MAAA,MAAM,IAAA,GAAO,EAAE,GAAG,IAAA,EAAK;AACvB,MAAA,OAAO,KAAK,GAAG,CAAA;AACf,MAAA,OAAO,IAAA;AAAA,IACR,CAAC,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,kBAAA,CAAmB,MAAM;AACxB,IAAA,YAAA,EAAa;AAAA,EACd,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,WAAA,GAAc,QAAA;AAAA,IACnB,YAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAO,YAAA,IAAgB;AAAA,GACxB;AAEA,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAM;AAC9B,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,MAAA,CAAO,aAAa,EAAE,IAAA,EAAK;AACnD,IAAA,OAAO,CAAC,GAAG,WAAA,EAAa,GAAG,QAAQ,CAAA;AAAA,EACpC,CAAA,EAAG,CAAC,WAAA,EAAa,aAAa,CAAC,CAAA;AAE/B,EAAA,MAAM,mBAAmB,mBAAA,CAAoB;AAAA,IAC5C,OAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA;AAAA,IACA,EAAA;AAAA,IACA;AAAA,GACA,CAAA;AAED,EAAA,MAAM,YAAA,GAAe,OAAA;AAAA,IACpB,OAAO;AAAA,MACN,QAAA;AAAA,MACA,UAAA;AAAA,MACA,SAAA,EAAW,YAAA;AAAA,MACX,OAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,IACA,CAAC,QAAA,EAAU,UAAA,EAAY,OAAA,EAAS,MAAM;AAAA,GACvC;AAEA,EAAA,uBACCF,GAAAA,CAAC,cAAA,CAAe,QAAA,EAAf,EAAwB,OAAO,YAAA,EAC/B,QAAA,kBAAA,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,GAAA,EAAK,YAAA;AAAA,MACL,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACN,QAAA,EAAU,UAAA;AAAA,QACV,UAAA,EACC,OAAA,IAAW,CAAC,MAAA,CAAO,qBAAqB,QAAA,GAAW,MAAA;AAAA,QACpD,GAAG;AAAA,OACJ;AAAA,MACA,eAAa,OAAA,IAAW,MAAA;AAAA,MACxB,qBAAA,EAAmB,IAAA;AAAA,MACnB,0BAAA,EACC,OAAA,IAAW,MAAA,CAAO,kBAAA,GAAqB,MAAA,GAAS,MAAA;AAAA,MAGhD,QAAA,EAAA;AAAA,QAAA,gBAAA;AAAA,QAEA,2BACAA,GAAAA;AAAA,UAAC,cAAA;AAAA,UAAA;AAAA,YACA,KAAA,EAAO,QAAA;AAAA,YACP,WAAW,MAAA,CAAO,SAAA;AAAA,YAClB,WAAW,MAAA,CAAO,SAAA;AAAA,YAClB,gBAAgB,MAAA,CAAO,cAAA;AAAA,YACvB,OAAO,MAAA,CAAO;AAAA;AAAA;AACf;AAAA;AAAA,GAEF,EACD,CAAA;AAEF;AAcA,SAAS,eAAA,CAAgB;AAAA,EACxB,EAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA;AACD,CAAA,EAAyB;AACxB,EAAA,MAAM,YAAA,GAAe,OAAuB,IAAI,CAAA;AAEhD,EAAA,MAAM,WAAA,GAAc,QAAA;AAAA,IACnB,YAAA;AAAA,IACA,aAAA,CAAc,OAAA;AAAA,IACd,OAAO,YAAA,IAAgB,MAAA;AAAA,IACvB,aAAA,CAAc;AAAA,GACf;AAEA,EAAAD,MAAAA,CAAM,gBAAgB,MAAM;AAC3B,IAAA,IAAI,CAAC,aAAA,CAAc,OAAA,IAAW,WAAA,CAAY,WAAW,CAAA,EAAG;AACvD,MAAA,aAAA,CAAc,WAAW,EAAE,CAAA;AAC3B,MAAA;AAAA,IACD;AACA,IAAA,aAAA,CAAc,QAAA,CAAS,IAAI,WAAW,CAAA;AACtC,IAAA,OAAO,MAAM;AACZ,MAAA,aAAA,CAAc,WAAW,EAAE,CAAA;AAAA,IAC5B,CAAA;AAAA,EACD,CAAA,EAAG,CAAC,WAAA,EAAa,aAAA,EAAe,EAAE,CAAC,CAAA;AAEnC,EAAA,MAAM,mBAAmB,mBAAA,CAAoB;AAAA,IAC5C,SAAS,aAAA,CAAc,OAAA;AAAA,IACvB,QAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA;AAAA,IACA,EAAA;AAAA,IACA;AAAA,GACA,CAAA;AAED,EAAA,uBACCC,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,GAAA,EAAK,YAAA;AAAA,MACL,uBAAA,EAAqB,IAAA;AAAA,MACrB,KAAA,EAAO,EAAE,OAAA,EAAS,UAAA,EAAW;AAAA,MAE5B,QAAA,EAAA;AAAA;AAAA,GACF;AAEF;AA0BA,SAAS,mBAAA,CAAoB;AAAA,EAC5B,OAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,EAAA;AAAA,EACA;AACD,CAAA,EAA+C;AAC9C,EAAA,IAAI,CAAC,SAAS,OAAO,QAAA;AAErB,EAAA,IAAI,EAAA,EAAI;AACP,IAAA,MAAM,KAAA,GAAQ,WAAA,IAAe,WAAA,GAAc,CAAA,GAAI,WAAA,GAAc,CAAA;AAC7D,IAAA,MAAM,SAAA,GAAY,EAAA;AAClB,IAAA,OAAO,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,OAAM,EAAG,CAAC,GAAG,CAAA,qBACxC,aAAA;AAAA,MAAC,SAAA;AAAA,MAAA;AAAA,QACC,GAAI,aAAa,EAAC;AAAA,QACnB,KAAK,kBAAA,CAAmB,CAAA,EAAG,EAAE,CAAA,IAAA,EAAO,CAAC,CAAA,CAAE;AAAA;AAAA,KAExC,CAAA;AAAA,EACF;AAEA,EAAA,MAAM,UAAA,GAAaD,MAAAA,CAAM,QAAA,CAAS,OAAA,CAAQ,QAAQ,CAAA;AAElD,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AAC1C,IAAA,IAAI,CAACA,MAAAA,CAAM,cAAA,CAAe,CAAC,GAAG,OAAO,CAAA;AACrC,IAAA,MAAM,MAAM,kBAAA,CAAmB,CAAA,EAAG,EAAE,CAAA,KAAA,EAAQ,CAAC,CAAA,CAAE,CAAA;AAC/C,IAAA,MAAM,KAAA,GAAQ,YAAY,EAAE,GAAG,WAAW,GAAA,EAAI,GAAI,EAAE,GAAA,EAAI;AACxD,IAAA,OAAOA,MAAAA,CAAM,YAAA,CAAa,CAAA,EAAyB,KAAY,CAAA;AAAA,EAChE,CAAC,CAAA;AAED,EAAA,IAAI,WAAA,IAAe,cAAc,CAAA,EAAG;AACnC,IAAA,MAAM,KAAA,GAAQ,UAAU,IAAA,CAAK,CAAC,MAAMA,MAAAA,CAAM,cAAA,CAAe,CAAC,CAAC,CAAA;AAG3D,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,OAAO,KAAA,CAAM,IAAA;AAAA,MAAK,EAAE,QAAQ,WAAA,EAAY;AAAA,MAAG,CAAC,CAAA,EAAG,CAAA,KAC9CA,MAAAA,CAAM,aAAa,KAAA,EAAO;AAAA,QACzB,KAAK,kBAAA,CAAmB,CAAA,EAAG,EAAE,CAAA,OAAA,EAAU,CAAC,CAAA,CAAE;AAAA,OACnC;AAAA,KACT;AAAA,EACD;AAEA,EAAA,OAAO,SAAA;AACR;ACnVO,SAAS,aAAA,CAAc,MAAA,GAAwB,EAAC,EAAG;AACxD,EAAA,MAAM,YAAA,GAAwC;AAAA,IAC5C,SAAA,EAAW,MAAA,CAAO,SAAA,IAAa,QAAA,CAAS,SAAA;AAAA,IACxC,SAAA,EAAW,MAAA,CAAO,SAAA,IAAa,QAAA,CAAS,SAAA;AAAA,IACxC,cAAA,EAAgB,MAAA,CAAO,cAAA,IAAkB,QAAA,CAAS,cAAA;AAAA,IAClD,KAAA,EAAO,MAAA,CAAO,KAAA,IAAS,QAAA,CAAS,KAAA;AAAA,IAChC,YAAA,EAAc,MAAA,CAAO,YAAA,IAAgB,QAAA,CAAS,YAAA;AAAA,IAC9C,kBAAA,EAAoB,MAAA,CAAO,kBAAA,IAAsB,QAAA,CAAS;AAAA,GAC5D;AAEA,EAAA,SAAS,kBAAkB,KAAA,EAAyE;AAClG,IAAA,uBACEC,GAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACE,GAAG,YAAA;AAAA,QACH,GAAG;AAAA;AAAA,KACN;AAAA,EAEJ;AAGA,EAAA,OAAO,iBAAA;AACT;ACGO,SAAS,eAAA,CAAgB;AAAA,EAC9B,QAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAyB;AACvB,EAAA,MAAM,eAAA,GACJ,QAAA,KAAa,MAAA,GACX,QAAA,mBAEAA,GAAAA,CAAC,mBAAA,CAAoB,QAAA,EAApB,EAA6B,KAAA,EAAO,IAAA,EAClC,QAAA,EACH,CAAA;AAGJ,EAAA,uBACEA,GAAAA;AAAA,IAACD,MAAAA,CAAM,QAAA;AAAA,IAAN;AAAA,MACC,QAAA,kBACEC,GAAAA,CAAC,OAAA,EAAA,EAAQ,SAAS,IAAA,EAAO,GAAG,eACzB,QAAA,EAAA,eAAA,EACH,CAAA;AAAA,MAGD;AAAA;AAAA,GACH;AAEJ","file":"index.mjs","sourcesContent":["import React, { ReactNode } from 'react';\r\n\r\n/**\r\n * Represents a measured rectangle of a traced DOM element,\r\n * positioned relative to the Master Shimmer container.\r\n */\r\nexport interface ShimmerRect {\r\n x: number;\r\n y: number;\r\n width: number;\r\n height: number;\r\n borderRadius: string;\r\n}\r\n\r\n/** Available animation types for the shimmer effect. */\r\nexport type AnimationType =\r\n | 'wave'\r\n | 'pulse'\r\n | 'shine'\r\n | 'glow'\r\n | 'gradient';\r\n\r\n/** Configuration options for the shimmer effect (all optional). */\r\nexport interface ShimmerConfig {\r\n /** Animation style. Defaults to 'wave'. */\r\n animation?: AnimationType;\r\n /** Base color of the shimmer blocks. Defaults to '#e0e0e0'. */\r\n baseColor?: string;\r\n /** Highlight color of the shimmer animation. Defaults to '#f5f5f5'. */\r\n highlightColor?: string;\r\n /** Animation duration in seconds. Defaults to 1.5. */\r\n speed?: number;\r\n /** Global border-radius override. If omitted, auto-detected from each element (defaults to 4px if detection is 0px). */\r\n borderRadius?: string;\r\n /**\r\n * Keep container backgrounds, borders, and padding visible while loading.\r\n * When `true` (default), only text and media leaves are hidden via\r\n * `color:transparent` / `opacity:0` so card backgrounds, borders, and\r\n * spacing remain visible underneath the shimmer overlay.\r\n *\r\n * Set `false` for legacy behavior (`visibility:hidden` on whole tree).\r\n */\r\n preserveBackground?: boolean;\r\n}\r\n\r\n/** Props for the Shimmer component. */\r\nexport interface ShimmerProps extends ShimmerConfig {\r\n /** Whether the loading state is active. */\r\n loading?: boolean;\r\n /** The children to trace and render shimmer over. */\r\n children: ReactNode;\r\n /**\r\n * Number of placeholder clones to generate for list-like loading states.\r\n *\r\n * When `loading=true` and `dummyLength` is set, Shimmer grabs the first\r\n * available child (or a cached template from the last loaded render) and\r\n * clones it `dummyLength` times to produce skeleton placeholders.\r\n *\r\n * When `loading=false`, children are rendered as-is.\r\n */\r\n dummyLength?: number;\r\n /**\r\n * Props injected into each child element while `loading=true` so the\r\n * skeleton renders with realistic shape without requiring real data.\r\n *\r\n * Example:\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyData={{ user: { name: 'Loading...', role: '...', avatar: '' } }}\r\n * >\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * While loading, each direct child is cloned with these props merged on top\r\n * of its own props. Ignored when `loading=false`.\r\n */\r\n dummyData?: Record<string, any>;\r\n /**\r\n * Component used to auto-generate skeleton elements while `loading=true`.\r\n *\r\n * When set, Shimmer ignores `children` during loading and renders\r\n * `dummyLength` (defaults to 1) instances of `<as {...dummyData} />`\r\n * to derive shape. Real children render once `loading=false`.\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * as={MovieCard}\r\n * dummyData={{ movie: movieTemplate }}\r\n * dummyLength={10}\r\n * >\r\n * {movies.map((m) => <MovieCard movie={m} key={m.id} />)}\r\n * </Shimmer>\r\n * ```\r\n */\r\n as?: React.ComponentType<any>;\r\n /** Force this Shimmer to be a Master renderer even if nested inside another Shimmer. */\r\n stopPropagation?: boolean;\r\n /**\r\n * className applied to the Master container div.\r\n * Use to control layout (e.g. display:flex) without losing position:relative.\r\n */\r\n className?: string;\r\n /**\r\n * Inline styles merged into the Master container div.\r\n * position:relative is always applied; everything else is overridable.\r\n */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n/** Default configuration values. */\r\nexport const DEFAULTS: Required<ShimmerConfig> = {\r\n animation: 'wave',\r\n baseColor: '#e0e0e0',\r\n highlightColor: '#f5f5f5',\r\n speed: 1.5,\r\n borderRadius: '',\r\n preserveBackground: true,\r\n};\r\n","\"use client\";\r\n\r\nimport { createContext, useContext, RefObject } from \"react\";\r\nimport { ShimmerRect, ShimmerConfig } from \"./types\";\r\n\r\nexport interface ShimmerContextValue {\r\n\tregister: (id: string, rects: ShimmerRect[]) => void;\r\n\tunregister: (id: string) => void;\r\n\t/** Ref object (not .current) so Reporters always read a fresh value. */\r\n\tmasterRef: RefObject<HTMLElement | null>;\r\n\tloading: boolean;\r\n\tconfig: Required<ShimmerConfig>;\r\n}\r\n\r\nexport const ShimmerContext = createContext<ShimmerContextValue | null>(null);\r\n\r\nexport function useShimmerContext(): ShimmerContextValue | null {\r\n\treturn useContext(ShimmerContext);\r\n}\r\n\r\n/**\r\n * True when rendered inside a ShimmerSuspense fallback (Option B).\r\n * Components use this to skip data fetching and return an empty shape.\r\n */\r\nexport const IsShimmeringContext = createContext<boolean>(false);\r\n\r\nexport function useIsShimmering(): boolean {\r\n\treturn useContext(IsShimmeringContext);\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { ShimmerRect, AnimationType } from './types';\r\n\r\ninterface ShimmerOverlayProps {\r\n rects: ShimmerRect[];\r\n animation: AnimationType;\r\n baseColor: string;\r\n highlightColor: string;\r\n speed: number;\r\n}\r\n\r\n/**\r\n * Returns animation-specific inline styles for each shimmer block.\r\n * For sweep-style animations (wave, shine), the colored gradient is rendered\r\n * as a child layer so the sweep can extend across the container in sync.\r\n */\r\nfunction getBlockStyles(\r\n animation: AnimationType,\r\n baseColor: string,\r\n highlightColor: string,\r\n speed: number,\r\n rect: ShimmerRect,\r\n): React.CSSProperties {\r\n const base: React.CSSProperties = {\r\n position: 'absolute',\r\n top: rect.y,\r\n left: rect.x,\r\n width: rect.width,\r\n height: rect.height,\r\n borderRadius: rect.borderRadius,\r\n overflow: 'hidden',\r\n };\r\n\r\n switch (animation) {\r\n case 'wave':\r\n case 'shine':\r\n return { ...base, background: baseColor };\r\n case 'pulse':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-pulse ${speed}s ease-in-out infinite`,\r\n };\r\n case 'glow':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-glow ${speed}s ease-in-out infinite`,\r\n };\r\n case 'gradient':\r\n return {\r\n ...base,\r\n backgroundImage: `linear-gradient(90deg, ${baseColor}, ${highlightColor}, ${baseColor})`,\r\n backgroundSize: '200% 100%',\r\n animation: `shimmer-gradient ${speed * 1.5}s ease-in-out infinite`,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Sweeping shine layer used by `wave` and `shine` animations.\r\n * Spans the full container width so the highlight sweeps in sync across all blocks.\r\n */\r\nconst SweepLayer: React.FC<{\r\n rect: ShimmerRect;\r\n highlightColor: string;\r\n speed: number;\r\n containerWidth: number;\r\n variant: 'wave' | 'shine';\r\n}> = ({ rect, highlightColor, speed, containerWidth, variant }) => {\r\n const isShine = variant === 'shine';\r\n return (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: -rect.x,\r\n width: containerWidth > 0 ? containerWidth : '100vw',\r\n height: '100%',\r\n background: isShine\r\n ? `linear-gradient(115deg, transparent 30%, ${highlightColor} 50%, transparent 70%)`\r\n : `linear-gradient(90deg, transparent 0%, ${highlightColor} 50%, transparent 100%)`,\r\n animation: `${isShine ? 'shimmer-shine' : 'shimmer-wave'} ${speed}s ease-in-out infinite`,\r\n }}\r\n />\r\n );\r\n};\r\n\r\n/**\r\n * The overlay component rendered by the Master Shimmer.\r\n *\r\n * Renders one absolutely-positioned div per traced rect. Sweep-style\r\n * animations (`wave`, `shine`) get an additional gradient layer that spans\r\n * the container so the highlight passes across all blocks in sync.\r\n */\r\nexport const ShimmerOverlay: React.FC<ShimmerOverlayProps> = ({\r\n rects,\r\n animation,\r\n baseColor,\r\n highlightColor,\r\n speed,\r\n}) => {\r\n const overlayRef = React.useRef<HTMLDivElement>(null);\r\n const [containerWidth, setContainerWidth] = React.useState(0);\r\n\r\n React.useLayoutEffect(() => {\r\n if (!overlayRef.current?.parentElement) return;\r\n setContainerWidth(overlayRef.current.parentElement.offsetWidth);\r\n }, [rects]);\r\n\r\n if (rects.length === 0) return null;\r\n\r\n const isSweep = animation === 'wave' || animation === 'shine';\r\n\r\n return (\r\n <div\r\n ref={overlayRef}\r\n role=\"status\"\r\n aria-busy=\"true\"\r\n aria-label=\"Loading content\"\r\n data-shimmer-ignore=\"true\"\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n zIndex: 1,\r\n pointerEvents: 'none',\r\n visibility: 'visible',\r\n }}\r\n >\r\n {rects.map((rect, i) => (\r\n <div\r\n key={i}\r\n style={getBlockStyles(animation, baseColor, highlightColor, speed, rect)}\r\n >\r\n {isSweep && (\r\n <SweepLayer\r\n rect={rect}\r\n highlightColor={highlightColor}\r\n speed={speed}\r\n containerWidth={containerWidth}\r\n variant={animation as 'wave' | 'shine'}\r\n />\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n};\r\n","/**\r\n * Generates a deterministic key for cloned elements.\r\n *\r\n * Prefix already includes the parent Shimmer's `useId()` plus a positional\r\n * index from the caller, so it is unique per render slot and stable across\r\n * SSR + client hydration. No module-scope counter — that caused hydration\r\n * mismatches and forced React to remount cloned children every render.\r\n */\r\nexport function generateShimmerKey(prefix: string = 'shimmer'): string {\r\n return prefix;\r\n}\r\n\r\n/**\r\n * Default fallback dimensions for common elements when their\r\n * measured dimensions are 0px (e.g., empty inputs, images not yet loaded).\r\n */\r\nexport const FALLBACK_DIMENSIONS: Record<string, { width: number; height: number }> = {\r\n INPUT: { width: 200, height: 36 },\r\n BUTTON: { width: 120, height: 36 },\r\n TEXTAREA: { width: 300, height: 80 },\r\n SELECT: { width: 200, height: 36 },\r\n IMG: { width: 100, height: 100 },\r\n H1: { width: 300, height: 36 },\r\n H2: { width: 260, height: 30 },\r\n H3: { width: 220, height: 26 },\r\n H4: { width: 200, height: 22 },\r\n H5: { width: 180, height: 20 },\r\n H6: { width: 160, height: 18 },\r\n P: { width: 250, height: 16 },\r\n SPAN: { width: 100, height: 16 },\r\n};\r\n","'use client';\r\n\r\nimport { useLayoutEffect, useState, RefObject, useCallback } from 'react';\r\nimport { ShimmerRect } from './types';\r\nimport { FALLBACK_DIMENSIONS } from './utils';\r\n\r\n/**\r\n * Tags that are always considered \"traceable\" leaf elements\r\n * whose dimensions should be captured for the shimmer overlay.\r\n */\r\nconst TRACEABLE_TAGS = new Set([\r\n // Text\r\n 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'SPAN', 'A', 'LI',\r\n 'LABEL', 'TD', 'TH', 'BLOCKQUOTE', 'CODE', 'PRE',\r\n // Media\r\n 'IMG', 'VIDEO', 'SVG', 'CANVAS', 'PICTURE',\r\n // Form\r\n 'INPUT', 'TEXTAREA', 'SELECT', 'BUTTON',\r\n // Misc\r\n 'HR',\r\n]);\r\n\r\n/**\r\n * Determines if an element should be traced.\r\n * Explicit data attributes override automatic detection.\r\n */\r\nfunction isTraceable(el: Element): boolean {\r\n if (el.hasAttribute('data-shimmer-ignore')) return false;\r\n if (el.hasAttribute('data-shimmer')) return true;\r\n if (TRACEABLE_TAGS.has(el.tagName)) return true;\r\n\r\n // Leaf element with visible dimensions → trace it\r\n if (el.children.length === 0) {\r\n const rect = el.getBoundingClientRect();\r\n return rect.width > 0 && rect.height > 0;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Detects elements whose coordinate space cannot follow the Master\r\n * container during scroll. `position: fixed` and `position: sticky`\r\n * both live in a different scroll context (viewport / nearest scroll\r\n * container) than the Master, so any container-relative rect we compute\r\n * for them becomes stale on the first scroll.\r\n *\r\n * Returning `true` here causes the entire subtree under `el` to be\r\n * skipped, since descendants of a fixed/sticky element inherit the same\r\n * broken coordinate space.\r\n */\r\nfunction isViewportLocked(el: Element): boolean {\r\n const pos = window.getComputedStyle(el).position;\r\n return pos === 'fixed' || pos === 'sticky';\r\n}\r\n\r\n/**\r\n * Tracks Master containers we've already warned about, so the\r\n * console.warn fires once per container instead of on every resize-driven\r\n * re-trace. WeakSet keeps the dedupe noise-free across container churn —\r\n * a fresh Master gets one warning, period.\r\n */\r\nconst warnedContainers = new WeakSet<Element>();\r\n\r\ninterface CollectResult {\r\n traced: Element[];\r\n skippedFixed: number;\r\n}\r\n\r\n/**\r\n * Recursively walks the DOM tree and collects all traceable elements.\r\n *\r\n * Also reports how many elements were silently skipped because they used\r\n * `position: fixed` / `position: sticky`. Users can override this skip\r\n * by adding `data-shimmer` to the element, but the resulting overlay\r\n * block will drift on scroll — that's their choice to make.\r\n */\r\nfunction collectTraceableElements(root: Element): CollectResult {\r\n const traced: Element[] = [];\r\n let skippedFixed = 0;\r\n\r\n function walk(el: Element) {\r\n if (el.hasAttribute('data-shimmer-ignore')) return;\r\n if (el.hasAttribute('data-shimmer-reporter')) return; // Ignore nested reporters, they report their own rects\r\n\r\n // `data-shimmer` is an explicit opt-in — trust the user and trace.\r\n // Without it, fixed/sticky elements are skipped entirely (including\r\n // their descendants, which share the broken coordinate space).\r\n if (!el.hasAttribute('data-shimmer') && isViewportLocked(el)) {\r\n skippedFixed += 1;\r\n return;\r\n }\r\n\r\n if (isTraceable(el)) {\r\n traced.push(el);\r\n return; // Don't recurse into traced elements\r\n }\r\n\r\n for (let i = 0; i < el.children.length; i++) {\r\n walk(el.children[i]);\r\n }\r\n }\r\n\r\n for (let i = 0; i < root.children.length; i++) {\r\n walk(root.children[i]);\r\n }\r\n\r\n return { traced, skippedFixed };\r\n}\r\n\r\n/**\r\n * Measures an element and returns its ShimmerRect relative to the container.\r\n */\r\nfunction measureElement(\r\n el: Element,\r\n containerRect: DOMRect,\r\n globalBorderRadius?: string,\r\n): ShimmerRect | null {\r\n const elRect = el.getBoundingClientRect();\r\n\r\n // If the element is display: none or detached, getBoundingClientRect returns all zeros.\r\n // We must not trace it, otherwise it ends up at the left edge of the screen.\r\n if (elRect.width === 0 && elRect.height === 0 && elRect.left === 0 && elRect.top === 0) {\r\n return null;\r\n }\r\n\r\n const computedStyle = window.getComputedStyle(el);\r\n let borderRadius = globalBorderRadius || computedStyle.borderRadius;\r\n\r\n // If the element has no border radius (common for text tags),\r\n // apply a small default to avoid sharp edges in the shimmer.\r\n const isZero = !borderRadius || borderRadius === 'none' || borderRadius.split(' ').every(v => v === '0' || v === '0px' || v === '0%');\r\n if (isZero) {\r\n borderRadius = '4px';\r\n }\r\n\r\n let width = elRect.width;\r\n let height = elRect.height;\r\n\r\n // Apply fallback dimensions if element has 0 size but is actually visible (e.g. empty inline element)\r\n if (width === 0 || height === 0) {\r\n const fallback = FALLBACK_DIMENSIONS[el.tagName];\r\n if (fallback) {\r\n width = width || fallback.width;\r\n height = height || fallback.height;\r\n }\r\n }\r\n\r\n return {\r\n x: elRect.left - containerRect.left,\r\n y: elRect.top - containerRect.top,\r\n width,\r\n height,\r\n borderRadius,\r\n };\r\n}\r\n\r\n/**\r\n * Performs a full trace of all traceable elements within the container.\r\n *\r\n * @param anchorRef - When provided (Reporter mode), elements are measured\r\n * relative to this element instead of the container. Allows Reporter's\r\n * wrapper to use display:contents without breaking measurement.\r\n */\r\nfunction performTrace(\r\n container: HTMLElement,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const anchor = anchorRef?.current ?? container;\r\n const anchorRect = anchor.getBoundingClientRect();\r\n const { traced, skippedFixed } = collectTraceableElements(container);\r\n\r\n if (skippedFixed > 0 && !warnedContainers.has(container)) {\r\n warnedContainers.add(container);\r\n // eslint-disable-next-line no-console\r\n console.warn(\r\n `[shimmer-trace] Skipped ${skippedFixed} element(s) with ` +\r\n `position:fixed or position:sticky. Their coordinate space ` +\r\n `cannot follow the Master container during scroll, so the overlay ` +\r\n `block would drift. Render a nested <Shimmer> inside the fixed/sticky ` +\r\n `element if you need a skeleton there, or add data-shimmer to opt in ` +\r\n `(positioning may still drift on scroll).`,\r\n );\r\n }\r\n\r\n return traced\r\n .map((el) => measureElement(el, anchorRect, globalBorderRadius))\r\n .filter((r): r is ShimmerRect => r !== null && r.width > 0 && r.height > 0);\r\n}\r\n\r\n/**\r\n * Hook that traces all visible leaf DOM elements within a container\r\n * and returns their measured ShimmerRects.\r\n *\r\n * Uses ResizeObserver to re-trace on container resize.\r\n *\r\n * @param anchorRef - When set, rects are relative to this element (Master).\r\n * Used by Reporter so its display:contents wrapper doesn't break measurement.\r\n */\r\nexport function useTrace(\r\n containerRef: RefObject<HTMLElement | null>,\r\n loading: boolean,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const [rects, setRects] = useState<ShimmerRect[]>([]);\r\n\r\n const trace = useCallback(() => {\r\n if (!containerRef.current) return;\r\n const traced = performTrace(containerRef.current, globalBorderRadius, anchorRef);\r\n setRects(traced);\r\n }, [containerRef, globalBorderRadius, anchorRef]);\r\n\r\n useLayoutEffect(() => {\r\n if (!loading || !containerRef.current) {\r\n setRects([]);\r\n return;\r\n }\r\n\r\n // Initial trace\r\n trace();\r\n\r\n // Re-trace on resize\r\n const observer = new ResizeObserver(() => {\r\n trace();\r\n });\r\n observer.observe(containerRef.current);\r\n\r\n return () => observer.disconnect();\r\n }, [loading, trace]);\r\n\r\n return rects;\r\n}\r\n","const SHIMMER_STYLES_ID = 'shimmer-trace-styles';\r\n\r\nconst CSS = `\r\n@keyframes shimmer-wave {\r\n 0% { transform: translateX(-100%); }\r\n 100% { transform: translateX(100%); }\r\n}\r\n\r\n@keyframes shimmer-pulse {\r\n 0%, 100% { opacity: 0.4; }\r\n 50% { opacity: 1; }\r\n}\r\n\r\n@keyframes shimmer-shine {\r\n 0% { transform: translateX(-150%) skewX(-20deg); }\r\n 100% { transform: translateX(150%) skewX(-20deg); }\r\n}\r\n\r\n@keyframes shimmer-glow {\r\n 0%, 100% { filter: brightness(1); }\r\n 50% { filter: brightness(1.35); }\r\n}\r\n\r\n@keyframes shimmer-gradient {\r\n 0% { background-position: 0% 50%; }\r\n 50% { background-position: 100% 50%; }\r\n 100% { background-position: 0% 50%; }\r\n}\r\n\r\n/* preserveBackground mode: hide text + media but keep container styles */\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(h1,h2,h3,h4,h5,h6,p,span,a,li,label,td,th,blockquote,code,pre,strong,em,small) {\r\n color: transparent !important;\r\n text-shadow: none !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(img,video,svg,canvas,picture) {\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(input,textarea,select,button) {\r\n color: transparent !important;\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] {\r\n pointer-events: none !important;\r\n user-select: none !important;\r\n}\r\n`;\r\n\r\n/**\r\n * Injects the shimmer keyframe animations into the document head.\r\n * Safe to call multiple times — only injects once.\r\n */\r\nexport function injectStyles(): void {\r\n if (typeof document === 'undefined') return;\r\n if (document.getElementById(SHIMMER_STYLES_ID)) return;\r\n\r\n const style = document.createElement('style');\r\n style.id = SHIMMER_STYLES_ID;\r\n style.textContent = CSS;\r\n document.head.appendChild(style);\r\n}\r\n","'use client';\r\n\r\nimport React, { useRef, useCallback, useState, useId, useMemo, useInsertionEffect } from \"react\";\r\nimport { ShimmerProps, ShimmerRect, DEFAULTS } from \"./types\";\r\nimport { ShimmerContext, useShimmerContext } from \"./ShimmerContext\";\r\nimport { ShimmerOverlay } from \"./ShimmerOverlay\";\r\nimport { useTrace } from \"./useTrace\";\r\nimport { injectStyles } from \"./styles\";\r\nimport { generateShimmerKey } from \"./utils\";\r\n\r\n/**\r\n * The main Shimmer component.\r\n *\r\n * Auto-detects **Master** (no parent Shimmer) vs **Reporter** (nested).\r\n * - Master: renders children hidden, traces DOM, paints overlay.\r\n * - Reporter: measures own rects, reports to parent Master.\r\n *\r\n * ### Skeleton shape via `dummyData`\r\n *\r\n * Pass `dummyData` so children render with realistic data while loading.\r\n * No render-prop, no manual `data || fallback` in JSX.\r\n *\r\n * ```tsx\r\n * const userTemplate = { name: 'Loading...', role: '...', avatar: '' };\r\n *\r\n * <Shimmer loading={loading} dummyData={{ user: userTemplate }}>\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * ### List mode (`dummyLength`)\r\n *\r\n * Combined with `dummyData`, clones the first child N times with\r\n * template props merged in:\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyLength={5}\r\n * dummyData={{ fruit: { name: 'xxxxx', price: '$0.00' } }}\r\n * >\r\n * <FruitCard fruit={undefined as any} />\r\n * </Shimmer>\r\n * ```\r\n */\r\nexport function Shimmer({\r\n\tloading = false,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tstopPropagation = false,\r\n\tanimation,\r\n\tbaseColor,\r\n\thighlightColor,\r\n\tspeed,\r\n\tborderRadius,\r\n\tpreserveBackground,\r\n\tclassName,\r\n\tstyle,\r\n}: ShimmerProps) {\r\n\tconst parentContext = useShimmerContext();\r\n\tconst isMaster = !parentContext || stopPropagation;\r\n\tconst id = useId();\r\n\r\n\tconst config = useMemo(\r\n\t\t() => ({\r\n\t\t\tanimation:\r\n\t\t\t\tanimation ?? parentContext?.config.animation ?? DEFAULTS.animation,\r\n\t\t\tbaseColor:\r\n\t\t\t\tbaseColor ?? parentContext?.config.baseColor ?? DEFAULTS.baseColor,\r\n\t\t\thighlightColor:\r\n\t\t\t\thighlightColor ??\r\n\t\t\t\tparentContext?.config.highlightColor ??\r\n\t\t\t\tDEFAULTS.highlightColor,\r\n\t\t\tspeed: speed ?? parentContext?.config.speed ?? DEFAULTS.speed,\r\n\t\t\tborderRadius:\r\n\t\t\t\tborderRadius ??\r\n\t\t\t\tparentContext?.config.borderRadius ??\r\n\t\t\t\tDEFAULTS.borderRadius,\r\n\t\t\tpreserveBackground:\r\n\t\t\t\tpreserveBackground ??\r\n\t\t\t\tparentContext?.config.preserveBackground ??\r\n\t\t\t\tDEFAULTS.preserveBackground,\r\n\t\t}),\r\n\t\t[\r\n\t\t\tanimation,\r\n\t\t\tbaseColor,\r\n\t\t\thighlightColor,\r\n\t\t\tspeed,\r\n\t\t\tborderRadius,\r\n\t\t\tpreserveBackground,\r\n\t\t\tparentContext?.config,\r\n\t\t],\r\n\t);\r\n\r\n\tif (isMaster) {\r\n\t\treturn (\r\n\t\t\t<MasterShimmer\r\n\t\t\t\tid={id}\r\n\t\t\t\tloading={loading}\r\n\t\t\t\tconfig={config}\r\n\t\t\t\tdummyLength={dummyLength}\r\n\t\t\t\tdummyData={dummyData}\r\n\t\t\t\tas={as}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={style}\r\n\t\t\t>\r\n\t\t\t\t{children}\r\n\t\t\t</MasterShimmer>\r\n\t\t);\r\n\t}\r\n\r\n\treturn (\r\n\t\t<ReporterShimmer\r\n\t\t\tid={id}\r\n\t\t\tparentContext={parentContext!}\r\n\t\t\tconfig={config}\r\n\t\t\tdummyLength={dummyLength}\r\n\t\t\tdummyData={dummyData}\r\n\t\t\tas={as}\r\n\t\t>\r\n\t\t\t{children}\r\n\t\t</ReporterShimmer>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Master ─────────────────── */\r\n\r\ninterface MasterShimmerProps {\r\n\tid: string;\r\n\tloading: boolean;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\nfunction MasterShimmer({\r\n\tid,\r\n\tloading,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tclassName,\r\n\tstyle,\r\n}: MasterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst [reporterRects, setReporterRects] = useState<\r\n\t\tRecord<string, ShimmerRect[]>\r\n\t>({});\r\n\r\n\tconst register = useCallback((rid: string, rects: ShimmerRect[]) => {\r\n\t\tsetReporterRects((prev) => ({ ...prev, [rid]: rects }));\r\n\t}, []);\r\n\r\n\tconst unregister = useCallback((rid: string) => {\r\n\t\tsetReporterRects((prev) => {\r\n\t\t\tconst next = { ...prev };\r\n\t\t\tdelete next[rid];\r\n\t\t\treturn next;\r\n\t\t});\r\n\t}, []);\r\n\r\n\tuseInsertionEffect(() => {\r\n\t\tinjectStyles();\r\n\t}, []);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tloading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t);\r\n\r\n\tconst allRects = useMemo(() => {\r\n\t\tconst reported = Object.values(reporterRects).flat();\r\n\t\treturn [...tracedRects, ...reported];\r\n\t}, [tracedRects, reporterRects]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\tconst contextValue = useMemo(\r\n\t\t() => ({\r\n\t\t\tregister,\r\n\t\t\tunregister,\r\n\t\t\tmasterRef: containerRef,\r\n\t\t\tloading,\r\n\t\t\tconfig,\r\n\t\t}),\r\n\t\t[register, unregister, loading, config],\r\n\t);\r\n\r\n\treturn (\r\n\t\t<ShimmerContext.Provider value={contextValue}>\r\n\t\t\t<div\r\n\t\t\t\tref={containerRef}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={{\r\n\t\t\t\t\tposition: \"relative\",\r\n\t\t\t\t\tvisibility:\r\n\t\t\t\t\t\tloading && !config.preserveBackground ? \"hidden\" : undefined,\r\n\t\t\t\t\t...style,\r\n\t\t\t\t}}\r\n\t\t\t\taria-hidden={loading || undefined}\r\n\t\t\t\tdata-shimmer-master\r\n\t\t\t\tdata-shimmer-preserve-bg={\r\n\t\t\t\t\tloading && config.preserveBackground ? \"true\" : undefined\r\n\t\t\t\t}\r\n\t\t\t>\r\n\t\t\t\t{renderedChildren}\r\n\r\n\t\t\t\t{loading && (\r\n\t\t\t\t\t<ShimmerOverlay\r\n\t\t\t\t\t\trects={allRects}\r\n\t\t\t\t\t\tanimation={config.animation}\r\n\t\t\t\t\t\tbaseColor={config.baseColor}\r\n\t\t\t\t\t\thighlightColor={config.highlightColor}\r\n\t\t\t\t\t\tspeed={config.speed}\r\n\t\t\t\t\t/>\r\n\t\t\t\t)}\r\n\t\t\t</div>\r\n\t\t</ShimmerContext.Provider>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Reporter ─────────────────── */\r\n\r\ninterface ReporterShimmerProps {\r\n\tid: string;\r\n\tparentContext: NonNullable<ReturnType<typeof useShimmerContext>>;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n}\r\n\r\nfunction ReporterShimmer({\r\n\tid,\r\n\tparentContext,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n}: ReporterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tparentContext.loading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t\tparentContext.masterRef,\r\n\t);\r\n\r\n\tReact.useLayoutEffect(() => {\r\n\t\tif (!parentContext.loading || tracedRects.length === 0) {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tparentContext.register(id, tracedRects);\r\n\t\treturn () => {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t};\r\n\t}, [tracedRects, parentContext, id]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading: parentContext.loading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\treturn (\r\n\t\t<div\r\n\t\t\tref={containerRef}\r\n\t\t\tdata-shimmer-reporter\r\n\t\t\tstyle={{ display: \"contents\" }}\r\n\t\t>\r\n\t\t\t{renderedChildren}\r\n\t\t</div>\r\n\t);\r\n}\r\n\r\n/* ─────────────── Skeleton Children ─────────────── */\r\n\r\ninterface UseSkeletonChildrenParams {\r\n\tloading: boolean;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tid: string;\r\n}\r\n\r\n/**\r\n * Build the rendered children tree.\r\n *\r\n * Priority during `loading=true`:\r\n * 1. `as` set → render `dummyLength` (or 1) instances of `<as {...dummyData} />`.\r\n * Children ignored. Cold-start safe.\r\n * 2. `dummyData` set + children present → clone each child merging\r\n * dummyData over its props. If `dummyLength` set, clone first\r\n * templated child N times.\r\n * 3. None → pass children through (e.g. `useIsShimmering` flow).\r\n *\r\n * `loading=false` → children untouched.\r\n */\r\nfunction useSkeletonChildren({\r\n\tloading,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tid,\r\n}: UseSkeletonChildrenParams): React.ReactNode {\r\n\tif (!loading) return children;\r\n\r\n\tif (as) {\r\n\t\tconst count = dummyLength && dummyLength > 0 ? dummyLength : 1;\r\n\t\tconst Component = as;\r\n\t\treturn Array.from({ length: count }, (_, i) => (\r\n\t\t\t<Component\r\n\t\t\t\t{...(dummyData || {})}\r\n\t\t\t\tkey={generateShimmerKey(`${id}-as-${i}`)}\r\n\t\t\t/>\r\n\t\t));\r\n\t}\r\n\r\n\tconst childArray = React.Children.toArray(children);\r\n\r\n\tconst templated = childArray.map((c, i) => {\r\n\t\tif (!React.isValidElement(c)) return c;\r\n\t\tconst key = generateShimmerKey(`${id}-tpl-${i}`);\r\n\t\tconst props = dummyData ? { ...dummyData, key } : { key };\r\n\t\treturn React.cloneElement(c as React.ReactElement, props as any);\r\n\t});\r\n\r\n\tif (dummyLength && dummyLength > 0) {\r\n\t\tconst first = templated.find((c) => React.isValidElement(c)) as\r\n\t\t\t| React.ReactElement\r\n\t\t\t| undefined;\r\n\t\tif (!first) return null;\r\n\t\treturn Array.from({ length: dummyLength }, (_, i) =>\r\n\t\t\tReact.cloneElement(first, {\r\n\t\t\t\tkey: generateShimmerKey(`${id}-clone-${i}`),\r\n\t\t\t} as any),\r\n\t\t);\r\n\t}\r\n\r\n\treturn templated;\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { ShimmerConfig, ShimmerProps, DEFAULTS } from './types';\r\n\r\n/**\r\n * Factory function to create a pre-configured Shimmer component.\r\n * Avoids \"Provider Hell\" by baking config into the returned component.\r\n *\r\n * All config properties are optional — defaults are used for anything\r\n * not specified.\r\n *\r\n * @example\r\n * ```tsx\r\n * const AppShimmer = createShimmer({\r\n * animation: 'pulse',\r\n * baseColor: '#1a1a2e',\r\n * highlightColor: '#16213e',\r\n * speed: 2,\r\n * });\r\n *\r\n * <AppShimmer loading={isLoading}>\r\n * <MyComponent />\r\n * </AppShimmer>\r\n * ```\r\n */\r\nexport function createShimmer(config: ShimmerConfig = {}) {\r\n const mergedConfig: Required<ShimmerConfig> = {\r\n animation: config.animation ?? DEFAULTS.animation,\r\n baseColor: config.baseColor ?? DEFAULTS.baseColor,\r\n highlightColor: config.highlightColor ?? DEFAULTS.highlightColor,\r\n speed: config.speed ?? DEFAULTS.speed,\r\n borderRadius: config.borderRadius ?? DEFAULTS.borderRadius,\r\n preserveBackground: config.preserveBackground ?? DEFAULTS.preserveBackground,\r\n };\r\n\r\n function ConfiguredShimmer(props: Omit<ShimmerProps, keyof ShimmerConfig> & Partial<ShimmerConfig>) {\r\n return (\r\n <Shimmer\r\n {...mergedConfig}\r\n {...props}\r\n />\r\n );\r\n }\r\n\r\n // Removed ConfiguredShimmer.displayName\r\n return ConfiguredShimmer;\r\n}\r\n","'use client';\r\n\r\nimport React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { IsShimmeringContext } from './ShimmerContext';\r\nimport { ShimmerConfig } from './types';\r\n\r\nexport interface ShimmerSuspenseProps extends ShimmerConfig {\r\n children: React.ReactNode;\r\n /**\r\n * Explicit skeleton template. Rendered hidden and traced for shimmer shape.\r\n *\r\n * Preferred — pass the same component with no data props:\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * If omitted, falls back to Option B: children are re-rendered with\r\n * `useIsShimmering()=true` so they can return an empty shape themselves.\r\n */\r\n template?: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Suspense boundary that automatically shows a shimmer skeleton while\r\n * children are suspended (e.g. useSuspenseQuery, use(promise), etc).\r\n *\r\n * **Option A — explicit template (preferred):**\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * **Option B — useIsShimmering hook (no template):**\r\n * ```tsx\r\n * function UserCard() {\r\n * const isShimmering = useIsShimmering();\r\n * const data = isShimmering ? null : useSuspenseQuery(...);\r\n * return <div><h3>{data?.name}</h3></div>;\r\n * }\r\n *\r\n * <ShimmerSuspense>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n * Components must use `useIsShimmering()` to skip data fetching in shimmer mode,\r\n * otherwise they will also suspend inside the fallback (causing an empty skeleton).\r\n */\r\nexport function ShimmerSuspense({\r\n children,\r\n template,\r\n ...shimmerConfig\r\n}: ShimmerSuspenseProps) {\r\n const skeletonContent =\r\n template !== undefined ? (\r\n template\r\n ) : (\r\n <IsShimmeringContext.Provider value={true}>\r\n {children}\r\n </IsShimmeringContext.Provider>\r\n );\r\n\r\n return (\r\n <React.Suspense\r\n fallback={\r\n <Shimmer loading={true} {...shimmerConfig}>\r\n {skeletonContent}\r\n </Shimmer>\r\n }\r\n >\r\n {children}\r\n </React.Suspense>\r\n );\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shimmer-trace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "High-performance React skeleton loaders that automatically trace your UI dimensions. Synchronized animations, zero CLS, and one-line implementation.",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -53,11 +53,12 @@
|
|
|
53
53
|
"react-dom": ">=18.0.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
+
"@types/node": "^25.9.1",
|
|
56
57
|
"@types/react": "^19.1.3",
|
|
57
58
|
"@types/react-dom": "^19.1.3",
|
|
59
|
+
"esbuild": "^0.25.0",
|
|
58
60
|
"react": "^19.1.0",
|
|
59
61
|
"react-dom": "^19.1.0",
|
|
60
|
-
"esbuild": "^0.25.0",
|
|
61
62
|
"tsup": "^8.5.0",
|
|
62
63
|
"typescript": "^6.0.0"
|
|
63
64
|
}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/ShimmerContext.tsx","../src/ShimmerOverlay.tsx","../src/utils.ts","../src/useTrace.ts","../src/styles.ts","../src/Shimmer.tsx","../src/createShimmer.tsx","../src/ShimmerSuspense.tsx"],"names":["createContext","useContext","jsx","React","useState","useCallback","useLayoutEffect","useId","useMemo","useRef","jsxs","createElement"],"mappings":"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","file":"index.js","sourcesContent":["import React, { ReactNode } from 'react';\r\n\r\n/**\r\n * Represents a measured rectangle of a traced DOM element,\r\n * positioned relative to the Master Shimmer container.\r\n */\r\nexport interface ShimmerRect {\r\n x: number;\r\n y: number;\r\n width: number;\r\n height: number;\r\n borderRadius: string;\r\n}\r\n\r\n/** Available animation types for the shimmer effect. */\r\nexport type AnimationType =\r\n | 'wave'\r\n | 'pulse'\r\n | 'shine'\r\n | 'glow'\r\n | 'gradient';\r\n\r\n/** Configuration options for the shimmer effect (all optional). */\r\nexport interface ShimmerConfig {\r\n /** Animation style. Defaults to 'wave'. */\r\n animation?: AnimationType;\r\n /** Base color of the shimmer blocks. Defaults to '#e0e0e0'. */\r\n baseColor?: string;\r\n /** Highlight color of the shimmer animation. Defaults to '#f5f5f5'. */\r\n highlightColor?: string;\r\n /** Animation duration in seconds. Defaults to 1.5. */\r\n speed?: number;\r\n /** Global border-radius override. If omitted, auto-detected from each element (defaults to 4px if detection is 0px). */\r\n borderRadius?: string;\r\n /**\r\n * Keep container backgrounds, borders, and padding visible while loading.\r\n * When `true` (default), only text and media leaves are hidden via\r\n * `color:transparent` / `opacity:0` so card backgrounds, borders, and\r\n * spacing remain visible underneath the shimmer overlay.\r\n *\r\n * Set `false` for legacy behavior (`visibility:hidden` on whole tree).\r\n */\r\n preserveBackground?: boolean;\r\n}\r\n\r\n/** Props for the Shimmer component. */\r\nexport interface ShimmerProps extends ShimmerConfig {\r\n /** Whether the loading state is active. */\r\n loading?: boolean;\r\n /** The children to trace and render shimmer over. */\r\n children: ReactNode;\r\n /**\r\n * Number of placeholder clones to generate for list-like loading states.\r\n *\r\n * When `loading=true` and `dummyLength` is set, Shimmer grabs the first\r\n * available child (or a cached template from the last loaded render) and\r\n * clones it `dummyLength` times to produce skeleton placeholders.\r\n *\r\n * When `loading=false`, children are rendered as-is.\r\n */\r\n dummyLength?: number;\r\n /**\r\n * Props injected into each child element while `loading=true` so the\r\n * skeleton renders with realistic shape without requiring real data.\r\n *\r\n * Example:\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyData={{ user: { name: 'Loading...', role: '...', avatar: '' } }}\r\n * >\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * While loading, each direct child is cloned with these props merged on top\r\n * of its own props. Ignored when `loading=false`.\r\n */\r\n dummyData?: Record<string, any>;\r\n /**\r\n * Component used to auto-generate skeleton elements while `loading=true`.\r\n *\r\n * When set, Shimmer ignores `children` during loading and renders\r\n * `dummyLength` (defaults to 1) instances of `<as {...dummyData} />`\r\n * to derive shape. Real children render once `loading=false`.\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * as={MovieCard}\r\n * dummyData={{ movie: movieTemplate }}\r\n * dummyLength={10}\r\n * >\r\n * {movies.map((m) => <MovieCard movie={m} key={m.id} />)}\r\n * </Shimmer>\r\n * ```\r\n */\r\n as?: React.ComponentType<any>;\r\n /** Force this Shimmer to be a Master renderer even if nested inside another Shimmer. */\r\n stopPropagation?: boolean;\r\n /**\r\n * className applied to the Master container div.\r\n * Use to control layout (e.g. display:flex) without losing position:relative.\r\n */\r\n className?: string;\r\n /**\r\n * Inline styles merged into the Master container div.\r\n * position:relative is always applied; everything else is overridable.\r\n */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n/** Default configuration values. */\r\nexport const DEFAULTS: Required<ShimmerConfig> = {\r\n animation: 'wave',\r\n baseColor: '#e0e0e0',\r\n highlightColor: '#f5f5f5',\r\n speed: 1.5,\r\n borderRadius: '',\r\n preserveBackground: true,\r\n};\r\n","import { createContext, useContext, RefObject } from 'react';\r\nimport { ShimmerRect, ShimmerConfig } from './types';\r\n\r\nexport interface ShimmerContextValue {\r\n register: (id: string, rects: ShimmerRect[]) => void;\r\n unregister: (id: string) => void;\r\n /** Ref object (not .current) so Reporters always read a fresh value. */\r\n masterRef: RefObject<HTMLElement | null>;\r\n loading: boolean;\r\n config: Required<ShimmerConfig>;\r\n}\r\n\r\nexport const ShimmerContext = createContext<ShimmerContextValue | null>(null);\r\n\r\nexport function useShimmerContext(): ShimmerContextValue | null {\r\n return useContext(ShimmerContext);\r\n}\r\n\r\n/**\r\n * True when rendered inside a ShimmerSuspense fallback (Option B).\r\n * Components use this to skip data fetching and return an empty shape.\r\n */\r\nexport const IsShimmeringContext = createContext<boolean>(false);\r\n\r\nexport function useIsShimmering(): boolean {\r\n return useContext(IsShimmeringContext);\r\n}\r\n","import React from 'react';\r\nimport { ShimmerRect, AnimationType } from './types';\r\n\r\ninterface ShimmerOverlayProps {\r\n rects: ShimmerRect[];\r\n animation: AnimationType;\r\n baseColor: string;\r\n highlightColor: string;\r\n speed: number;\r\n}\r\n\r\n/**\r\n * Returns animation-specific inline styles for each shimmer block.\r\n * For sweep-style animations (wave, shine), the colored gradient is rendered\r\n * as a child layer so the sweep can extend across the container in sync.\r\n */\r\nfunction getBlockStyles(\r\n animation: AnimationType,\r\n baseColor: string,\r\n highlightColor: string,\r\n speed: number,\r\n rect: ShimmerRect,\r\n): React.CSSProperties {\r\n const base: React.CSSProperties = {\r\n position: 'absolute',\r\n top: rect.y,\r\n left: rect.x,\r\n width: rect.width,\r\n height: rect.height,\r\n borderRadius: rect.borderRadius,\r\n overflow: 'hidden',\r\n };\r\n\r\n switch (animation) {\r\n case 'wave':\r\n case 'shine':\r\n return { ...base, background: baseColor };\r\n case 'pulse':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-pulse ${speed}s ease-in-out infinite`,\r\n };\r\n case 'glow':\r\n return {\r\n ...base,\r\n background: baseColor,\r\n animation: `shimmer-glow ${speed}s ease-in-out infinite`,\r\n };\r\n case 'gradient':\r\n return {\r\n ...base,\r\n backgroundImage: `linear-gradient(90deg, ${baseColor}, ${highlightColor}, ${baseColor})`,\r\n backgroundSize: '200% 100%',\r\n animation: `shimmer-gradient ${speed * 1.5}s ease-in-out infinite`,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Sweeping shine layer used by `wave` and `shine` animations.\r\n * Spans the full container width so the highlight sweeps in sync across all blocks.\r\n */\r\nconst SweepLayer: React.FC<{\r\n rect: ShimmerRect;\r\n highlightColor: string;\r\n speed: number;\r\n containerWidth: number;\r\n variant: 'wave' | 'shine';\r\n}> = ({ rect, highlightColor, speed, containerWidth, variant }) => {\r\n const isShine = variant === 'shine';\r\n return (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: -rect.x,\r\n width: containerWidth > 0 ? containerWidth : '100vw',\r\n height: '100%',\r\n background: isShine\r\n ? `linear-gradient(115deg, transparent 30%, ${highlightColor} 50%, transparent 70%)`\r\n : `linear-gradient(90deg, transparent 0%, ${highlightColor} 50%, transparent 100%)`,\r\n animation: `${isShine ? 'shimmer-shine' : 'shimmer-wave'} ${speed}s ease-in-out infinite`,\r\n }}\r\n />\r\n );\r\n};\r\n\r\n/**\r\n * The overlay component rendered by the Master Shimmer.\r\n *\r\n * Renders one absolutely-positioned div per traced rect. Sweep-style\r\n * animations (`wave`, `shine`) get an additional gradient layer that spans\r\n * the container so the highlight passes across all blocks in sync.\r\n */\r\nexport const ShimmerOverlay: React.FC<ShimmerOverlayProps> = ({\r\n rects,\r\n animation,\r\n baseColor,\r\n highlightColor,\r\n speed,\r\n}) => {\r\n const overlayRef = React.useRef<HTMLDivElement>(null);\r\n const [containerWidth, setContainerWidth] = React.useState(0);\r\n\r\n React.useLayoutEffect(() => {\r\n if (!overlayRef.current?.parentElement) return;\r\n setContainerWidth(overlayRef.current.parentElement.offsetWidth);\r\n }, [rects]);\r\n\r\n if (rects.length === 0) return null;\r\n\r\n const isSweep = animation === 'wave' || animation === 'shine';\r\n\r\n return (\r\n <div\r\n ref={overlayRef}\r\n role=\"status\"\r\n aria-busy=\"true\"\r\n aria-label=\"Loading content\"\r\n data-shimmer-ignore=\"true\"\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n zIndex: 1,\r\n pointerEvents: 'none',\r\n visibility: 'visible',\r\n }}\r\n >\r\n {rects.map((rect, i) => (\r\n <div\r\n key={i}\r\n style={getBlockStyles(animation, baseColor, highlightColor, speed, rect)}\r\n >\r\n {isSweep && (\r\n <SweepLayer\r\n rect={rect}\r\n highlightColor={highlightColor}\r\n speed={speed}\r\n containerWidth={containerWidth}\r\n variant={animation as 'wave' | 'shine'}\r\n />\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n};\r\n","/**\r\n * Generates a unique key for cloned elements to prevent\r\n * React \"missing key\" warnings during loading state.\r\n */\r\nlet counter = 0;\r\nexport function generateShimmerKey(prefix: string = 'shimmer'): string {\r\n return `${prefix}-clone-${++counter}`;\r\n}\r\n\r\n/**\r\n * Default fallback dimensions for common elements when their\r\n * measured dimensions are 0px (e.g., empty inputs, images not yet loaded).\r\n */\r\nexport const FALLBACK_DIMENSIONS: Record<string, { width: number; height: number }> = {\r\n INPUT: { width: 200, height: 36 },\r\n BUTTON: { width: 120, height: 36 },\r\n TEXTAREA: { width: 300, height: 80 },\r\n SELECT: { width: 200, height: 36 },\r\n IMG: { width: 100, height: 100 },\r\n H1: { width: 300, height: 36 },\r\n H2: { width: 260, height: 30 },\r\n H3: { width: 220, height: 26 },\r\n H4: { width: 200, height: 22 },\r\n H5: { width: 180, height: 20 },\r\n H6: { width: 160, height: 18 },\r\n P: { width: 250, height: 16 },\r\n SPAN: { width: 100, height: 16 },\r\n};\r\n","import { useLayoutEffect, useState, RefObject, useCallback } from 'react';\r\nimport { ShimmerRect } from './types';\r\nimport { FALLBACK_DIMENSIONS } from './utils';\r\n\r\n/**\r\n * Tags that are always considered \"traceable\" leaf elements\r\n * whose dimensions should be captured for the shimmer overlay.\r\n */\r\nconst TRACEABLE_TAGS = new Set([\r\n // Text\r\n 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'SPAN', 'A', 'LI',\r\n 'LABEL', 'TD', 'TH', 'BLOCKQUOTE', 'CODE', 'PRE',\r\n // Media\r\n 'IMG', 'VIDEO', 'SVG', 'CANVAS', 'PICTURE',\r\n // Form\r\n 'INPUT', 'TEXTAREA', 'SELECT', 'BUTTON',\r\n // Misc\r\n 'HR',\r\n]);\r\n\r\n/**\r\n * Determines if an element should be traced.\r\n * Explicit data attributes override automatic detection.\r\n */\r\nfunction isTraceable(el: Element): boolean {\r\n if (el.hasAttribute('data-shimmer-ignore')) return false;\r\n if (el.hasAttribute('data-shimmer')) return true;\r\n if (TRACEABLE_TAGS.has(el.tagName)) return true;\r\n\r\n // Leaf element with visible dimensions → trace it\r\n if (el.children.length === 0) {\r\n const rect = el.getBoundingClientRect();\r\n return rect.width > 0 && rect.height > 0;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Recursively walks the DOM tree and collects all traceable elements.\r\n */\r\nfunction collectTraceableElements(root: Element): Element[] {\r\n const result: Element[] = [];\r\n\r\n function walk(el: Element) {\r\n if (el.hasAttribute('data-shimmer-ignore')) return;\r\n if (el.hasAttribute('data-shimmer-reporter')) return; // Ignore nested reporters, they report their own rects\r\n\r\n if (isTraceable(el)) {\r\n result.push(el);\r\n return; // Don't recurse into traced elements\r\n }\r\n\r\n for (let i = 0; i < el.children.length; i++) {\r\n walk(el.children[i]);\r\n }\r\n }\r\n\r\n for (let i = 0; i < root.children.length; i++) {\r\n walk(root.children[i]);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Measures an element and returns its ShimmerRect relative to the container.\r\n */\r\nfunction measureElement(\r\n el: Element,\r\n containerRect: DOMRect,\r\n globalBorderRadius?: string,\r\n): ShimmerRect | null {\r\n const elRect = el.getBoundingClientRect();\r\n\r\n // If the element is display: none or detached, getBoundingClientRect returns all zeros.\r\n // We must not trace it, otherwise it ends up at the left edge of the screen.\r\n if (elRect.width === 0 && elRect.height === 0 && elRect.left === 0 && elRect.top === 0) {\r\n return null;\r\n }\r\n\r\n const computedStyle = window.getComputedStyle(el);\r\n let borderRadius = globalBorderRadius || computedStyle.borderRadius;\r\n\r\n // If the element has no border radius (common for text tags),\r\n // apply a small default to avoid sharp edges in the shimmer.\r\n const isZero = !borderRadius || borderRadius === 'none' || borderRadius.split(' ').every(v => v === '0' || v === '0px' || v === '0%');\r\n if (isZero) {\r\n borderRadius = '4px';\r\n }\r\n\r\n let width = elRect.width;\r\n let height = elRect.height;\r\n\r\n // Apply fallback dimensions if element has 0 size but is actually visible (e.g. empty inline element)\r\n if (width === 0 || height === 0) {\r\n const fallback = FALLBACK_DIMENSIONS[el.tagName];\r\n if (fallback) {\r\n width = width || fallback.width;\r\n height = height || fallback.height;\r\n }\r\n }\r\n\r\n return {\r\n x: elRect.left - containerRect.left,\r\n y: elRect.top - containerRect.top,\r\n width,\r\n height,\r\n borderRadius,\r\n };\r\n}\r\n\r\n/**\r\n * Performs a full trace of all traceable elements within the container.\r\n *\r\n * @param anchorRef - When provided (Reporter mode), elements are measured\r\n * relative to this element instead of the container. Allows Reporter's\r\n * wrapper to use display:contents without breaking measurement.\r\n */\r\nfunction performTrace(\r\n container: HTMLElement,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const anchor = anchorRef?.current ?? container;\r\n const anchorRect = anchor.getBoundingClientRect();\r\n const elements = collectTraceableElements(container);\r\n\r\n return elements\r\n .map((el) => measureElement(el, anchorRect, globalBorderRadius))\r\n .filter((r): r is ShimmerRect => r !== null && r.width > 0 && r.height > 0);\r\n}\r\n\r\n/**\r\n * Hook that traces all visible leaf DOM elements within a container\r\n * and returns their measured ShimmerRects.\r\n *\r\n * Uses ResizeObserver to re-trace on container resize.\r\n *\r\n * @param anchorRef - When set, rects are relative to this element (Master).\r\n * Used by Reporter so its display:contents wrapper doesn't break measurement.\r\n */\r\nexport function useTrace(\r\n containerRef: RefObject<HTMLElement | null>,\r\n loading: boolean,\r\n globalBorderRadius?: string,\r\n anchorRef?: RefObject<HTMLElement | null>,\r\n): ShimmerRect[] {\r\n const [rects, setRects] = useState<ShimmerRect[]>([]);\r\n\r\n const trace = useCallback(() => {\r\n if (!containerRef.current) return;\r\n const traced = performTrace(containerRef.current, globalBorderRadius, anchorRef);\r\n setRects(traced);\r\n }, [containerRef, globalBorderRadius, anchorRef]);\r\n\r\n useLayoutEffect(() => {\r\n if (!loading || !containerRef.current) {\r\n setRects([]);\r\n return;\r\n }\r\n\r\n // Initial trace\r\n trace();\r\n\r\n // Re-trace on resize\r\n const observer = new ResizeObserver(() => {\r\n trace();\r\n });\r\n observer.observe(containerRef.current);\r\n\r\n return () => observer.disconnect();\r\n }, [loading, trace]);\r\n\r\n return rects;\r\n}\r\n","const SHIMMER_STYLES_ID = 'shimmer-trace-styles';\r\n\r\nconst CSS = `\r\n@keyframes shimmer-wave {\r\n 0% { transform: translateX(-100%); }\r\n 100% { transform: translateX(100%); }\r\n}\r\n\r\n@keyframes shimmer-pulse {\r\n 0%, 100% { opacity: 0.4; }\r\n 50% { opacity: 1; }\r\n}\r\n\r\n@keyframes shimmer-shine {\r\n 0% { transform: translateX(-150%) skewX(-20deg); }\r\n 100% { transform: translateX(150%) skewX(-20deg); }\r\n}\r\n\r\n@keyframes shimmer-glow {\r\n 0%, 100% { filter: brightness(1); }\r\n 50% { filter: brightness(1.35); }\r\n}\r\n\r\n@keyframes shimmer-gradient {\r\n 0% { background-position: 0% 50%; }\r\n 50% { background-position: 100% 50%; }\r\n 100% { background-position: 0% 50%; }\r\n}\r\n\r\n/* preserveBackground mode: hide text + media but keep container styles */\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(h1,h2,h3,h4,h5,h6,p,span,a,li,label,td,th,blockquote,code,pre,strong,em,small) {\r\n color: transparent !important;\r\n text-shadow: none !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(img,video,svg,canvas,picture) {\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] :is(input,textarea,select,button) {\r\n color: transparent !important;\r\n opacity: 0 !important;\r\n}\r\n[data-shimmer-master][data-shimmer-preserve-bg=\"true\"] {\r\n pointer-events: none !important;\r\n user-select: none !important;\r\n}\r\n`;\r\n\r\n/**\r\n * Injects the shimmer keyframe animations into the document head.\r\n * Safe to call multiple times — only injects once.\r\n */\r\nexport function injectStyles(): void {\r\n if (typeof document === 'undefined') return;\r\n if (document.getElementById(SHIMMER_STYLES_ID)) return;\r\n\r\n const style = document.createElement('style');\r\n style.id = SHIMMER_STYLES_ID;\r\n style.textContent = CSS;\r\n document.head.appendChild(style);\r\n}\r\n","import React, { useRef, useCallback, useState, useId, useMemo } from \"react\";\r\nimport { ShimmerProps, ShimmerRect, DEFAULTS } from \"./types\";\r\nimport { ShimmerContext, useShimmerContext } from \"./ShimmerContext\";\r\nimport { ShimmerOverlay } from \"./ShimmerOverlay\";\r\nimport { useTrace } from \"./useTrace\";\r\nimport { injectStyles } from \"./styles\";\r\nimport { generateShimmerKey } from \"./utils\";\r\n\r\n/**\r\n * The main Shimmer component.\r\n *\r\n * Auto-detects **Master** (no parent Shimmer) vs **Reporter** (nested).\r\n * - Master: renders children hidden, traces DOM, paints overlay.\r\n * - Reporter: measures own rects, reports to parent Master.\r\n *\r\n * ### Skeleton shape via `dummyData`\r\n *\r\n * Pass `dummyData` so children render with realistic data while loading.\r\n * No render-prop, no manual `data || fallback` in JSX.\r\n *\r\n * ```tsx\r\n * const userTemplate = { name: 'Loading...', role: '...', avatar: '' };\r\n *\r\n * <Shimmer loading={loading} dummyData={{ user: userTemplate }}>\r\n * <UserCard user={user} />\r\n * </Shimmer>\r\n * ```\r\n *\r\n * ### List mode (`dummyLength`)\r\n *\r\n * Combined with `dummyData`, clones the first child N times with\r\n * template props merged in:\r\n *\r\n * ```tsx\r\n * <Shimmer\r\n * loading={loading}\r\n * dummyLength={5}\r\n * dummyData={{ fruit: { name: 'xxxxx', price: '$0.00' } }}\r\n * >\r\n * <FruitCard fruit={undefined as any} />\r\n * </Shimmer>\r\n * ```\r\n */\r\nexport function Shimmer({\r\n\tloading = false,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tstopPropagation = false,\r\n\tanimation,\r\n\tbaseColor,\r\n\thighlightColor,\r\n\tspeed,\r\n\tborderRadius,\r\n\tpreserveBackground,\r\n\tclassName,\r\n\tstyle,\r\n}: ShimmerProps) {\r\n\tconst parentContext = useShimmerContext();\r\n\tconst isMaster = !parentContext || stopPropagation;\r\n\tconst id = useId();\r\n\r\n\tconst config = useMemo(\r\n\t\t() => ({\r\n\t\t\tanimation:\r\n\t\t\t\tanimation ?? parentContext?.config.animation ?? DEFAULTS.animation,\r\n\t\t\tbaseColor:\r\n\t\t\t\tbaseColor ?? parentContext?.config.baseColor ?? DEFAULTS.baseColor,\r\n\t\t\thighlightColor:\r\n\t\t\t\thighlightColor ??\r\n\t\t\t\tparentContext?.config.highlightColor ??\r\n\t\t\t\tDEFAULTS.highlightColor,\r\n\t\t\tspeed: speed ?? parentContext?.config.speed ?? DEFAULTS.speed,\r\n\t\t\tborderRadius:\r\n\t\t\t\tborderRadius ??\r\n\t\t\t\tparentContext?.config.borderRadius ??\r\n\t\t\t\tDEFAULTS.borderRadius,\r\n\t\t\tpreserveBackground:\r\n\t\t\t\tpreserveBackground ??\r\n\t\t\t\tparentContext?.config.preserveBackground ??\r\n\t\t\t\tDEFAULTS.preserveBackground,\r\n\t\t}),\r\n\t\t[\r\n\t\t\tanimation,\r\n\t\t\tbaseColor,\r\n\t\t\thighlightColor,\r\n\t\t\tspeed,\r\n\t\t\tborderRadius,\r\n\t\t\tpreserveBackground,\r\n\t\t\tparentContext?.config,\r\n\t\t],\r\n\t);\r\n\r\n\tif (isMaster) {\r\n\t\treturn (\r\n\t\t\t<MasterShimmer\r\n\t\t\t\tid={id}\r\n\t\t\t\tloading={loading}\r\n\t\t\t\tconfig={config}\r\n\t\t\t\tdummyLength={dummyLength}\r\n\t\t\t\tdummyData={dummyData}\r\n\t\t\t\tas={as}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={style}\r\n\t\t\t>\r\n\t\t\t\t{children}\r\n\t\t\t</MasterShimmer>\r\n\t\t);\r\n\t}\r\n\r\n\treturn (\r\n\t\t<ReporterShimmer\r\n\t\t\tid={id}\r\n\t\t\tparentContext={parentContext!}\r\n\t\t\tconfig={config}\r\n\t\t\tdummyLength={dummyLength}\r\n\t\t\tdummyData={dummyData}\r\n\t\t\tas={as}\r\n\t\t>\r\n\t\t\t{children}\r\n\t\t</ReporterShimmer>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Master ─────────────────── */\r\n\r\ninterface MasterShimmerProps {\r\n\tid: string;\r\n\tloading: boolean;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tclassName?: string;\r\n\tstyle?: React.CSSProperties;\r\n}\r\n\r\nfunction MasterShimmer({\r\n\tid,\r\n\tloading,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tclassName,\r\n\tstyle,\r\n}: MasterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst [reporterRects, setReporterRects] = useState<\r\n\t\tRecord<string, ShimmerRect[]>\r\n\t>({});\r\n\r\n\tconst register = useCallback((rid: string, rects: ShimmerRect[]) => {\r\n\t\tsetReporterRects((prev) => ({ ...prev, [rid]: rects }));\r\n\t}, []);\r\n\r\n\tconst unregister = useCallback((rid: string) => {\r\n\t\tsetReporterRects((prev) => {\r\n\t\t\tconst next = { ...prev };\r\n\t\t\tdelete next[rid];\r\n\t\t\treturn next;\r\n\t\t});\r\n\t}, []);\r\n\r\n\tReact.useEffect(() => {\r\n\t\tinjectStyles();\r\n\t}, []);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tloading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t);\r\n\r\n\tconst allRects = useMemo(() => {\r\n\t\tconst reported = Object.values(reporterRects).flat();\r\n\t\treturn [...tracedRects, ...reported];\r\n\t}, [tracedRects, reporterRects]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\tconst contextValue = useMemo(\r\n\t\t() => ({\r\n\t\t\tregister,\r\n\t\t\tunregister,\r\n\t\t\tmasterRef: containerRef,\r\n\t\t\tloading,\r\n\t\t\tconfig,\r\n\t\t}),\r\n\t\t[register, unregister, loading, config],\r\n\t);\r\n\r\n\treturn (\r\n\t\t<ShimmerContext.Provider value={contextValue}>\r\n\t\t\t<div\r\n\t\t\t\tref={containerRef}\r\n\t\t\t\tclassName={className}\r\n\t\t\t\tstyle={{\r\n\t\t\t\t\tposition: \"relative\",\r\n\t\t\t\t\tvisibility:\r\n\t\t\t\t\t\tloading && !config.preserveBackground ? \"hidden\" : undefined,\r\n\t\t\t\t\t...style,\r\n\t\t\t\t}}\r\n\t\t\t\taria-hidden={loading || undefined}\r\n\t\t\t\tdata-shimmer-master\r\n\t\t\t\tdata-shimmer-preserve-bg={\r\n\t\t\t\t\tloading && config.preserveBackground ? \"true\" : undefined\r\n\t\t\t\t}\r\n\t\t\t>\r\n\t\t\t\t{renderedChildren}\r\n\r\n\t\t\t\t{loading && (\r\n\t\t\t\t\t<ShimmerOverlay\r\n\t\t\t\t\t\trects={allRects}\r\n\t\t\t\t\t\tanimation={config.animation}\r\n\t\t\t\t\t\tbaseColor={config.baseColor}\r\n\t\t\t\t\t\thighlightColor={config.highlightColor}\r\n\t\t\t\t\t\tspeed={config.speed}\r\n\t\t\t\t\t/>\r\n\t\t\t\t)}\r\n\t\t\t</div>\r\n\t\t</ShimmerContext.Provider>\r\n\t);\r\n}\r\n\r\n/* ─────────────────── Reporter ─────────────────── */\r\n\r\ninterface ReporterShimmerProps {\r\n\tid: string;\r\n\tparentContext: NonNullable<ReturnType<typeof useShimmerContext>>;\r\n\tconfig: Required<typeof DEFAULTS>;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n}\r\n\r\nfunction ReporterShimmer({\r\n\tid,\r\n\tparentContext,\r\n\tconfig,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n}: ReporterShimmerProps) {\r\n\tconst containerRef = useRef<HTMLDivElement>(null);\r\n\r\n\tconst tracedRects = useTrace(\r\n\t\tcontainerRef,\r\n\t\tparentContext.loading,\r\n\t\tconfig.borderRadius || undefined,\r\n\t\tparentContext.masterRef,\r\n\t);\r\n\r\n\tReact.useLayoutEffect(() => {\r\n\t\tif (!parentContext.loading || tracedRects.length === 0) {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tparentContext.register(id, tracedRects);\r\n\t\treturn () => {\r\n\t\t\tparentContext.unregister(id);\r\n\t\t};\r\n\t}, [tracedRects, parentContext, id]);\r\n\r\n\tconst renderedChildren = useSkeletonChildren({\r\n\t\tloading: parentContext.loading,\r\n\t\tchildren,\r\n\t\tdummyLength,\r\n\t\tdummyData,\r\n\t\tas,\r\n\t\tid,\r\n\t});\r\n\r\n\treturn (\r\n\t\t<div\r\n\t\t\tref={containerRef}\r\n\t\t\tdata-shimmer-reporter\r\n\t\t\tstyle={{ display: \"contents\" }}\r\n\t\t>\r\n\t\t\t{renderedChildren}\r\n\t\t</div>\r\n\t);\r\n}\r\n\r\n/* ─────────────── Skeleton Children ─────────────── */\r\n\r\ninterface UseSkeletonChildrenParams {\r\n\tloading: boolean;\r\n\tchildren: React.ReactNode;\r\n\tdummyLength?: number;\r\n\tdummyData?: Record<string, any>;\r\n\tas?: React.ComponentType<any>;\r\n\tid: string;\r\n}\r\n\r\n/**\r\n * Build the rendered children tree.\r\n *\r\n * Priority during `loading=true`:\r\n * 1. `as` set → render `dummyLength` (or 1) instances of `<as {...dummyData} />`.\r\n * Children ignored. Cold-start safe.\r\n * 2. `dummyData` set + children present → clone each child merging\r\n * dummyData over its props. If `dummyLength` set, clone first\r\n * templated child N times.\r\n * 3. None → pass children through (e.g. `useIsShimmering` flow).\r\n *\r\n * `loading=false` → children untouched.\r\n */\r\nfunction useSkeletonChildren({\r\n\tloading,\r\n\tchildren,\r\n\tdummyLength,\r\n\tdummyData,\r\n\tas,\r\n\tid,\r\n}: UseSkeletonChildrenParams): React.ReactNode {\r\n\tif (!loading) return children;\r\n\r\n\tif (as) {\r\n\t\tconst count = dummyLength && dummyLength > 0 ? dummyLength : 1;\r\n\t\tconst Component = as;\r\n\t\treturn Array.from({ length: count }, (_, i) => (\r\n\t\t\t<Component\r\n\t\t\t\t{...(dummyData || {})}\r\n\t\t\t\tkey={generateShimmerKey(`${id}-as-${i}`)}\r\n\t\t\t/>\r\n\t\t));\r\n\t}\r\n\r\n\tconst childArray = React.Children.toArray(children);\r\n\r\n\tconst templated = childArray.map((c, i) => {\r\n\t\tif (!React.isValidElement(c)) return c;\r\n\t\tconst key = generateShimmerKey(`${id}-tpl-${i}`);\r\n\t\tconst props = dummyData ? { ...dummyData, key } : { key };\r\n\t\treturn React.cloneElement(c as React.ReactElement, props as any);\r\n\t});\r\n\r\n\tif (dummyLength && dummyLength > 0) {\r\n\t\tconst first = templated.find((c) => React.isValidElement(c)) as\r\n\t\t\t| React.ReactElement\r\n\t\t\t| undefined;\r\n\t\tif (!first) return null;\r\n\t\treturn Array.from({ length: dummyLength }, (_, i) =>\r\n\t\t\tReact.cloneElement(first, {\r\n\t\t\t\tkey: generateShimmerKey(`${id}-clone-${i}`),\r\n\t\t\t} as any),\r\n\t\t);\r\n\t}\r\n\r\n\treturn templated;\r\n}\r\n","import React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { ShimmerConfig, ShimmerProps, DEFAULTS } from './types';\r\n\r\n/**\r\n * Factory function to create a pre-configured Shimmer component.\r\n * Avoids \"Provider Hell\" by baking config into the returned component.\r\n *\r\n * All config properties are optional — defaults are used for anything\r\n * not specified.\r\n *\r\n * @example\r\n * ```tsx\r\n * const AppShimmer = createShimmer({\r\n * animation: 'pulse',\r\n * baseColor: '#1a1a2e',\r\n * highlightColor: '#16213e',\r\n * speed: 2,\r\n * });\r\n *\r\n * <AppShimmer loading={isLoading}>\r\n * <MyComponent />\r\n * </AppShimmer>\r\n * ```\r\n */\r\nexport function createShimmer(config: ShimmerConfig = {}) {\r\n const mergedConfig: Required<ShimmerConfig> = {\r\n animation: config.animation ?? DEFAULTS.animation,\r\n baseColor: config.baseColor ?? DEFAULTS.baseColor,\r\n highlightColor: config.highlightColor ?? DEFAULTS.highlightColor,\r\n speed: config.speed ?? DEFAULTS.speed,\r\n borderRadius: config.borderRadius ?? DEFAULTS.borderRadius,\r\n preserveBackground: config.preserveBackground ?? DEFAULTS.preserveBackground,\r\n };\r\n\r\n function ConfiguredShimmer(props: Omit<ShimmerProps, keyof ShimmerConfig> & Partial<ShimmerConfig>) {\r\n return (\r\n <Shimmer\r\n {...mergedConfig}\r\n {...props}\r\n />\r\n );\r\n }\r\n\r\n // Removed ConfiguredShimmer.displayName\r\n return ConfiguredShimmer;\r\n}\r\n","import React from 'react';\r\nimport { Shimmer } from './Shimmer';\r\nimport { IsShimmeringContext } from './ShimmerContext';\r\nimport { ShimmerConfig } from './types';\r\n\r\nexport interface ShimmerSuspenseProps extends ShimmerConfig {\r\n children: React.ReactNode;\r\n /**\r\n * Explicit skeleton template. Rendered hidden and traced for shimmer shape.\r\n *\r\n * Preferred — pass the same component with no data props:\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * If omitted, falls back to Option B: children are re-rendered with\r\n * `useIsShimmering()=true` so they can return an empty shape themselves.\r\n */\r\n template?: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Suspense boundary that automatically shows a shimmer skeleton while\r\n * children are suspended (e.g. useSuspenseQuery, use(promise), etc).\r\n *\r\n * **Option A — explicit template (preferred):**\r\n * ```tsx\r\n * <ShimmerSuspense template={<UserCard />}>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n *\r\n * **Option B — useIsShimmering hook (no template):**\r\n * ```tsx\r\n * function UserCard() {\r\n * const isShimmering = useIsShimmering();\r\n * const data = isShimmering ? null : useSuspenseQuery(...);\r\n * return <div><h3>{data?.name}</h3></div>;\r\n * }\r\n *\r\n * <ShimmerSuspense>\r\n * <UserCard />\r\n * </ShimmerSuspense>\r\n * ```\r\n * Components must use `useIsShimmering()` to skip data fetching in shimmer mode,\r\n * otherwise they will also suspend inside the fallback (causing an empty skeleton).\r\n */\r\nexport function ShimmerSuspense({\r\n children,\r\n template,\r\n ...shimmerConfig\r\n}: ShimmerSuspenseProps) {\r\n const skeletonContent =\r\n template !== undefined ? (\r\n template\r\n ) : (\r\n <IsShimmeringContext.Provider value={true}>\r\n {children}\r\n </IsShimmeringContext.Provider>\r\n );\r\n\r\n return (\r\n <React.Suspense\r\n fallback={\r\n <Shimmer loading={true} {...shimmerConfig}>\r\n {skeletonContent}\r\n </Shimmer>\r\n }\r\n >\r\n {children}\r\n </React.Suspense>\r\n );\r\n}\r\n"]}
|