@syraui/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SYRA Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software...
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @syraui/core
2
+
3
+ > UX components every developer is too lazy to build from scratch.
4
+ > Works with React, React Native, Next.js, Vite, and Expo — same API, different import path.
5
+
6
+ [![npm version](https://img.shields.io/npm/v/@syraui/core.svg)](https://www.npmjs.com/package/@syraui/core)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @syraui/core
16
+ ```
17
+
18
+ ---
19
+
20
+ ## Platform Support
21
+
22
+ | Platform | Import |
23
+ | ------------ | --------------------- |
24
+ | React | `@syraui/core` |
25
+ | Next.js | `@syraui/core` |
26
+ | Vite | `@syraui/core` |
27
+ | React Native | `@syraui/core/native` |
28
+ | Expo | `@syraui/core/native` |
29
+
30
+ ---
31
+
32
+ ## Components
33
+
34
+ - [`<Skeleton>`](#skeleton) — Auto-generates shimmer from any component's shape
35
+
36
+ ---
37
+
38
+ ## Skeleton
39
+
40
+ Wrap any component. Get its skeleton. Zero config.
41
+
42
+ ### Web — React / Next.js / Vite
43
+
44
+ ```tsx
45
+ import { Skeleton } from "@syraui/core";
46
+
47
+ <Skeleton loading={isLoading}>
48
+ <ProfileCard user={user} />
49
+ </Skeleton>;
50
+ ```
51
+
52
+ ### React Native / Expo
53
+
54
+ Wrap elements you want shimmer'd with `<SkeletonItem>`. It reads dimensions directly from the child's style — no duplication needed.
55
+
56
+ ```tsx
57
+ import { Skeleton, SkeletonItem } from "@syraui/core/native";
58
+
59
+ <Skeleton loading={isLoading}>
60
+ <View style={s.card}>
61
+ <SkeletonItem>
62
+ <Image source={{ uri: user.avatar }} style={s.avatar} />
63
+ </SkeletonItem>
64
+ <SkeletonItem>
65
+ <Text style={s.name}>{user.name}</Text>
66
+ </SkeletonItem>
67
+ <SkeletonItem>
68
+ <Text style={s.bio}>{user.bio}</Text>
69
+ </SkeletonItem>
70
+ <Button title="Follow" />
71
+ </View>
72
+ </Skeleton>;
73
+ ```
74
+
75
+ > **Expo** — requires `expo prebuild` or bare workflow. Does not run in Expo Go.
76
+
77
+ ---
78
+
79
+ ## Props
80
+
81
+ ### `<Skeleton>`
82
+
83
+ | Prop | Type | Default | Description |
84
+ | ---------------- | -------------------------------- | ----------- | ------------------------------------------------- |
85
+ | `loading` | `boolean` | — | Toggle between shimmer and real content |
86
+ | `children` | `ReactNode` | — | Your real component |
87
+ | `animation` | `"shimmer" \| "pulse" \| "none"` | `"shimmer"` | Animation style |
88
+ | `baseColor` | `string` | `"#EAECF0"` | Background color of skeleton blocks |
89
+ | `highlightColor` | `string` | `"#F8F9FB"` | Shimmer highlight color |
90
+ | `speed` | `number` | `1.8` | Animation speed in seconds. Higher = slower |
91
+ | `borderRadius` | `string \| number` | auto | Override border radius on all blocks |
92
+ | `direction` | `"ltr" \| "rtl"` | `"ltr"` | Shimmer sweep direction |
93
+ | `fadeDuration` | `number` | `250` | Fade-in duration (ms) when loading ends. Web only |
94
+ | `className` | `string` | — | CSS class on wrapper. Web only |
95
+ | `style` | `CSSProperties \| ViewStyle` | — | Inline styles |
96
+
97
+ ### `<SkeletonItem>` _(React Native only)_
98
+
99
+ | Prop | Type | Default | Description |
100
+ | -------------- | -------------- | ------- | ------------------------------------- |
101
+ | `children` | `ReactElement` | — | The element to shimmer |
102
+ | `borderRadius` | `number` | auto | Override border radius for this block |
103
+
104
+ ---
105
+
106
+ ## Theming
107
+
108
+ ```tsx
109
+ // Dark mode
110
+ <Skeleton loading={isLoading} baseColor="#1E293B" highlightColor="#334155">
111
+ <Card />
112
+ </Skeleton>
113
+
114
+ // Pulse animation
115
+ <Skeleton loading={isLoading} animation="pulse" speed={1.5}>
116
+ <Card />
117
+ </Skeleton>
118
+
119
+ // RTL
120
+ <Skeleton loading={isLoading} direction="rtl">
121
+ <Card />
122
+ </Skeleton>
123
+ ```
124
+
125
+ ---
126
+
127
+ ## License
128
+
129
+ MIT © [SyraUI](https://github.com/syraui)
@@ -0,0 +1,29 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+
4
+ interface SkeletonTheme {
5
+ baseColor?: string;
6
+ highlightColor?: string;
7
+ speed?: number;
8
+ animation?: "shimmer" | "pulse" | "none";
9
+ borderRadius?: string | number;
10
+ fadeDuration?: number;
11
+ direction?: "ltr" | "rtl";
12
+ }
13
+ interface SkeletonBlock {
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ height: number;
18
+ borderRadius: number;
19
+ }
20
+
21
+ interface SkeletonProps extends SkeletonTheme {
22
+ loading: boolean;
23
+ children: ReactNode;
24
+ className?: string;
25
+ style?: CSSProperties;
26
+ }
27
+ declare function Skeleton({ loading, children, className, style, ...theme }: SkeletonProps): react_jsx_runtime.JSX.Element;
28
+
29
+ export { Skeleton, type SkeletonBlock, type SkeletonProps, type SkeletonTheme };
@@ -0,0 +1,29 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode, CSSProperties } from 'react';
3
+
4
+ interface SkeletonTheme {
5
+ baseColor?: string;
6
+ highlightColor?: string;
7
+ speed?: number;
8
+ animation?: "shimmer" | "pulse" | "none";
9
+ borderRadius?: string | number;
10
+ fadeDuration?: number;
11
+ direction?: "ltr" | "rtl";
12
+ }
13
+ interface SkeletonBlock {
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ height: number;
18
+ borderRadius: number;
19
+ }
20
+
21
+ interface SkeletonProps extends SkeletonTheme {
22
+ loading: boolean;
23
+ children: ReactNode;
24
+ className?: string;
25
+ style?: CSSProperties;
26
+ }
27
+ declare function Skeleton({ loading, children, className, style, ...theme }: SkeletonProps): react_jsx_runtime.JSX.Element;
28
+
29
+ export { Skeleton, type SkeletonBlock, type SkeletonProps, type SkeletonTheme };
package/dist/index.js ADDED
@@ -0,0 +1,234 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __objRest = (source, exclude) => {
10
+ var target = {};
11
+ for (var prop in source)
12
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
13
+ target[prop] = source[prop];
14
+ if (source != null && __getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(source)) {
16
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
17
+ target[prop] = source[prop];
18
+ }
19
+ return target;
20
+ };
21
+ var _injected = false;
22
+ function injectKeyframes() {
23
+ if (_injected || typeof document === "undefined") return;
24
+ _injected = true;
25
+ const el = document.createElement("style");
26
+ el.textContent = `
27
+ @keyframes _sx { 0% { background-position: 100% 50% } 100% { background-position: 0% 50% } }
28
+ @keyframes _sx-rtl { 0% { background-position: 0% 50% } 100% { background-position: 100% 50% } }
29
+ @keyframes _sx-pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.45 } }
30
+ @keyframes _sx-fadein { from { opacity: 0 } to { opacity: 1 } }
31
+ [data-sx] * { visibility: hidden !important; }
32
+ [data-sx-cover] { background: transparent !important; background-image: none !important; }
33
+ .syra-fadein { animation: _sx-fadein var(--sx-fade, 250ms) ease-out forwards; }
34
+ `;
35
+ document.head.appendChild(el);
36
+ }
37
+ var MEDIA = /* @__PURE__ */ new Set(["img", "svg", "video", "picture", "canvas"]);
38
+ var LEAF = /* @__PURE__ */ new Set(["button", "input", "textarea", "select"]);
39
+ var TEXT = /* @__PURE__ */ new Set([
40
+ "p",
41
+ "span",
42
+ "h1",
43
+ "h2",
44
+ "h3",
45
+ "h4",
46
+ "h5",
47
+ "h6",
48
+ "label",
49
+ "li",
50
+ "a",
51
+ "strong",
52
+ "em",
53
+ "small",
54
+ "time",
55
+ "code"
56
+ ]);
57
+ function classify(el) {
58
+ var _a, _b;
59
+ const tag = el.tagName.toLowerCase();
60
+ const cs = window.getComputedStyle(el);
61
+ if (cs.display === "none") return "skip";
62
+ const rect = el.getBoundingClientRect();
63
+ if (rect.width < 2 || rect.height < 2) return "skip";
64
+ if (MEDIA.has(tag)) return "media";
65
+ if (LEAF.has(tag)) return "leaf";
66
+ if (TEXT.has(tag)) {
67
+ const hasVisibleChildren = Array.from(el.children).some(
68
+ (c) => window.getComputedStyle(c).display !== "none"
69
+ );
70
+ return hasVisibleChildren ? "cover" : "leaf";
71
+ }
72
+ if (!el.children.length && ((_a = el.textContent) == null ? void 0 : _a.trim())) return "leaf";
73
+ const bgImg = cs.backgroundImage;
74
+ if (bgImg && bgImg !== "none") return "leaf";
75
+ const cls = (_b = el.className) != null ? _b : "";
76
+ if (typeof cls === "string" && (cls.includes("bg-gradient") || cls.includes("from-")))
77
+ return "leaf";
78
+ if (!el.children.length) {
79
+ const bg = cs.backgroundColor;
80
+ if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") return "leaf";
81
+ }
82
+ return "cover";
83
+ }
84
+ var SAVED = /* @__PURE__ */ new WeakMap();
85
+ function saveEl(el) {
86
+ var _a;
87
+ SAVED.set(el, {
88
+ style: (_a = el.getAttribute("style")) != null ? _a : "",
89
+ src: el.src,
90
+ srcset: el.srcset
91
+ });
92
+ }
93
+ function restoreAll(tagged, fade, fadeDuration) {
94
+ var _a;
95
+ tagged.forEach((el) => {
96
+ var _a2;
97
+ el.removeAttribute("data-sx");
98
+ el.removeAttribute("data-sx-cover");
99
+ const saved = SAVED.get(el);
100
+ if (!saved) return;
101
+ const h = el;
102
+ if (el.tagName.toLowerCase() === "img" && saved.src) {
103
+ el.src = saved.src;
104
+ el.srcset = (_a2 = saved.srcset) != null ? _a2 : "";
105
+ }
106
+ saved.style ? h.setAttribute("style", saved.style) : h.removeAttribute("style");
107
+ SAVED.delete(el);
108
+ });
109
+ if (!fade || tagged.length === 0 || fadeDuration <= 0) return;
110
+ const root = (_a = tagged[0]) == null ? void 0 : _a.closest("[aria-busy]");
111
+ if (!root) return;
112
+ root.style.setProperty("--sx-fade", `${fadeDuration}ms`);
113
+ root.classList.add("syra-fadein");
114
+ root.addEventListener(
115
+ "animationend",
116
+ () => {
117
+ root.classList.remove("syra-fadein");
118
+ root.style.removeProperty("--sx-fade");
119
+ },
120
+ { once: true }
121
+ );
122
+ }
123
+ function shimmerStyle(base, hl, speed, br, anim, dir) {
124
+ const base_props = [
125
+ `color: transparent`,
126
+ `overflow: hidden`,
127
+ `border-radius: ${br}`
128
+ ];
129
+ if (anim === "pulse")
130
+ return [
131
+ ...base_props,
132
+ `background: ${base}`,
133
+ `animation: _sx-pulse ${speed}s ease-in-out infinite`
134
+ ].join("; ");
135
+ if (anim === "none") return [...base_props, `background: ${base}`].join("; ");
136
+ const kf = dir === "rtl" ? "_sx-rtl" : "_sx";
137
+ const pos = dir === "rtl" ? "0% 50%" : "100% 50%";
138
+ return [
139
+ ...base_props,
140
+ `background: linear-gradient(90deg, ${base} 0%, ${base} 30%, ${hl} 50%, ${base} 70%, ${base} 100%)`,
141
+ `background-size: 300% 100%`,
142
+ `background-position: ${pos}`,
143
+ `animation: ${kf} ${speed}s ease-in-out infinite`
144
+ ].join("; ");
145
+ }
146
+ function applyAll(root, theme) {
147
+ var _a, _b, _c, _d, _e, _f, _g;
148
+ const tagged = [];
149
+ const base = (_a = theme.baseColor) != null ? _a : "#EAECF0";
150
+ const hl = (_b = theme.highlightColor) != null ? _b : "#F8F9FB";
151
+ const speed = (_c = theme.speed) != null ? _c : 1.8;
152
+ const anim = (_d = theme.animation) != null ? _d : "shimmer";
153
+ const dir = (_e = theme.direction) != null ? _e : "ltr";
154
+ const defaultBr = typeof theme.borderRadius === "number" ? `${theme.borderRadius}px` : (_f = theme.borderRadius) != null ? _f : "6px";
155
+ const textBr = typeof theme.borderRadius === "number" ? `${theme.borderRadius}px` : (_g = theme.borderRadius) != null ? _g : "4px";
156
+ function walk(el) {
157
+ const type = classify(el);
158
+ if (type === "skip") return;
159
+ const h = el;
160
+ if (type === "media") {
161
+ const cs = window.getComputedStyle(el);
162
+ const rect = el.getBoundingClientRect();
163
+ const isCircle = Math.abs(rect.width - rect.height) < 4 && parseInt(cs.borderRadius) >= rect.width / 2 - 2;
164
+ const br = isCircle ? "50%" : cs.borderRadius !== "0px" ? cs.borderRadius : defaultBr;
165
+ saveEl(el);
166
+ if (el.tagName.toLowerCase() === "img") {
167
+ el.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
168
+ el.srcset = "";
169
+ }
170
+ el.setAttribute("data-sx", "");
171
+ h.style.cssText = shimmerStyle(base, hl, speed, br, anim, dir);
172
+ tagged.push(el);
173
+ return;
174
+ }
175
+ if (type === "leaf") {
176
+ const cs = window.getComputedStyle(el);
177
+ const tag = el.tagName.toLowerCase();
178
+ const br = cs.borderRadius !== "0px" ? cs.borderRadius : TEXT.has(tag) ? textBr : defaultBr;
179
+ saveEl(el);
180
+ el.setAttribute("data-sx", "");
181
+ h.style.cssText = shimmerStyle(base, hl, speed, br, anim, dir);
182
+ tagged.push(el);
183
+ return;
184
+ }
185
+ saveEl(el);
186
+ el.setAttribute("data-sx-cover", "");
187
+ h.style.setProperty("background", "transparent", "important");
188
+ h.style.setProperty("background-image", "none", "important");
189
+ tagged.push(el);
190
+ Array.from(el.children).forEach(walk);
191
+ }
192
+ Array.from(root.children).forEach((card) => {
193
+ const type = classify(card);
194
+ if (type === "skip") return;
195
+ if (type === "media" || type === "leaf") {
196
+ walk(card);
197
+ return;
198
+ }
199
+ tagged.push(card);
200
+ Array.from(card.children).forEach(walk);
201
+ });
202
+ return tagged;
203
+ }
204
+ function Skeleton(_a) {
205
+ var _b = _a, {
206
+ loading,
207
+ children,
208
+ className,
209
+ style
210
+ } = _b, theme = __objRest(_b, [
211
+ "loading",
212
+ "children",
213
+ "className",
214
+ "style"
215
+ ]);
216
+ injectKeyframes();
217
+ const wrapRef = react.useRef(null);
218
+ const taggedRef = react.useRef([]);
219
+ const prevRef = react.useRef(null);
220
+ react.useLayoutEffect(() => {
221
+ var _a2;
222
+ const el = wrapRef.current;
223
+ if (!el || prevRef.current === loading) return;
224
+ prevRef.current = loading;
225
+ restoreAll(taggedRef.current, !loading, (_a2 = theme.fadeDuration) != null ? _a2 : 250);
226
+ taggedRef.current = [];
227
+ if (loading) taggedRef.current = applyAll(el, theme);
228
+ });
229
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: wrapRef, className, style, "aria-busy": loading, children });
230
+ }
231
+
232
+ exports.Skeleton = Skeleton;
233
+ //# sourceMappingURL=index.js.map
234
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/skeleton/Skeleton.web.tsx"],"names":["_a","useRef","useLayoutEffect","jsx"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport {\n useLayoutEffect,\n useRef,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport type { SkeletonTheme } from \"../types/skeleton\";\n\nlet _injected = false;\n\nfunction injectKeyframes() {\n if (_injected || typeof document === \"undefined\") return;\n _injected = true;\n const el = document.createElement(\"style\");\n el.textContent = `\n @keyframes _sx { 0% { background-position: 100% 50% } 100% { background-position: 0% 50% } }\n @keyframes _sx-rtl { 0% { background-position: 0% 50% } 100% { background-position: 100% 50% } }\n @keyframes _sx-pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.45 } }\n @keyframes _sx-fadein { from { opacity: 0 } to { opacity: 1 } }\n [data-sx] * { visibility: hidden !important; }\n [data-sx-cover] { background: transparent !important; background-image: none !important; }\n .syra-fadein { animation: _sx-fadein var(--sx-fade, 250ms) ease-out forwards; }\n `;\n document.head.appendChild(el);\n}\n\nconst MEDIA = new Set([\"img\", \"svg\", \"video\", \"picture\", \"canvas\"]);\nconst LEAF = new Set([\"button\", \"input\", \"textarea\", \"select\"]);\nconst TEXT = new Set([\n \"p\",\n \"span\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"label\",\n \"li\",\n \"a\",\n \"strong\",\n \"em\",\n \"small\",\n \"time\",\n \"code\",\n]);\n\ntype ElType = \"media\" | \"leaf\" | \"cover\" | \"skip\";\n\nfunction classify(el: Element): ElType {\n const tag = el.tagName.toLowerCase();\n const cs = window.getComputedStyle(el);\n if (cs.display === \"none\") return \"skip\";\n const rect = el.getBoundingClientRect();\n if (rect.width < 2 || rect.height < 2) return \"skip\";\n if (MEDIA.has(tag)) return \"media\";\n if (LEAF.has(tag)) return \"leaf\";\n if (TEXT.has(tag)) {\n const hasVisibleChildren = Array.from(el.children).some(\n (c) => window.getComputedStyle(c).display !== \"none\",\n );\n return hasVisibleChildren ? \"cover\" : \"leaf\";\n }\n if (!el.children.length && el.textContent?.trim()) return \"leaf\";\n\n // Element with a background-image (gradient, url) — treat as leaf\n // Works for both CSS classes (Tailwind bg-*) and inline styles\n const bgImg = cs.backgroundImage;\n if (bgImg && bgImg !== \"none\") return \"leaf\";\n\n // Tailwind gradient classes use background-image — catch them via className\n // e.g. bg-gradient-to-r, from-*, via-*, to-*\n const cls = (el as HTMLElement).className ?? \"\";\n if (\n typeof cls === \"string\" &&\n (cls.includes(\"bg-gradient\") || cls.includes(\"from-\"))\n )\n return \"leaf\";\n\n // Non-transparent background-color with no children = decorative block (banner, divider)\n if (!el.children.length) {\n const bg = cs.backgroundColor;\n if (bg && bg !== \"rgba(0, 0, 0, 0)\" && bg !== \"transparent\") return \"leaf\";\n }\n\n return \"cover\";\n}\n\ninterface Saved {\n style: string;\n src?: string;\n srcset?: string;\n}\nconst SAVED = new WeakMap<Element, Saved>();\n\nfunction saveEl(el: Element) {\n SAVED.set(el, {\n style: (el as HTMLElement).getAttribute(\"style\") ?? \"\",\n src: (el as HTMLImageElement).src,\n srcset: (el as HTMLImageElement).srcset,\n });\n}\n\nfunction restoreAll(tagged: Element[], fade: boolean, fadeDuration: number) {\n tagged.forEach((el) => {\n el.removeAttribute(\"data-sx\");\n el.removeAttribute(\"data-sx-cover\");\n const saved = SAVED.get(el);\n if (!saved) return;\n const h = el as HTMLElement;\n if (el.tagName.toLowerCase() === \"img\" && saved.src) {\n (el as HTMLImageElement).src = saved.src;\n (el as HTMLImageElement).srcset = saved.srcset ?? \"\";\n }\n saved.style\n ? h.setAttribute(\"style\", saved.style)\n : h.removeAttribute(\"style\");\n SAVED.delete(el);\n });\n if (!fade || tagged.length === 0 || fadeDuration <= 0) return;\n const root = tagged[0]?.closest(\"[aria-busy]\") as HTMLElement | null;\n if (!root) return;\n root.style.setProperty(\"--sx-fade\", `${fadeDuration}ms`);\n root.classList.add(\"syra-fadein\");\n root.addEventListener(\n \"animationend\",\n () => {\n root.classList.remove(\"syra-fadein\");\n root.style.removeProperty(\"--sx-fade\");\n },\n { once: true },\n );\n}\n\nfunction shimmerStyle(\n base: string,\n hl: string,\n speed: number,\n br: string,\n anim: \"shimmer\" | \"pulse\" | \"none\",\n dir: \"ltr\" | \"rtl\",\n): string {\n const base_props = [\n `color: transparent`,\n `overflow: hidden`,\n `border-radius: ${br}`,\n ];\n if (anim === \"pulse\")\n return [\n ...base_props,\n `background: ${base}`,\n `animation: _sx-pulse ${speed}s ease-in-out infinite`,\n ].join(\"; \");\n if (anim === \"none\") return [...base_props, `background: ${base}`].join(\"; \");\n const kf = dir === \"rtl\" ? \"_sx-rtl\" : \"_sx\";\n const pos = dir === \"rtl\" ? \"0% 50%\" : \"100% 50%\";\n return [\n ...base_props,\n `background: linear-gradient(90deg, ${base} 0%, ${base} 30%, ${hl} 50%, ${base} 70%, ${base} 100%)`,\n `background-size: 300% 100%`,\n `background-position: ${pos}`,\n `animation: ${kf} ${speed}s ease-in-out infinite`,\n ].join(\"; \");\n}\n\nfunction applyAll(root: HTMLElement, theme: SkeletonTheme): Element[] {\n const tagged: Element[] = [];\n const base = theme.baseColor ?? \"#EAECF0\";\n const hl = theme.highlightColor ?? \"#F8F9FB\";\n const speed = theme.speed ?? 1.8;\n const anim = theme.animation ?? \"shimmer\";\n const dir = theme.direction ?? \"ltr\";\n const defaultBr =\n typeof theme.borderRadius === \"number\"\n ? `${theme.borderRadius}px`\n : (theme.borderRadius ?? \"6px\");\n const textBr =\n typeof theme.borderRadius === \"number\"\n ? `${theme.borderRadius}px`\n : (theme.borderRadius ?? \"4px\");\n\n function walk(el: Element) {\n const type = classify(el);\n if (type === \"skip\") return;\n const h = el as HTMLElement;\n\n if (type === \"media\") {\n const cs = window.getComputedStyle(el);\n const rect = el.getBoundingClientRect();\n const isCircle =\n Math.abs(rect.width - rect.height) < 4 &&\n parseInt(cs.borderRadius) >= rect.width / 2 - 2;\n const br = isCircle\n ? \"50%\"\n : cs.borderRadius !== \"0px\"\n ? cs.borderRadius\n : defaultBr;\n saveEl(el);\n if (el.tagName.toLowerCase() === \"img\") {\n (el as HTMLImageElement).src =\n \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\";\n (el as HTMLImageElement).srcset = \"\";\n }\n el.setAttribute(\"data-sx\", \"\");\n h.style.cssText = shimmerStyle(base, hl, speed, br, anim, dir);\n tagged.push(el);\n return;\n }\n\n if (type === \"leaf\") {\n const cs = window.getComputedStyle(el);\n const tag = el.tagName.toLowerCase();\n const br =\n cs.borderRadius !== \"0px\"\n ? cs.borderRadius\n : TEXT.has(tag)\n ? textBr\n : defaultBr;\n saveEl(el);\n el.setAttribute(\"data-sx\", \"\");\n h.style.cssText = shimmerStyle(base, hl, speed, br, anim, dir);\n tagged.push(el);\n return;\n }\n\n saveEl(el);\n el.setAttribute(\"data-sx-cover\", \"\");\n h.style.setProperty(\"background\", \"transparent\", \"important\");\n h.style.setProperty(\"background-image\", \"none\", \"important\");\n tagged.push(el);\n Array.from(el.children).forEach(walk);\n }\n\n Array.from(root.children).forEach((card) => {\n const type = classify(card);\n if (type === \"skip\") return;\n if (type === \"media\" || type === \"leaf\") {\n walk(card);\n return;\n }\n tagged.push(card);\n Array.from(card.children).forEach(walk);\n });\n\n return tagged;\n}\n\nexport interface SkeletonProps extends SkeletonTheme {\n loading: boolean;\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function Skeleton({\n loading,\n children,\n className,\n style,\n ...theme\n}: SkeletonProps) {\n injectKeyframes();\n\n const wrapRef = useRef<HTMLDivElement>(null);\n const taggedRef = useRef<Element[]>([]);\n const prevRef = useRef<boolean | null>(null);\n\n useLayoutEffect(() => {\n const el = wrapRef.current;\n if (!el || prevRef.current === loading) return;\n prevRef.current = loading;\n restoreAll(taggedRef.current, !loading, theme.fadeDuration ?? 250);\n taggedRef.current = [];\n if (loading) taggedRef.current = applyAll(el, theme);\n });\n\n return (\n <div ref={wrapRef} className={className} style={style} aria-busy={loading}>\n {children}\n </div>\n );\n}\n"]}