@webority-technologies/mobile 0.0.5 → 0.0.7
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/README.md +3 -435
- package/lib/commonjs/components/Accordion/Accordion.js +20 -1
- package/lib/commonjs/components/Banner/Banner.js +17 -5
- package/lib/commonjs/components/Button/Button.js +5 -2
- package/lib/commonjs/components/Carousel/Carousel.js +18 -1
- package/lib/commonjs/components/Chip/Chip.js +27 -15
- package/lib/commonjs/components/ImageGallery/ImageGallery.js +19 -9
- package/lib/commonjs/components/Rating/Rating.js +11 -1
- package/lib/commonjs/components/Skeleton/Skeleton.js +20 -12
- package/lib/commonjs/components/Skeleton/SkeletonContent.js +25 -9
- package/lib/commonjs/components/Skeleton/SkeletonList.js +7 -2
- package/lib/commonjs/components/Skeleton/SkeletonProvider.js +48 -0
- package/lib/commonjs/components/Skeleton/SkeletonSkip.js +37 -0
- package/lib/commonjs/components/Skeleton/index.js +20 -0
- package/lib/commonjs/components/Stepper/Stepper.js +13 -3
- package/lib/commonjs/components/index.js +18 -0
- package/lib/module/components/Accordion/Accordion.js +20 -1
- package/lib/module/components/Banner/Banner.js +17 -5
- package/lib/module/components/Button/Button.js +6 -3
- package/lib/module/components/Carousel/Carousel.js +18 -1
- package/lib/module/components/Chip/Chip.js +27 -15
- package/lib/module/components/ImageGallery/ImageGallery.js +11 -1
- package/lib/module/components/Rating/Rating.js +11 -1
- package/lib/module/components/Skeleton/Skeleton.js +20 -12
- package/lib/module/components/Skeleton/SkeletonContent.js +25 -9
- package/lib/module/components/Skeleton/SkeletonList.js +7 -2
- package/lib/module/components/Skeleton/SkeletonProvider.js +41 -0
- package/lib/module/components/Skeleton/SkeletonSkip.js +31 -0
- package/lib/module/components/Skeleton/index.js +2 -0
- package/lib/module/components/Stepper/Stepper.js +13 -3
- package/lib/module/components/index.js +1 -1
- package/lib/typescript/commonjs/components/Accordion/Accordion.d.ts +7 -0
- package/lib/typescript/commonjs/components/Banner/Banner.d.ts +6 -0
- package/lib/typescript/commonjs/components/Button/Button.d.ts +5 -0
- package/lib/typescript/commonjs/components/Carousel/Carousel.d.ts +7 -0
- package/lib/typescript/commonjs/components/Chip/Chip.d.ts +6 -0
- package/lib/typescript/commonjs/components/ImageGallery/ImageGallery.d.ts +6 -0
- package/lib/typescript/commonjs/components/Rating/Rating.d.ts +6 -0
- package/lib/typescript/commonjs/components/Skeleton/Skeleton.d.ts +9 -0
- package/lib/typescript/commonjs/components/Skeleton/SkeletonContent.d.ts +6 -0
- package/lib/typescript/commonjs/components/Skeleton/SkeletonList.d.ts +3 -0
- package/lib/typescript/commonjs/components/Skeleton/SkeletonProvider.d.ts +32 -0
- package/lib/typescript/commonjs/components/Skeleton/SkeletonSkip.d.ts +25 -0
- package/lib/typescript/commonjs/components/Skeleton/index.d.ts +4 -0
- package/lib/typescript/commonjs/components/Stepper/Stepper.d.ts +6 -0
- package/lib/typescript/commonjs/components/index.d.ts +2 -2
- package/lib/typescript/module/components/Accordion/Accordion.d.ts +7 -0
- package/lib/typescript/module/components/Banner/Banner.d.ts +6 -0
- package/lib/typescript/module/components/Button/Button.d.ts +5 -0
- package/lib/typescript/module/components/Carousel/Carousel.d.ts +7 -0
- package/lib/typescript/module/components/Chip/Chip.d.ts +6 -0
- package/lib/typescript/module/components/ImageGallery/ImageGallery.d.ts +6 -0
- package/lib/typescript/module/components/Rating/Rating.d.ts +6 -0
- package/lib/typescript/module/components/Skeleton/Skeleton.d.ts +9 -0
- package/lib/typescript/module/components/Skeleton/SkeletonContent.d.ts +6 -0
- package/lib/typescript/module/components/Skeleton/SkeletonList.d.ts +3 -0
- package/lib/typescript/module/components/Skeleton/SkeletonProvider.d.ts +32 -0
- package/lib/typescript/module/components/Skeleton/SkeletonSkip.d.ts +25 -0
- package/lib/typescript/module/components/Skeleton/index.d.ts +4 -0
- package/lib/typescript/module/components/Stepper/Stepper.d.ts +6 -0
- package/lib/typescript/module/components/index.d.ts +2 -2
- package/package.json +1 -1
|
@@ -273,6 +273,18 @@ Object.defineProperty(exports, "SkeletonListItem", {
|
|
|
273
273
|
return _index35.SkeletonListItem;
|
|
274
274
|
}
|
|
275
275
|
});
|
|
276
|
+
Object.defineProperty(exports, "SkeletonProvider", {
|
|
277
|
+
enumerable: true,
|
|
278
|
+
get: function () {
|
|
279
|
+
return _index35.SkeletonProvider;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
Object.defineProperty(exports, "SkeletonSkip", {
|
|
283
|
+
enumerable: true,
|
|
284
|
+
get: function () {
|
|
285
|
+
return _index35.SkeletonSkip;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
276
288
|
Object.defineProperty(exports, "SkeletonText", {
|
|
277
289
|
enumerable: true,
|
|
278
290
|
get: function () {
|
|
@@ -345,6 +357,12 @@ Object.defineProperty(exports, "toast", {
|
|
|
345
357
|
return _index41.toast;
|
|
346
358
|
}
|
|
347
359
|
});
|
|
360
|
+
Object.defineProperty(exports, "useSkeletonDefaults", {
|
|
361
|
+
enumerable: true,
|
|
362
|
+
get: function () {
|
|
363
|
+
return _index35.useSkeletonDefaults;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
348
366
|
Object.defineProperty(exports, "useToast", {
|
|
349
367
|
enumerable: true,
|
|
350
368
|
get: function () {
|
|
@@ -4,6 +4,7 @@ import React, { createContext, useCallback, useContext, useEffect, useMemo, useR
|
|
|
4
4
|
import { Animated, Easing, LayoutAnimation, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
7
|
+
import { SkeletonContent } from "../Skeleton/index.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
const AccordionGroupContext = /*#__PURE__*/createContext(null);
|
|
9
10
|
const useAccordionGroup = () => useContext(AccordionGroupContext);
|
|
@@ -86,7 +87,8 @@ const Accordion = props => {
|
|
|
86
87
|
style,
|
|
87
88
|
headerStyle,
|
|
88
89
|
contentStyle,
|
|
89
|
-
testID
|
|
90
|
+
testID,
|
|
91
|
+
loading = false
|
|
90
92
|
} = props;
|
|
91
93
|
const theme = useTheme();
|
|
92
94
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
@@ -152,6 +154,23 @@ const Accordion = props => {
|
|
|
152
154
|
backgroundColor: theme.colors.background.elevated,
|
|
153
155
|
borderRadius: theme.radius.md
|
|
154
156
|
}] : null;
|
|
157
|
+
if (loading) {
|
|
158
|
+
return /*#__PURE__*/_jsx(SkeletonContent, {
|
|
159
|
+
loading: true,
|
|
160
|
+
mode: "auto",
|
|
161
|
+
style: [cardStyle, style],
|
|
162
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
163
|
+
style: styles.header,
|
|
164
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
165
|
+
style: [styles.title, {
|
|
166
|
+
fontSize: theme.typography.fontSize.base
|
|
167
|
+
}],
|
|
168
|
+
numberOfLines: 1,
|
|
169
|
+
children: title ?? ' '
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
});
|
|
173
|
+
}
|
|
155
174
|
return /*#__PURE__*/_jsxs(View, {
|
|
156
175
|
style: [cardStyle, style],
|
|
157
176
|
testID: testID,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
|
+
import { SkeletonContent } from "../Skeleton/index.js";
|
|
6
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
8
|
const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
8
9
|
const {
|
|
@@ -15,6 +16,7 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
15
16
|
onDismiss,
|
|
16
17
|
visible = true,
|
|
17
18
|
animateMount = true,
|
|
19
|
+
loading = false,
|
|
18
20
|
accessibilityLabel,
|
|
19
21
|
style,
|
|
20
22
|
testID
|
|
@@ -59,6 +61,8 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
59
61
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
62
|
}, [visible]);
|
|
61
63
|
if (!mounted) return null;
|
|
64
|
+
const safeTitle = title ?? ' ';
|
|
65
|
+
const safeMessageString = typeof message === 'string' ? message ?? ' ' : '';
|
|
62
66
|
const renderIcon = () => {
|
|
63
67
|
if (icon === false) return null;
|
|
64
68
|
if (icon) return /*#__PURE__*/_jsx(View, {
|
|
@@ -81,7 +85,7 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
81
85
|
const messageString = typeof message === 'string' ? message : '';
|
|
82
86
|
const builtA11y = accessibilityLabel ?? (title ? `${title}. ${messageString}` : messageString || undefined);
|
|
83
87
|
const liveRegion = variant === 'error' ? 'assertive' : 'polite';
|
|
84
|
-
|
|
88
|
+
const rendered = /*#__PURE__*/_jsxs(Animated.View, {
|
|
85
89
|
ref: ref,
|
|
86
90
|
accessibilityRole: 'alert',
|
|
87
91
|
accessibilityLiveRegion: liveRegion,
|
|
@@ -104,7 +108,7 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
104
108
|
style: styles.row,
|
|
105
109
|
children: [renderIcon(), /*#__PURE__*/_jsxs(View, {
|
|
106
110
|
style: styles.textBlock,
|
|
107
|
-
children: [title ? /*#__PURE__*/_jsx(Text, {
|
|
111
|
+
children: [title || loading ? /*#__PURE__*/_jsx(Text, {
|
|
108
112
|
style: {
|
|
109
113
|
color: theme.colors.text.primary,
|
|
110
114
|
fontSize: theme.typography.fontSize.base,
|
|
@@ -112,15 +116,15 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
112
116
|
lineHeight: 20
|
|
113
117
|
},
|
|
114
118
|
numberOfLines: 2,
|
|
115
|
-
children:
|
|
116
|
-
}) : null, typeof message === 'string' ? /*#__PURE__*/_jsx(Text, {
|
|
119
|
+
children: safeTitle
|
|
120
|
+
}) : null, typeof message === 'string' || loading ? /*#__PURE__*/_jsx(Text, {
|
|
117
121
|
style: {
|
|
118
122
|
color: theme.colors.text.secondary,
|
|
119
123
|
fontSize: theme.typography.fontSize.sm,
|
|
120
124
|
lineHeight: 18,
|
|
121
125
|
marginTop: title ? 2 : 0
|
|
122
126
|
},
|
|
123
|
-
children:
|
|
127
|
+
children: safeMessageString || ' '
|
|
124
128
|
}) : /*#__PURE__*/_jsx(View, {
|
|
125
129
|
style: {
|
|
126
130
|
marginTop: title ? 2 : 0
|
|
@@ -174,6 +178,14 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
174
178
|
})
|
|
175
179
|
}) : null]
|
|
176
180
|
});
|
|
181
|
+
if (loading) {
|
|
182
|
+
return /*#__PURE__*/_jsx(SkeletonContent, {
|
|
183
|
+
loading: true,
|
|
184
|
+
mode: "auto",
|
|
185
|
+
children: rendered
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return rendered;
|
|
177
189
|
});
|
|
178
190
|
Banner.displayName = 'Banner';
|
|
179
191
|
const tintFor = (theme, variant) => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React, { forwardRef, useMemo } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
6
|
import { usePressAnimation } from "../../hooks/usePressAnimation.js";
|
|
7
7
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
8
|
+
import { Spinner } from "../Spinner/index.js";
|
|
8
9
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
10
|
const Button = /*#__PURE__*/forwardRef((props, ref) => {
|
|
10
11
|
const {
|
|
@@ -50,9 +51,11 @@ const Button = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
50
51
|
const accessibleLabel = accessibilityLabel ?? title;
|
|
51
52
|
const content = /*#__PURE__*/_jsx(View, {
|
|
52
53
|
style: styles.contentRow,
|
|
53
|
-
children: loading ? /*#__PURE__*/_jsx(
|
|
54
|
+
children: loading ? /*#__PURE__*/_jsx(Spinner, {
|
|
55
|
+
variant: "circular",
|
|
56
|
+
size: "small",
|
|
54
57
|
color: variantStyles.textColor,
|
|
55
|
-
|
|
58
|
+
accessibilityLabel: "Loading"
|
|
56
59
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
57
60
|
children: [leftIcon ? /*#__PURE__*/_jsx(View, {
|
|
58
61
|
style: styles.iconLeft,
|
|
@@ -26,6 +26,7 @@ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo
|
|
|
26
26
|
import { Animated, Dimensions, FlatList, Image, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
27
27
|
import { useTheme } from "../../theme/index.js";
|
|
28
28
|
import { triggerHaptic } from "../../utils/index.js";
|
|
29
|
+
import { SkeletonContent } from "../Skeleton/index.js";
|
|
29
30
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
30
31
|
// We intentionally use a generic forwardRef wrapper — the inner component is
|
|
31
32
|
// untyped at the generic level (forwardRef can't preserve generics in TS yet),
|
|
@@ -49,7 +50,8 @@ const CarouselInner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
49
50
|
accessibilityLabel,
|
|
50
51
|
testID,
|
|
51
52
|
showThumbnails = false,
|
|
52
|
-
renderThumbnail
|
|
53
|
+
renderThumbnail,
|
|
54
|
+
loading = false
|
|
53
55
|
} = props;
|
|
54
56
|
const theme = useTheme();
|
|
55
57
|
const isControlled = typeof controlledIndex === 'number';
|
|
@@ -210,6 +212,21 @@ const CarouselInner = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
210
212
|
offset: slideStride * i,
|
|
211
213
|
index: i
|
|
212
214
|
}), [slideStride]);
|
|
215
|
+
if (loading) {
|
|
216
|
+
const slideWidth = itemWidth === 'screen' ? containerWidth || 320 : itemWidth;
|
|
217
|
+
return /*#__PURE__*/_jsx(SkeletonContent, {
|
|
218
|
+
loading: true,
|
|
219
|
+
mode: "auto",
|
|
220
|
+
style: [styles.container, containerStyle],
|
|
221
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
222
|
+
style: {
|
|
223
|
+
width: slideWidth,
|
|
224
|
+
height: 160,
|
|
225
|
+
borderRadius: 12
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
});
|
|
229
|
+
}
|
|
213
230
|
return /*#__PURE__*/_jsxs(View, {
|
|
214
231
|
style: [styles.container, containerStyle],
|
|
215
232
|
onLayout: e => setContainerWidth(e.nativeEvent.layout.width),
|
|
@@ -5,6 +5,7 @@ import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
6
|
import { usePressAnimation } from "../../hooks/usePressAnimation.js";
|
|
7
7
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
8
|
+
import { SkeletonContent } from "../Skeleton/index.js";
|
|
8
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
10
|
const toneFor = (theme, tone) => {
|
|
10
11
|
switch (tone) {
|
|
@@ -96,6 +97,7 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
96
97
|
tone = 'neutral',
|
|
97
98
|
size = 'md',
|
|
98
99
|
disabled = false,
|
|
100
|
+
loading = false,
|
|
99
101
|
accessibilityLabel,
|
|
100
102
|
style,
|
|
101
103
|
textStyle,
|
|
@@ -154,7 +156,7 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
154
156
|
fontWeight: theme.typography.fontWeight.medium
|
|
155
157
|
}, textStyle],
|
|
156
158
|
numberOfLines: 1,
|
|
157
|
-
children: label
|
|
159
|
+
children: label ?? ' '
|
|
158
160
|
}), onClose ? /*#__PURE__*/_jsx(Pressable, {
|
|
159
161
|
onPress: handleClose,
|
|
160
162
|
disabled: disabled,
|
|
@@ -195,8 +197,9 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
195
197
|
borderColor,
|
|
196
198
|
opacity: disabled ? 0.55 : 1
|
|
197
199
|
};
|
|
200
|
+
let rendered;
|
|
198
201
|
if (isPressable) {
|
|
199
|
-
|
|
202
|
+
rendered = /*#__PURE__*/_jsx(Animated.View, {
|
|
200
203
|
style: {
|
|
201
204
|
transform: [{
|
|
202
205
|
scale
|
|
@@ -228,20 +231,29 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
228
231
|
children: content
|
|
229
232
|
})
|
|
230
233
|
});
|
|
234
|
+
} else {
|
|
235
|
+
rendered = /*#__PURE__*/_jsx(View, {
|
|
236
|
+
ref: ref,
|
|
237
|
+
style: [styles.base, baseStyle, style],
|
|
238
|
+
accessible: true,
|
|
239
|
+
accessibilityRole: "text",
|
|
240
|
+
accessibilityLabel: a11yLabel,
|
|
241
|
+
accessibilityState: {
|
|
242
|
+
selected,
|
|
243
|
+
disabled
|
|
244
|
+
},
|
|
245
|
+
testID: testID,
|
|
246
|
+
children: content
|
|
247
|
+
});
|
|
231
248
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
disabled
|
|
241
|
-
},
|
|
242
|
-
testID: testID,
|
|
243
|
-
children: content
|
|
244
|
-
});
|
|
249
|
+
if (loading) {
|
|
250
|
+
return /*#__PURE__*/_jsx(SkeletonContent, {
|
|
251
|
+
loading: true,
|
|
252
|
+
mode: "auto",
|
|
253
|
+
children: rendered
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
return rendered;
|
|
245
257
|
});
|
|
246
258
|
Chip.displayName = 'Chip';
|
|
247
259
|
const styles = StyleSheet.create({
|
|
@@ -25,6 +25,7 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
|
25
25
|
import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
26
26
|
import { Carousel } from "../Carousel/index.js";
|
|
27
27
|
import { AppIcon } from "../AppIcon/index.js";
|
|
28
|
+
import { SkeletonContent } from "../Skeleton/index.js";
|
|
28
29
|
import { useTheme } from "../../theme/index.js";
|
|
29
30
|
import { triggerHaptic } from "../../utils/index.js";
|
|
30
31
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -44,6 +45,7 @@ const ImageGallery = ({
|
|
|
44
45
|
enableLightbox = true,
|
|
45
46
|
enablePinchZoom = true,
|
|
46
47
|
onIndexChange,
|
|
48
|
+
loading = false,
|
|
47
49
|
accessibilityLabel,
|
|
48
50
|
containerStyle,
|
|
49
51
|
testID
|
|
@@ -85,7 +87,7 @@ const ImageGallery = ({
|
|
|
85
87
|
style: styles.thumbnailImage,
|
|
86
88
|
resizeMode: "cover"
|
|
87
89
|
}), [styles]);
|
|
88
|
-
|
|
90
|
+
const rendered = /*#__PURE__*/_jsxs(View, {
|
|
89
91
|
style: [styles.container, containerStyle],
|
|
90
92
|
testID: testID,
|
|
91
93
|
accessibilityLabel: accessibilityLabel ?? 'Image gallery',
|
|
@@ -120,6 +122,14 @@ const ImageGallery = ({
|
|
|
120
122
|
onIndexChange: handleIndexChange
|
|
121
123
|
}) : null]
|
|
122
124
|
});
|
|
125
|
+
if (loading) {
|
|
126
|
+
return /*#__PURE__*/_jsx(SkeletonContent, {
|
|
127
|
+
loading: true,
|
|
128
|
+
mode: "auto",
|
|
129
|
+
children: rendered
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return rendered;
|
|
123
133
|
};
|
|
124
134
|
ImageGallery.displayName = 'ImageGallery';
|
|
125
135
|
|
|
@@ -4,6 +4,7 @@ import React, { forwardRef, useCallback, useMemo, useRef } from 'react';
|
|
|
4
4
|
import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
7
|
+
import { SkeletonContent } from "../Skeleton/index.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
const sizePxMap = {
|
|
9
10
|
sm: 16,
|
|
@@ -78,6 +79,7 @@ const Rating = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
78
79
|
size = 'md',
|
|
79
80
|
tone = 'warning',
|
|
80
81
|
label,
|
|
82
|
+
loading = false,
|
|
81
83
|
accessibilityLabel,
|
|
82
84
|
style,
|
|
83
85
|
testID
|
|
@@ -163,7 +165,7 @@ const Rating = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
163
165
|
children: star
|
|
164
166
|
}, i));
|
|
165
167
|
}
|
|
166
|
-
|
|
168
|
+
const rendered = /*#__PURE__*/_jsxs(View, {
|
|
167
169
|
style: [styles.wrapper, style],
|
|
168
170
|
ref: ref,
|
|
169
171
|
testID: testID,
|
|
@@ -193,6 +195,14 @@ const Rating = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
193
195
|
children: stars
|
|
194
196
|
})]
|
|
195
197
|
});
|
|
198
|
+
if (loading) {
|
|
199
|
+
return /*#__PURE__*/_jsx(SkeletonContent, {
|
|
200
|
+
loading: true,
|
|
201
|
+
mode: "auto",
|
|
202
|
+
children: rendered
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return rendered;
|
|
196
206
|
});
|
|
197
207
|
Rating.displayName = 'Rating';
|
|
198
208
|
const starStyles = StyleSheet.create({
|
|
@@ -4,6 +4,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
|
4
4
|
import { Animated, Easing, StyleSheet, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
6
|
import { Responsive } from "../../utils/index.js";
|
|
7
|
+
import { useSkeletonDefaults } from "./SkeletonProvider.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
const SPEED_DURATION = {
|
|
9
10
|
slow: 1800,
|
|
@@ -28,23 +29,30 @@ const resolveWidth = width => {
|
|
|
28
29
|
const Skeleton = ({
|
|
29
30
|
width = '100%',
|
|
30
31
|
height = 16,
|
|
31
|
-
radius
|
|
32
|
-
variant
|
|
33
|
-
speed
|
|
32
|
+
radius,
|
|
33
|
+
variant,
|
|
34
|
+
speed,
|
|
35
|
+
colors,
|
|
34
36
|
style,
|
|
35
37
|
testID
|
|
36
38
|
}) => {
|
|
37
39
|
const theme = useTheme();
|
|
40
|
+
const defaults = useSkeletonDefaults();
|
|
41
|
+
const resolvedVariant = variant ?? defaults.variant ?? 'shimmer';
|
|
42
|
+
const resolvedSpeed = speed ?? defaults.speed ?? 'normal';
|
|
43
|
+
const resolvedRadiusToken = radius ?? defaults.radius ?? 'sm';
|
|
44
|
+
const backgroundColor = colors?.background ?? defaults.colors?.background ?? theme.colors.skeleton.background;
|
|
45
|
+
const highlightColor = colors?.highlight ?? defaults.colors?.highlight ?? theme.colors.skeleton.highlight;
|
|
38
46
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
39
|
-
const borderRadius = resolveRadius(theme,
|
|
47
|
+
const borderRadius = resolveRadius(theme, resolvedRadiusToken);
|
|
40
48
|
const resolvedWidth = resolveWidth(width);
|
|
41
49
|
const resolvedHeight = Responsive.size(height);
|
|
42
50
|
const progress = useRef(new Animated.Value(0)).current;
|
|
43
51
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
44
52
|
useEffect(() => {
|
|
45
53
|
progress.setValue(0);
|
|
46
|
-
const duration =
|
|
47
|
-
const animation =
|
|
54
|
+
const duration = resolvedVariant === 'pulse' ? PULSE_DURATION : SPEED_DURATION[resolvedSpeed];
|
|
55
|
+
const animation = resolvedVariant === 'pulse' ? Animated.loop(Animated.sequence([Animated.timing(progress, {
|
|
48
56
|
toValue: 1,
|
|
49
57
|
duration: duration / 2,
|
|
50
58
|
easing: Easing.inOut(Easing.ease),
|
|
@@ -64,17 +72,17 @@ const Skeleton = ({
|
|
|
64
72
|
return () => {
|
|
65
73
|
animation.stop();
|
|
66
74
|
};
|
|
67
|
-
}, [progress,
|
|
75
|
+
}, [progress, resolvedSpeed, resolvedVariant]);
|
|
68
76
|
const handleLayout = event => {
|
|
69
77
|
const next = event.nativeEvent.layout.width;
|
|
70
78
|
if (next !== containerWidth) setContainerWidth(next);
|
|
71
79
|
};
|
|
72
80
|
const overlay = useMemo(() => {
|
|
73
|
-
if (
|
|
81
|
+
if (resolvedVariant === 'pulse') {
|
|
74
82
|
return /*#__PURE__*/_jsx(Animated.View, {
|
|
75
83
|
pointerEvents: "none",
|
|
76
84
|
style: [StyleSheet.absoluteFillObject, {
|
|
77
|
-
backgroundColor:
|
|
85
|
+
backgroundColor: highlightColor,
|
|
78
86
|
opacity: progress.interpolate({
|
|
79
87
|
inputRange: [0, 1],
|
|
80
88
|
outputRange: [0, 0.6]
|
|
@@ -92,13 +100,13 @@ const Skeleton = ({
|
|
|
92
100
|
pointerEvents: "none",
|
|
93
101
|
style: [styles.shimmer, {
|
|
94
102
|
width: highlightWidth,
|
|
95
|
-
backgroundColor:
|
|
103
|
+
backgroundColor: highlightColor,
|
|
96
104
|
transform: [{
|
|
97
105
|
translateX
|
|
98
106
|
}]
|
|
99
107
|
}]
|
|
100
108
|
});
|
|
101
|
-
}, [containerWidth, progress, styles.shimmer,
|
|
109
|
+
}, [containerWidth, progress, styles.shimmer, highlightColor, resolvedVariant]);
|
|
102
110
|
return /*#__PURE__*/_jsx(View, {
|
|
103
111
|
onLayout: handleLayout,
|
|
104
112
|
accessible: true,
|
|
@@ -110,7 +118,7 @@ const Skeleton = ({
|
|
|
110
118
|
width: resolvedWidth,
|
|
111
119
|
height: resolvedHeight,
|
|
112
120
|
borderRadius,
|
|
113
|
-
backgroundColor
|
|
121
|
+
backgroundColor
|
|
114
122
|
}, style],
|
|
115
123
|
children: overlay
|
|
116
124
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React, { Children, cloneElement, isValidElement, useState } from 'react';
|
|
4
4
|
import { Image, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { Skeleton } from "./Skeleton.js";
|
|
6
|
+
import { SkeletonSkip } from "./SkeletonSkip.js";
|
|
6
7
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
8
|
const flattenStyle = style => {
|
|
8
9
|
if (!style) return {};
|
|
@@ -22,7 +23,7 @@ const resolveRadius = (raw, fallback) => {
|
|
|
22
23
|
if (typeof raw === 'number') return raw;
|
|
23
24
|
return fallback;
|
|
24
25
|
};
|
|
25
|
-
const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
26
|
+
const skeletonizeNode = (node, variant, speed, colors, keyHint = 'r') => {
|
|
26
27
|
if (node == null || typeof node === 'boolean') return null;
|
|
27
28
|
if (typeof node === 'string' || typeof node === 'number') {
|
|
28
29
|
return /*#__PURE__*/_jsx(Skeleton, {
|
|
@@ -30,11 +31,12 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
30
31
|
height: 14,
|
|
31
32
|
radius: "sm",
|
|
32
33
|
variant: variant,
|
|
33
|
-
speed: speed
|
|
34
|
+
speed: speed,
|
|
35
|
+
colors: colors
|
|
34
36
|
}, `${keyHint}-text`);
|
|
35
37
|
}
|
|
36
38
|
if (Array.isArray(node)) {
|
|
37
|
-
return Children.map(node, (child, index) => skeletonizeNode(child, variant, speed, `${keyHint}-${index}`));
|
|
39
|
+
return Children.map(node, (child, index) => skeletonizeNode(child, variant, speed, colors, `${keyHint}-${index}`));
|
|
38
40
|
}
|
|
39
41
|
if (! /*#__PURE__*/isValidElement(node)) return null;
|
|
40
42
|
const element = node;
|
|
@@ -42,6 +44,13 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
42
44
|
const elementStyle = flattenStyle(props.style);
|
|
43
45
|
const elementType = element.type;
|
|
44
46
|
|
|
47
|
+
// <SkeletonSkip /> → opt-out marker; render its children unchanged
|
|
48
|
+
if (elementType === SkeletonSkip) {
|
|
49
|
+
return /*#__PURE__*/cloneElement(element, {
|
|
50
|
+
key: `${keyHint}-skip`
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
// <Image /> → fixed-size skeleton block matching style.width/height/borderRadius
|
|
46
55
|
if (elementType === Image) {
|
|
47
56
|
const width = resolveWidth(elementStyle.width, '100%');
|
|
@@ -53,6 +62,7 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
53
62
|
radius: radius,
|
|
54
63
|
variant: variant,
|
|
55
64
|
speed: speed,
|
|
65
|
+
colors: colors,
|
|
56
66
|
style: {
|
|
57
67
|
margin: typeof elementStyle.margin === 'number' ? elementStyle.margin : undefined
|
|
58
68
|
}
|
|
@@ -69,6 +79,7 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
69
79
|
radius: "sm",
|
|
70
80
|
variant: variant,
|
|
71
81
|
speed: speed,
|
|
82
|
+
colors: colors,
|
|
72
83
|
style: {
|
|
73
84
|
marginVertical: 2
|
|
74
85
|
}
|
|
@@ -79,7 +90,7 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
79
90
|
if (props.children != null) {
|
|
80
91
|
return /*#__PURE__*/cloneElement(element, {
|
|
81
92
|
key: `${keyHint}-w`
|
|
82
|
-
}, skeletonizeNode(props.children, variant, speed, `${keyHint}-c`));
|
|
93
|
+
}, skeletonizeNode(props.children, variant, speed, colors, `${keyHint}-c`));
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
// Leaf <View /> with explicit dimensions → skeleton block
|
|
@@ -92,7 +103,8 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
92
103
|
height: height,
|
|
93
104
|
radius: radius,
|
|
94
105
|
variant: variant,
|
|
95
|
-
speed: speed
|
|
106
|
+
speed: speed,
|
|
107
|
+
colors: colors
|
|
96
108
|
}, `${keyHint}-vw`);
|
|
97
109
|
}
|
|
98
110
|
|
|
@@ -105,10 +117,11 @@ const skeletonizeNode = (node, variant, speed, keyHint = 'r') => {
|
|
|
105
117
|
const SkeletonContent = ({
|
|
106
118
|
loading,
|
|
107
119
|
children,
|
|
108
|
-
variant
|
|
109
|
-
speed
|
|
120
|
+
variant,
|
|
121
|
+
speed,
|
|
110
122
|
mode = 'auto',
|
|
111
123
|
count = 1,
|
|
124
|
+
colors,
|
|
112
125
|
style,
|
|
113
126
|
testID
|
|
114
127
|
}) => {
|
|
@@ -128,6 +141,7 @@ const SkeletonContent = ({
|
|
|
128
141
|
testID: testID,
|
|
129
142
|
variant: variant,
|
|
130
143
|
speed: speed,
|
|
144
|
+
colors: colors,
|
|
131
145
|
children: repeated
|
|
132
146
|
});
|
|
133
147
|
}
|
|
@@ -138,7 +152,7 @@ const SkeletonContent = ({
|
|
|
138
152
|
accessibilityLabel: "Loading",
|
|
139
153
|
accessibilityRole: "progressbar",
|
|
140
154
|
accessibilityLiveRegion: "polite",
|
|
141
|
-
children: skeletonizeNode(repeated, variant, speed)
|
|
155
|
+
children: skeletonizeNode(repeated, variant, speed, colors)
|
|
142
156
|
});
|
|
143
157
|
};
|
|
144
158
|
SkeletonContent.displayName = 'SkeletonContent';
|
|
@@ -146,6 +160,7 @@ const BlockSkeleton = ({
|
|
|
146
160
|
children,
|
|
147
161
|
variant,
|
|
148
162
|
speed,
|
|
163
|
+
colors,
|
|
149
164
|
style,
|
|
150
165
|
testID
|
|
151
166
|
}) => {
|
|
@@ -182,7 +197,8 @@ const BlockSkeleton = ({
|
|
|
182
197
|
height: size.height,
|
|
183
198
|
radius: "md",
|
|
184
199
|
variant: variant,
|
|
185
|
-
speed: speed
|
|
200
|
+
speed: speed,
|
|
201
|
+
colors: colors
|
|
186
202
|
})
|
|
187
203
|
}) : null]
|
|
188
204
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { FlatList, View } from 'react-native';
|
|
5
5
|
import { SkeletonContent } from "./SkeletonContent.js";
|
|
6
|
+
import { useSkeletonDefaults } from "./SkeletonProvider.js";
|
|
6
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
8
|
/**
|
|
8
9
|
* Drop-in FlatList replacement that renders `placeholderCount` skeleton rows while
|
|
@@ -27,9 +28,10 @@ function SkeletonListInner(props) {
|
|
|
27
28
|
data,
|
|
28
29
|
renderItem,
|
|
29
30
|
renderPlaceholder,
|
|
30
|
-
placeholderCount
|
|
31
|
+
placeholderCount,
|
|
31
32
|
variant,
|
|
32
33
|
speed,
|
|
34
|
+
colors,
|
|
33
35
|
placeholderContainerStyle,
|
|
34
36
|
horizontal,
|
|
35
37
|
contentContainerStyle,
|
|
@@ -37,9 +39,11 @@ function SkeletonListInner(props) {
|
|
|
37
39
|
testID,
|
|
38
40
|
...rest
|
|
39
41
|
} = props;
|
|
42
|
+
const defaults = useSkeletonDefaults();
|
|
43
|
+
const resolvedCount = placeholderCount ?? defaults.placeholderCount ?? 3;
|
|
40
44
|
if (loading) {
|
|
41
45
|
const slots = Array.from({
|
|
42
|
-
length: Math.max(0,
|
|
46
|
+
length: Math.max(0, resolvedCount)
|
|
43
47
|
}, (_, index) => /*#__PURE__*/_jsx(React.Fragment, {
|
|
44
48
|
children: renderPlaceholder ? renderPlaceholder(index) : null
|
|
45
49
|
}, `sk-list-${index}`));
|
|
@@ -47,6 +51,7 @@ function SkeletonListInner(props) {
|
|
|
47
51
|
loading: true,
|
|
48
52
|
variant: variant,
|
|
49
53
|
speed: speed,
|
|
54
|
+
colors: colors,
|
|
50
55
|
style: style,
|
|
51
56
|
testID: testID,
|
|
52
57
|
children: /*#__PURE__*/_jsx(View, {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-instance color override for a skeleton. `background` is the resting tone
|
|
7
|
+
* of the placeholder; `highlight` is the moving shimmer band (or pulse fade).
|
|
8
|
+
* Either can be omitted — missing keys fall back to the active theme.
|
|
9
|
+
*/
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
const SkeletonDefaultsContext = /*#__PURE__*/createContext({});
|
|
12
|
+
export const SkeletonProvider = ({
|
|
13
|
+
children,
|
|
14
|
+
variant,
|
|
15
|
+
speed,
|
|
16
|
+
placeholderCount,
|
|
17
|
+
radius,
|
|
18
|
+
colors
|
|
19
|
+
}) => {
|
|
20
|
+
const value = useMemo(() => ({
|
|
21
|
+
variant,
|
|
22
|
+
speed,
|
|
23
|
+
placeholderCount,
|
|
24
|
+
radius,
|
|
25
|
+
colors
|
|
26
|
+
}), [variant, speed, placeholderCount, radius, colors]);
|
|
27
|
+
return /*#__PURE__*/_jsx(SkeletonDefaultsContext.Provider, {
|
|
28
|
+
value: value,
|
|
29
|
+
children: children
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
SkeletonProvider.displayName = 'SkeletonProvider';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Read the current SkeletonProvider defaults. Returns an empty object when no
|
|
36
|
+
* provider is mounted, so the skeleton primitives still work outside of one.
|
|
37
|
+
*/
|
|
38
|
+
export const useSkeletonDefaults = () => {
|
|
39
|
+
return useContext(SkeletonDefaultsContext);
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=SkeletonProvider.js.map
|