react-native-skelo 0.0.1 → 0.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/LICENSE +21 -0
- package/README.md +149 -20
- package/lib/commonjs/Skelo.js +104 -0
- package/lib/commonjs/Skelo.js.map +1 -0
- package/lib/commonjs/animations/AnimationWrapper.js +76 -0
- package/lib/commonjs/animations/AnimationWrapper.js.map +1 -0
- package/lib/commonjs/animations/PulseAnimation.js +52 -0
- package/lib/commonjs/animations/PulseAnimation.js.map +1 -0
- package/lib/commonjs/animations/ShimmerAnimation.js +73 -0
- package/lib/commonjs/animations/ShimmerAnimation.js.map +1 -0
- package/lib/commonjs/animations/index.js +27 -0
- package/lib/commonjs/animations/index.js.map +1 -0
- package/lib/commonjs/components/MeasuredBlock.js +64 -0
- package/lib/commonjs/components/MeasuredBlock.js.map +1 -0
- package/lib/commonjs/components/Skeleton.js +144 -0
- package/lib/commonjs/components/Skeleton.js.map +1 -0
- package/lib/commonjs/components/SkeletonIgnore.js +30 -0
- package/lib/commonjs/components/SkeletonIgnore.js.map +1 -0
- package/lib/commonjs/components/SkeletonRenderer.js +270 -0
- package/lib/commonjs/components/SkeletonRenderer.js.map +1 -0
- package/lib/commonjs/components/primitives/SkeletonBox.js +47 -0
- package/lib/commonjs/components/primitives/SkeletonBox.js.map +1 -0
- package/lib/commonjs/components/primitives/SkeletonCircle.js +44 -0
- package/lib/commonjs/components/primitives/SkeletonCircle.js.map +1 -0
- package/lib/commonjs/components/primitives/SkeletonImage.js +46 -0
- package/lib/commonjs/components/primitives/SkeletonImage.js.map +1 -0
- package/lib/commonjs/components/primitives/SkeletonText.js +60 -0
- package/lib/commonjs/components/primitives/SkeletonText.js.map +1 -0
- package/lib/commonjs/components/primitives/index.js +34 -0
- package/lib/commonjs/components/primitives/index.js.map +1 -0
- package/lib/commonjs/components/withSkeleton.js +45 -0
- package/lib/commonjs/components/withSkeleton.js.map +1 -0
- package/lib/commonjs/constants/defaults.js +43 -0
- package/lib/commonjs/constants/defaults.js.map +1 -0
- package/lib/commonjs/core/deepRegistry.js +18 -0
- package/lib/commonjs/core/deepRegistry.js.map +1 -0
- package/lib/commonjs/core/generator/StyleSkeleton.js +101 -0
- package/lib/commonjs/core/generator/StyleSkeleton.js.map +1 -0
- package/lib/commonjs/core/parser/Parser.js +84 -0
- package/lib/commonjs/core/parser/Parser.js.map +1 -0
- package/lib/commonjs/core/parser/expandOffline.js +83 -0
- package/lib/commonjs/core/parser/expandOffline.js.map +1 -0
- package/lib/commonjs/core/parser/hostTreeReconciler.js +228 -0
- package/lib/commonjs/core/parser/hostTreeReconciler.js.map +1 -0
- package/lib/commonjs/core/parser/index.js +13 -0
- package/lib/commonjs/core/parser/index.js.map +1 -0
- package/lib/commonjs/core/registry/SkeletonRegistry.js +113 -0
- package/lib/commonjs/core/registry/SkeletonRegistry.js.map +1 -0
- package/lib/commonjs/core/registry/index.js +19 -0
- package/lib/commonjs/core/registry/index.js.map +1 -0
- package/lib/commonjs/core/resolver/StyleResolver.js +83 -0
- package/lib/commonjs/core/resolver/StyleResolver.js.map +1 -0
- package/lib/commonjs/core/resolver/index.js +13 -0
- package/lib/commonjs/core/resolver/index.js.map +1 -0
- package/lib/commonjs/deep.js +18 -0
- package/lib/commonjs/deep.js.map +1 -0
- package/lib/commonjs/index.js +66 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/plugins/ImagePlugin.js +60 -0
- package/lib/commonjs/plugins/ImagePlugin.js.map +1 -0
- package/lib/commonjs/plugins/TextPlugin.js +70 -0
- package/lib/commonjs/plugins/TextPlugin.js.map +1 -0
- package/lib/commonjs/plugins/ViewPlugin.js +71 -0
- package/lib/commonjs/plugins/ViewPlugin.js.map +1 -0
- package/lib/commonjs/plugins/index.js +27 -0
- package/lib/commonjs/plugins/index.js.map +1 -0
- package/lib/commonjs/types/index.js +2 -0
- package/lib/commonjs/types/index.js.map +1 -0
- package/lib/commonjs/types/plugin.js +6 -0
- package/lib/commonjs/types/plugin.js.map +1 -0
- package/lib/commonjs/types/skeleton.js +6 -0
- package/lib/commonjs/types/skeleton.js.map +1 -0
- package/lib/commonjs/utils/componentUtils.js +138 -0
- package/lib/commonjs/utils/componentUtils.js.map +1 -0
- package/lib/commonjs/utils/index.js +62 -0
- package/lib/commonjs/utils/index.js.map +1 -0
- package/lib/commonjs/utils/styleUtils.js +76 -0
- package/lib/commonjs/utils/styleUtils.js.map +1 -0
- package/lib/module/Skelo.js +98 -0
- package/lib/module/Skelo.js.map +1 -0
- package/lib/module/animations/AnimationWrapper.js +69 -0
- package/lib/module/animations/AnimationWrapper.js.map +1 -0
- package/lib/module/animations/PulseAnimation.js +45 -0
- package/lib/module/animations/PulseAnimation.js.map +1 -0
- package/lib/module/animations/ShimmerAnimation.js +66 -0
- package/lib/module/animations/ShimmerAnimation.js.map +1 -0
- package/lib/module/animations/index.js +4 -0
- package/lib/module/animations/index.js.map +1 -0
- package/lib/module/components/MeasuredBlock.js +57 -0
- package/lib/module/components/MeasuredBlock.js.map +1 -0
- package/lib/module/components/Skeleton.js +138 -0
- package/lib/module/components/Skeleton.js.map +1 -0
- package/lib/module/components/SkeletonIgnore.js +23 -0
- package/lib/module/components/SkeletonIgnore.js.map +1 -0
- package/lib/module/components/SkeletonRenderer.js +264 -0
- package/lib/module/components/SkeletonRenderer.js.map +1 -0
- package/lib/module/components/primitives/SkeletonBox.js +40 -0
- package/lib/module/components/primitives/SkeletonBox.js.map +1 -0
- package/lib/module/components/primitives/SkeletonCircle.js +37 -0
- package/lib/module/components/primitives/SkeletonCircle.js.map +1 -0
- package/lib/module/components/primitives/SkeletonImage.js +39 -0
- package/lib/module/components/primitives/SkeletonImage.js.map +1 -0
- package/lib/module/components/primitives/SkeletonText.js +53 -0
- package/lib/module/components/primitives/SkeletonText.js.map +1 -0
- package/lib/module/components/primitives/index.js +5 -0
- package/lib/module/components/primitives/index.js.map +1 -0
- package/lib/module/components/withSkeleton.js +38 -0
- package/lib/module/components/withSkeleton.js.map +1 -0
- package/lib/module/constants/defaults.js +37 -0
- package/lib/module/constants/defaults.js.map +1 -0
- package/lib/module/core/deepRegistry.js +11 -0
- package/lib/module/core/deepRegistry.js.map +1 -0
- package/lib/module/core/generator/StyleSkeleton.js +95 -0
- package/lib/module/core/generator/StyleSkeleton.js.map +1 -0
- package/lib/module/core/parser/Parser.js +77 -0
- package/lib/module/core/parser/Parser.js.map +1 -0
- package/lib/module/core/parser/expandOffline.js +77 -0
- package/lib/module/core/parser/expandOffline.js.map +1 -0
- package/lib/module/core/parser/hostTreeReconciler.js +222 -0
- package/lib/module/core/parser/hostTreeReconciler.js.map +1 -0
- package/lib/module/core/parser/index.js +2 -0
- package/lib/module/core/parser/index.js.map +1 -0
- package/lib/module/core/registry/SkeletonRegistry.js +106 -0
- package/lib/module/core/registry/SkeletonRegistry.js.map +1 -0
- package/lib/module/core/registry/index.js +2 -0
- package/lib/module/core/registry/index.js.map +1 -0
- package/lib/module/core/resolver/StyleResolver.js +77 -0
- package/lib/module/core/resolver/StyleResolver.js.map +1 -0
- package/lib/module/core/resolver/index.js +2 -0
- package/lib/module/core/resolver/index.js.map +1 -0
- package/lib/module/deep.js +11 -0
- package/lib/module/deep.js.map +1 -0
- package/lib/module/index.js +21 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/plugins/ImagePlugin.js +53 -0
- package/lib/module/plugins/ImagePlugin.js.map +1 -0
- package/lib/module/plugins/TextPlugin.js +63 -0
- package/lib/module/plugins/TextPlugin.js.map +1 -0
- package/lib/module/plugins/ViewPlugin.js +64 -0
- package/lib/module/plugins/ViewPlugin.js.map +1 -0
- package/lib/module/plugins/index.js +4 -0
- package/lib/module/plugins/index.js.map +1 -0
- package/lib/module/types/index.js +2 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/types/plugin.js +2 -0
- package/lib/module/types/plugin.js.map +1 -0
- package/lib/module/types/skeleton.js +2 -0
- package/lib/module/types/skeleton.js.map +1 -0
- package/lib/module/utils/componentUtils.js +123 -0
- package/lib/module/utils/componentUtils.js.map +1 -0
- package/lib/module/utils/index.js +3 -0
- package/lib/module/utils/index.js.map +1 -0
- package/lib/module/utils/styleUtils.js +66 -0
- package/lib/module/utils/styleUtils.js.map +1 -0
- package/lib/typescript/Skelo.d.ts +76 -0
- package/lib/typescript/Skelo.d.ts.map +1 -0
- package/lib/typescript/animations/AnimationWrapper.d.ts +66 -0
- package/lib/typescript/animations/AnimationWrapper.d.ts.map +1 -0
- package/lib/typescript/animations/PulseAnimation.d.ts +15 -0
- package/lib/typescript/animations/PulseAnimation.d.ts.map +1 -0
- package/lib/typescript/animations/ShimmerAnimation.d.ts +15 -0
- package/lib/typescript/animations/ShimmerAnimation.d.ts.map +1 -0
- package/lib/typescript/animations/index.d.ts +5 -0
- package/lib/typescript/animations/index.d.ts.map +1 -0
- package/lib/typescript/components/MeasuredBlock.d.ts +32 -0
- package/lib/typescript/components/MeasuredBlock.d.ts.map +1 -0
- package/lib/typescript/components/Skeleton.d.ts +37 -0
- package/lib/typescript/components/Skeleton.d.ts.map +1 -0
- package/lib/typescript/components/SkeletonIgnore.d.ts +26 -0
- package/lib/typescript/components/SkeletonIgnore.d.ts.map +1 -0
- package/lib/typescript/components/SkeletonRenderer.d.ts +39 -0
- package/lib/typescript/components/SkeletonRenderer.d.ts.map +1 -0
- package/lib/typescript/components/primitives/SkeletonBox.d.ts +21 -0
- package/lib/typescript/components/primitives/SkeletonBox.d.ts.map +1 -0
- package/lib/typescript/components/primitives/SkeletonCircle.d.ts +21 -0
- package/lib/typescript/components/primitives/SkeletonCircle.d.ts.map +1 -0
- package/lib/typescript/components/primitives/SkeletonImage.d.ts +21 -0
- package/lib/typescript/components/primitives/SkeletonImage.d.ts.map +1 -0
- package/lib/typescript/components/primitives/SkeletonText.d.ts +21 -0
- package/lib/typescript/components/primitives/SkeletonText.d.ts.map +1 -0
- package/lib/typescript/components/primitives/index.d.ts +5 -0
- package/lib/typescript/components/primitives/index.d.ts.map +1 -0
- package/lib/typescript/components/withSkeleton.d.ts +37 -0
- package/lib/typescript/components/withSkeleton.d.ts.map +1 -0
- package/lib/typescript/constants/defaults.d.ts +29 -0
- package/lib/typescript/constants/defaults.d.ts.map +1 -0
- package/lib/typescript/core/deepRegistry.d.ts +7 -0
- package/lib/typescript/core/deepRegistry.d.ts.map +1 -0
- package/lib/typescript/core/generator/StyleSkeleton.d.ts +36 -0
- package/lib/typescript/core/generator/StyleSkeleton.d.ts.map +1 -0
- package/lib/typescript/core/parser/Parser.d.ts +30 -0
- package/lib/typescript/core/parser/Parser.d.ts.map +1 -0
- package/lib/typescript/core/parser/expandOffline.d.ts +10 -0
- package/lib/typescript/core/parser/expandOffline.d.ts.map +1 -0
- package/lib/typescript/core/parser/hostTreeReconciler.d.ts +28 -0
- package/lib/typescript/core/parser/hostTreeReconciler.d.ts.map +1 -0
- package/lib/typescript/core/parser/index.d.ts +2 -0
- package/lib/typescript/core/parser/index.d.ts.map +1 -0
- package/lib/typescript/core/registry/SkeletonRegistry.d.ts +57 -0
- package/lib/typescript/core/registry/SkeletonRegistry.d.ts.map +1 -0
- package/lib/typescript/core/registry/index.d.ts +2 -0
- package/lib/typescript/core/registry/index.d.ts.map +1 -0
- package/lib/typescript/core/resolver/StyleResolver.d.ts +54 -0
- package/lib/typescript/core/resolver/StyleResolver.d.ts.map +1 -0
- package/lib/typescript/core/resolver/index.d.ts +2 -0
- package/lib/typescript/core/resolver/index.d.ts.map +1 -0
- package/lib/typescript/deep.d.ts +2 -0
- package/lib/typescript/deep.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +17 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/plugins/ImagePlugin.d.ts +11 -0
- package/lib/typescript/plugins/ImagePlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/TextPlugin.d.ts +12 -0
- package/lib/typescript/plugins/TextPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/ViewPlugin.d.ts +11 -0
- package/lib/typescript/plugins/ViewPlugin.d.ts.map +1 -0
- package/lib/typescript/plugins/index.d.ts +4 -0
- package/lib/typescript/plugins/index.d.ts.map +1 -0
- package/lib/typescript/types/index.d.ts +3 -0
- package/lib/typescript/types/index.d.ts.map +1 -0
- package/lib/typescript/types/plugin.d.ts +144 -0
- package/lib/typescript/types/plugin.d.ts.map +1 -0
- package/lib/typescript/types/skeleton.d.ts +176 -0
- package/lib/typescript/types/skeleton.d.ts.map +1 -0
- package/lib/typescript/utils/componentUtils.d.ts +40 -0
- package/lib/typescript/utils/componentUtils.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +3 -0
- package/lib/typescript/utils/index.d.ts.map +1 -0
- package/lib/typescript/utils/styleUtils.d.ts +41 -0
- package/lib/typescript/utils/styleUtils.d.ts.map +1 -0
- package/package.json +169 -11
- package/src/Skelo.ts +103 -0
- package/src/animations/AnimationWrapper.tsx +139 -0
- package/src/animations/PulseAnimation.tsx +76 -0
- package/src/animations/ShimmerAnimation.tsx +101 -0
- package/src/animations/index.ts +4 -0
- package/src/components/MeasuredBlock.tsx +85 -0
- package/src/components/Skeleton.tsx +149 -0
- package/src/components/SkeletonIgnore.tsx +27 -0
- package/src/components/SkeletonRenderer.tsx +341 -0
- package/src/components/primitives/SkeletonBox.tsx +53 -0
- package/src/components/primitives/SkeletonCircle.tsx +48 -0
- package/src/components/primitives/SkeletonImage.tsx +50 -0
- package/src/components/primitives/SkeletonText.tsx +69 -0
- package/src/components/primitives/index.ts +4 -0
- package/src/components/withSkeleton.tsx +62 -0
- package/src/constants/defaults.ts +38 -0
- package/src/core/deepRegistry.ts +16 -0
- package/src/core/generator/StyleSkeleton.tsx +138 -0
- package/src/core/parser/Parser.ts +86 -0
- package/src/core/parser/expandOffline.tsx +83 -0
- package/src/core/parser/hostTreeReconciler.tsx +271 -0
- package/src/core/parser/index.ts +1 -0
- package/src/core/registry/SkeletonRegistry.ts +111 -0
- package/src/core/registry/index.ts +1 -0
- package/src/core/resolver/StyleResolver.ts +80 -0
- package/src/core/resolver/index.ts +1 -0
- package/src/deep.ts +12 -0
- package/src/index.ts +30 -0
- package/src/plugins/ImagePlugin.tsx +65 -0
- package/src/plugins/TextPlugin.tsx +71 -0
- package/src/plugins/ViewPlugin.tsx +67 -0
- package/src/plugins/index.ts +3 -0
- package/src/types/index.ts +14 -0
- package/src/types/plugin.ts +164 -0
- package/src/types/skeleton.ts +197 -0
- package/src/utils/componentUtils.ts +166 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/styleUtils.ts +72 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { SkeletonPlugin } from '../types';
|
|
4
|
+
import type { ViewProps } from 'react-native';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Built-in plugin for React Native View component
|
|
8
|
+
*
|
|
9
|
+
* Renders View as:
|
|
10
|
+
* - SkeletonBox if has explicit width/height
|
|
11
|
+
* - Container with parsed children otherwise
|
|
12
|
+
*/
|
|
13
|
+
export const ViewPlugin: SkeletonPlugin<ViewProps> = {
|
|
14
|
+
name: 'View',
|
|
15
|
+
component: View,
|
|
16
|
+
version: '1.0.0',
|
|
17
|
+
|
|
18
|
+
strategy: (props, context) => {
|
|
19
|
+
const { primitives, parseChildren, config } = context;
|
|
20
|
+
const style = props.style || {};
|
|
21
|
+
const flatStyle = Array.isArray(style) ? Object.assign({}, ...style) : style;
|
|
22
|
+
|
|
23
|
+
const width = flatStyle.width;
|
|
24
|
+
const height = flatStyle.height;
|
|
25
|
+
const borderRadius = flatStyle.borderRadius ?? config.borderRadius;
|
|
26
|
+
|
|
27
|
+
// If View has explicit dimensions, render as box
|
|
28
|
+
if (width && height) {
|
|
29
|
+
return (
|
|
30
|
+
<primitives.Box
|
|
31
|
+
width={width}
|
|
32
|
+
height={height}
|
|
33
|
+
borderRadius={borderRadius}
|
|
34
|
+
animation={config.animation}
|
|
35
|
+
baseColor={config.baseColor}
|
|
36
|
+
highlightColor={config.highlightColor}
|
|
37
|
+
duration={config.duration}
|
|
38
|
+
style={flatStyle}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If View has children, render as container
|
|
44
|
+
if (props.children) {
|
|
45
|
+
return <View style={flatStyle}>{parseChildren(props.children)}</View>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Empty View - render as small placeholder
|
|
49
|
+
return (
|
|
50
|
+
<primitives.Box
|
|
51
|
+
width={width ?? 100}
|
|
52
|
+
height={height ?? 20}
|
|
53
|
+
borderRadius={borderRadius}
|
|
54
|
+
animation={config.animation}
|
|
55
|
+
baseColor={config.baseColor}
|
|
56
|
+
highlightColor={config.highlightColor}
|
|
57
|
+
duration={config.duration}
|
|
58
|
+
style={flatStyle}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
metadata: {
|
|
64
|
+
description: 'Automatic skeleton generation for View components',
|
|
65
|
+
author: 'Skelo',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type { SkeletonProps, SkeletonConfig, ResolvedStyle, ComponentNode } from './skeleton';
|
|
2
|
+
|
|
3
|
+
export type {
|
|
4
|
+
SkeletonPlugin,
|
|
5
|
+
SkeletonStrategy,
|
|
6
|
+
SkeletonContext,
|
|
7
|
+
SkeletonPrimitives,
|
|
8
|
+
SkeletonUtils,
|
|
9
|
+
PluginMetadata,
|
|
10
|
+
BoxProps,
|
|
11
|
+
TextProps,
|
|
12
|
+
ImageProps,
|
|
13
|
+
CircleProps,
|
|
14
|
+
} from './plugin';
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { ComponentType, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import type { DimensionValue } from 'react-native';
|
|
3
|
+
import type { SkeletonConfig, ResolvedStyle } from './skeleton';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Plugin for custom component skeleton generation
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const AvatarPlugin: SkeletonPlugin<AvatarProps> = {
|
|
11
|
+
* name: 'Avatar',
|
|
12
|
+
* component: Avatar,
|
|
13
|
+
* strategy: (props, { primitives }) => {
|
|
14
|
+
* return <primitives.Circle size={props.size || 40} />;
|
|
15
|
+
* },
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export interface SkeletonPlugin<P = any> {
|
|
20
|
+
/**
|
|
21
|
+
* Unique identifier for the plugin
|
|
22
|
+
*/
|
|
23
|
+
name: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Component type this plugin handles
|
|
27
|
+
*/
|
|
28
|
+
component: ComponentType<P>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Strategy function to generate skeleton
|
|
32
|
+
*/
|
|
33
|
+
strategy: SkeletonStrategy<P>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Optional: Plugin version
|
|
37
|
+
*/
|
|
38
|
+
version?: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Optional: Plugin metadata
|
|
42
|
+
*/
|
|
43
|
+
metadata?: PluginMetadata;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Strategy function signature
|
|
48
|
+
*/
|
|
49
|
+
export type SkeletonStrategy<P = any> = (props: P, context: SkeletonContext) => ReactElement | null;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Context provided to plugin strategies
|
|
53
|
+
*/
|
|
54
|
+
export interface SkeletonContext {
|
|
55
|
+
/**
|
|
56
|
+
* Configuration from parent Skeleton component
|
|
57
|
+
*/
|
|
58
|
+
config: SkeletonConfig;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Theme information (light/dark)
|
|
62
|
+
*/
|
|
63
|
+
theme: 'light' | 'dark';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Recursively parse children
|
|
67
|
+
*/
|
|
68
|
+
parseChildren: (children: ReactNode) => ReactNode;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Access to skeleton primitives
|
|
72
|
+
*/
|
|
73
|
+
primitives: SkeletonPrimitives;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Utility functions
|
|
77
|
+
*/
|
|
78
|
+
utils: SkeletonUtils;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Skeleton primitive components
|
|
83
|
+
*/
|
|
84
|
+
export interface SkeletonPrimitives {
|
|
85
|
+
Box: ComponentType<BoxProps>;
|
|
86
|
+
Text: ComponentType<TextProps>;
|
|
87
|
+
Image: ComponentType<ImageProps>;
|
|
88
|
+
Circle: ComponentType<CircleProps>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Utility functions for plugins
|
|
93
|
+
*/
|
|
94
|
+
export interface SkeletonUtils {
|
|
95
|
+
/**
|
|
96
|
+
* Flatten and resolve styles
|
|
97
|
+
*/
|
|
98
|
+
resolveStyle: (style: any) => ResolvedStyle;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extract dimensions from a resolved style
|
|
102
|
+
*/
|
|
103
|
+
extractDimensions: (style: ResolvedStyle) => {
|
|
104
|
+
width?: DimensionValue;
|
|
105
|
+
height?: DimensionValue;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Generate random width for natural look
|
|
110
|
+
*/
|
|
111
|
+
randomWidth: (min?: number, max?: number) => string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Plugin metadata
|
|
116
|
+
*/
|
|
117
|
+
export interface PluginMetadata {
|
|
118
|
+
author?: string;
|
|
119
|
+
description?: string;
|
|
120
|
+
repository?: string;
|
|
121
|
+
license?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Animation and color props shared by all skeleton primitives
|
|
126
|
+
*/
|
|
127
|
+
export interface SkeletonAnimationProps {
|
|
128
|
+
animation?: 'shimmer' | 'pulse' | 'none';
|
|
129
|
+
baseColor?: string;
|
|
130
|
+
highlightColor?: string;
|
|
131
|
+
duration?: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Props for skeleton primitives
|
|
136
|
+
*/
|
|
137
|
+
export interface BoxProps extends SkeletonAnimationProps {
|
|
138
|
+
width?: DimensionValue;
|
|
139
|
+
height?: DimensionValue;
|
|
140
|
+
borderRadius?: number;
|
|
141
|
+
style?: any;
|
|
142
|
+
children?: ReactNode;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface TextProps extends SkeletonAnimationProps {
|
|
146
|
+
lines?: number;
|
|
147
|
+
fontSize?: number;
|
|
148
|
+
lineHeight?: number;
|
|
149
|
+
width?: DimensionValue;
|
|
150
|
+
lastLineWidth?: DimensionValue;
|
|
151
|
+
style?: any;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface ImageProps extends SkeletonAnimationProps {
|
|
155
|
+
width?: DimensionValue;
|
|
156
|
+
height?: DimensionValue;
|
|
157
|
+
borderRadius?: number;
|
|
158
|
+
style?: any;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface CircleProps extends SkeletonAnimationProps {
|
|
162
|
+
size?: number;
|
|
163
|
+
style?: any;
|
|
164
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import type { DimensionValue, FlexAlignType } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props for the main Skeleton component
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <Skeleton loading={true} animation="shimmer">
|
|
10
|
+
* <YourComponent />
|
|
11
|
+
* </Skeleton>
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export interface SkeletonProps {
|
|
15
|
+
/**
|
|
16
|
+
* Whether to show skeleton (true) or actual content (false)
|
|
17
|
+
*/
|
|
18
|
+
loading: boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Animation type
|
|
22
|
+
* - 'shimmer': Moving gradient animation (default)
|
|
23
|
+
* - 'pulse': Fade in/out animation
|
|
24
|
+
* - 'none': Static skeleton
|
|
25
|
+
*
|
|
26
|
+
* @default 'shimmer'
|
|
27
|
+
*/
|
|
28
|
+
animation?: 'shimmer' | 'pulse' | 'none';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Animation duration in milliseconds
|
|
32
|
+
* @default 1200
|
|
33
|
+
*/
|
|
34
|
+
duration?: number;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Base skeleton color
|
|
38
|
+
* @default Auto-detected based on color scheme (light/dark)
|
|
39
|
+
*/
|
|
40
|
+
baseColor?: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Shimmer highlight color
|
|
44
|
+
* @default Auto-detected based on color scheme (light/dark)
|
|
45
|
+
*/
|
|
46
|
+
highlightColor?: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Default border radius for skeleton elements
|
|
50
|
+
* @default 4
|
|
51
|
+
*/
|
|
52
|
+
borderRadius?: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Components to render.
|
|
56
|
+
*
|
|
57
|
+
* When `styles` is provided, `children` is optional — the skeleton is derived
|
|
58
|
+
* from the styles instead of the child tree.
|
|
59
|
+
*/
|
|
60
|
+
children?: ReactNode;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate the skeleton directly from a set of styles instead of from the
|
|
64
|
+
* child element tree. Accepts a `StyleSheet.create` object (values used in
|
|
65
|
+
* definition order) or an array of styles.
|
|
66
|
+
*
|
|
67
|
+
* Because a StyleSheet has no hierarchy, the result is a vertical stack of
|
|
68
|
+
* skeleton shapes — one per style that has visual dimensions.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* <Skeleton loading={loading} styles={styles} />
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
styles?: Record<string, unknown> | unknown[];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* When using `styles`, style keys to skip (e.g. layout containers).
|
|
79
|
+
*/
|
|
80
|
+
excludeStyles?: string[];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Deep mode: expand opaque (custom) child components into their real
|
|
84
|
+
* host-element tree via offline rendering, producing a structured skeleton
|
|
85
|
+
* without inlining host elements.
|
|
86
|
+
*
|
|
87
|
+
* @experimental Renders the children offline to introspect them, so their
|
|
88
|
+
* mount effects run during expansion and they render outside your app's
|
|
89
|
+
* providers (navigation/theme/redux). Best for presentational trees; guard
|
|
90
|
+
* effectful/context-dependent screens or register a plugin for them. Falls
|
|
91
|
+
* back to static parsing if offline rendering is unavailable or fails.
|
|
92
|
+
* Requires `react-reconciler` to be installed.
|
|
93
|
+
*
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
deep?: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Enable accessibility features
|
|
100
|
+
* @default true
|
|
101
|
+
*/
|
|
102
|
+
accessible?: boolean;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Screen reader label
|
|
106
|
+
* @default "Loading content"
|
|
107
|
+
*/
|
|
108
|
+
accessibilityLabel?: string;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Debug mode (development only)
|
|
112
|
+
* Shows component boundaries and names
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
debug?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Configuration options for SkeletonProvider
|
|
120
|
+
*/
|
|
121
|
+
export interface SkeletonConfig {
|
|
122
|
+
/**
|
|
123
|
+
* Default animation type
|
|
124
|
+
* @default 'shimmer'
|
|
125
|
+
*/
|
|
126
|
+
animation?: 'shimmer' | 'pulse' | 'none';
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Default animation duration in milliseconds
|
|
130
|
+
* @default 1200
|
|
131
|
+
*/
|
|
132
|
+
duration?: number;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Default base color
|
|
136
|
+
*/
|
|
137
|
+
baseColor?: string;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Default highlight color
|
|
141
|
+
*/
|
|
142
|
+
highlightColor?: string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Default border radius
|
|
146
|
+
* @default 4
|
|
147
|
+
*/
|
|
148
|
+
borderRadius?: number;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Enable debug mode globally
|
|
152
|
+
* @default false
|
|
153
|
+
*/
|
|
154
|
+
debug?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Resolved style properties extracted from React Native styles
|
|
159
|
+
*/
|
|
160
|
+
export interface ResolvedStyle {
|
|
161
|
+
width?: DimensionValue;
|
|
162
|
+
height?: DimensionValue;
|
|
163
|
+
borderRadius?: number;
|
|
164
|
+
backgroundColor?: string;
|
|
165
|
+
fontSize?: number;
|
|
166
|
+
lineHeight?: number;
|
|
167
|
+
marginTop?: number;
|
|
168
|
+
marginBottom?: number;
|
|
169
|
+
marginLeft?: number;
|
|
170
|
+
marginRight?: number;
|
|
171
|
+
paddingTop?: number;
|
|
172
|
+
paddingBottom?: number;
|
|
173
|
+
paddingLeft?: number;
|
|
174
|
+
paddingRight?: number;
|
|
175
|
+
flexDirection?: 'row' | 'column';
|
|
176
|
+
alignItems?: FlexAlignType;
|
|
177
|
+
justifyContent?:
|
|
178
|
+
'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Parsed component node in the tree
|
|
183
|
+
*/
|
|
184
|
+
export interface ComponentNode {
|
|
185
|
+
type: string;
|
|
186
|
+
props: any;
|
|
187
|
+
style: ResolvedStyle;
|
|
188
|
+
children: ComponentNode[];
|
|
189
|
+
/**
|
|
190
|
+
* The original React element this node was parsed from.
|
|
191
|
+
*
|
|
192
|
+
* Retained so the renderer can fall back to rendering-and-measuring the
|
|
193
|
+
* real element when it is an opaque (non-host) component the parser can't
|
|
194
|
+
* introspect. Excluded from debug serialization.
|
|
195
|
+
*/
|
|
196
|
+
element?: ReactElement;
|
|
197
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React, { isValidElement } from 'react';
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
View,
|
|
5
|
+
Text,
|
|
6
|
+
Image,
|
|
7
|
+
ImageBackground,
|
|
8
|
+
ScrollView,
|
|
9
|
+
SafeAreaView,
|
|
10
|
+
FlatList,
|
|
11
|
+
SectionList,
|
|
12
|
+
VirtualizedList,
|
|
13
|
+
TouchableOpacity,
|
|
14
|
+
TouchableHighlight,
|
|
15
|
+
TouchableWithoutFeedback,
|
|
16
|
+
Pressable,
|
|
17
|
+
KeyboardAvoidingView,
|
|
18
|
+
} from 'react-native';
|
|
19
|
+
|
|
20
|
+
// Re-exported so consumers can access it from the skelo utils surface
|
|
21
|
+
export { isValidElement };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reference map of React Native host components to their canonical names.
|
|
25
|
+
*
|
|
26
|
+
* Modern RN components (View/Text/Image/...) are `forwardRef`/`memo` objects,
|
|
27
|
+
* not plain functions or strings, so name/displayName sniffing is unreliable.
|
|
28
|
+
* Identifying them by reference is exact. This works across the library/app
|
|
29
|
+
* module boundary because Metro resolves a single `react-native` instance.
|
|
30
|
+
*/
|
|
31
|
+
const RN_COMPONENT_NAMES = new Map<unknown, string>([
|
|
32
|
+
[View, 'View'],
|
|
33
|
+
[Text, 'Text'],
|
|
34
|
+
[Image, 'Image'],
|
|
35
|
+
[ImageBackground, 'ImageBackground'],
|
|
36
|
+
[ScrollView, 'ScrollView'],
|
|
37
|
+
[SafeAreaView, 'SafeAreaView'],
|
|
38
|
+
[FlatList, 'FlatList'],
|
|
39
|
+
[SectionList, 'SectionList'],
|
|
40
|
+
[VirtualizedList, 'VirtualizedList'],
|
|
41
|
+
[TouchableOpacity, 'TouchableOpacity'],
|
|
42
|
+
[TouchableHighlight, 'TouchableHighlight'],
|
|
43
|
+
[TouchableWithoutFeedback, 'TouchableWithoutFeedback'],
|
|
44
|
+
[Pressable, 'Pressable'],
|
|
45
|
+
[KeyboardAvoidingView, 'KeyboardAvoidingView'],
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get component display name or type name
|
|
50
|
+
*
|
|
51
|
+
* @param element - React element
|
|
52
|
+
* @returns Component name as string
|
|
53
|
+
*/
|
|
54
|
+
export function getComponentName(element: ReactElement): string {
|
|
55
|
+
const type: unknown = element.type;
|
|
56
|
+
|
|
57
|
+
if (type === React.Fragment) {
|
|
58
|
+
return 'Fragment';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Exact identification of RN host components (forwardRef/memo objects).
|
|
62
|
+
const mapped = RN_COMPONENT_NAMES.get(type);
|
|
63
|
+
if (mapped) {
|
|
64
|
+
return mapped;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Intrinsic host string (e.g. web/DOM or raw native tags).
|
|
68
|
+
if (typeof type === 'string') {
|
|
69
|
+
return type;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Plain function/class components.
|
|
73
|
+
if (typeof type === 'function') {
|
|
74
|
+
const fn = type as { displayName?: string; name?: string };
|
|
75
|
+
return fn.displayName || fn.name || 'Anonymous';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// forwardRef / memo wrappers: unwrap to find a usable name.
|
|
79
|
+
if (typeof type === 'object' && type !== null) {
|
|
80
|
+
const obj = type as {
|
|
81
|
+
displayName?: string;
|
|
82
|
+
render?: { displayName?: string; name?: string };
|
|
83
|
+
type?: unknown;
|
|
84
|
+
};
|
|
85
|
+
if (obj.displayName) {
|
|
86
|
+
return obj.displayName;
|
|
87
|
+
}
|
|
88
|
+
if (obj.render) {
|
|
89
|
+
return obj.render.displayName || obj.render.name || 'Anonymous';
|
|
90
|
+
}
|
|
91
|
+
// memo(Component) nests the real component under `.type`.
|
|
92
|
+
if (obj.type) {
|
|
93
|
+
return getComponentName({ type: obj.type } as ReactElement);
|
|
94
|
+
}
|
|
95
|
+
return 'Anonymous';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return 'Unknown';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if element is a React Fragment
|
|
103
|
+
*
|
|
104
|
+
* @param element - React element
|
|
105
|
+
* @returns True if element is a Fragment
|
|
106
|
+
*/
|
|
107
|
+
export function isFragment(element: ReactElement): boolean {
|
|
108
|
+
return element.type === React.Fragment;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Flatten children, handling Fragments recursively
|
|
113
|
+
*
|
|
114
|
+
* @param children - React children
|
|
115
|
+
* @returns Flattened array of children
|
|
116
|
+
*/
|
|
117
|
+
export function flattenChildren(children: ReactNode): ReactNode[] {
|
|
118
|
+
const result: ReactNode[] = [];
|
|
119
|
+
|
|
120
|
+
React.Children.forEach(children, child => {
|
|
121
|
+
if (!isValidElement(child)) {
|
|
122
|
+
result.push(child);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (isFragment(child)) {
|
|
127
|
+
// Recursively flatten fragment children
|
|
128
|
+
const fragmentChildren = flattenChildren(child.props.children);
|
|
129
|
+
result.push(...fragmentChildren);
|
|
130
|
+
} else {
|
|
131
|
+
result.push(child);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check if component is a touchable component
|
|
140
|
+
*
|
|
141
|
+
* @param element - React element
|
|
142
|
+
* @returns True if component is touchable
|
|
143
|
+
*/
|
|
144
|
+
export function isTouchableComponent(element: ReactElement): boolean {
|
|
145
|
+
const name = getComponentName(element);
|
|
146
|
+
const touchableTypes = [
|
|
147
|
+
'TouchableOpacity',
|
|
148
|
+
'TouchableHighlight',
|
|
149
|
+
'TouchableWithoutFeedback',
|
|
150
|
+
'TouchableNativeFeedback',
|
|
151
|
+
'Pressable',
|
|
152
|
+
];
|
|
153
|
+
return touchableTypes.includes(name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Generate random width percentage for natural skeleton look
|
|
158
|
+
*
|
|
159
|
+
* @param min - Minimum percentage (0-100)
|
|
160
|
+
* @param max - Maximum percentage (0-100)
|
|
161
|
+
* @returns Random percentage as string (e.g., "75%")
|
|
162
|
+
*/
|
|
163
|
+
export function randomWidth(min: number = 60, max: number = 90): string {
|
|
164
|
+
const random = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
165
|
+
return `${random}%`;
|
|
166
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import type { DimensionValue } from 'react-native';
|
|
3
|
+
import type { ResolvedStyle } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flatten and resolve styles from React Native style prop
|
|
7
|
+
*
|
|
8
|
+
* @param style - Style prop (can be object, array, or StyleSheet reference)
|
|
9
|
+
* @returns Flattened style object
|
|
10
|
+
*/
|
|
11
|
+
export function resolveStyle(style: any): ResolvedStyle {
|
|
12
|
+
if (!style) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const flattened = StyleSheet.flatten(style);
|
|
18
|
+
return flattened as ResolvedStyle;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
// Handle invalid styles gracefully
|
|
21
|
+
if (__DEV__) {
|
|
22
|
+
console.warn('[Skelo] Failed to resolve style:', error);
|
|
23
|
+
}
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Extract dimensions from resolved style
|
|
30
|
+
*
|
|
31
|
+
* @param style - Resolved style object
|
|
32
|
+
* @returns Object with width and height (if present)
|
|
33
|
+
*/
|
|
34
|
+
export function extractDimensions(style: ResolvedStyle): {
|
|
35
|
+
width?: DimensionValue;
|
|
36
|
+
height?: DimensionValue;
|
|
37
|
+
} {
|
|
38
|
+
return {
|
|
39
|
+
width: style.width,
|
|
40
|
+
height: style.height,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a resolved style declares both an explicit width and height
|
|
46
|
+
*
|
|
47
|
+
* @param style - Resolved style object
|
|
48
|
+
* @returns True if both width and height are defined
|
|
49
|
+
*/
|
|
50
|
+
export function hasExplicitSize(style: ResolvedStyle): boolean {
|
|
51
|
+
return style.width !== undefined && style.height !== undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if a value is a percentage string
|
|
56
|
+
*
|
|
57
|
+
* @param value - Value to check
|
|
58
|
+
* @returns True if value is a percentage string
|
|
59
|
+
*/
|
|
60
|
+
export function isPercentage(value: any): boolean {
|
|
61
|
+
return typeof value === 'string' && value.includes('%');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Convert percentage to number (strips %)
|
|
66
|
+
*
|
|
67
|
+
* @param value - Percentage string (e.g., "50%")
|
|
68
|
+
* @returns Number without % sign
|
|
69
|
+
*/
|
|
70
|
+
export function percentageToNumber(value: string): number {
|
|
71
|
+
return parseFloat(value.replace('%', ''));
|
|
72
|
+
}
|