@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 +9 -0
- package/README.md +129 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +234 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +232 -0
- package/dist/index.mjs.map +1 -0
- package/dist/native/index.d.mts +54 -0
- package/dist/native/index.d.ts +54 -0
- package/dist/native/index.js +273 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/index.mjs +266 -0
- package/dist/native/index.mjs.map +1 -0
- package/package.json +84 -0
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
|
+
[](https://www.npmjs.com/package/@syraui/core)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](./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)
|
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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"]}
|