@hero-design/rn 8.16.0 → 8.17.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.
@@ -0,0 +1,498 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Skeleton renders correctly by default 1`] = `
4
+ <View
5
+ onLayout={[Function]}
6
+ style={
7
+ Array [
8
+ Object {},
9
+ Array [
10
+ Object {
11
+ "backgroundColor": "#f6f6f7",
12
+ "borderRadius": 16,
13
+ },
14
+ undefined,
15
+ ],
16
+ ]
17
+ }
18
+ themeIntent="light"
19
+ themeVariant="rounded"
20
+ >
21
+ <View
22
+ style={
23
+ Array [
24
+ Object {},
25
+ Array [
26
+ Object {
27
+ "borderRadius": 16,
28
+ "overflow": "hidden",
29
+ },
30
+ undefined,
31
+ ],
32
+ ]
33
+ }
34
+ themeVariant="rounded"
35
+ >
36
+ <BVLinearGradient
37
+ collapsable={false}
38
+ colors={
39
+ Array [
40
+ 4294375159,
41
+ 4294046451,
42
+ 4294375159,
43
+ ]
44
+ }
45
+ endPoint={
46
+ Object {
47
+ "x": 1,
48
+ "y": 0,
49
+ }
50
+ }
51
+ locations={null}
52
+ startPoint={
53
+ Object {
54
+ "x": 0,
55
+ "y": 0,
56
+ }
57
+ }
58
+ style={
59
+ Object {
60
+ "height": 0,
61
+ "transform": Array [
62
+ Object {
63
+ "translateX": -0,
64
+ },
65
+ ],
66
+ "width": 0,
67
+ }
68
+ }
69
+ />
70
+ </View>
71
+ </View>
72
+ `;
73
+
74
+ exports[`Skeleton renders correctly when intent is dark and variant is circular 1`] = `
75
+ <View
76
+ onLayout={[Function]}
77
+ style={
78
+ Array [
79
+ Object {},
80
+ Array [
81
+ Object {
82
+ "backgroundColor": "#ffffff",
83
+ "borderRadius": 999,
84
+ },
85
+ undefined,
86
+ ],
87
+ ]
88
+ }
89
+ themeIntent="dark"
90
+ themeVariant="circular"
91
+ >
92
+ <View
93
+ style={
94
+ Array [
95
+ Object {},
96
+ Array [
97
+ Object {
98
+ "borderRadius": 999,
99
+ "overflow": "hidden",
100
+ },
101
+ undefined,
102
+ ],
103
+ ]
104
+ }
105
+ themeVariant="circular"
106
+ >
107
+ <BVLinearGradient
108
+ collapsable={false}
109
+ colors={
110
+ Array [
111
+ 4294967295,
112
+ 4294046451,
113
+ 4294967295,
114
+ ]
115
+ }
116
+ endPoint={
117
+ Object {
118
+ "x": 1,
119
+ "y": 0,
120
+ }
121
+ }
122
+ locations={null}
123
+ startPoint={
124
+ Object {
125
+ "x": 0,
126
+ "y": 0,
127
+ }
128
+ }
129
+ style={
130
+ Object {
131
+ "height": 0,
132
+ "transform": Array [
133
+ Object {
134
+ "translateX": -0,
135
+ },
136
+ ],
137
+ "width": 0,
138
+ }
139
+ }
140
+ />
141
+ </View>
142
+ </View>
143
+ `;
144
+
145
+ exports[`Skeleton renders correctly when intent is dark and variant is rectangular 1`] = `
146
+ <View
147
+ onLayout={[Function]}
148
+ style={
149
+ Array [
150
+ Object {},
151
+ Array [
152
+ Object {
153
+ "backgroundColor": "#ffffff",
154
+ "borderRadius": 0,
155
+ },
156
+ undefined,
157
+ ],
158
+ ]
159
+ }
160
+ themeIntent="dark"
161
+ themeVariant="rectangular"
162
+ >
163
+ <View
164
+ style={
165
+ Array [
166
+ Object {},
167
+ Array [
168
+ Object {
169
+ "borderRadius": 0,
170
+ "overflow": "hidden",
171
+ },
172
+ undefined,
173
+ ],
174
+ ]
175
+ }
176
+ themeVariant="rectangular"
177
+ >
178
+ <BVLinearGradient
179
+ collapsable={false}
180
+ colors={
181
+ Array [
182
+ 4294967295,
183
+ 4294046451,
184
+ 4294967295,
185
+ ]
186
+ }
187
+ endPoint={
188
+ Object {
189
+ "x": 1,
190
+ "y": 0,
191
+ }
192
+ }
193
+ locations={null}
194
+ startPoint={
195
+ Object {
196
+ "x": 0,
197
+ "y": 0,
198
+ }
199
+ }
200
+ style={
201
+ Object {
202
+ "height": 0,
203
+ "transform": Array [
204
+ Object {
205
+ "translateX": -0,
206
+ },
207
+ ],
208
+ "width": 0,
209
+ }
210
+ }
211
+ />
212
+ </View>
213
+ </View>
214
+ `;
215
+
216
+ exports[`Skeleton renders correctly when intent is dark and variant is rounded 1`] = `
217
+ <View
218
+ onLayout={[Function]}
219
+ style={
220
+ Array [
221
+ Object {},
222
+ Array [
223
+ Object {
224
+ "backgroundColor": "#ffffff",
225
+ "borderRadius": 16,
226
+ },
227
+ undefined,
228
+ ],
229
+ ]
230
+ }
231
+ themeIntent="dark"
232
+ themeVariant="rounded"
233
+ >
234
+ <View
235
+ style={
236
+ Array [
237
+ Object {},
238
+ Array [
239
+ Object {
240
+ "borderRadius": 16,
241
+ "overflow": "hidden",
242
+ },
243
+ undefined,
244
+ ],
245
+ ]
246
+ }
247
+ themeVariant="rounded"
248
+ >
249
+ <BVLinearGradient
250
+ collapsable={false}
251
+ colors={
252
+ Array [
253
+ 4294967295,
254
+ 4294046451,
255
+ 4294967295,
256
+ ]
257
+ }
258
+ endPoint={
259
+ Object {
260
+ "x": 1,
261
+ "y": 0,
262
+ }
263
+ }
264
+ locations={null}
265
+ startPoint={
266
+ Object {
267
+ "x": 0,
268
+ "y": 0,
269
+ }
270
+ }
271
+ style={
272
+ Object {
273
+ "height": 0,
274
+ "transform": Array [
275
+ Object {
276
+ "translateX": -0,
277
+ },
278
+ ],
279
+ "width": 0,
280
+ }
281
+ }
282
+ />
283
+ </View>
284
+ </View>
285
+ `;
286
+
287
+ exports[`Skeleton renders correctly when intent is light and variant is circular 1`] = `
288
+ <View
289
+ onLayout={[Function]}
290
+ style={
291
+ Array [
292
+ Object {},
293
+ Array [
294
+ Object {
295
+ "backgroundColor": "#f6f6f7",
296
+ "borderRadius": 999,
297
+ },
298
+ undefined,
299
+ ],
300
+ ]
301
+ }
302
+ themeIntent="light"
303
+ themeVariant="circular"
304
+ >
305
+ <View
306
+ style={
307
+ Array [
308
+ Object {},
309
+ Array [
310
+ Object {
311
+ "borderRadius": 999,
312
+ "overflow": "hidden",
313
+ },
314
+ undefined,
315
+ ],
316
+ ]
317
+ }
318
+ themeVariant="circular"
319
+ >
320
+ <BVLinearGradient
321
+ collapsable={false}
322
+ colors={
323
+ Array [
324
+ 4294375159,
325
+ 4294046451,
326
+ 4294375159,
327
+ ]
328
+ }
329
+ endPoint={
330
+ Object {
331
+ "x": 1,
332
+ "y": 0,
333
+ }
334
+ }
335
+ locations={null}
336
+ startPoint={
337
+ Object {
338
+ "x": 0,
339
+ "y": 0,
340
+ }
341
+ }
342
+ style={
343
+ Object {
344
+ "height": 0,
345
+ "transform": Array [
346
+ Object {
347
+ "translateX": -0,
348
+ },
349
+ ],
350
+ "width": 0,
351
+ }
352
+ }
353
+ />
354
+ </View>
355
+ </View>
356
+ `;
357
+
358
+ exports[`Skeleton renders correctly when intent is light and variant is rectangular 1`] = `
359
+ <View
360
+ onLayout={[Function]}
361
+ style={
362
+ Array [
363
+ Object {},
364
+ Array [
365
+ Object {
366
+ "backgroundColor": "#f6f6f7",
367
+ "borderRadius": 0,
368
+ },
369
+ undefined,
370
+ ],
371
+ ]
372
+ }
373
+ themeIntent="light"
374
+ themeVariant="rectangular"
375
+ >
376
+ <View
377
+ style={
378
+ Array [
379
+ Object {},
380
+ Array [
381
+ Object {
382
+ "borderRadius": 0,
383
+ "overflow": "hidden",
384
+ },
385
+ undefined,
386
+ ],
387
+ ]
388
+ }
389
+ themeVariant="rectangular"
390
+ >
391
+ <BVLinearGradient
392
+ collapsable={false}
393
+ colors={
394
+ Array [
395
+ 4294375159,
396
+ 4294046451,
397
+ 4294375159,
398
+ ]
399
+ }
400
+ endPoint={
401
+ Object {
402
+ "x": 1,
403
+ "y": 0,
404
+ }
405
+ }
406
+ locations={null}
407
+ startPoint={
408
+ Object {
409
+ "x": 0,
410
+ "y": 0,
411
+ }
412
+ }
413
+ style={
414
+ Object {
415
+ "height": 0,
416
+ "transform": Array [
417
+ Object {
418
+ "translateX": -0,
419
+ },
420
+ ],
421
+ "width": 0,
422
+ }
423
+ }
424
+ />
425
+ </View>
426
+ </View>
427
+ `;
428
+
429
+ exports[`Skeleton renders correctly when intent is light and variant is rounded 1`] = `
430
+ <View
431
+ onLayout={[Function]}
432
+ style={
433
+ Array [
434
+ Object {},
435
+ Array [
436
+ Object {
437
+ "backgroundColor": "#f6f6f7",
438
+ "borderRadius": 16,
439
+ },
440
+ undefined,
441
+ ],
442
+ ]
443
+ }
444
+ themeIntent="light"
445
+ themeVariant="rounded"
446
+ >
447
+ <View
448
+ style={
449
+ Array [
450
+ Object {},
451
+ Array [
452
+ Object {
453
+ "borderRadius": 16,
454
+ "overflow": "hidden",
455
+ },
456
+ undefined,
457
+ ],
458
+ ]
459
+ }
460
+ themeVariant="rounded"
461
+ >
462
+ <BVLinearGradient
463
+ collapsable={false}
464
+ colors={
465
+ Array [
466
+ 4294375159,
467
+ 4294046451,
468
+ 4294375159,
469
+ ]
470
+ }
471
+ endPoint={
472
+ Object {
473
+ "x": 1,
474
+ "y": 0,
475
+ }
476
+ }
477
+ locations={null}
478
+ startPoint={
479
+ Object {
480
+ "x": 0,
481
+ "y": 0,
482
+ }
483
+ }
484
+ style={
485
+ Object {
486
+ "height": 0,
487
+ "transform": Array [
488
+ Object {
489
+ "translateX": -0,
490
+ },
491
+ ],
492
+ "width": 0,
493
+ }
494
+ }
495
+ />
496
+ </View>
497
+ </View>
498
+ `;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import renderWithTheme from '../../../testHelpers/renderWithTheme';
3
+ import Skeleton from '..';
4
+
5
+ describe('Skeleton', () => {
6
+ it('renders correctly by default', () => {
7
+ const { toJSON } = renderWithTheme(<Skeleton />);
8
+
9
+ expect(toJSON()).toMatchSnapshot();
10
+ });
11
+
12
+ it.each`
13
+ variant | intent
14
+ ${'circular'} | ${'light'}
15
+ ${'circular'} | ${'dark'}
16
+ ${'rectangular'} | ${'light'}
17
+ ${'rectangular'} | ${'dark'}
18
+ ${'rounded'} | ${'light'}
19
+ ${'rounded'} | ${'dark'}
20
+ `(
21
+ 'renders correctly when intent is $intent and variant is $variant',
22
+ ({ variant, intent }) => {
23
+ const { toJSON } = renderWithTheme(
24
+ <Skeleton intent={intent} variant={variant} />
25
+ );
26
+
27
+ expect(toJSON()).toMatchSnapshot();
28
+ }
29
+ );
30
+ });
@@ -0,0 +1,134 @@
1
+ import LinearGradient from 'react-native-linear-gradient';
2
+ import React, {
3
+ useEffect,
4
+ useMemo,
5
+ useState,
6
+ useCallback,
7
+ useRef,
8
+ } from 'react';
9
+ import {
10
+ Animated,
11
+ Easing,
12
+ LayoutChangeEvent,
13
+ ViewProps,
14
+ Platform,
15
+ StyleSheet,
16
+ } from 'react-native';
17
+ import { Theme, useTheme } from '../../theme';
18
+ import { StyledContainer, StyledGradientContainer } from './StyledSkeleton';
19
+
20
+ export interface SkeletonProps extends ViewProps {
21
+ /**
22
+ * Intent of the component.
23
+ */
24
+ intent?: 'light' | 'dark';
25
+ /**
26
+ * Variant of the component.
27
+ */
28
+ variant?: 'circular' | 'rectangular' | 'rounded';
29
+ }
30
+
31
+ const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient);
32
+
33
+ const gradientPositions = {
34
+ start: { x: 0, y: 0 },
35
+ end: { x: 1, y: 0 },
36
+ };
37
+
38
+ const getGradientColors = (theme: Theme, intent: 'light' | 'dark') => {
39
+ switch (intent) {
40
+ case 'light': {
41
+ return [
42
+ theme.__hd__.skeleton.colors.lightGradientStart,
43
+ theme.__hd__.skeleton.colors.lightGradientEnd,
44
+ theme.__hd__.skeleton.colors.lightGradientStart,
45
+ ];
46
+ }
47
+ case 'dark': {
48
+ return [
49
+ theme.__hd__.skeleton.colors.darkGradientStart,
50
+ theme.__hd__.skeleton.colors.darkGradientEnd,
51
+ theme.__hd__.skeleton.colors.darkGradientStart,
52
+ ];
53
+ }
54
+ }
55
+ };
56
+
57
+ const Skeleton = ({
58
+ intent = 'light',
59
+ variant = 'rounded',
60
+ style,
61
+ onLayout,
62
+ ...props
63
+ }: SkeletonProps) => {
64
+ const theme = useTheme();
65
+ const colors = useMemo(
66
+ () => getGradientColors(theme, intent),
67
+ [theme, intent]
68
+ );
69
+ const [containerWidth, setContainerWidth] = useState(
70
+ Number(StyleSheet.flatten(style)?.width) || 0
71
+ );
72
+ const [containerHeight, setContainerHeight] = useState(
73
+ Number(StyleSheet.flatten(style)?.height) || 0
74
+ );
75
+
76
+ const [shouldStartAnimation, setShouldStartAnimation] = useState(false);
77
+
78
+ const animatedValue = useRef(new Animated.Value(0)).current;
79
+
80
+ useEffect(() => {
81
+ if (shouldStartAnimation) {
82
+ Animated.loop(
83
+ Animated.timing(animatedValue, {
84
+ toValue: 1,
85
+ duration: 1000,
86
+ easing: Easing.linear,
87
+ useNativeDriver: Platform.OS === 'ios' || Platform.OS === 'android',
88
+ })
89
+ ).start();
90
+ }
91
+ }, [shouldStartAnimation]);
92
+
93
+ const translateX = animatedValue.interpolate({
94
+ inputRange: [0, 1],
95
+ outputRange: [-containerWidth, containerWidth],
96
+ });
97
+
98
+ const onContainerLayout = useCallback((e: LayoutChangeEvent) => {
99
+ const { width, height } = e.nativeEvent.layout;
100
+ setContainerHeight(height);
101
+ setContainerWidth(width);
102
+
103
+ if (!shouldStartAnimation) {
104
+ setShouldStartAnimation(true);
105
+ }
106
+
107
+ onLayout?.(e);
108
+ }, []);
109
+
110
+ return (
111
+ <StyledContainer
112
+ style={style}
113
+ themeVariant={variant}
114
+ themeIntent={intent}
115
+ onLayout={onContainerLayout}
116
+ {...props}
117
+ >
118
+ <StyledGradientContainer themeVariant={variant}>
119
+ <AnimatedLinearGradient
120
+ start={gradientPositions.start}
121
+ end={gradientPositions.end}
122
+ style={{
123
+ width: containerWidth,
124
+ height: containerHeight,
125
+ transform: [{ translateX }],
126
+ }}
127
+ colors={colors}
128
+ />
129
+ </StyledGradientContainer>
130
+ </StyledContainer>
131
+ );
132
+ };
133
+
134
+ export default Skeleton;
package/src/index.ts CHANGED
@@ -45,6 +45,7 @@ import Swipeable from './components/Swipeable';
45
45
  import Radio from './components/Radio';
46
46
  import SectionHeading from './components/SectionHeading';
47
47
  import Select from './components/Select';
48
+ import Skeleton from './components/Skeleton';
48
49
  import Switch from './components/Switch';
49
50
  import Tabs from './components/Tabs';
50
51
  import Tag from './components/Tag';
@@ -99,6 +100,7 @@ export {
99
100
  PinInput,
100
101
  Progress,
101
102
  PageControl,
103
+ Skeleton,
102
104
  Slider,
103
105
  Spinner,
104
106
  Swipeable,
@@ -737,6 +737,21 @@ Object {
737
737
  "suffixMarginRight": 12,
738
738
  },
739
739
  },
740
+ "skeleton": Object {
741
+ "colors": Object {
742
+ "darkBackground": "#ffffff",
743
+ "darkGradientEnd": "#f1f2f3",
744
+ "darkGradientStart": "#ffffff",
745
+ "lightBackground": "#f6f6f7",
746
+ "lightGradientEnd": "#f1f2f3",
747
+ "lightGradientStart": "#f6f6f7",
748
+ },
749
+ "radii": Object {
750
+ "circular": 999,
751
+ "rectangular": 0,
752
+ "rounded": 16,
753
+ },
754
+ },
740
755
  "slider": Object {
741
756
  "colors": Object {
742
757
  "maximumTrackTint": "#ece8ef",
@@ -0,0 +1,22 @@
1
+ import type { GlobalTheme } from '../global';
2
+
3
+ const getSkeletonTheme = (theme: GlobalTheme) => {
4
+ const colors = {
5
+ lightBackground: theme.colors.neutralGlobalSurface,
6
+ darkBackground: theme.colors.defaultGlobalSurface,
7
+ lightGradientStart: theme.colors.neutralGlobalSurface,
8
+ lightGradientEnd: theme.colors.archivedSurface,
9
+ darkGradientStart: theme.colors.defaultGlobalSurface,
10
+ darkGradientEnd: theme.colors.archivedSurface,
11
+ };
12
+
13
+ const radii = {
14
+ rectangular: 0,
15
+ circular: theme.radii.rounded,
16
+ rounded: theme.radii.xlarge,
17
+ };
18
+
19
+ return { colors, radii };
20
+ };
21
+
22
+ export default getSkeletonTheme;