auto-loading-skeleton 1.0.1 → 1.0.3

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/src/index.d.ts CHANGED
@@ -1,51 +1,51 @@
1
- import { ReactNode, CSSProperties, FC } from 'react';
2
-
3
- export type SkeletonAnimation = 'shimmer' | 'pulse' | 'wave' | 'none';
4
-
5
- export interface SkeletonTheme {
6
- /** Base skeleton color. Default: #e2e8f0 */
7
- baseColor?: string;
8
- /** Highlight color for shimmer/wave. Default: #f8fafc */
9
- highlightColor?: string;
10
- }
11
-
12
- export interface AutoSkeletonProps {
13
- /** Show skeleton when true, render real children when false */
14
- loading: boolean;
15
- children: ReactNode;
16
- /** Animation style. Default: 'shimmer' */
17
- animation?: SkeletonAnimation;
18
- /** Repeat the skeleton pattern N times. Useful for lists. Default: 1 */
19
- repeat?: number;
20
- /** Theme overrides */
21
- theme?: SkeletonTheme;
22
- style?: CSSProperties;
23
- className?: string;
24
- /** Accessible label for the loading region. Default: 'Loading…' */
25
- ariaLabel?: string;
26
- }
27
-
28
- export type SkeletonItemType = 'text' | 'image' | 'avatar' | 'button' | 'input';
29
-
30
- export interface SkeletonItemProps {
31
- type?: SkeletonItemType;
32
- lines?: number;
33
- width?: number | string;
34
- height?: number | string;
35
- /** For avatar type: shorthand for equal width and height */
36
- size?: number;
37
- animation?: SkeletonAnimation;
38
- theme?: SkeletonTheme;
39
- style?: CSSProperties;
40
- }
41
-
42
- /** Main wrapper component — auto-generates a skeleton from its children */
43
- export const AutoSkeleton: FC<AutoSkeletonProps>;
44
-
45
- /** Primitive building block for manually-crafted skeletons */
46
- export const SkeletonItem: FC<SkeletonItemProps>;
47
-
48
- /** Inject global CSS (called automatically; export for SSR use) */
49
- export function injectStyles(): void;
50
-
51
- export default AutoSkeleton;
1
+ import { ReactNode, CSSProperties, FC } from 'react';
2
+
3
+ export type SkeletonAnimation = 'shimmer' | 'pulse' | 'wave' | 'none';
4
+
5
+ export interface SkeletonTheme {
6
+ /** Base skeleton color. Default: #e2e8f0 */
7
+ baseColor?: string;
8
+ /** Highlight color for shimmer/wave. Default: #f8fafc */
9
+ highlightColor?: string;
10
+ }
11
+
12
+ export interface AutoSkeletonProps {
13
+ /** Show skeleton when true, render real children when false */
14
+ loading: boolean;
15
+ children: ReactNode;
16
+ /** Animation style. Default: 'shimmer' */
17
+ animation?: SkeletonAnimation;
18
+ /** Repeat the skeleton pattern N times. Useful for lists. Default: 1 */
19
+ repeat?: number;
20
+ /** Theme overrides */
21
+ theme?: SkeletonTheme;
22
+ style?: CSSProperties;
23
+ className?: string;
24
+ /** Accessible label for the loading region. Default: 'Loading…' */
25
+ ariaLabel?: string;
26
+ }
27
+
28
+ export type SkeletonItemType = 'text' | 'image' | 'avatar' | 'button' | 'input';
29
+
30
+ export interface SkeletonItemProps {
31
+ type?: SkeletonItemType;
32
+ lines?: number;
33
+ width?: number | string;
34
+ height?: number | string;
35
+ /** For avatar type: shorthand for equal width and height */
36
+ size?: number;
37
+ animation?: SkeletonAnimation;
38
+ theme?: SkeletonTheme;
39
+ style?: CSSProperties;
40
+ }
41
+
42
+ /** Main wrapper component — auto-generates a skeleton from its children */
43
+ export const AutoSkeleton: FC<AutoSkeletonProps>;
44
+
45
+ /** Primitive building block for manually-crafted skeletons */
46
+ export const SkeletonItem: FC<SkeletonItemProps>;
47
+
48
+ /** Inject global CSS (called automatically; export for SSR use) */
49
+ export function injectStyles(): void;
50
+
51
+ export default AutoSkeleton;
package/src/index.js CHANGED
@@ -1,102 +1,102 @@
1
- const React = require('react');
2
- const { analyzeElement } = require('./analyzer');
3
- const { renderNode } = require('./renderer');
4
- const { injectStyles, blockClass, buildThemeVars } = require('./styles');
5
-
6
- function AutoSkeleton(props) {
7
- var loading = props.loading;
8
- var animation = props.animation || 'shimmer';
9
- var theme = props.theme || {};
10
- var count = props.count || 1;
11
- var children = props.children;
12
- var className = props.className || '';
13
- var style = props.style || {};
14
-
15
- React.useEffect(function() { injectStyles(); }, []);
16
- if (typeof document !== 'undefined') injectStyles();
17
-
18
- if (!loading) return children;
19
-
20
- var cssVarStyle = {};
21
- var varMap = { baseColor: '--ask-base-color', shimmerColor: '--ask-shimmer-color', duration: '--ask-duration', borderRadius: '--ask-border-radius' };
22
- Object.entries(theme).forEach(function(pair) { if (varMap[pair[0]]) cssVarStyle[varMap[pair[0]]] = pair[1]; });
23
-
24
- var descriptor = analyzeElement(children);
25
- var skeletonEl = renderNode(descriptor, { animation: animation });
26
-
27
- var items = [];
28
- for (var i = 0; i < Math.max(1, count); i++) {
29
- items.push(React.createElement('div', { key: i, style: i < count - 1 ? { marginBottom: '16px' } : {} }, skeletonEl));
30
- }
31
-
32
- return React.createElement('div', {
33
- className: 'ask-wrapper' + (className ? ' ' + className : ''),
34
- style: Object.assign({}, style, cssVarStyle),
35
- 'aria-busy': 'true',
36
- 'aria-label': 'Loading\u2026',
37
- }, items);
38
- }
39
-
40
- function SkeletonBlock(props) {
41
- var width = props.width || '100%';
42
- var height = props.height || '16px';
43
- var shape = props.shape;
44
- var animation = props.animation || 'shimmer';
45
- var style = props.style || {};
46
- var className = props.className || '';
47
- React.useEffect(function() { injectStyles(); }, []);
48
- if (typeof document !== 'undefined') injectStyles();
49
- return React.createElement('span', {
50
- className: blockClass(animation, shape) + (className ? ' ' + className : ''),
51
- style: Object.assign({ width: width, height: height, display: 'block' }, style),
52
- 'aria-hidden': 'true',
53
- });
54
- }
55
-
56
- function SkeletonText(props) {
57
- var lines = props.lines || 3;
58
- var animation = props.animation || 'shimmer';
59
- var lastLineWidth = props.lastLineWidth || '60%';
60
- var lineHeight = props.lineHeight || '1em';
61
- var gap = props.gap || '8px';
62
- var style = props.style || {};
63
- React.useEffect(function() { injectStyles(); }, []);
64
- if (typeof document !== 'undefined') injectStyles();
65
- var lineEls = [];
66
- for (var i = 0; i < lines; i++) {
67
- lineEls.push(React.createElement('span', { key: i, className: blockClass(animation), style: { display: 'block', width: i === lines - 1 ? lastLineWidth : '100%', height: lineHeight, marginBottom: i < lines - 1 ? gap : 0 }, 'aria-hidden': 'true' }));
68
- }
69
- return React.createElement('div', { style: style, 'aria-hidden': 'true' }, lineEls);
70
- }
71
-
72
- function SkeletonAvatar(props) {
73
- var size = props.size || '40px';
74
- return React.createElement(SkeletonBlock, { width: size, height: size, shape: 'circle', animation: props.animation || 'shimmer', style: props.style || {} });
75
- }
76
-
77
- function useSkeleton(initialLoading, options) {
78
- initialLoading = initialLoading !== undefined ? initialLoading : true;
79
- options = options || {};
80
- var state = React.useState(initialLoading);
81
- var loading = state[0];
82
- var setLoading = state[1];
83
- return { loading: loading, setLoading: setLoading, skeletonProps: Object.assign({ loading: loading }, options) };
84
- }
85
-
86
- function withSkeleton(WrappedComponent, defaultOptions) {
87
- defaultOptions = defaultOptions || {};
88
- var displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
89
- function WithSkeletonWrapper(props) {
90
- var loading = props.loading !== undefined ? props.loading : false;
91
- var skeletonAnimation = props.skeletonAnimation;
92
- var skeletonTheme = props.skeletonTheme;
93
- var skeletonCount = props.skeletonCount;
94
- var rest = Object.assign({}, props);
95
- delete rest.loading; delete rest.skeletonAnimation; delete rest.skeletonTheme; delete rest.skeletonCount;
96
- return React.createElement(AutoSkeleton, { loading: loading, animation: skeletonAnimation || defaultOptions.animation || 'shimmer', theme: skeletonTheme || defaultOptions.theme || {}, count: skeletonCount || defaultOptions.count || 1 }, React.createElement(WrappedComponent, rest));
97
- }
98
- WithSkeletonWrapper.displayName = 'WithSkeleton(' + displayName + ')';
99
- return WithSkeletonWrapper;
100
- }
101
-
102
- module.exports = { AutoSkeleton, SkeletonBlock, SkeletonText, SkeletonAvatar, useSkeleton, withSkeleton, analyzeElement, renderNode, injectStyles };
1
+ const React = require('react');
2
+ const { analyzeElement } = require('./analyzer');
3
+ const { renderNode } = require('./renderer');
4
+ const { injectStyles, blockClass, buildThemeVars } = require('./styles');
5
+
6
+ function AutoSkeleton(props) {
7
+ var loading = props.loading;
8
+ var animation = props.animation || 'shimmer';
9
+ var theme = props.theme || {};
10
+ var count = props.count || 1;
11
+ var children = props.children;
12
+ var className = props.className || '';
13
+ var style = props.style || {};
14
+
15
+ React.useEffect(function() { injectStyles(); }, []);
16
+ if (typeof document !== 'undefined') injectStyles();
17
+
18
+ if (!loading) return children;
19
+
20
+ var cssVarStyle = {};
21
+ var varMap = { baseColor: '--ask-base-color', shimmerColor: '--ask-shimmer-color', duration: '--ask-duration', borderRadius: '--ask-border-radius' };
22
+ Object.entries(theme).forEach(function(pair) { if (varMap[pair[0]]) cssVarStyle[varMap[pair[0]]] = pair[1]; });
23
+
24
+ var descriptor = analyzeElement(children);
25
+ var skeletonEl = renderNode(descriptor, { animation: animation });
26
+
27
+ var items = [];
28
+ for (var i = 0; i < Math.max(1, count); i++) {
29
+ items.push(React.createElement('div', { key: i, style: i < count - 1 ? { marginBottom: '16px' } : {} }, skeletonEl));
30
+ }
31
+
32
+ return React.createElement('div', {
33
+ className: 'ask-wrapper' + (className ? ' ' + className : ''),
34
+ style: Object.assign({}, style, cssVarStyle),
35
+ 'aria-busy': 'true',
36
+ 'aria-label': 'Loading\u2026',
37
+ }, items);
38
+ }
39
+
40
+ function SkeletonBlock(props) {
41
+ var width = props.width || '100%';
42
+ var height = props.height || '16px';
43
+ var shape = props.shape;
44
+ var animation = props.animation || 'shimmer';
45
+ var style = props.style || {};
46
+ var className = props.className || '';
47
+ React.useEffect(function() { injectStyles(); }, []);
48
+ if (typeof document !== 'undefined') injectStyles();
49
+ return React.createElement('span', {
50
+ className: blockClass(animation, shape) + (className ? ' ' + className : ''),
51
+ style: Object.assign({ width: width, height: height, display: 'block' }, style),
52
+ 'aria-hidden': 'true',
53
+ });
54
+ }
55
+
56
+ function SkeletonText(props) {
57
+ var lines = props.lines || 3;
58
+ var animation = props.animation || 'shimmer';
59
+ var lastLineWidth = props.lastLineWidth || '60%';
60
+ var lineHeight = props.lineHeight || '1em';
61
+ var gap = props.gap || '8px';
62
+ var style = props.style || {};
63
+ React.useEffect(function() { injectStyles(); }, []);
64
+ if (typeof document !== 'undefined') injectStyles();
65
+ var lineEls = [];
66
+ for (var i = 0; i < lines; i++) {
67
+ lineEls.push(React.createElement('span', { key: i, className: blockClass(animation), style: { display: 'block', width: i === lines - 1 ? lastLineWidth : '100%', height: lineHeight, marginBottom: i < lines - 1 ? gap : 0 }, 'aria-hidden': 'true' }));
68
+ }
69
+ return React.createElement('div', { style: style, 'aria-hidden': 'true' }, lineEls);
70
+ }
71
+
72
+ function SkeletonAvatar(props) {
73
+ var size = props.size || '40px';
74
+ return React.createElement(SkeletonBlock, { width: size, height: size, shape: 'circle', animation: props.animation || 'shimmer', style: props.style || {} });
75
+ }
76
+
77
+ function useSkeleton(initialLoading, options) {
78
+ initialLoading = initialLoading !== undefined ? initialLoading : true;
79
+ options = options || {};
80
+ var state = React.useState(initialLoading);
81
+ var loading = state[0];
82
+ var setLoading = state[1];
83
+ return { loading: loading, setLoading: setLoading, skeletonProps: Object.assign({ loading: loading }, options) };
84
+ }
85
+
86
+ function withSkeleton(WrappedComponent, defaultOptions) {
87
+ defaultOptions = defaultOptions || {};
88
+ var displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
89
+ function WithSkeletonWrapper(props) {
90
+ var loading = props.loading !== undefined ? props.loading : false;
91
+ var skeletonAnimation = props.skeletonAnimation;
92
+ var skeletonTheme = props.skeletonTheme;
93
+ var skeletonCount = props.skeletonCount;
94
+ var rest = Object.assign({}, props);
95
+ delete rest.loading; delete rest.skeletonAnimation; delete rest.skeletonTheme; delete rest.skeletonCount;
96
+ return React.createElement(AutoSkeleton, { loading: loading, animation: skeletonAnimation || defaultOptions.animation || 'shimmer', theme: skeletonTheme || defaultOptions.theme || {}, count: skeletonCount || defaultOptions.count || 1 }, React.createElement(WrappedComponent, rest));
97
+ }
98
+ WithSkeletonWrapper.displayName = 'WithSkeleton(' + displayName + ')';
99
+ return WithSkeletonWrapper;
100
+ }
101
+
102
+ module.exports = { AutoSkeleton, SkeletonBlock, SkeletonText, SkeletonAvatar, useSkeleton, withSkeleton, analyzeElement, renderNode, injectStyles };
package/src/renderer.js CHANGED
@@ -1,84 +1,84 @@
1
- const React = require('react');
2
- const { ELEMENT_TYPES } = require('./analyzer');
3
- const { blockClass } = require('./styles');
4
-
5
- const DEFAULTS = {
6
- text: { width: '80%', height: '1em' },
7
- heading: { width: '60%', height: '1.4em' },
8
- image: { width: '100%', height: '200px' },
9
- avatar: { width: '40px', height: '40px' },
10
- button: { width: '100px',height: '36px' },
11
- input: { width: '100%', height: '38px' },
12
- icon: { width: '24px', height: '24px' },
13
- };
14
-
15
- let _key = 0;
16
- function nk() { return 'ask-' + (++_key); }
17
-
18
- function bStyle(def, hints, extra) {
19
- hints = hints || {};
20
- extra = extra || {};
21
- return Object.assign({ width: hints.width || def.width, height: hints.height || def.height, display: 'block', margin: hints.margin || '0' }, extra);
22
- }
23
-
24
- function renderNode(node, options) {
25
- if (!node) return null;
26
- options = options || {};
27
- var animation = options.animation || 'shimmer';
28
- var nodeType = node.nodeType;
29
- var hints = node.styleHints || {};
30
-
31
- if (nodeType === ELEMENT_TYPES.TEXT) {
32
- var isHeading = node.isHeading;
33
- var def = isHeading ? DEFAULTS.heading : DEFAULTS.text;
34
- var len = (node.content || '').length;
35
- var lines = Math.min(Math.max(1, Math.round(len / 40)), 4);
36
- if (lines <= 1) {
37
- return React.createElement('span', { key: nk(), className: blockClass(animation), style: bStyle(def, hints, { width: hints.width || (len > 60 ? '95%' : def.width) }), 'aria-hidden': 'true' });
38
- }
39
- var lineEls = [];
40
- for (var i = 0; i < lines; i++) {
41
- lineEls.push(React.createElement('span', { key: nk(), className: blockClass(animation), style: { display: 'block', width: i === lines-1 ? '65%' : '100%', height: def.height, marginBottom: '6px' }, 'aria-hidden': 'true' }));
42
- }
43
- return React.createElement('div', { key: nk() }, lineEls);
44
- }
45
-
46
- if (nodeType === ELEMENT_TYPES.IMAGE) {
47
- return React.createElement('span', { key: nk(), className: blockClass(animation, 'rounded'), style: bStyle(DEFAULTS.image, hints), 'aria-hidden': 'true' });
48
- }
49
-
50
- if (nodeType === ELEMENT_TYPES.AVATAR) {
51
- return React.createElement('span', { key: nk(), className: blockClass(animation, 'circle'), style: bStyle(DEFAULTS.avatar, hints), 'aria-hidden': 'true' });
52
- }
53
-
54
- if (nodeType === ELEMENT_TYPES.ICON) {
55
- return React.createElement('span', { key: nk(), className: blockClass(animation, 'circle'), style: bStyle(DEFAULTS.icon, hints), 'aria-hidden': 'true' });
56
- }
57
-
58
- if (nodeType === ELEMENT_TYPES.BUTTON) {
59
- return React.createElement('span', { key: nk(), className: blockClass(animation, 'pill'), style: bStyle(DEFAULTS.button, hints), 'aria-hidden': 'true' });
60
- }
61
-
62
- if (nodeType === ELEMENT_TYPES.INPUT) {
63
- return React.createElement('span', { key: nk(), className: blockClass(animation, 'rounded'), style: bStyle(DEFAULTS.input, hints), 'aria-hidden': 'true' });
64
- }
65
-
66
- if (nodeType === ELEMENT_TYPES.CONTAINER) {
67
- var children = (node.children || []).map(function(c) { return renderNode(c, options); }).filter(Boolean);
68
- var tag = node.tag || 'div';
69
- var cStyle = {};
70
- if (hints.display) cStyle.display = hints.display;
71
- if (hints.flexDirection) cStyle.flexDirection = hints.flexDirection;
72
- if (hints.gap) cStyle.gap = hints.gap;
73
- if (hints.padding) cStyle.padding = hints.padding;
74
- if (hints.margin) cStyle.margin = hints.margin;
75
- if (children.length === 0) {
76
- return React.createElement('span', { key: nk(), className: blockClass(animation), style: bStyle({ width: '100%', height: '20px' }, hints), 'aria-hidden': 'true' });
77
- }
78
- return React.createElement(tag, { key: nk(), style: cStyle, className: 'ask-wrapper' }, children);
79
- }
80
-
81
- return null;
82
- }
83
-
84
- module.exports = { renderNode };
1
+ const React = require('react');
2
+ const { ELEMENT_TYPES } = require('./analyzer');
3
+ const { blockClass } = require('./styles');
4
+
5
+ const DEFAULTS = {
6
+ text: { width: '80%', height: '1em' },
7
+ heading: { width: '60%', height: '1.4em' },
8
+ image: { width: '100%', height: '200px' },
9
+ avatar: { width: '40px', height: '40px' },
10
+ button: { width: '100px',height: '36px' },
11
+ input: { width: '100%', height: '38px' },
12
+ icon: { width: '24px', height: '24px' },
13
+ };
14
+
15
+ let _key = 0;
16
+ function nk() { return 'ask-' + (++_key); }
17
+
18
+ function bStyle(def, hints, extra) {
19
+ hints = hints || {};
20
+ extra = extra || {};
21
+ return Object.assign({ width: hints.width || def.width, height: hints.height || def.height, display: 'block', margin: hints.margin || '0' }, extra);
22
+ }
23
+
24
+ function renderNode(node, options) {
25
+ if (!node) return null;
26
+ options = options || {};
27
+ var animation = options.animation || 'shimmer';
28
+ var nodeType = node.nodeType;
29
+ var hints = node.styleHints || {};
30
+
31
+ if (nodeType === ELEMENT_TYPES.TEXT) {
32
+ var isHeading = node.isHeading;
33
+ var def = isHeading ? DEFAULTS.heading : DEFAULTS.text;
34
+ var len = (node.content || '').length;
35
+ var lines = Math.min(Math.max(1, Math.round(len / 40)), 4);
36
+ if (lines <= 1) {
37
+ return React.createElement('span', { key: nk(), className: blockClass(animation), style: bStyle(def, hints, { width: hints.width || (len > 60 ? '95%' : def.width) }), 'aria-hidden': 'true' });
38
+ }
39
+ var lineEls = [];
40
+ for (var i = 0; i < lines; i++) {
41
+ lineEls.push(React.createElement('span', { key: nk(), className: blockClass(animation), style: { display: 'block', width: i === lines-1 ? '65%' : '100%', height: def.height, marginBottom: '6px' }, 'aria-hidden': 'true' }));
42
+ }
43
+ return React.createElement('div', { key: nk() }, lineEls);
44
+ }
45
+
46
+ if (nodeType === ELEMENT_TYPES.IMAGE) {
47
+ return React.createElement('span', { key: nk(), className: blockClass(animation, 'rounded'), style: bStyle(DEFAULTS.image, hints), 'aria-hidden': 'true' });
48
+ }
49
+
50
+ if (nodeType === ELEMENT_TYPES.AVATAR) {
51
+ return React.createElement('span', { key: nk(), className: blockClass(animation, 'circle'), style: bStyle(DEFAULTS.avatar, hints), 'aria-hidden': 'true' });
52
+ }
53
+
54
+ if (nodeType === ELEMENT_TYPES.ICON) {
55
+ return React.createElement('span', { key: nk(), className: blockClass(animation, 'circle'), style: bStyle(DEFAULTS.icon, hints), 'aria-hidden': 'true' });
56
+ }
57
+
58
+ if (nodeType === ELEMENT_TYPES.BUTTON) {
59
+ return React.createElement('span', { key: nk(), className: blockClass(animation, 'pill'), style: bStyle(DEFAULTS.button, hints), 'aria-hidden': 'true' });
60
+ }
61
+
62
+ if (nodeType === ELEMENT_TYPES.INPUT) {
63
+ return React.createElement('span', { key: nk(), className: blockClass(animation, 'rounded'), style: bStyle(DEFAULTS.input, hints), 'aria-hidden': 'true' });
64
+ }
65
+
66
+ if (nodeType === ELEMENT_TYPES.CONTAINER) {
67
+ var children = (node.children || []).map(function(c) { return renderNode(c, options); }).filter(Boolean);
68
+ var tag = node.tag || 'div';
69
+ var cStyle = {};
70
+ if (hints.display) cStyle.display = hints.display;
71
+ if (hints.flexDirection) cStyle.flexDirection = hints.flexDirection;
72
+ if (hints.gap) cStyle.gap = hints.gap;
73
+ if (hints.padding) cStyle.padding = hints.padding;
74
+ if (hints.margin) cStyle.margin = hints.margin;
75
+ if (children.length === 0) {
76
+ return React.createElement('span', { key: nk(), className: blockClass(animation), style: bStyle({ width: '100%', height: '20px' }, hints), 'aria-hidden': 'true' });
77
+ }
78
+ return React.createElement(tag, { key: nk(), style: cStyle, className: 'ask-wrapper' }, children);
79
+ }
80
+
81
+ return null;
82
+ }
83
+
84
+ module.exports = { renderNode };
package/src/styles.js CHANGED
@@ -1,76 +1,76 @@
1
- const STYLE_ID = 'auto-skeleton-styles';
2
-
3
- const CSS = `
4
- .ask-block {
5
- display: inline-block;
6
- position: relative;
7
- overflow: hidden;
8
- background-color: var(--ask-base-color, #e2e8f0);
9
- border-radius: var(--ask-border-radius, 4px);
10
- vertical-align: middle;
11
- }
12
- .ask-shimmer::after {
13
- content: '';
14
- position: absolute;
15
- inset: 0;
16
- transform: translateX(-100%);
17
- background: linear-gradient(90deg, transparent 0%, var(--ask-shimmer-color, rgba(255,255,255,0.55)) 50%, transparent 100%);
18
- animation: ask-shimmer var(--ask-duration, 1.4s) infinite;
19
- }
20
- @keyframes ask-shimmer { 100% { transform: translateX(100%); } }
21
- .ask-pulse { animation: ask-pulse var(--ask-duration, 1.8s) ease-in-out infinite; }
22
- @keyframes ask-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
23
- .ask-wave::after {
24
- content: '';
25
- position: absolute;
26
- inset: 0;
27
- transform: translateX(-100%);
28
- background: linear-gradient(90deg, transparent 0%, var(--ask-shimmer-color, rgba(255,255,255,0.8)) 50%, transparent 100%);
29
- animation: ask-wave var(--ask-duration, 1.6s) ease-in-out infinite;
30
- overflow: hidden;
31
- }
32
- @keyframes ask-wave { 0% { transform: translateX(-100%); } 100% { transform: translateX(150%); } }
33
- @media (prefers-color-scheme: dark) {
34
- .ask-block { background-color: var(--ask-base-color-dark, #2d3748); }
35
- }
36
- .ask-circle { border-radius: 50%; }
37
- .ask-rounded { border-radius: 8px; }
38
- .ask-pill { border-radius: 9999px; }
39
- .ask-wrapper { pointer-events: none; user-select: none; }
40
- `;
41
-
42
- let injected = false;
43
-
44
- function injectStyles() {
45
- if (injected || typeof document === 'undefined') return;
46
- if (document.getElementById(STYLE_ID)) { injected = true; return; }
47
- const style = document.createElement('style');
48
- style.id = STYLE_ID;
49
- style.textContent = CSS;
50
- document.head.appendChild(style);
51
- injected = true;
52
- }
53
-
54
- function blockClass(animation, shape) {
55
- animation = animation || 'shimmer';
56
- const parts = ['ask-block'];
57
- if (animation !== 'none') parts.push('ask-' + animation);
58
- if (shape) parts.push('ask-' + shape);
59
- return parts.join(' ');
60
- }
61
-
62
- function buildThemeVars(theme) {
63
- theme = theme || {};
64
- const map = {
65
- baseColor: '--ask-base-color',
66
- shimmerColor: '--ask-shimmer-color',
67
- duration: '--ask-duration',
68
- borderRadius: '--ask-border-radius',
69
- };
70
- return Object.entries(theme)
71
- .filter(function(pair) { return map[pair[0]]; })
72
- .map(function(pair) { return map[pair[0]] + ':' + pair[1]; })
73
- .join(';');
74
- }
75
-
76
- module.exports = { injectStyles, blockClass, buildThemeVars, CSS };
1
+ const STYLE_ID = 'auto-skeleton-styles';
2
+
3
+ const CSS = `
4
+ .ask-block {
5
+ display: inline-block;
6
+ position: relative;
7
+ overflow: hidden;
8
+ background-color: var(--ask-base-color, #e2e8f0);
9
+ border-radius: var(--ask-border-radius, 4px);
10
+ vertical-align: middle;
11
+ }
12
+ .ask-shimmer::after {
13
+ content: '';
14
+ position: absolute;
15
+ inset: 0;
16
+ transform: translateX(-100%);
17
+ background: linear-gradient(90deg, transparent 0%, var(--ask-shimmer-color, rgba(255,255,255,0.55)) 50%, transparent 100%);
18
+ animation: ask-shimmer var(--ask-duration, 1.4s) infinite;
19
+ }
20
+ @keyframes ask-shimmer { 100% { transform: translateX(100%); } }
21
+ .ask-pulse { animation: ask-pulse var(--ask-duration, 1.8s) ease-in-out infinite; }
22
+ @keyframes ask-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
23
+ .ask-wave::after {
24
+ content: '';
25
+ position: absolute;
26
+ inset: 0;
27
+ transform: translateX(-100%);
28
+ background: linear-gradient(90deg, transparent 0%, var(--ask-shimmer-color, rgba(255,255,255,0.8)) 50%, transparent 100%);
29
+ animation: ask-wave var(--ask-duration, 1.6s) ease-in-out infinite;
30
+ overflow: hidden;
31
+ }
32
+ @keyframes ask-wave { 0% { transform: translateX(-100%); } 100% { transform: translateX(150%); } }
33
+ @media (prefers-color-scheme: dark) {
34
+ .ask-block { background-color: var(--ask-base-color-dark, #2d3748); }
35
+ }
36
+ .ask-circle { border-radius: 50%; }
37
+ .ask-rounded { border-radius: 8px; }
38
+ .ask-pill { border-radius: 9999px; }
39
+ .ask-wrapper { pointer-events: none; user-select: none; }
40
+ `;
41
+
42
+ let injected = false;
43
+
44
+ function injectStyles() {
45
+ if (injected || typeof document === 'undefined') return;
46
+ if (document.getElementById(STYLE_ID)) { injected = true; return; }
47
+ const style = document.createElement('style');
48
+ style.id = STYLE_ID;
49
+ style.textContent = CSS;
50
+ document.head.appendChild(style);
51
+ injected = true;
52
+ }
53
+
54
+ function blockClass(animation, shape) {
55
+ animation = animation || 'shimmer';
56
+ const parts = ['ask-block'];
57
+ if (animation !== 'none') parts.push('ask-' + animation);
58
+ if (shape) parts.push('ask-' + shape);
59
+ return parts.join(' ');
60
+ }
61
+
62
+ function buildThemeVars(theme) {
63
+ theme = theme || {};
64
+ const map = {
65
+ baseColor: '--ask-base-color',
66
+ shimmerColor: '--ask-shimmer-color',
67
+ duration: '--ask-duration',
68
+ borderRadius: '--ask-border-radius',
69
+ };
70
+ return Object.entries(theme)
71
+ .filter(function(pair) { return map[pair[0]]; })
72
+ .map(function(pair) { return map[pair[0]] + ':' + pair[1]; })
73
+ .join(';');
74
+ }
75
+
76
+ module.exports = { injectStyles, blockClass, buildThemeVars, CSS };