@idealyst/components 1.0.82 → 1.0.84
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/CLAUDE.md +199 -232
- package/README.md +5 -5
- package/package.json +25 -7
- package/plugin/README.md +272 -0
- package/plugin/test-cases.jsx +112 -0
- package/plugin/web-legacy.js +320 -0
- package/plugin/web.js +422 -124
- package/src/Accordion/Accordion.native.tsx +182 -0
- package/src/Accordion/Accordion.styles.tsx +260 -0
- package/src/Accordion/Accordion.web.tsx +147 -0
- package/src/Accordion/index.native.tsx +3 -0
- package/src/Accordion/index.ts +3 -0
- package/src/Accordion/index.web.tsx +3 -0
- package/src/Accordion/types.ts +23 -0
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +17 -12
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +83 -109
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +23 -17
- package/src/ActivityIndicator/index.ts +5 -2
- package/src/ActivityIndicator/index.web.ts +5 -2
- package/src/ActivityIndicator/types.ts +15 -10
- package/src/Alert/Alert.native.tsx +113 -0
- package/src/Alert/Alert.styles.tsx +304 -0
- package/src/Alert/Alert.web.tsx +123 -0
- package/src/Alert/index.native.ts +5 -0
- package/src/Alert/index.ts +5 -0
- package/src/Alert/index.web.ts +5 -0
- package/src/Alert/types.ts +21 -0
- package/src/Avatar/Avatar.native.tsx +8 -6
- package/src/Avatar/Avatar.styles.tsx +64 -58
- package/src/Avatar/Avatar.web.tsx +13 -8
- package/src/Avatar/index.ts +5 -2
- package/src/Avatar/index.web.ts +5 -2
- package/src/Avatar/types.ts +19 -13
- package/src/Badge/Badge.native.tsx +59 -14
- package/src/Badge/Badge.styles.tsx +125 -139
- package/src/Badge/Badge.web.tsx +72 -16
- package/src/Badge/index.ts +5 -2
- package/src/Badge/index.web.ts +5 -2
- package/src/Badge/types.ts +23 -11
- package/src/Breadcrumb/Breadcrumb.native.tsx +225 -0
- package/src/Breadcrumb/Breadcrumb.styles.tsx +234 -0
- package/src/Breadcrumb/Breadcrumb.web.tsx +268 -0
- package/src/Breadcrumb/index.native.ts +5 -0
- package/src/Breadcrumb/index.ts +5 -0
- package/src/Breadcrumb/index.web.ts +5 -0
- package/src/Breadcrumb/types.ts +56 -0
- package/src/Button/Button.native.tsx +75 -24
- package/src/Button/Button.styles.tsx +248 -205
- package/src/Button/Button.web.tsx +82 -25
- package/src/Button/index.ts +5 -5
- package/src/Button/index.web.ts +5 -3
- package/src/Button/types.ts +32 -15
- package/src/Card/Card.native.tsx +14 -11
- package/src/Card/Card.styles.tsx +146 -220
- package/src/Card/Card.web.tsx +20 -21
- package/src/Card/index.ts +5 -5
- package/src/Card/index.web.ts +5 -3
- package/src/Card/types.ts +24 -17
- package/src/Checkbox/Checkbox.native.tsx +24 -34
- package/src/Checkbox/Checkbox.styles.tsx +223 -275
- package/src/Checkbox/Checkbox.web.tsx +30 -37
- package/src/Checkbox/index.ts +5 -5
- package/src/Checkbox/index.web.ts +5 -3
- package/src/Checkbox/types.ts +26 -20
- package/src/Chip/Chip.native.tsx +126 -0
- package/src/Chip/Chip.styles.tsx +138 -0
- package/src/Chip/Chip.web.tsx +154 -0
- package/src/Chip/index.native.ts +5 -0
- package/src/Chip/index.ts +5 -0
- package/src/Chip/index.web.ts +5 -0
- package/src/Chip/types.ts +51 -0
- package/src/Dialog/Dialog.native.tsx +65 -12
- package/src/Dialog/Dialog.styles.tsx +154 -136
- package/src/Dialog/Dialog.web.tsx +16 -11
- package/src/Dialog/index.ts +5 -2
- package/src/Dialog/index.web.ts +5 -2
- package/src/Dialog/types.ts +22 -16
- package/src/Divider/Divider.native.tsx +19 -14
- package/src/Divider/Divider.styles.tsx +273 -595
- package/src/Divider/Divider.web.tsx +19 -12
- package/src/Divider/index.ts +5 -5
- package/src/Divider/index.web.ts +5 -3
- package/src/Divider/types.ts +28 -19
- package/src/Icon/Icon.native.tsx +17 -24
- package/src/Icon/Icon.styles.tsx +64 -48
- package/src/Icon/Icon.web.tsx +14 -11
- package/src/Icon/IconSvg/IconSvg.native.tsx +42 -0
- package/src/Icon/IconSvg/IconSvg.web.tsx +40 -0
- package/src/Icon/IconSvg/index.native.ts +1 -0
- package/src/Icon/IconSvg/index.ts +1 -0
- package/src/Icon/icon-resolver.native.ts +27 -0
- package/src/Icon/icon-resolver.ts +70 -0
- package/src/Icon/index.ts +5 -5
- package/src/Icon/index.web.ts +5 -3
- package/src/Icon/types.ts +17 -11
- package/src/Image/Image.native.tsx +86 -0
- package/src/Image/Image.styles.tsx +57 -0
- package/src/Image/Image.web.tsx +92 -0
- package/src/Image/index.native.ts +5 -0
- package/src/Image/index.ts +5 -0
- package/src/Image/types.ts +21 -0
- package/src/Input/Input.native.tsx +103 -26
- package/src/Input/Input.styles.tsx +240 -177
- package/src/Input/Input.web.tsx +141 -38
- package/src/Input/index.ts +5 -5
- package/src/Input/index.web.ts +5 -3
- package/src/Input/types.ts +43 -20
- package/src/List/List.native.tsx +56 -0
- package/src/List/List.styles.tsx +257 -0
- package/src/List/List.web.tsx +43 -0
- package/src/List/ListContext.tsx +16 -0
- package/src/List/ListItem.native.tsx +111 -0
- package/src/List/ListItem.web.tsx +110 -0
- package/src/List/ListSection.native.tsx +31 -0
- package/src/List/ListSection.web.tsx +33 -0
- package/src/List/index.native.tsx +5 -0
- package/src/List/index.ts +5 -0
- package/src/List/index.web.tsx +5 -0
- package/src/List/types.ts +42 -0
- package/src/Menu/Menu.native.tsx +150 -0
- package/src/Menu/Menu.styles.tsx +185 -0
- package/src/Menu/Menu.web.tsx +99 -0
- package/src/Menu/MenuItem.native.tsx +66 -0
- package/src/Menu/MenuItem.styles.tsx +119 -0
- package/src/Menu/MenuItem.web.tsx +67 -0
- package/src/Menu/index.native.ts +3 -0
- package/src/Menu/index.ts +3 -0
- package/src/Menu/index.web.ts +3 -0
- package/src/Menu/types.ts +30 -0
- package/src/Popover/Popover.native.tsx +102 -32
- package/src/Popover/Popover.styles.tsx +100 -67
- package/src/Popover/Popover.web.tsx +36 -260
- package/src/Popover/index.ts +5 -2
- package/src/Popover/index.web.ts +5 -2
- package/src/Popover/types.ts +14 -13
- package/src/Pressable/Pressable.native.tsx +7 -6
- package/src/Pressable/Pressable.web.tsx +8 -6
- package/src/Pressable/index.ts +5 -2
- package/src/Pressable/index.web.ts +5 -2
- package/src/Pressable/types.ts +11 -10
- package/src/Progress/Progress.native.tsx +179 -0
- package/src/Progress/Progress.styles.tsx +164 -0
- package/src/Progress/Progress.web.tsx +144 -0
- package/src/Progress/index.native.ts +1 -0
- package/src/Progress/index.ts +5 -0
- package/src/Progress/index.web.ts +5 -0
- package/src/Progress/types.ts +21 -0
- package/src/RadioButton/RadioButton.native.tsx +88 -0
- package/src/RadioButton/RadioButton.styles.tsx +163 -0
- package/src/RadioButton/RadioButton.web.tsx +85 -0
- package/src/RadioButton/RadioGroup.native.tsx +43 -0
- package/src/RadioButton/RadioGroup.web.tsx +49 -0
- package/src/RadioButton/index.native.ts +2 -0
- package/src/RadioButton/index.ts +2 -0
- package/src/RadioButton/index.web.ts +2 -0
- package/src/RadioButton/types.ts +29 -0
- package/src/SVGImage/SVGImage.native.tsx +9 -7
- package/src/SVGImage/SVGImage.styles.tsx +63 -55
- package/src/SVGImage/SVGImage.web.tsx +16 -13
- package/src/SVGImage/index.ts +5 -5
- package/src/SVGImage/index.web.ts +5 -2
- package/src/SVGImage/types.ts +7 -3
- package/src/Screen/Screen.native.tsx +43 -17
- package/src/Screen/Screen.styles.tsx +58 -54
- package/src/Screen/Screen.web.tsx +11 -5
- package/src/Screen/index.ts +5 -2
- package/src/Screen/index.web.ts +5 -2
- package/src/Screen/types.ts +23 -9
- package/src/Select/Select.native.tsx +347 -0
- package/src/Select/Select.styles.tsx +335 -0
- package/src/Select/Select.web.tsx +276 -0
- package/src/Select/index.native.ts +2 -0
- package/src/Select/index.ts +5 -0
- package/src/Select/index.web.ts +5 -0
- package/src/Select/types.ts +124 -0
- package/src/Skeleton/Skeleton.native.tsx +139 -0
- package/src/Skeleton/Skeleton.styles.tsx +59 -0
- package/src/Skeleton/Skeleton.web.tsx +112 -0
- package/src/Skeleton/index.native.ts +4 -0
- package/src/Skeleton/index.ts +5 -0
- package/src/Skeleton/index.web.ts +5 -0
- package/src/Skeleton/types.ts +75 -0
- package/src/Slider/Slider.native.tsx +248 -0
- package/src/Slider/Slider.styles.tsx +241 -0
- package/src/Slider/Slider.web.tsx +226 -0
- package/src/Slider/index.native.ts +3 -0
- package/src/Slider/index.ts +5 -0
- package/src/Slider/index.web.ts +5 -0
- package/src/Slider/types.ts +31 -0
- package/src/Switch/Switch.native.tsx +131 -0
- package/src/Switch/Switch.styles.tsx +169 -0
- package/src/Switch/Switch.web.tsx +121 -0
- package/src/Switch/index.native.ts +3 -0
- package/src/Switch/index.ts +5 -0
- package/src/Switch/index.web.ts +5 -0
- package/src/Switch/types.ts +21 -0
- package/src/TabBar/TabBar.native.tsx +142 -0
- package/src/TabBar/TabBar.styles.tsx +399 -0
- package/src/TabBar/TabBar.web.tsx +205 -0
- package/src/TabBar/index.native.tsx +3 -0
- package/src/TabBar/index.ts +3 -0
- package/src/TabBar/index.web.tsx +3 -0
- package/src/TabBar/types.ts +26 -0
- package/src/Table/Table.native.tsx +122 -0
- package/src/Table/Table.styles.tsx +283 -0
- package/src/Table/Table.web.tsx +112 -0
- package/src/Table/index.native.tsx +3 -0
- package/src/Table/index.ts +3 -0
- package/src/Table/index.web.tsx +3 -0
- package/src/Table/types.ts +28 -0
- package/src/Text/Text.native.tsx +12 -11
- package/src/Text/Text.styles.tsx +76 -64
- package/src/Text/Text.web.tsx +14 -9
- package/src/Text/index.ts +5 -5
- package/src/Text/index.web.ts +5 -3
- package/src/Text/types.ts +20 -13
- package/src/TextArea/TextArea.native.tsx +134 -0
- package/src/TextArea/TextArea.styles.tsx +175 -0
- package/src/TextArea/TextArea.web.tsx +156 -0
- package/src/TextArea/index.native.ts +3 -0
- package/src/TextArea/index.ts +3 -0
- package/src/TextArea/index.web.ts +3 -0
- package/src/TextArea/types.ts +30 -0
- package/src/Tooltip/Tooltip.native.tsx +165 -0
- package/src/Tooltip/Tooltip.styles.tsx +73 -0
- package/src/Tooltip/Tooltip.web.tsx +87 -0
- package/src/Tooltip/index.native.ts +3 -0
- package/src/Tooltip/index.ts +3 -0
- package/src/Tooltip/types.ts +18 -0
- package/src/Video/Video.native.tsx +105 -0
- package/src/Video/Video.styles.tsx +39 -0
- package/src/Video/Video.web.tsx +115 -0
- package/src/Video/index.native.ts +5 -0
- package/src/Video/index.ts +5 -0
- package/src/Video/types.ts +29 -0
- package/src/View/View.native.tsx +9 -14
- package/src/View/View.styles.tsx +101 -93
- package/src/View/View.web.tsx +16 -17
- package/src/View/index.ts +5 -5
- package/src/View/index.web.ts +5 -3
- package/src/View/types.ts +29 -21
- package/src/examples/AccordionExamples.tsx +126 -0
- package/src/examples/AlertExamples.tsx +280 -0
- package/src/examples/AvatarExamples.tsx +23 -23
- package/src/examples/BadgeExamples.tsx +109 -41
- package/src/examples/BreadcrumbExamples.tsx +312 -0
- package/src/examples/ButtonExamples.tsx +160 -33
- package/src/examples/CardExamples.tsx +40 -40
- package/src/examples/CheckboxExamples.tsx +12 -12
- package/src/examples/ChipExamples.tsx +197 -0
- package/src/examples/DialogExamples.tsx +22 -22
- package/src/examples/DividerExamples.tsx +49 -49
- package/src/examples/IconExamples.tsx +270 -54
- package/src/examples/ImageExamples.tsx +174 -0
- package/src/examples/InputExamples.tsx +75 -17
- package/src/examples/ListExamples.tsx +288 -0
- package/src/examples/MenuExamples.tsx +144 -0
- package/src/examples/PopoverExamples.tsx +69 -73
- package/src/examples/ProgressExamples.tsx +137 -0
- package/src/examples/RadioButtonExamples.tsx +161 -0
- package/src/examples/SVGImageExamples.tsx +19 -17
- package/src/examples/ScreenExamples.tsx +31 -31
- package/src/examples/SelectExamples.tsx +423 -0
- package/src/examples/SkeletonExamples.tsx +206 -0
- package/src/examples/SliderExamples.tsx +200 -0
- package/src/examples/SwitchExamples.tsx +182 -0
- package/src/examples/TabBarExamples.tsx +143 -0
- package/src/examples/TableExamples.tsx +280 -0
- package/src/examples/TextAreaExamples.tsx +173 -0
- package/src/examples/TextExamples.tsx +28 -32
- package/src/examples/ThemeExtensionExamples.tsx +10 -10
- package/src/examples/TooltipExamples.tsx +126 -0
- package/src/examples/VideoExamples.tsx +144 -0
- package/src/examples/ViewExamples.tsx +64 -56
- package/src/examples/index.ts +18 -3
- package/src/hooks/useMergeRefs.ts +16 -0
- package/src/hooks/useSmartPosition.native.ts +169 -0
- package/src/index.native.ts +80 -9
- package/src/index.ts +75 -1
- package/src/internal/BoundedModalContent.native.tsx +58 -0
- package/src/internal/PositionedPortal.tsx +254 -0
- package/src/internal/SafeAreaDebugOverlay.native.tsx +173 -0
- package/src/unistyles.d.ts +6 -0
- package/src/utils/buildSizeVariants.ts +16 -0
- package/src/utils/deepMerge.ts +43 -0
- package/src/utils/positionUtils.native.ts +280 -0
- package/src/utils/styleHelpers.ts +48 -0
- package/LLM-ACCESS-GUIDE.md +0 -143
- package/src/ActivityIndicator/README.md +0 -132
- package/src/Avatar/README.md +0 -139
- package/src/Badge/README.md +0 -170
- package/src/Button/Button.types.ts +0 -12
- package/src/Button/README.md +0 -262
- package/src/Card/README.md +0 -258
- package/src/Checkbox/README.md +0 -102
- package/src/Dialog/README.md +0 -210
- package/src/Divider/README.md +0 -108
- package/src/Icon/README.md +0 -81
- package/src/Input/README.md +0 -100
- package/src/SVGImage/README.md +0 -209
- package/src/Screen/README.md +0 -86
- package/src/Text/README.md +0 -94
- package/src/View/README.md +0 -107
- package/src/examples/AllExamples.tsx +0 -84
- package/src/examples/README.md +0 -136
- package/src/examples/ValidationExamples.tsx +0 -95
- package/src/examples/extendedTheme.ts +0 -329
- package/src/theme/breakpoints.ts +0 -8
- package/src/theme/colorResolver.ts +0 -218
- package/src/theme/colors.ts +0 -315
- package/src/theme/defaultThemes.ts +0 -326
- package/src/theme/index.ts +0 -188
- package/src/theme/themeBuilder.ts +0 -602
- package/src/theme/unistyles.d.ts +0 -6
- package/src/theme/variantHelpers.ts +0 -584
- package/src/theme/variants.ts +0 -56
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Intent, Size } from '@idealyst/theme';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
|
|
5
|
+
// Component-specific type aliases for future extensibility
|
|
6
|
+
export type SelectIntentVariant = Intent;
|
|
7
|
+
export type SelectSizeVariant = Size;
|
|
8
|
+
export type SelectType = 'outlined' | 'filled';
|
|
9
|
+
|
|
10
|
+
export interface SelectOption {
|
|
11
|
+
/**
|
|
12
|
+
* The unique value for this option
|
|
13
|
+
*/
|
|
14
|
+
value: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The display label for this option
|
|
18
|
+
*/
|
|
19
|
+
label: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether this option is disabled
|
|
23
|
+
*/
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Optional icon or custom content to display before the label
|
|
28
|
+
*/
|
|
29
|
+
icon?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SelectProps {
|
|
33
|
+
/**
|
|
34
|
+
* Array of options to display in the select
|
|
35
|
+
*/
|
|
36
|
+
options: SelectOption[];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The currently selected value
|
|
40
|
+
*/
|
|
41
|
+
value?: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Called when the selected value changes
|
|
45
|
+
*/
|
|
46
|
+
onValueChange?: (value: string) => void;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Placeholder text when no value is selected
|
|
50
|
+
*/
|
|
51
|
+
placeholder?: string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether the select is disabled
|
|
55
|
+
*/
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Whether the select shows an error state
|
|
60
|
+
*/
|
|
61
|
+
error?: boolean;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Helper text to display below the select
|
|
65
|
+
*/
|
|
66
|
+
helperText?: string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Label text to display above the select
|
|
70
|
+
*/
|
|
71
|
+
label?: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The visual type of the select
|
|
75
|
+
*/
|
|
76
|
+
type?: SelectType,
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The intent/color scheme of the select
|
|
80
|
+
*/
|
|
81
|
+
intent?: SelectIntentVariant;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The size of the select
|
|
85
|
+
*/
|
|
86
|
+
size?: SelectSizeVariant;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Whether to show a search/filter input (web only)
|
|
90
|
+
*/
|
|
91
|
+
searchable?: boolean;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Custom search filter function (used with searchable)
|
|
95
|
+
*/
|
|
96
|
+
filterOption?: (option: SelectOption, searchTerm: string) => boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Native iOS presentation mode (native only)
|
|
100
|
+
* 'dropdown' uses a standard dropdown overlay
|
|
101
|
+
* 'actionSheet' uses iOS ActionSheet for selection
|
|
102
|
+
*/
|
|
103
|
+
presentationMode?: 'dropdown' | 'actionSheet';
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Maximum height for the dropdown content
|
|
107
|
+
*/
|
|
108
|
+
maxHeight?: number;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Additional styles (platform-specific)
|
|
112
|
+
*/
|
|
113
|
+
style?: StyleProp<ViewStyle>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Test ID for testing
|
|
117
|
+
*/
|
|
118
|
+
testID?: string;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Accessibility label
|
|
122
|
+
*/
|
|
123
|
+
accessibilityLabel?: string;
|
|
124
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React, { useEffect, forwardRef } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withRepeat,
|
|
7
|
+
withSequence,
|
|
8
|
+
withTiming,
|
|
9
|
+
Easing,
|
|
10
|
+
} from 'react-native-reanimated';
|
|
11
|
+
import { skeletonStyles } from './Skeleton.styles';
|
|
12
|
+
import type { SkeletonProps, SkeletonGroupProps } from './types';
|
|
13
|
+
|
|
14
|
+
const Skeleton = forwardRef<View, SkeletonProps>(({
|
|
15
|
+
width = '100%',
|
|
16
|
+
height = 20,
|
|
17
|
+
shape = 'rectangle',
|
|
18
|
+
borderRadius,
|
|
19
|
+
animation = 'pulse',
|
|
20
|
+
style,
|
|
21
|
+
testID,
|
|
22
|
+
}, ref) => {
|
|
23
|
+
skeletonStyles.useVariants({
|
|
24
|
+
shape,
|
|
25
|
+
animation,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const animatedValue = useSharedValue(0);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (animation === 'pulse') {
|
|
32
|
+
// Pulse animation: opacity from 1 -> 0.5 -> 1
|
|
33
|
+
animatedValue.value = withRepeat(
|
|
34
|
+
withSequence(
|
|
35
|
+
withTiming(1, { duration: 750, easing: Easing.inOut(Easing.ease) }),
|
|
36
|
+
withTiming(0, { duration: 750, easing: Easing.inOut(Easing.ease) })
|
|
37
|
+
),
|
|
38
|
+
-1, // infinite
|
|
39
|
+
false
|
|
40
|
+
);
|
|
41
|
+
} else if (animation === 'wave') {
|
|
42
|
+
// Wave animation: translateX from -100% -> 100%
|
|
43
|
+
animatedValue.value = withRepeat(
|
|
44
|
+
withTiming(1, { duration: 1500, easing: Easing.inOut(Easing.ease) }),
|
|
45
|
+
-1, // infinite
|
|
46
|
+
false
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}, [animation]);
|
|
50
|
+
|
|
51
|
+
const pulseAnimatedStyle = useAnimatedStyle(() => {
|
|
52
|
+
if (animation === 'pulse') {
|
|
53
|
+
return {
|
|
54
|
+
opacity: animatedValue.value === 0 ? 1 : 1 - animatedValue.value * 0.5,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const waveAnimatedStyle = useAnimatedStyle(() => {
|
|
61
|
+
if (animation === 'wave') {
|
|
62
|
+
return {
|
|
63
|
+
transform: [
|
|
64
|
+
{
|
|
65
|
+
translateX: (animatedValue.value - 0.5) * 400, // -200 to 200
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const customStyles = {
|
|
74
|
+
...(shape === 'rounded' && borderRadius ? { borderRadius } : {}),
|
|
75
|
+
...(shape === 'circle' ? { aspectRatio: 1 } : {}),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Animated.View
|
|
80
|
+
ref={ref as any}
|
|
81
|
+
style={[
|
|
82
|
+
skeletonStyles.skeleton,
|
|
83
|
+
customStyles,
|
|
84
|
+
style,
|
|
85
|
+
pulseAnimatedStyle,
|
|
86
|
+
]}
|
|
87
|
+
testID={testID}
|
|
88
|
+
>
|
|
89
|
+
{animation === 'wave' && (
|
|
90
|
+
<Animated.View
|
|
91
|
+
style={[
|
|
92
|
+
{
|
|
93
|
+
position: 'absolute',
|
|
94
|
+
top: 0,
|
|
95
|
+
left: 0,
|
|
96
|
+
right: 0,
|
|
97
|
+
bottom: 0,
|
|
98
|
+
backgroundColor: 'rgba(255, 255, 255, 0.3)',
|
|
99
|
+
},
|
|
100
|
+
waveAnimatedStyle,
|
|
101
|
+
]}
|
|
102
|
+
/>
|
|
103
|
+
)}
|
|
104
|
+
</Animated.View>
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
Skeleton.displayName = 'Skeleton';
|
|
109
|
+
|
|
110
|
+
export const SkeletonGroup: React.FC<SkeletonGroupProps> = ({
|
|
111
|
+
count = 3,
|
|
112
|
+
spacing = 12,
|
|
113
|
+
skeletonProps,
|
|
114
|
+
style,
|
|
115
|
+
testID,
|
|
116
|
+
}) => {
|
|
117
|
+
skeletonStyles.useVariants({});
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<View
|
|
121
|
+
style={[
|
|
122
|
+
skeletonStyles.group,
|
|
123
|
+
{ gap: spacing },
|
|
124
|
+
style,
|
|
125
|
+
]}
|
|
126
|
+
testID={testID}
|
|
127
|
+
>
|
|
128
|
+
{Array.from({ length: count }).map((_, index) => (
|
|
129
|
+
<Skeleton
|
|
130
|
+
key={index}
|
|
131
|
+
{...skeletonProps}
|
|
132
|
+
testID={testID ? `${testID}-item-${index}` : undefined}
|
|
133
|
+
/>
|
|
134
|
+
))}
|
|
135
|
+
</View>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export default Skeleton;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
import { Theme, StylesheetStyles} from '@idealyst/theme';
|
|
3
|
+
|
|
4
|
+
type SkeletonShape = 'rectangle' | 'rounded' | 'circle';
|
|
5
|
+
type SkeletonAnimation = 'pulse' | 'wave' | 'none';
|
|
6
|
+
|
|
7
|
+
type SkeletonVariants = {
|
|
8
|
+
shape: SkeletonShape;
|
|
9
|
+
animation: SkeletonAnimation;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ExpandedSkeletonStyles = StylesheetStyles<keyof SkeletonVariants>;
|
|
13
|
+
export type ExpandedSkeletonGroupStyles = StylesheetStyles<never>;
|
|
14
|
+
|
|
15
|
+
export type SkeletonStylesheet = {
|
|
16
|
+
skeleton: ExpandedSkeletonStyles;
|
|
17
|
+
group: ExpandedSkeletonGroupStyles;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create shape variants for skeleton
|
|
22
|
+
*/
|
|
23
|
+
function createShapeVariants(theme: Theme) {
|
|
24
|
+
return {
|
|
25
|
+
rectangle: {
|
|
26
|
+
borderRadius: 0,
|
|
27
|
+
},
|
|
28
|
+
rounded: {
|
|
29
|
+
borderRadius: 8,
|
|
30
|
+
},
|
|
31
|
+
circle: {
|
|
32
|
+
borderRadius: 9999,
|
|
33
|
+
},
|
|
34
|
+
} as const;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
|
|
38
|
+
// transform on native cannot resolve function calls to extract variant structures.
|
|
39
|
+
// @ts-ignore - TS language server needs restart to pick up theme structure changes
|
|
40
|
+
export const skeletonStyles = StyleSheet.create((theme: Theme) => {
|
|
41
|
+
return {
|
|
42
|
+
skeleton: {
|
|
43
|
+
backgroundColor: theme.colors.surface.tertiary,
|
|
44
|
+
overflow: 'hidden',
|
|
45
|
+
variants: {
|
|
46
|
+
shape: createShapeVariants(theme),
|
|
47
|
+
animation: {
|
|
48
|
+
pulse: {},
|
|
49
|
+
wave: {},
|
|
50
|
+
none: {},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
group: {
|
|
55
|
+
display: 'flex',
|
|
56
|
+
flexDirection: 'column',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
+
import { skeletonStyles } from './Skeleton.styles';
|
|
4
|
+
import type { SkeletonProps, SkeletonGroupProps } from './types';
|
|
5
|
+
|
|
6
|
+
const Skeleton: React.FC<SkeletonProps> = ({
|
|
7
|
+
width = '100%',
|
|
8
|
+
height = 20,
|
|
9
|
+
shape = 'rectangle',
|
|
10
|
+
borderRadius,
|
|
11
|
+
animation = 'pulse',
|
|
12
|
+
style,
|
|
13
|
+
testID,
|
|
14
|
+
}) => {
|
|
15
|
+
skeletonStyles.useVariants({
|
|
16
|
+
shape,
|
|
17
|
+
animation,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const skeletonProps = getWebProps([skeletonStyles.skeleton, style as any]);
|
|
21
|
+
|
|
22
|
+
// Apply custom border radius if provided and shape is 'rounded'
|
|
23
|
+
const customStyles: React.CSSProperties = {
|
|
24
|
+
width: typeof width === 'number' ? `${width}px` : width,
|
|
25
|
+
height: typeof height === 'number' ? `${height}px` : height,
|
|
26
|
+
...(shape === 'rounded' && borderRadius ? { borderRadius: `${borderRadius}px` } : {}),
|
|
27
|
+
...(shape === 'circle' ? { aspectRatio: '1' } : {}),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Animation styles
|
|
31
|
+
const animationStyles: React.CSSProperties = {};
|
|
32
|
+
if (animation === 'pulse') {
|
|
33
|
+
animationStyles.animation = 'skeleton-pulse 1.5s ease-in-out infinite';
|
|
34
|
+
} else if (animation === 'wave') {
|
|
35
|
+
animationStyles.position = 'relative';
|
|
36
|
+
animationStyles.overflow = 'hidden';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{animation === 'pulse' && (
|
|
42
|
+
<style>{`
|
|
43
|
+
@keyframes skeleton-pulse {
|
|
44
|
+
0%, 100% { opacity: 1; }
|
|
45
|
+
50% { opacity: 0.5; }
|
|
46
|
+
}
|
|
47
|
+
`}</style>
|
|
48
|
+
)}
|
|
49
|
+
{animation === 'wave' && (
|
|
50
|
+
<style>{`
|
|
51
|
+
@keyframes skeleton-wave {
|
|
52
|
+
0% { transform: translateX(-100%); }
|
|
53
|
+
100% { transform: translateX(100%); }
|
|
54
|
+
}
|
|
55
|
+
`}</style>
|
|
56
|
+
)}
|
|
57
|
+
<div
|
|
58
|
+
{...skeletonProps}
|
|
59
|
+
style={{
|
|
60
|
+
...customStyles,
|
|
61
|
+
...animationStyles,
|
|
62
|
+
}}
|
|
63
|
+
data-testid={testID}
|
|
64
|
+
>
|
|
65
|
+
{animation === 'wave' && (
|
|
66
|
+
<div
|
|
67
|
+
style={{
|
|
68
|
+
position: 'absolute',
|
|
69
|
+
top: 0,
|
|
70
|
+
left: 0,
|
|
71
|
+
right: 0,
|
|
72
|
+
bottom: 0,
|
|
73
|
+
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)',
|
|
74
|
+
animation: 'skeleton-wave 1.5s ease-in-out infinite',
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const SkeletonGroup: React.FC<SkeletonGroupProps> = ({
|
|
84
|
+
count = 3,
|
|
85
|
+
spacing = 12,
|
|
86
|
+
skeletonProps,
|
|
87
|
+
style,
|
|
88
|
+
testID,
|
|
89
|
+
}) => {
|
|
90
|
+
skeletonStyles.useVariants({});
|
|
91
|
+
const groupProps = getWebProps([skeletonStyles.group, style as any]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
{...groupProps}
|
|
96
|
+
style={{
|
|
97
|
+
gap: `${spacing}px`,
|
|
98
|
+
}}
|
|
99
|
+
data-testid={testID}
|
|
100
|
+
>
|
|
101
|
+
{Array.from({ length: count }).map((_, index) => (
|
|
102
|
+
<Skeleton
|
|
103
|
+
key={index}
|
|
104
|
+
{...skeletonProps}
|
|
105
|
+
testID={testID ? `${testID}-item-${index}` : undefined}
|
|
106
|
+
/>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export default Skeleton;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import SkeletonComponent from './Skeleton.web';
|
|
2
|
+
export { SkeletonGroup } from './Skeleton.web';
|
|
3
|
+
export default SkeletonComponent;
|
|
4
|
+
export { SkeletonComponent as Skeleton };
|
|
5
|
+
export type { SkeletonProps, SkeletonGroupProps, SkeletonShape, SkeletonAnimation } from './types';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import SkeletonComponent from './Skeleton.web';
|
|
2
|
+
export { SkeletonGroup } from './Skeleton.web';
|
|
3
|
+
export default SkeletonComponent;
|
|
4
|
+
export { SkeletonComponent as Skeleton };
|
|
5
|
+
export type { SkeletonProps, SkeletonGroupProps, SkeletonShape, SkeletonAnimation } from './types';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export type SkeletonShape = 'rectangle' | 'circle' | 'rounded';
|
|
4
|
+
export type SkeletonAnimation = 'pulse' | 'wave' | 'none';
|
|
5
|
+
|
|
6
|
+
export interface SkeletonProps {
|
|
7
|
+
/**
|
|
8
|
+
* Width of the skeleton (number in pixels or string with units)
|
|
9
|
+
* @default '100%'
|
|
10
|
+
*/
|
|
11
|
+
width?: number | string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Height of the skeleton (number in pixels or string with units)
|
|
15
|
+
* @default 20
|
|
16
|
+
*/
|
|
17
|
+
height?: number | string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Shape of the skeleton
|
|
21
|
+
* @default 'rectangle'
|
|
22
|
+
*/
|
|
23
|
+
shape?: SkeletonShape;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Border radius for 'rounded' shape (in pixels)
|
|
27
|
+
* @default 8
|
|
28
|
+
*/
|
|
29
|
+
borderRadius?: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Animation type
|
|
33
|
+
* @default 'pulse'
|
|
34
|
+
*/
|
|
35
|
+
animation?: SkeletonAnimation;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Additional custom styles
|
|
39
|
+
*/
|
|
40
|
+
style?: StyleProp<ViewStyle>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Test ID for testing
|
|
44
|
+
*/
|
|
45
|
+
testID?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SkeletonGroupProps {
|
|
49
|
+
/**
|
|
50
|
+
* Number of skeleton items to render
|
|
51
|
+
* @default 3
|
|
52
|
+
*/
|
|
53
|
+
count?: number;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Spacing between skeleton items (in pixels)
|
|
57
|
+
* @default 12
|
|
58
|
+
*/
|
|
59
|
+
spacing?: number;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Props to pass to each skeleton item
|
|
63
|
+
*/
|
|
64
|
+
skeletonProps?: Omit<SkeletonProps, 'testID'>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Additional custom styles for the container
|
|
68
|
+
*/
|
|
69
|
+
style?: StyleProp<ViewStyle>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Test ID for testing
|
|
73
|
+
*/
|
|
74
|
+
testID?: string;
|
|
75
|
+
}
|