react-native-skelo 0.0.5 → 0.1.0-alpha.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.
Files changed (55) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +77 -88
  3. package/lib/commonjs/animations/PulseAnimation.js +42 -17
  4. package/lib/commonjs/animations/PulseAnimation.js.map +1 -1
  5. package/lib/commonjs/animations/ShimmerAnimation.js +65 -54
  6. package/lib/commonjs/animations/ShimmerAnimation.js.map +1 -1
  7. package/lib/commonjs/components/Skeleton.js +8 -20
  8. package/lib/commonjs/components/Skeleton.js.map +1 -1
  9. package/lib/commonjs/index.js +0 -1
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/module/animations/PulseAnimation.js +43 -18
  12. package/lib/module/animations/PulseAnimation.js.map +1 -1
  13. package/lib/module/animations/ShimmerAnimation.js +66 -55
  14. package/lib/module/animations/ShimmerAnimation.js.map +1 -1
  15. package/lib/module/components/Skeleton.js +8 -20
  16. package/lib/module/components/Skeleton.js.map +1 -1
  17. package/lib/module/index.js +0 -4
  18. package/lib/module/index.js.map +1 -1
  19. package/lib/typescript/animations/PulseAnimation.d.ts +44 -0
  20. package/lib/typescript/animations/PulseAnimation.d.ts.map +1 -1
  21. package/lib/typescript/animations/ShimmerAnimation.d.ts +44 -0
  22. package/lib/typescript/animations/ShimmerAnimation.d.ts.map +1 -1
  23. package/lib/typescript/components/Skeleton.d.ts +1 -1
  24. package/lib/typescript/components/Skeleton.d.ts.map +1 -1
  25. package/lib/typescript/index.d.ts +0 -1
  26. package/lib/typescript/index.d.ts.map +1 -1
  27. package/lib/typescript/types/skeleton.d.ts +0 -8
  28. package/lib/typescript/types/skeleton.d.ts.map +1 -1
  29. package/package.json +12 -23
  30. package/src/animations/PulseAnimation.tsx +89 -17
  31. package/src/animations/ShimmerAnimation.tsx +118 -39
  32. package/src/components/Skeleton.tsx +8 -22
  33. package/src/index.ts +0 -4
  34. package/src/types/skeleton.ts +0 -9
  35. package/lib/commonjs/core/deepRegistry.js +0 -18
  36. package/lib/commonjs/core/deepRegistry.js.map +0 -1
  37. package/lib/commonjs/core/parser/expandLists.js +0 -67
  38. package/lib/commonjs/core/parser/expandLists.js.map +0 -1
  39. package/lib/commonjs/deep.js +0 -18
  40. package/lib/commonjs/deep.js.map +0 -1
  41. package/lib/module/core/deepRegistry.js +0 -11
  42. package/lib/module/core/deepRegistry.js.map +0 -1
  43. package/lib/module/core/parser/expandLists.js +0 -61
  44. package/lib/module/core/parser/expandLists.js.map +0 -1
  45. package/lib/module/deep.js +0 -11
  46. package/lib/module/deep.js.map +0 -1
  47. package/lib/typescript/core/deepRegistry.d.ts +0 -7
  48. package/lib/typescript/core/deepRegistry.d.ts.map +0 -1
  49. package/lib/typescript/core/parser/expandLists.d.ts +0 -3
  50. package/lib/typescript/core/parser/expandLists.d.ts.map +0 -1
  51. package/lib/typescript/deep.d.ts +0 -2
  52. package/lib/typescript/deep.d.ts.map +0 -1
  53. package/src/core/deepRegistry.ts +0 -16
  54. package/src/core/parser/expandLists.tsx +0 -77
  55. package/src/deep.ts +0 -12
