@theengineeringmind/auto-skeleton 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -0,0 +1,95 @@
1
+ import { S as SkeletonBlock, a as SkeletonLayout, b as SkeletonTheme, R as RenderOptions, M as MeasureOptions, c as ResolvedMeasureOptions, C as Classification, B as BlockKind } from './cache-BEybr_z-.cjs';
2
+ export { D as DEFAULT_MEASURE_OPTIONS, L as LayoutCache, d as STYLE_ID, e as autoskeletonCSS, f as defaultLayoutCache, g as ensureStyles, l as layoutCacheKey, m as measure, r as resolveMeasureOptions } from './cache-BEybr_z-.cjs';
3
+
4
+ declare const CONTAINER_CLASS = "auto-skeleton";
5
+ declare const BLOCK_CLASS = "auto-skeleton__block";
6
+ declare const FADING_CLASS = "auto-skeleton--fading";
7
+ /** Inline custom properties for a theme. Empty when nothing is set. */
8
+ declare function themeStyle(theme?: SkeletonTheme): Record<string, string>;
9
+ /** Inline style for the container. Shared by the DOM renderer and framework wrappers. */
10
+ declare function containerStyle(layout: SkeletonLayout, theme?: SkeletonTheme): Record<string, string>;
11
+ /** Inline style for one block. Shared by the DOM renderer and framework wrappers. */
12
+ declare function blockStyle(block: SkeletonBlock): Record<string, string>;
13
+ declare function blockClassName(block: SkeletonBlock): string;
14
+ /** Replace the container's blocks with those from a new layout. */
15
+ declare function updateSkeleton(container: HTMLElement, layout: SkeletonLayout, theme?: SkeletonTheme): void;
16
+ /**
17
+ * Build a skeleton DOM element from a layout. The result is a `div` with
18
+ * `role="status"` and `aria-busy="true"` that you can insert anywhere.
19
+ */
20
+ declare function renderSkeleton(layout: SkeletonLayout, options?: RenderOptions): HTMLElement;
21
+
22
+ interface SkeletonHandle {
23
+ /** The skeleton element. Insert it wherever the content will appear. */
24
+ readonly element: HTMLElement;
25
+ /** The layout currently painted. */
26
+ readonly layout: SkeletonLayout;
27
+ /** Re-measure the source and repaint. Call after the container resizes. */
28
+ update(): SkeletonLayout;
29
+ /** Remove the element from the DOM. */
30
+ destroy(): void;
31
+ }
32
+ /**
33
+ * Measure `source` and return a live skeleton element for it. This is the
34
+ * one-call vanilla API; frameworks wrappers compose `measure` and
35
+ * `renderSkeleton` directly.
36
+ */
37
+ declare function createSkeleton(source: Element, options?: MeasureOptions & RenderOptions): SkeletonHandle;
38
+
39
+ declare const DATA_ATTRIBUTE = "data-auto-skeleton";
40
+ declare function kindOf(element: Element): BlockKind;
41
+ /**
42
+ * Decide how to treat an element. Order of precedence:
43
+ * 1. `options.classify` callback
44
+ * 2. `data-auto-skeleton="skip|block|descend"` attribute
45
+ * 3. Built-in rules (hidden, replaced/media, form controls, small decorated boxes)
46
+ */
47
+ interface ClassifyContext {
48
+ /**
49
+ * The root passed to `measure`. Roots are often deliberately hidden (a
50
+ * clone used only for measurement), so hidden-ness is ignored for them.
51
+ */
52
+ root?: boolean;
53
+ /**
54
+ * Computed `visibility` of the parent. A child that merely inherits
55
+ * `hidden` from a hidden root is still measured; one that sets it itself
56
+ * is skipped.
57
+ */
58
+ parentVisibility?: string;
59
+ }
60
+ declare function classify(element: Element, window: Window, options: ResolvedMeasureOptions, context?: ClassifyContext): Classification;
61
+ /** Read the element's border radius as a single CSS shorthand value. */
62
+ declare function radiusOf(style: CSSStyleDeclaration): string;
63
+
64
+ interface Rect {
65
+ x: number;
66
+ y: number;
67
+ width: number;
68
+ height: number;
69
+ }
70
+ /** Round to two decimals so layouts serialize cleanly and compare stably. */
71
+ declare function round(n: number): number;
72
+ /** Convert a viewport-relative DOMRect into coordinates relative to `origin`. */
73
+ declare function toRelative(rect: DOMRectReadOnly, origin: {
74
+ left: number;
75
+ top: number;
76
+ }): Rect;
77
+ /** Shrink a rect vertically around its centre. */
78
+ declare function scaleHeight(rect: Rect, scale: number): Rect;
79
+ /** True when the two rects share at least half of the smaller one's vertical span. */
80
+ declare function sameLine(a: Rect, b: Rect): boolean;
81
+ /** Bounding box of two rects. */
82
+ declare function union(a: Rect, b: Rect): Rect;
83
+ /**
84
+ * Merge text fragments that sit on the same line and are horizontally close
85
+ * into a single bar. Fragments are first grouped by vertical overlap, then
86
+ * merged left to right when the gap between them is at most `gap`.
87
+ */
88
+ declare function mergeLineRects(rects: readonly Rect[], gap: number): Rect[];
89
+ /** Drop rects that are too small to matter or that fall entirely outside the bounds. */
90
+ declare function isVisible(rect: Rect, bounds: {
91
+ width: number;
92
+ height: number;
93
+ }, minSize: number): boolean;
94
+
95
+ export { BLOCK_CLASS, BlockKind, CONTAINER_CLASS, Classification, type ClassifyContext, DATA_ATTRIBUTE, FADING_CLASS, MeasureOptions, type Rect, RenderOptions, ResolvedMeasureOptions, SkeletonBlock, type SkeletonHandle, SkeletonLayout, SkeletonTheme, blockClassName, blockStyle, classify, containerStyle, createSkeleton, isVisible, kindOf, mergeLineRects, radiusOf, renderSkeleton, round, sameLine, scaleHeight, themeStyle, toRelative, union, updateSkeleton };
@@ -0,0 +1,95 @@
1
+ import { S as SkeletonBlock, a as SkeletonLayout, b as SkeletonTheme, R as RenderOptions, M as MeasureOptions, c as ResolvedMeasureOptions, C as Classification, B as BlockKind } from './cache-BEybr_z-.js';
2
+ export { D as DEFAULT_MEASURE_OPTIONS, L as LayoutCache, d as STYLE_ID, e as autoskeletonCSS, f as defaultLayoutCache, g as ensureStyles, l as layoutCacheKey, m as measure, r as resolveMeasureOptions } from './cache-BEybr_z-.js';
3
+
4
+ declare const CONTAINER_CLASS = "auto-skeleton";
5
+ declare const BLOCK_CLASS = "auto-skeleton__block";
6
+ declare const FADING_CLASS = "auto-skeleton--fading";
7
+ /** Inline custom properties for a theme. Empty when nothing is set. */
8
+ declare function themeStyle(theme?: SkeletonTheme): Record<string, string>;
9
+ /** Inline style for the container. Shared by the DOM renderer and framework wrappers. */
10
+ declare function containerStyle(layout: SkeletonLayout, theme?: SkeletonTheme): Record<string, string>;
11
+ /** Inline style for one block. Shared by the DOM renderer and framework wrappers. */
12
+ declare function blockStyle(block: SkeletonBlock): Record<string, string>;
13
+ declare function blockClassName(block: SkeletonBlock): string;
14
+ /** Replace the container's blocks with those from a new layout. */
15
+ declare function updateSkeleton(container: HTMLElement, layout: SkeletonLayout, theme?: SkeletonTheme): void;
16
+ /**
17
+ * Build a skeleton DOM element from a layout. The result is a `div` with
18
+ * `role="status"` and `aria-busy="true"` that you can insert anywhere.
19
+ */
20
+ declare function renderSkeleton(layout: SkeletonLayout, options?: RenderOptions): HTMLElement;
21
+
22
+ interface SkeletonHandle {
23
+ /** The skeleton element. Insert it wherever the content will appear. */
24
+ readonly element: HTMLElement;
25
+ /** The layout currently painted. */
26
+ readonly layout: SkeletonLayout;
27
+ /** Re-measure the source and repaint. Call after the container resizes. */
28
+ update(): SkeletonLayout;
29
+ /** Remove the element from the DOM. */
30
+ destroy(): void;
31
+ }
32
+ /**
33
+ * Measure `source` and return a live skeleton element for it. This is the
34
+ * one-call vanilla API; frameworks wrappers compose `measure` and
35
+ * `renderSkeleton` directly.
36
+ */
37
+ declare function createSkeleton(source: Element, options?: MeasureOptions & RenderOptions): SkeletonHandle;
38
+
39
+ declare const DATA_ATTRIBUTE = "data-auto-skeleton";
40
+ declare function kindOf(element: Element): BlockKind;
41
+ /**
42
+ * Decide how to treat an element. Order of precedence:
43
+ * 1. `options.classify` callback
44
+ * 2. `data-auto-skeleton="skip|block|descend"` attribute
45
+ * 3. Built-in rules (hidden, replaced/media, form controls, small decorated boxes)
46
+ */
47
+ interface ClassifyContext {
48
+ /**
49
+ * The root passed to `measure`. Roots are often deliberately hidden (a
50
+ * clone used only for measurement), so hidden-ness is ignored for them.
51
+ */
52
+ root?: boolean;
53
+ /**
54
+ * Computed `visibility` of the parent. A child that merely inherits
55
+ * `hidden` from a hidden root is still measured; one that sets it itself
56
+ * is skipped.
57
+ */
58
+ parentVisibility?: string;
59
+ }
60
+ declare function classify(element: Element, window: Window, options: ResolvedMeasureOptions, context?: ClassifyContext): Classification;
61
+ /** Read the element's border radius as a single CSS shorthand value. */
62
+ declare function radiusOf(style: CSSStyleDeclaration): string;
63
+
64
+ interface Rect {
65
+ x: number;
66
+ y: number;
67
+ width: number;
68
+ height: number;
69
+ }
70
+ /** Round to two decimals so layouts serialize cleanly and compare stably. */
71
+ declare function round(n: number): number;
72
+ /** Convert a viewport-relative DOMRect into coordinates relative to `origin`. */
73
+ declare function toRelative(rect: DOMRectReadOnly, origin: {
74
+ left: number;
75
+ top: number;
76
+ }): Rect;
77
+ /** Shrink a rect vertically around its centre. */
78
+ declare function scaleHeight(rect: Rect, scale: number): Rect;
79
+ /** True when the two rects share at least half of the smaller one's vertical span. */
80
+ declare function sameLine(a: Rect, b: Rect): boolean;
81
+ /** Bounding box of two rects. */
82
+ declare function union(a: Rect, b: Rect): Rect;
83
+ /**
84
+ * Merge text fragments that sit on the same line and are horizontally close
85
+ * into a single bar. Fragments are first grouped by vertical overlap, then
86
+ * merged left to right when the gap between them is at most `gap`.
87
+ */
88
+ declare function mergeLineRects(rects: readonly Rect[], gap: number): Rect[];
89
+ /** Drop rects that are too small to matter or that fall entirely outside the bounds. */
90
+ declare function isVisible(rect: Rect, bounds: {
91
+ width: number;
92
+ height: number;
93
+ }, minSize: number): boolean;
94
+
95
+ export { BLOCK_CLASS, BlockKind, CONTAINER_CLASS, Classification, type ClassifyContext, DATA_ATTRIBUTE, FADING_CLASS, MeasureOptions, type Rect, RenderOptions, ResolvedMeasureOptions, SkeletonBlock, type SkeletonHandle, SkeletonLayout, SkeletonTheme, blockClassName, blockStyle, classify, containerStyle, createSkeleton, isVisible, kindOf, mergeLineRects, radiusOf, renderSkeleton, round, sameLine, scaleHeight, themeStyle, toRelative, union, updateSkeleton };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { BLOCK_CLASS, CONTAINER_CLASS, DATA_ATTRIBUTE, DEFAULT_MEASURE_OPTIONS, FADING_CLASS, LayoutCache, STYLE_ID, autoskeletonCSS, blockClassName, blockStyle, classify, containerStyle, createSkeleton, defaultLayoutCache, ensureStyles, isVisible, kindOf, layoutCacheKey, measure, mergeLineRects, radiusOf, renderSkeleton, resolveMeasureOptions, round, sameLine, scaleHeight, themeStyle, toRelative, union, updateSkeleton } from './chunk-VI2M6DZS.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/dist/react.cjs ADDED
@@ -0,0 +1,244 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var chunkVTA53XYO_cjs = require('./chunk-VTA53XYO.cjs');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ var AutoSkeletonContext = react.createContext({});
9
+ AutoSkeletonContext.displayName = "AutoSkeletonContext";
10
+ function AutoSkeletonProvider({ children, ...config }) {
11
+ const parent = react.useContext(AutoSkeletonContext);
12
+ const value = react.useMemo(() => mergeConfig(parent, config), [parent, config]);
13
+ return /* @__PURE__ */ jsxRuntime.jsx(AutoSkeletonContext.Provider, { value, children });
14
+ }
15
+ function useAutoSkeletonConfig() {
16
+ return react.useContext(AutoSkeletonContext);
17
+ }
18
+ function mergeConfig(base, override) {
19
+ const out = { ...base };
20
+ for (const key of Object.keys(override)) {
21
+ const value = override[key];
22
+ if (value !== void 0) out[key] = value;
23
+ }
24
+ if (base.layouts && override.layouts) out.layouts = { ...base.layouts, ...override.layouts };
25
+ return out;
26
+ }
27
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
28
+ var MEASURE_STYLE = {
29
+ position: "absolute",
30
+ top: 0,
31
+ left: 0,
32
+ width: "100%",
33
+ clipPath: "inset(50%)",
34
+ overflow: "hidden",
35
+ pointerEvents: "none",
36
+ userSelect: "none"
37
+ };
38
+ var OVERLAY_STYLE = { position: "absolute", top: 0, left: 0, right: 0 };
39
+ function cx(...names) {
40
+ return names.filter(Boolean).join(" ");
41
+ }
42
+ var isDev = typeof process !== "undefined" && process.env?.["NODE_ENV"] !== "production";
43
+ var AutoSkeleton = react.forwardRef(function AutoSkeleton2(props, ref) {
44
+ const context = useAutoSkeletonConfig();
45
+ const {
46
+ loading,
47
+ children,
48
+ placeholder,
49
+ layout: layoutProp,
50
+ cacheKey,
51
+ minHeight,
52
+ as: Tag = "div",
53
+ className,
54
+ style,
55
+ skeletonClassName,
56
+ onLayout,
57
+ ...ownConfig
58
+ } = props;
59
+ const config = mergeConfig(context, ownConfig);
60
+ const {
61
+ fadeDuration = 200,
62
+ animate = true,
63
+ label = "Loading",
64
+ injectStyles = true,
65
+ nonce,
66
+ cache = chunkVTA53XYO_cjs.defaultLayoutCache,
67
+ layouts,
68
+ onError,
69
+ color,
70
+ highlight,
71
+ duration,
72
+ fade,
73
+ textScale,
74
+ minSize,
75
+ maxBackgroundBlock,
76
+ mergeGap,
77
+ textRadius,
78
+ maxBlocks,
79
+ classify
80
+ } = config;
81
+ const wrapperRef = react.useRef(null);
82
+ react.useImperativeHandle(ref, () => wrapperRef.current, []);
83
+ const measureRef = react.useRef(null);
84
+ const [initialLayout] = react.useState(() => {
85
+ if (layoutProp) return layoutProp;
86
+ if (cacheKey === void 0) return null;
87
+ return layouts?.[cacheKey] ?? cache.latest(cacheKey) ?? null;
88
+ });
89
+ const [measured, setMeasured] = react.useState(null);
90
+ const layout = layoutProp ?? measured ?? initialLayout;
91
+ const [needsSource, setNeedsSource] = react.useState(initialLayout === null && layoutProp === void 0);
92
+ const [prevLoading, setPrevLoading] = react.useState(loading);
93
+ const [fading, setFading] = react.useState(false);
94
+ if (prevLoading !== loading) {
95
+ setPrevLoading(loading);
96
+ setFading(!loading && layout !== null && fadeDuration > 0);
97
+ }
98
+ react.useEffect(() => {
99
+ if (!fading) return;
100
+ const timer = setTimeout(() => setFading(false), fadeDuration);
101
+ return () => clearTimeout(timer);
102
+ }, [fading, fadeDuration]);
103
+ const measureOptions = react.useMemo(
104
+ () => ({ textScale, minSize, maxBackgroundBlock, mergeGap, textRadius, maxBlocks, classify }),
105
+ [textScale, minSize, maxBackgroundBlock, mergeGap, textRadius, maxBlocks, classify]
106
+ );
107
+ const theme = react.useMemo(
108
+ () => ({ color, highlight, duration, fade }),
109
+ [color, highlight, duration, fade]
110
+ );
111
+ const callbacks = react.useRef({ onLayout, onError });
112
+ react.useEffect(() => {
113
+ callbacks.current = { onLayout, onError };
114
+ }, [onLayout, onError]);
115
+ const layoutRef = react.useRef(null);
116
+ useIsomorphicLayoutEffect(() => {
117
+ layoutRef.current = layout;
118
+ });
119
+ const reported = react.useRef(null);
120
+ const report = react.useCallback((next) => {
121
+ if (reported.current === next) return;
122
+ reported.current = next;
123
+ callbacks.current.onLayout?.(next);
124
+ }, []);
125
+ const runMeasure = react.useCallback(() => {
126
+ const wrapper = wrapperRef.current;
127
+ if (!wrapper || layoutProp) return;
128
+ const width = wrapper.clientWidth;
129
+ const current = layoutRef.current;
130
+ if (current && Math.abs(current.width - width) <= 1) {
131
+ setNeedsSource(false);
132
+ report(current);
133
+ return;
134
+ }
135
+ const key = cacheKey === void 0 ? void 0 : chunkVTA53XYO_cjs.layoutCacheKey(cacheKey, width);
136
+ let next = key === void 0 ? void 0 : lookupLayout(cache, layouts, cacheKey, key, width);
137
+ if (!next) {
138
+ const source2 = measureRef.current;
139
+ if (!source2) {
140
+ setNeedsSource(true);
141
+ return;
142
+ }
143
+ try {
144
+ next = chunkVTA53XYO_cjs.measure(source2, measureOptions);
145
+ } catch (error) {
146
+ callbacks.current.onError?.(error);
147
+ if (isDev && !callbacks.current.onError) console.error("auto-skeleton: measurement failed", error);
148
+ return;
149
+ }
150
+ if (key !== void 0) cache.set(key, next);
151
+ }
152
+ setMeasured(next);
153
+ setNeedsSource(false);
154
+ report(next);
155
+ }, [cache, cacheKey, layoutProp, layouts, measureOptions, report]);
156
+ useIsomorphicLayoutEffect(() => {
157
+ if (!loading) return;
158
+ if (injectStyles) chunkVTA53XYO_cjs.ensureStyles(document, nonce);
159
+ if (layoutProp) {
160
+ report(layoutProp);
161
+ return;
162
+ }
163
+ const source2 = measureRef.current;
164
+ if (source2) source2.inert = true;
165
+ runMeasure();
166
+ const wrapper = wrapperRef.current;
167
+ if (!wrapper || typeof ResizeObserver === "undefined") return;
168
+ let frame = 0;
169
+ let lastWidth = wrapper.clientWidth;
170
+ const observer = new ResizeObserver(() => {
171
+ const width = wrapper.clientWidth;
172
+ if (width === lastWidth) return;
173
+ lastWidth = width;
174
+ cancelAnimationFrame(frame);
175
+ frame = requestAnimationFrame(runMeasure);
176
+ });
177
+ observer.observe(wrapper);
178
+ return () => {
179
+ observer.disconnect();
180
+ cancelAnimationFrame(frame);
181
+ };
182
+ }, [loading, needsSource, runMeasure, injectStyles, nonce, layoutProp, report]);
183
+ const source = placeholder === void 0 ? children : placeholder;
184
+ const mountSource = loading && needsSource && source != null;
185
+ const showSkeleton = loading || fading;
186
+ const state = loading ? "loading" : fading ? "fading" : "idle";
187
+ if (isDev && loading && layout === null && source == null && minHeight === void 0) {
188
+ console.warn(
189
+ "auto-skeleton: nothing to measure and no layout, cacheKey, or minHeight given; the skeleton will be empty."
190
+ );
191
+ }
192
+ const skeletonStyle = layout ? { ...chunkVTA53XYO_cjs.containerStyle(layout, theme), ...fading ? OVERLAY_STYLE : null } : { height: minHeight ?? 0 };
193
+ return /* @__PURE__ */ jsxRuntime.jsxs(
194
+ Tag,
195
+ {
196
+ ref: wrapperRef,
197
+ className,
198
+ style: { position: "relative", ...style },
199
+ "data-auto-skeleton-state": state,
200
+ children: [
201
+ mountSource ? /* @__PURE__ */ jsxRuntime.jsx("div", { ref: measureRef, "aria-hidden": true, style: MEASURE_STYLE, children: source }) : null,
202
+ showSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(
203
+ "div",
204
+ {
205
+ className: cx(chunkVTA53XYO_cjs.CONTAINER_CLASS, fading && chunkVTA53XYO_cjs.FADING_CLASS, skeletonClassName),
206
+ role: "status",
207
+ "aria-busy": loading,
208
+ "aria-label": label,
209
+ "data-animate": String(animate),
210
+ style: skeletonStyle,
211
+ children: layout?.blocks.map((block, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkVTA53XYO_cjs.blockClassName(block), "aria-hidden": true, style: chunkVTA53XYO_cjs.blockStyle(block) }, index))
212
+ }
213
+ ) : null,
214
+ loading ? null : children
215
+ ]
216
+ }
217
+ );
218
+ });
219
+ function lookupLayout(cache, layouts, name, key, width) {
220
+ const cached = cache.get(key);
221
+ if (cached) return cached;
222
+ const provided = name === void 0 ? void 0 : layouts?.[name];
223
+ if (provided && Math.abs(provided.width - width) <= 1) return provided;
224
+ return void 0;
225
+ }
226
+
227
+ Object.defineProperty(exports, "LayoutCache", {
228
+ enumerable: true,
229
+ get: function () { return chunkVTA53XYO_cjs.LayoutCache; }
230
+ });
231
+ Object.defineProperty(exports, "autoskeletonCSS", {
232
+ enumerable: true,
233
+ get: function () { return chunkVTA53XYO_cjs.autoskeletonCSS; }
234
+ });
235
+ Object.defineProperty(exports, "measure", {
236
+ enumerable: true,
237
+ get: function () { return chunkVTA53XYO_cjs.measure; }
238
+ });
239
+ exports.AutoSkeleton = AutoSkeleton;
240
+ exports.AutoSkeletonProvider = AutoSkeletonProvider;
241
+ exports.mergeConfig = mergeConfig;
242
+ exports.useAutoSkeletonConfig = useAutoSkeletonConfig;
243
+ //# sourceMappingURL=react.cjs.map
244
+ //# sourceMappingURL=react.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/context.tsx","../src/react/AutoSkeleton.tsx"],"names":["createContext","useContext","useMemo","jsx","useEffect","useLayoutEffect","forwardRef","AutoSkeleton","defaultLayoutCache","useRef","useImperativeHandle","useState","useCallback","layoutCacheKey","source","measure","ensureStyles","containerStyle","jsxs","CONTAINER_CLASS","FADING_CLASS","blockClassName","blockStyle"],"mappings":"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","file":"react.cjs","sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport type { LayoutCache, MeasureOptions, SkeletonLayout, SkeletonTheme } from '../core';\n\n/**\n * Defaults shared by every `<AutoSkeleton>` below the provider. Any prop set\n * on an individual component wins over these.\n */\nexport interface AutoSkeletonConfig extends MeasureOptions, SkeletonTheme {\n /** Run the shimmer animation. */\n animate?: boolean;\n /** Cross-fade duration in ms when content arrives. */\n fadeDuration?: number;\n /** Accessible label for loading regions. */\n label?: string;\n /** Inject the default stylesheet into the document. */\n injectStyles?: boolean;\n /** CSP nonce for the injected stylesheet. */\n nonce?: string;\n /** Cache instance for `cacheKey` lookups. */\n cache?: LayoutCache;\n /**\n * Precomputed layouts keyed by `cacheKey`. Used on the very first render,\n * including on the server, so skeletons stream down as real blocks with no\n * layout shift and no client-side measurement. Generate them once with\n * `measure()` in a browser (for example a Playwright script in CI) and\n * ship the JSON.\n */\n layouts?: Readonly<Record<string, SkeletonLayout>>;\n /** Called when measurement throws. The component falls back to `minHeight`. */\n onError?: (error: unknown) => void;\n}\n\nconst AutoSkeletonContext = createContext<AutoSkeletonConfig>({});\nAutoSkeletonContext.displayName = 'AutoSkeletonContext';\n\nexport interface AutoSkeletonProviderProps extends AutoSkeletonConfig {\n children?: ReactNode;\n}\n\n/**\n * Provide app-wide defaults (theme, cache, precomputed layouts, measure\n * options). Providers nest: inner values override outer ones per key.\n */\nexport function AutoSkeletonProvider({ children, ...config }: AutoSkeletonProviderProps): ReactNode {\n const parent = useContext(AutoSkeletonContext);\n const value = useMemo(() => mergeConfig(parent, config), [parent, config]);\n return <AutoSkeletonContext.Provider value={value}>{children}</AutoSkeletonContext.Provider>;\n}\n\nexport function useAutoSkeletonConfig(): AutoSkeletonConfig {\n return useContext(AutoSkeletonContext);\n}\n\n/** Merge two configs, ignoring `undefined` so an explicit prop never erases a default. */\nexport function mergeConfig(base: AutoSkeletonConfig, override: AutoSkeletonConfig): AutoSkeletonConfig {\n const out: AutoSkeletonConfig = { ...base };\n for (const key of Object.keys(override) as (keyof AutoSkeletonConfig)[]) {\n const value = override[key];\n if (value !== undefined) (out as Record<string, unknown>)[key] = value;\n }\n if (base.layouts && override.layouts) out.layouts = { ...base.layouts, ...override.layouts };\n return out;\n}\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ElementType,\n type ReactNode,\n} from 'react';\nimport {\n CONTAINER_CLASS,\n FADING_CLASS,\n blockClassName,\n blockStyle,\n containerStyle,\n defaultLayoutCache,\n ensureStyles,\n layoutCacheKey,\n measure,\n type MeasureOptions,\n type SkeletonLayout,\n type SkeletonTheme,\n} from '../core';\nimport { mergeConfig, useAutoSkeletonConfig, type AutoSkeletonConfig } from './context';\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport interface AutoSkeletonProps extends AutoSkeletonConfig {\n /** Show the skeleton instead of `children`. */\n loading: boolean;\n /** The real content. Also used as the measurement source unless `placeholder` is set. */\n children?: ReactNode;\n /**\n * What to measure while loading. Defaults to `children`. Pass a version\n * rendered with sample data when `children` cannot render without data,\n * or `null` to rely purely on `layout`, `cacheKey` and `minHeight`.\n */\n placeholder?: ReactNode;\n /**\n * A precomputed layout. Rendered as-is on the server and the client with\n * no measurement at all. Takes precedence over `cacheKey` and `layouts`.\n */\n layout?: SkeletonLayout;\n /**\n * Reuse a layout across mounts and across `loading` cycles. Layouts are\n * keyed by this name and the container width. A cached or provided layout\n * for the name is painted on the first frame; the source is only mounted\n * and measured when no layout exists for the current width.\n */\n cacheKey?: string;\n /** Height used before the first measurement and during SSR without a layout. */\n minHeight?: number | string;\n /** Wrapper element type. Default `'div'`. */\n as?: ElementType;\n className?: string;\n style?: CSSProperties;\n /** Extra class names for the skeleton container. */\n skeletonClassName?: string;\n /** Called whenever a layout is measured, provided, or read from the cache. */\n onLayout?: (layout: SkeletonLayout) => void;\n}\n\nconst MEASURE_STYLE: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n clipPath: 'inset(50%)',\n overflow: 'hidden',\n pointerEvents: 'none',\n userSelect: 'none',\n};\n\nconst OVERLAY_STYLE: CSSProperties = { position: 'absolute', top: 0, left: 0, right: 0 };\n\nfunction cx(...names: (string | false | null | undefined)[]): string {\n return names.filter(Boolean).join(' ');\n}\n\nconst isDev = typeof process !== 'undefined' && process.env?.['NODE_ENV'] !== 'production';\n\n/**\n * Wrap any content and get a pixel-accurate skeleton for it while `loading`\n * is true, with a cross-fade to the real content when it flips to false.\n */\nexport const AutoSkeleton = forwardRef<HTMLElement, AutoSkeletonProps>(function AutoSkeleton(props, ref) {\n const context = useAutoSkeletonConfig();\n const {\n loading,\n children,\n placeholder,\n layout: layoutProp,\n cacheKey,\n minHeight,\n as: Tag = 'div',\n className,\n style,\n skeletonClassName,\n onLayout,\n ...ownConfig\n } = props;\n const config = mergeConfig(context, ownConfig);\n const {\n fadeDuration = 200,\n animate = true,\n label = 'Loading',\n injectStyles = true,\n nonce,\n cache = defaultLayoutCache,\n layouts,\n onError,\n color,\n highlight,\n duration,\n fade,\n textScale,\n minSize,\n maxBackgroundBlock,\n mergeGap,\n textRadius,\n maxBlocks,\n classify,\n } = config;\n\n const wrapperRef = useRef<HTMLElement | null>(null);\n useImperativeHandle(ref, () => wrapperRef.current as HTMLElement, []);\n const measureRef = useRef<HTMLDivElement | null>(null);\n\n // First-frame layout without touching the DOM: an explicit prop, a\n // provided (server-shipped) layout, or the freshest cache entry for the key.\n const [initialLayout] = useState<SkeletonLayout | null>(() => {\n if (layoutProp) return layoutProp;\n if (cacheKey === undefined) return null;\n return layouts?.[cacheKey] ?? cache.latest(cacheKey) ?? null;\n });\n const [measured, setMeasured] = useState<SkeletonLayout | null>(null);\n const layout = layoutProp ?? measured ?? initialLayout;\n\n // The source is only mounted when there is nothing better to show, which\n // keeps repeat loads free of any measurement or extra rendering.\n const [needsSource, setNeedsSource] = useState(initialLayout === null && layoutProp === undefined);\n\n const [prevLoading, setPrevLoading] = useState(loading);\n const [fading, setFading] = useState(false);\n if (prevLoading !== loading) {\n setPrevLoading(loading);\n setFading(!loading && layout !== null && fadeDuration > 0);\n }\n useEffect(() => {\n if (!fading) return;\n const timer = setTimeout(() => setFading(false), fadeDuration);\n return () => clearTimeout(timer);\n }, [fading, fadeDuration]);\n\n const measureOptions = useMemo<MeasureOptions>(\n () => ({ textScale, minSize, maxBackgroundBlock, mergeGap, textRadius, maxBlocks, classify }),\n [textScale, minSize, maxBackgroundBlock, mergeGap, textRadius, maxBlocks, classify],\n );\n const theme = useMemo<SkeletonTheme>(\n () => ({ color, highlight, duration, fade }),\n [color, highlight, duration, fade],\n );\n\n const callbacks = useRef({ onLayout, onError });\n useEffect(() => {\n callbacks.current = { onLayout, onError };\n }, [onLayout, onError]);\n const layoutRef = useRef<SkeletonLayout | null>(null);\n useIsomorphicLayoutEffect(() => {\n layoutRef.current = layout;\n });\n const reported = useRef<SkeletonLayout | null>(null);\n const report = useCallback((next: SkeletonLayout) => {\n if (reported.current === next) return;\n reported.current = next;\n callbacks.current.onLayout?.(next);\n }, []);\n\n const runMeasure = useCallback(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper || layoutProp) return;\n const width = wrapper.clientWidth;\n\n // Already have a layout for this width: nothing to do, and the source\n // can go (or stay unmounted).\n const current = layoutRef.current;\n if (current && Math.abs(current.width - width) <= 1) {\n setNeedsSource(false);\n report(current);\n return;\n }\n\n const key = cacheKey === undefined ? undefined : layoutCacheKey(cacheKey, width);\n let next = key === undefined ? undefined : lookupLayout(cache, layouts, cacheKey, key, width);\n if (!next) {\n const source = measureRef.current;\n if (!source) {\n // Nothing to measure at this width. Ask for the source and try again\n // once it has rendered.\n setNeedsSource(true);\n return;\n }\n try {\n next = measure(source, measureOptions);\n } catch (error) {\n callbacks.current.onError?.(error);\n if (isDev && !callbacks.current.onError) console.error('auto-skeleton: measurement failed', error);\n return;\n }\n if (key !== undefined) cache.set(key, next);\n }\n setMeasured(next);\n setNeedsSource(false);\n report(next);\n }, [cache, cacheKey, layoutProp, layouts, measureOptions, report]);\n\n useIsomorphicLayoutEffect(() => {\n if (!loading) return;\n if (injectStyles) ensureStyles(document, nonce);\n if (layoutProp) {\n report(layoutProp);\n return;\n }\n const source = measureRef.current;\n if (source) source.inert = true;\n runMeasure();\n\n const wrapper = wrapperRef.current;\n if (!wrapper || typeof ResizeObserver === 'undefined') return;\n let frame = 0;\n let lastWidth = wrapper.clientWidth;\n const observer = new ResizeObserver(() => {\n const width = wrapper.clientWidth;\n if (width === lastWidth) return;\n lastWidth = width;\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(runMeasure);\n });\n observer.observe(wrapper);\n return () => {\n observer.disconnect();\n cancelAnimationFrame(frame);\n };\n }, [loading, needsSource, runMeasure, injectStyles, nonce, layoutProp, report]);\n\n const source = placeholder === undefined ? children : placeholder;\n const mountSource = loading && needsSource && source != null;\n const showSkeleton = loading || fading;\n const state = loading ? 'loading' : fading ? 'fading' : 'idle';\n\n if (isDev && loading && layout === null && source == null && minHeight === undefined) {\n console.warn(\n 'auto-skeleton: nothing to measure and no layout, cacheKey, or minHeight given; the skeleton will be empty.',\n );\n }\n\n const skeletonStyle: CSSProperties = layout\n ? { ...(containerStyle(layout, theme) as CSSProperties), ...(fading ? OVERLAY_STYLE : null) }\n : { height: minHeight ?? 0 };\n\n return (\n <Tag\n ref={wrapperRef}\n className={className}\n style={{ position: 'relative', ...style }}\n data-auto-skeleton-state={state}\n >\n {mountSource ? (\n <div ref={measureRef} aria-hidden style={MEASURE_STYLE}>\n {source}\n </div>\n ) : null}\n {showSkeleton ? (\n <div\n className={cx(CONTAINER_CLASS, fading && FADING_CLASS, skeletonClassName)}\n role=\"status\"\n aria-busy={loading}\n aria-label={label}\n data-animate={String(animate)}\n style={skeletonStyle}\n >\n {layout?.blocks.map((block, index) => (\n <div key={index} className={blockClassName(block)} aria-hidden style={blockStyle(block)} />\n ))}\n </div>\n ) : null}\n {loading ? null : children}\n </Tag>\n );\n});\n\n/** Resolve a layout for a key at a width from the cache or provided layouts, without measuring. */\nfunction lookupLayout(\n cache: NonNullable<AutoSkeletonConfig['cache']>,\n layouts: AutoSkeletonConfig['layouts'],\n name: string | undefined,\n key: string,\n width: number,\n): SkeletonLayout | undefined {\n const cached = cache.get(key);\n if (cached) return cached;\n const provided = name === undefined ? undefined : layouts?.[name];\n if (provided && Math.abs(provided.width - width) <= 1) return provided;\n return undefined;\n}\n"]}
@@ -0,0 +1,86 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ElementType, CSSProperties } from 'react';
3
+ import { M as MeasureOptions, b as SkeletonTheme, L as LayoutCache, a as SkeletonLayout } from './cache-BEybr_z-.cjs';
4
+ export { B as BlockKind, C as Classification, S as SkeletonBlock, e as autoskeletonCSS, m as measure } from './cache-BEybr_z-.cjs';
5
+
6
+ /**
7
+ * Defaults shared by every `<AutoSkeleton>` below the provider. Any prop set
8
+ * on an individual component wins over these.
9
+ */
10
+ interface AutoSkeletonConfig extends MeasureOptions, SkeletonTheme {
11
+ /** Run the shimmer animation. */
12
+ animate?: boolean;
13
+ /** Cross-fade duration in ms when content arrives. */
14
+ fadeDuration?: number;
15
+ /** Accessible label for loading regions. */
16
+ label?: string;
17
+ /** Inject the default stylesheet into the document. */
18
+ injectStyles?: boolean;
19
+ /** CSP nonce for the injected stylesheet. */
20
+ nonce?: string;
21
+ /** Cache instance for `cacheKey` lookups. */
22
+ cache?: LayoutCache;
23
+ /**
24
+ * Precomputed layouts keyed by `cacheKey`. Used on the very first render,
25
+ * including on the server, so skeletons stream down as real blocks with no
26
+ * layout shift and no client-side measurement. Generate them once with
27
+ * `measure()` in a browser (for example a Playwright script in CI) and
28
+ * ship the JSON.
29
+ */
30
+ layouts?: Readonly<Record<string, SkeletonLayout>>;
31
+ /** Called when measurement throws. The component falls back to `minHeight`. */
32
+ onError?: (error: unknown) => void;
33
+ }
34
+ interface AutoSkeletonProviderProps extends AutoSkeletonConfig {
35
+ children?: ReactNode;
36
+ }
37
+ /**
38
+ * Provide app-wide defaults (theme, cache, precomputed layouts, measure
39
+ * options). Providers nest: inner values override outer ones per key.
40
+ */
41
+ declare function AutoSkeletonProvider({ children, ...config }: AutoSkeletonProviderProps): ReactNode;
42
+ declare function useAutoSkeletonConfig(): AutoSkeletonConfig;
43
+ /** Merge two configs, ignoring `undefined` so an explicit prop never erases a default. */
44
+ declare function mergeConfig(base: AutoSkeletonConfig, override: AutoSkeletonConfig): AutoSkeletonConfig;
45
+
46
+ interface AutoSkeletonProps extends AutoSkeletonConfig {
47
+ /** Show the skeleton instead of `children`. */
48
+ loading: boolean;
49
+ /** The real content. Also used as the measurement source unless `placeholder` is set. */
50
+ children?: ReactNode;
51
+ /**
52
+ * What to measure while loading. Defaults to `children`. Pass a version
53
+ * rendered with sample data when `children` cannot render without data,
54
+ * or `null` to rely purely on `layout`, `cacheKey` and `minHeight`.
55
+ */
56
+ placeholder?: ReactNode;
57
+ /**
58
+ * A precomputed layout. Rendered as-is on the server and the client with
59
+ * no measurement at all. Takes precedence over `cacheKey` and `layouts`.
60
+ */
61
+ layout?: SkeletonLayout;
62
+ /**
63
+ * Reuse a layout across mounts and across `loading` cycles. Layouts are
64
+ * keyed by this name and the container width. A cached or provided layout
65
+ * for the name is painted on the first frame; the source is only mounted
66
+ * and measured when no layout exists for the current width.
67
+ */
68
+ cacheKey?: string;
69
+ /** Height used before the first measurement and during SSR without a layout. */
70
+ minHeight?: number | string;
71
+ /** Wrapper element type. Default `'div'`. */
72
+ as?: ElementType;
73
+ className?: string;
74
+ style?: CSSProperties;
75
+ /** Extra class names for the skeleton container. */
76
+ skeletonClassName?: string;
77
+ /** Called whenever a layout is measured, provided, or read from the cache. */
78
+ onLayout?: (layout: SkeletonLayout) => void;
79
+ }
80
+ /**
81
+ * Wrap any content and get a pixel-accurate skeleton for it while `loading`
82
+ * is true, with a cross-fade to the real content when it flips to false.
83
+ */
84
+ declare const AutoSkeleton: react.ForwardRefExoticComponent<AutoSkeletonProps & react.RefAttributes<HTMLElement>>;
85
+
86
+ export { AutoSkeleton, type AutoSkeletonConfig, type AutoSkeletonProps, AutoSkeletonProvider, type AutoSkeletonProviderProps, LayoutCache, MeasureOptions, SkeletonLayout, SkeletonTheme, mergeConfig, useAutoSkeletonConfig };