@proulr/sdui-components 0.1.75 → 0.1.76

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proulr/sdui-components",
3
- "version": "0.1.75",
3
+ "version": "0.1.76",
4
4
  "type": "module",
5
5
  "main": "./src/renderer/index.ts",
6
6
  "types": "./src/renderer/index.ts",
@@ -0,0 +1,108 @@
1
+ import React, { useEffect } from 'react';
2
+ import { View, type DimensionValue, type StyleProp, type ViewStyle } from 'react-native';
3
+ import Animated, {
4
+ Easing,
5
+ ReduceMotion,
6
+ useAnimatedStyle,
7
+ useSharedValue,
8
+ withRepeat,
9
+ withTiming,
10
+ } from 'react-native-reanimated';
11
+ import { m3Colors, m3Shape, spacing } from '../theme/proulr-theme';
12
+
13
+ const PULSE_DURATION_MS = 900;
14
+ const PULSE_MIN_OPACITY = 0.45;
15
+
16
+ /**
17
+ * Pulso de opacidade compartilhado por cada bloco de skeleton. Respeita o
18
+ * "reduzir movimento" do sistema (fica estatico). Como `react-native-reanimated`
19
+ * ja e usado nos primitivos do pacote (ex.: RangeSlider), importamos direto.
20
+ */
21
+ function usePulseStyle() {
22
+ const progress = useSharedValue(PULSE_MIN_OPACITY);
23
+
24
+ useEffect(() => {
25
+ progress.value = withRepeat(
26
+ withTiming(1, {
27
+ duration: PULSE_DURATION_MS,
28
+ easing: Easing.inOut(Easing.ease),
29
+ reduceMotion: ReduceMotion.System,
30
+ }),
31
+ -1,
32
+ true,
33
+ );
34
+ }, [progress]);
35
+
36
+ return useAnimatedStyle(() => ({ opacity: progress.value }));
37
+ }
38
+
39
+ export type SkeletonProps = {
40
+ width?: DimensionValue;
41
+ height?: DimensionValue;
42
+ radius?: number;
43
+ style?: StyleProp<ViewStyle>;
44
+ };
45
+
46
+ /** Bloco tonal com shimmer — base de qualquer skeleton. */
47
+ export function Skeleton({
48
+ width = '100%',
49
+ height = 16,
50
+ radius = m3Shape.cornerSmall,
51
+ style,
52
+ }: SkeletonProps) {
53
+ const pulse = usePulseStyle();
54
+ return (
55
+ <Animated.View
56
+ accessibilityRole="none"
57
+ style={[
58
+ { width, height, borderRadius: radius, backgroundColor: m3Colors.surfaceContainerHigh },
59
+ pulse,
60
+ style,
61
+ ]}
62
+ />
63
+ );
64
+ }
65
+
66
+ export type SkeletonCircleProps = {
67
+ size?: number;
68
+ style?: StyleProp<ViewStyle>;
69
+ };
70
+
71
+ /** Placeholder circular (avatar). */
72
+ export function SkeletonCircle({ size = 48, style }: SkeletonCircleProps) {
73
+ return <Skeleton width={size} height={size} radius={size / 2} style={style} />;
74
+ }
75
+
76
+ export type SkeletonTextProps = {
77
+ lines?: number;
78
+ lineHeight?: number;
79
+ gap?: number;
80
+ /** Largura da ultima linha (efeito paragrafo). */
81
+ lastLineWidth?: DimensionValue;
82
+ style?: StyleProp<ViewStyle>;
83
+ };
84
+
85
+ /** Bloco de linhas de texto (ultima linha mais curta). */
86
+ export function SkeletonText({
87
+ lines = 3,
88
+ lineHeight = 12,
89
+ gap = spacing.xs,
90
+ lastLineWidth = '60%',
91
+ style,
92
+ }: SkeletonTextProps) {
93
+ return (
94
+ <View style={[{ gap }, style]}>
95
+ {Array.from({ length: Math.max(1, lines) }).map((_, index) => {
96
+ const isLast = index === lines - 1;
97
+ return (
98
+ <Skeleton
99
+ key={index}
100
+ height={lineHeight}
101
+ width={isLast && lines > 1 ? lastLineWidth : '100%'}
102
+ radius={m3Shape.cornerFull}
103
+ />
104
+ );
105
+ })}
106
+ </View>
107
+ );
108
+ }
@@ -59,6 +59,14 @@ export { ProgressBar } from './ProgressBar';
59
59
  export { RangeSlider, type RangeSliderProps } from './RangeSlider';
60
60
  export { SectionHeader } from './SectionHeader';
61
61
  export { SelectRow, MultiSelectRow } from './SelectRow';
62
+ export {
63
+ Skeleton,
64
+ SkeletonCircle,
65
+ SkeletonText,
66
+ type SkeletonProps,
67
+ type SkeletonCircleProps,
68
+ type SkeletonTextProps,
69
+ } from './Skeleton';
62
70
  export { StatRow } from './StatRow';
63
71
  export { ToggleRow } from './ToggleRow';
64
72
  export { PasswordField, TextField } from './TextField';