package/package.json CHANGED
@@ -1,29 +1,12 @@
1
1
  {
2
2
  "name": "react-native-skelo",
3
- "version": "0.0.5",
3
+ "version": "0.1.0-alpha.0",
4
4
  "description": "Automatic skeleton loading for React Native. Write your UI once, Skelo builds the loading state automatically.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
7
7
  "types": "lib/typescript/index.d.ts",
8
8
  "react-native": "src/index.ts",
9
9
  "source": "src/index.ts",
10
- "exports": {
11
- ".": {
12
- "types": "./lib/typescript/index.d.ts",
13
- "react-native": "./src/index.ts",
14
- "import": "./lib/module/index.js",
15
- "require": "./lib/commonjs/index.js",
16
- "default": "./lib/commonjs/index.js"
17
- },
18
- "./deep": {
19
- "types": "./lib/typescript/deep.d.ts",
20
- "react-native": "./src/deep.ts",
21
- "import": "./lib/module/deep.js",
22
- "require": "./lib/commonjs/deep.js",
23
- "default": "./lib/commonjs/deep.js"
24
- },
25
- "./package.json": "./package.json"
26
- },
27
10
  "files": [
28
11
  "src",
29
12
  "lib",
@@ -35,7 +18,6 @@
35
18
  "test:watch": "jest --watch",
36
19
  "test:coverage": "jest --coverage",
37
20
  "typecheck": "tsc --noEmit",
38
- "build": "bob build",
39
21
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
22
  "lint:fix": "eslint \"**/*.{js,ts,tsx}\" --fix",
41
23
  "format": "prettier --write \"**/*.{js,ts,tsx,json,md}\"",
@@ -87,10 +69,15 @@
87
69
  },
88
70
  "peerDependencies": {
89
71
  "react": "*",
90
- "react-native": "*"
72
+ "react-native": "*",
73
+ "react-native-linear-gradient": ">=2.8.0",
74
+ "react-native-reanimated": ">=3.0.0",
75
+ "react-reconciler": ">=0.29.0"
91
76
  },
92
- "dependencies": {
93
- "react-reconciler": "^0.31.0"
77
+ "peerDependenciesMeta": {
78
+ "react-reconciler": {
79
+ "optional": true
80
+ }
94
81
  },
95
82
  "devDependencies": {
96
83
  "@commitlint/config-conventional": "^18.4.3",
@@ -108,6 +95,8 @@
108
95
  "react": "18.2.0",
109
96
  "react-native": "0.73.1",
110
97
  "react-native-builder-bob": "^0.23.2",
98
+ "react-native-linear-gradient": "^2.8.3",
99
+ "react-native-reanimated": "^3.6.1",
111
100
  "react-reconciler": "^0.29.2",
112
101
  "react-test-renderer": "18.2.0",
113
102
  "release-it": "^17.0.1",
@@ -122,7 +111,7 @@
122
111
  "<rootDir>/lib/"
123
112
  ],
124
113
  "transformIgnorePatterns": [
125
- "node_modules/(?!(@react-native|react-native)/)"
114
+ "node_modules/(?!(@react-native|react-native|react-native-reanimated|react-native-linear-gradient)/)"
126
115
  ],
127
116
  "collectCoverageFrom": [
128
117
  "src/**/*.{ts,tsx}",
@@ -1,19 +1,77 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import type { DimensionValue, StyleProp, ViewStyle } from 'react-native';
3
- import { Animated, Easing, StyleSheet } from 'react-native';
3
+ import { StyleSheet } from 'react-native';
4
+ import Animated, {
5
+ useSharedValue,
6
+ useAnimatedStyle,
7
+ withRepeat,
8
+ withSequence,
9
+ withTiming,
10
+ Easing,
11
+ } from 'react-native-reanimated';
4
12
 
5
13
  interface PulseProps {
14
+ /**
15
+ * Width of the pulse container
16
+ */
6
17
  width?: DimensionValue;
18
+
19
+ /**
20
+ * Height of the pulse container
21
+ */
7
22
  height?: DimensionValue;
23
+
24
+ /**
25
+ * Base color of the skeleton
26
+ * @default '#E1E9EE'
27
+ */
8
28
  baseColor?: string;
29
+
30
+ /**
31
+ * Highlight color for pulse
32
+ * @default '#F2F8FC'
33
+ */
9
34
  highlightColor?: string;
35
+
36
+ /**
37
+ * Animation duration in milliseconds
38
+ * @default 1000
39
+ */
10
40
  duration?: number;
41
+
42
+ /**
43
+ * Border radius
44
+ */
11
45
  borderRadius?: number;
46
+
47
+ /**
48
+ * Additional styles
49
+ */
12
50
  style?: StyleProp<ViewStyle>;
51
+
52
+ /**
53
+ * Child content (rendered inside pulse)
54
+ */
13
55
  children?: React.ReactNode;
14
56
  }
15
57
 
16
- // Breathing opacity loop using React Native's built-in Animated (native driver).
58
+ /**
59
+ * Pulse animation component using Reanimated 3
60
+ *
61
+ * Creates a subtle breathing/pulsing effect using:
62
+ * - Shared values for performance
63
+ * - Worklets for 60 FPS on UI thread
64
+ * - Opacity animation for pulse effect
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * <PulseAnimation
69
+ * width={200}
70
+ * height={20}
71
+ * borderRadius={4}
72
+ * />
73
+ * ```
74
+ */
17
75
  export function PulseAnimation({
18
76
  width,
19
77
  height,
@@ -23,34 +81,48 @@ export function PulseAnimation({
23
81
  style,
24
82
  children,
25
83
  }: PulseProps) {
26
- const opacity = useRef(new Animated.Value(1)).current;
84
+ // Shared value for opacity (0 to 1)
85
+ const opacity = useSharedValue(1);
27
86
 
28
87
  useEffect(() => {
29
- const loop = Animated.loop(
30
- Animated.sequence([
31
- Animated.timing(opacity, {
32
- toValue: 0.5,
88
+ // Start infinite pulse animation
89
+ // Sequence: fade out -> fade in
90
+ opacity.value = withRepeat(
91
+ withSequence(
92
+ withTiming(0.5, {
33
93
  duration: duration / 2,
34
94
  easing: Easing.inOut(Easing.ease),
35
- useNativeDriver: true,
36
95
  }),
37
- Animated.timing(opacity, {
38
- toValue: 1,
96
+ withTiming(1, {
39
97
  duration: duration / 2,
40
98
  easing: Easing.inOut(Easing.ease),
41
- useNativeDriver: true,
42
- }),
43
- ])
99
+ })
100
+ ),
101
+ -1, // Infinite repeat
102
+ false // Don't reverse
44
103
  );
45
- loop.start();
46
- return () => loop.stop();
47
104
  }, [duration, opacity]);
48
105
 
106
+ // Animated style for pulse effect
107
+ const animatedStyle = useAnimatedStyle(() => {
108
+ 'worklet';
109
+
110
+ return {
111
+ opacity: opacity.value,
112
+ };
113
+ }, []);
114
+
49
115
  return (
50
116
  <Animated.View
51
117
  style={[
52
118
  styles.container,
53
- { width, height, borderRadius, backgroundColor: baseColor, opacity },
119
+ {
120
+ width,
121
+ height,
122
+ borderRadius,
123
+ backgroundColor: baseColor,
124
+ },
125
+ animatedStyle,
54
126
  style,
55
127
  ]}
56
128
  >
@@ -1,21 +1,78 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import type { DimensionValue, LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';
3
- import { Animated, Easing, StyleSheet, View } from 'react-native';
1
+ import React, { useEffect } from 'react';
2
+ import type { DimensionValue, StyleProp, ViewStyle } from 'react-native';
3
+ import { StyleSheet, View } from 'react-native';
4
+ import Animated, {
5
+ useSharedValue,
6
+ useAnimatedStyle,
7
+ withRepeat,
8
+ withTiming,
9
+ interpolate,
10
+ Easing,
11
+ } from 'react-native-reanimated';
12
+ import { LinearGradient } from 'react-native-linear-gradient';
4
13
 
5
14
  interface ShimmerProps {
15
+ /**
16
+ * Width of the shimmer container
17
+ */
6
18
  width?: DimensionValue;
19
+
20
+ /**
21
+ * Height of the shimmer container
22
+ */
7
23
  height?: DimensionValue;
24
+
25
+ /**
26
+ * Base color of the skeleton
27
+ * @default '#E1E9EE'
28
+ */
8
29
  baseColor?: string;
30
+
31
+ /**
32
+ * Highlight color that shimmers across
33
+ * @default '#F2F8FC'
34
+ */
9
35
  highlightColor?: string;
36
+
37
+ /**
38
+ * Animation duration in milliseconds
39
+ * @default 1500
40
+ */
10
41
  duration?: number;
42
+
43
+ /**
44
+ * Border radius
45
+ */
11
46
  borderRadius?: number;
47
+
48
+ /**
49
+ * Additional styles
50
+ */
12
51
  style?: StyleProp<ViewStyle>;
52
+
53
+ /**
54
+ * Child content (rendered on top of shimmer)
55
+ */
13
56
  children?: React.ReactNode;
14
57
  }
15
58
 
16
- // Zero-dependency shimmer: a highlight band (dim/bright/dim segments to fake a
17
- // soft gradient) sweeps across the base, driven by React Native's built-in
18
- // Animated native driver. Plain Views only — guaranteed to render and animate.
59
+ /**
60
+ * Shimmer animation component using Reanimated 3
61
+ *
62
+ * Creates a smooth left-to-right shimmer effect using:
63
+ * - Shared values for performance
64
+ * - Worklets for 60 FPS on UI thread
65
+ * - Linear gradient for shimmer effect
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * <ShimmerAnimation
70
+ * width={200}
71
+ * height={20}
72
+ * borderRadius={4}
73
+ * />
74
+ * ```
75
+ */
19
76
  export function ShimmerAnimation({
20
77
  width,
21
78
  height,
@@ -26,49 +83,77 @@ export function ShimmerAnimation({
26
83
  style,
27
84
  children,
28
85
  }: ShimmerProps) {
29
- const progress = useRef(new Animated.Value(0)).current;
30
- const [measuredWidth, setMeasuredWidth] = useState(0);
86
+ // Shared value for animation progress (0 to 1)
87
+ const progress = useSharedValue(0);
31
88
 
32
89
  useEffect(() => {
33
- const loop = Animated.loop(
34
- Animated.timing(progress, {
35
- toValue: 1,
90
+ // Start infinite shimmer animation
91
+ progress.value = withRepeat(
92
+ withTiming(1, {
36
93
  duration,
37
94
  easing: Easing.linear,
38
- useNativeDriver: true,
39
- })
95
+ }),
96
+ -1, // Infinite repeat
97
+ false // Don't reverse
40
98
  );
41
- loop.start();
42
- return () => loop.stop();
43
99
  }, [duration, progress]);
44
100
 
45
- const onLayout = (event: LayoutChangeEvent) => {
46
- const w = event.nativeEvent.layout.width;
47
- setMeasuredWidth(prev => (prev === w ? prev : w));
48
- };
101
+ // Animated style for shimmer overlay
102
+ const animatedStyle = useAnimatedStyle(() => {
103
+ 'worklet';
49
104
 
50
- const containerWidth = typeof width === 'number' ? width : measuredWidth || 200;
51
- const bandWidth = Math.max(containerWidth * 0.6, 80);
105
+ // Calculate shimmer position
106
+ // Interpolate from -100% to 100% to create left-to-right sweep
107
+ const translateX = interpolate(progress.value, [0, 1], [-1, 1]);
52
108
 
53
- const translateX = progress.interpolate({
54
- inputRange: [0, 1],
55
- outputRange: [-bandWidth, containerWidth],
56
- });
109
+ return {
110
+ transform: [{ translateX: translateX * (typeof width === 'number' ? width : 200) }],
111
+ };
112
+ }, [width]);
57
113
 
58
114
  return (
59
115
  <View
60
- onLayout={onLayout}
61
- style={[styles.container, { width, height, borderRadius, backgroundColor: baseColor }, style]}
116
+ style={[
117
+ styles.container,
118
+ {
119
+ width,
120
+ height,
121
+ borderRadius,
122
+ backgroundColor: baseColor,
123
+ },
124
+ style,
125
+ ]}
62
126
  >
127
+ {/* Shimmer gradient overlay */}
63
128
  <Animated.View
64
- pointerEvents="none"
65
- style={[styles.band, { width: bandWidth, transform: [{ translateX }] }]}
129
+ style={[
130
+ StyleSheet.absoluteFill,
131
+ {
132
+ overflow: 'hidden',
133
+ borderRadius,
134
+ },
135
+ ]}
66
136
  >
67
- <View style={[styles.segment, { backgroundColor: highlightColor, opacity: 0.25 }]} />
68
- <View style={[styles.segment, { backgroundColor: highlightColor, opacity: 0.9 }]} />
69
- <View style={[styles.segment, { backgroundColor: highlightColor, opacity: 0.25 }]} />
137
+ <Animated.View
138
+ style={[
139
+ styles.shimmer,
140
+ {
141
+ width: typeof width === 'number' ? width : '100%',
142
+ height: '100%',
143
+ },
144
+ animatedStyle,
145
+ ]}
146
+ >
147
+ <LinearGradient
148
+ colors={[baseColor, highlightColor, highlightColor, baseColor]}
149
+ start={{ x: 0, y: 0 }}
150
+ end={{ x: 1, y: 0 }}
151
+ style={StyleSheet.absoluteFill}
152
+ />
153
+ </Animated.View>
70
154
  </Animated.View>
71
155
 
156
+ {/* Child content */}
72
157
  {children}
73
158
  </View>
74
159
  );
@@ -78,13 +163,7 @@ const styles = StyleSheet.create({
78
163
  container: {
79
164
  overflow: 'hidden',
80
165
  },
81
- band: {
166
+ shimmer: {
82
167
  position: 'absolute',
83
- top: 0,
84
- bottom: 0,
85
- flexDirection: 'row',
86
- },
87
- segment: {
88
- flex: 1,
89
168
  },
90
169
  });
@@ -4,8 +4,7 @@ import { Parser } from '../core/parser';
4
4
  import { SkeletonRenderer } from './SkeletonRenderer';
5
5
  import { SkeletonIgnore } from './SkeletonIgnore';
6
6
  import { StyleSkeleton } from '../core/generator/StyleSkeleton';
7
- import { getDeepExpander } from '../core/deepRegistry';
8
- import { expandListPlaceholders } from '../core/parser/expandLists';
7
+ import { expandToComponentNodes } from '../core/parser/expandOffline';
9
8
  import { DEFAULT_CONFIG } from '../constants/defaults';
10
9
 
11
10
  /**
@@ -41,8 +40,7 @@ export function Skeleton({
41
40
  children,
42
41
  styles,
43
42
  excludeStyles,
44
- deep,
45
- count = 6,
43
+ deep = false,
46
44
  animation = DEFAULT_CONFIG.animation,
47
45
  duration = DEFAULT_CONFIG.duration,
48
46
  baseColor = DEFAULT_CONFIG.baseColor,
@@ -65,31 +63,19 @@ export function Skeleton({
65
63
  }
66
64
 
67
65
  try {
68
- // Turn FlatList/SectionList into placeholder rows so lists show a
69
- // skeleton even though their data (and rows) don't exist yet.
70
- const prepared = expandListPlaceholders(children, count);
71
-
72
- // Deep mode: expand opaque components into their real host tree via the
73
- // opt-in expander (registered by importing 'react-native-skelo/deep').
74
- // When `deep` is not set explicitly, it's used automatically whenever the
75
- // expander is available. Falls back to static parsing otherwise.
76
- const expand = getDeepExpander();
77
- const wantDeep = deep === undefined ? expand !== null : deep;
78
- if (wantDeep && expand) {
79
- const expanded = expand(<>{prepared}</>);
66
+ // Deep mode: expand opaque components into their real host tree by
67
+ // rendering them offline. Falls back to static parsing on failure.
68
+ if (deep) {
69
+ const expanded = expandToComponentNodes(<>{children}</>);
80
70
  if (expanded && expanded.length > 0) {
81
71
  return expanded;
82
72
  }
83
73
  if (__DEV__) {
84
74
  console.warn('[Skelo] deep expansion returned nothing; falling back to static parsing.');
85
75
  }
86
- } else if (deep === true && !expand && __DEV__) {
87
- console.warn(
88
- "[Skelo] `deep` requires react-reconciler. Install it and add `import 'react-native-skelo/deep';` in your app entry."
89
- );
90
76
  }
91
77
 
92
- const parsed = Parser.parse(prepared);
78
+ const parsed = Parser.parse(children);
93
79
 
94
80
  if (__DEV__ && debug) {
95
81
  // Log a lightweight summary: the raw nodes hold React elements
@@ -110,7 +96,7 @@ export function Skeleton({
110
96
  }
111
97
  return [];
112
98
  }
113
- }, [loading, styles, deep, count, children, debug]);
99
+ }, [loading, styles, deep, children, debug]);
114
100
 
115
101
  // Create skeleton config
116
102
  const config = useMemo(
package/src/index.ts CHANGED
@@ -6,10 +6,6 @@
6
6
  * @packageDocumentation
7
7
  */
8
8
 
9
- // Auto-register deep expansion (react-reconciler ships as a dependency), so
10
- // `<Skeleton deep>` / opaque components work with zero setup.
11
- import './deep';
12
-
13
9
  // Main component
14
10
  export { Skeleton } from './components/Skeleton';
15
11
  export { SkeletonIgnore } from './components/SkeletonIgnore';
@@ -79,15 +79,6 @@ export interface SkeletonProps {
79
79
  */
80
80
  excludeStyles?: string[];
81
81
 
82
- /**
83
- * Number of placeholder rows to render for a `FlatList` / `SectionList`
84
- * while its data is still loading (its rows don't exist yet, so Skelo calls
85
- * `renderItem` with a placeholder and repeats it `count` times).
86
- *
87
- * @default 6
88
- */
89
- count?: number;
90
-
91
82
  /**
92
83
  * Deep mode: expand opaque (custom) child components into their real
93
84
  * host-element tree via offline rendering, producing a structured skeleton
@@ -1,18 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.getDeepExpander = getDeepExpander;
7
- exports.setDeepExpander = setDeepExpander;
8
- // Deep expansion is opt-in so the core bundle never depends on react-reconciler.
9
- // `react-native-skelo/deep` registers the expander; Skeleton reads it here.
10
-
11
- let expander = null;
12
- function setDeepExpander(fn) {
13
- expander = fn;
14
- }
15
- function getDeepExpander() {
16
- return expander;
17
- }
18
- //# sourceMappingURL=deepRegistry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["expander","setDeepExpander","fn","getDeepExpander"],"sourceRoot":"../../../src","sources":["core/deepRegistry.ts"],"mappings":";;;;;;;AAGA;AACA;;AAGA,IAAIA,QAA6B,GAAG,IAAI;AAEjC,SAASC,eAAeA,CAACC,EAAgB,EAAQ;EACtDF,QAAQ,GAAGE,EAAE;AACf;AAEO,SAASC,eAAeA,CAAA,EAAwB;EACrD,OAAOH,QAAQ;AACjB","ignoreList":[]}
@@ -1,67 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.expandListPlaceholders = expandListPlaceholders;
7
- var _react = _interopRequireWildcard(require("react"));
8
- var _reactNative = require("react-native");
9
- var _componentUtils = require("../../utils/componentUtils");
10
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
- // List components whose rows come from `renderItem` (not JSX children).
12
- const LIST_TYPES = new Set(['FlatList', 'SectionList', 'VirtualizedList', 'FlashList']);
13
- const NOOP_SEPARATORS = {
14
- highlight: () => {},
15
- unhighlight: () => {},
16
- updateProps: () => {}
17
- };
18
-
19
- // Renders `count` sample rows from a list's `renderItem` so there's something
20
- // to skeletonize while the real data is still loading (the rows don't exist in
21
- // the tree yet). Rows that throw on placeholder data are skipped.
22
- function renderSampleRows(list, count) {
23
- const props = list.props || {};
24
- const data = Array.isArray(props.data) ? props.data : [];
25
- const sample = data.length > 0 ? data[0] : {};
26
- const rows = [];
27
- for (let i = 0; i < count; i++) {
28
- try {
29
- const row = props.renderItem?.({
30
- item: sample,
31
- index: i,
32
- section: {},
33
- separators: NOOP_SEPARATORS
34
- });
35
- if (row) {
36
- rows.push(/*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
37
- key: i
38
- }, row));
39
- }
40
- } catch {
41
- // Row couldn't render with placeholder data — skip it.
42
- }
43
- }
44
- return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
45
- style: props.contentContainerStyle
46
- }, rows);
47
- }
48
-
49
- // Walks a child tree and replaces FlatList/SectionList with sample rows so a
50
- // plain `<Skeleton><FlatList/></Skeleton>` produces a skeleton while loading.
51
- function expandListPlaceholders(children, count) {
52
- return _react.default.Children.map(children, child => {
53
- if (! /*#__PURE__*/(0, _react.isValidElement)(child)) {
54
- return child;
55
- }
56
- const name = (0, _componentUtils.getComponentName)(child);
57
- const props = child.props || {};
58
- if (LIST_TYPES.has(name) && typeof props.renderItem === 'function') {
59
- return renderSampleRows(child, count);
60
- }
61
- if (props.children) {
62
- return /*#__PURE__*/_react.default.cloneElement(child, undefined, expandListPlaceholders(props.children, count));
63
- }
64
- return child;
65
- });
66
- }
67
- //# sourceMappingURL=expandLists.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_componentUtils","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","LIST_TYPES","Set","NOOP_SEPARATORS","highlight","unhighlight","updateProps","renderSampleRows","list","count","props","data","Array","isArray","sample","length","rows","row","renderItem","item","index","section","separators","push","createElement","Fragment","key","View","style","contentContainerStyle","expandListPlaceholders","children","React","Children","map","child","isValidElement","name","getComponentName","cloneElement","undefined"],"sourceRoot":"../../../../src","sources":["core/parser/expandLists.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,18 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.deepEnabled = void 0;
7
- var _deepRegistry = require("./core/deepRegistry");
8
- var _expandOffline = require("./core/parser/expandOffline");
9
- // Opt-in deep mode. Import once in your app entry to enable `<Skeleton deep>`:
10
- //
11
- // import 'react-native-skelo/deep';
12
- //
13
- // This is the only module that pulls in react-reconciler, so apps that don't
14
- // use deep mode never bundle it. Requires `react-reconciler` to be installed.
15
-
16
- (0, _deepRegistry.setDeepExpander)(_expandOffline.expandToComponentNodes);
17
- const deepEnabled = exports.deepEnabled = true;
18
- //# sourceMappingURL=deep.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_deepRegistry","require","_expandOffline","setDeepExpander","expandToComponentNodes","deepEnabled","exports"],"sourceRoot":"../../src","sources":["deep.ts"],"mappings":";;;;;;AAMA,IAAAA,aAAA,GAAAC,OAAA;AACA,IAAAC,cAAA,GAAAD,OAAA;AAPA;AACA;AACA;AACA;AACA;AACA;;AAIA,IAAAE,6BAAe,EAACC,qCAAsB,CAAC;AAEhC,MAAMC,WAAW,GAAAC,OAAA,CAAAD,WAAA,GAAG,IAAI","ignoreList":[]}
@@ -1,11 +0,0 @@
1
- // Deep expansion is opt-in so the core bundle never depends on react-reconciler.
2
- // `react-native-skelo/deep` registers the expander; Skeleton reads it here.
3
-
4
- let expander = null;
5
- export function setDeepExpander(fn) {
6
- expander = fn;
7
- }
8
- export function getDeepExpander() {
9
- return expander;
10
- }
11
- //# sourceMappingURL=deepRegistry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["expander","setDeepExpander","fn","getDeepExpander"],"sourceRoot":"../../../src","sources":["core/deepRegistry.ts"],"mappings":"AAGA;AACA;;AAGA,IAAIA,QAA6B,GAAG,IAAI;AAExC,OAAO,SAASC,eAAeA,CAACC,EAAgB,EAAQ;EACtDF,QAAQ,GAAGE,EAAE;AACf;AAEA,OAAO,SAASC,eAAeA,CAAA,EAAwB;EACrD,OAAOH,QAAQ;AACjB","ignoreList":[]}