@idealyst/components 1.0.68 → 1.0.69
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 +2 -2
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +53 -0
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +113 -0
- package/src/ActivityIndicator/ActivityIndicator.web.css +8 -0
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +63 -0
- package/src/ActivityIndicator/README.md +132 -0
- package/src/ActivityIndicator/index.ts +2 -0
- package/src/ActivityIndicator/index.web.ts +2 -0
- package/src/ActivityIndicator/types.ts +42 -0
- package/src/index.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/components",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.69",
|
|
4
4
|
"description": "Shared component library for React and React Native",
|
|
5
5
|
"documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
|
|
6
6
|
"readme": "README.md",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"publish:npm": "npm publish"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@idealyst/theme": "^1.0.
|
|
44
|
+
"@idealyst/theme": "^1.0.69",
|
|
45
45
|
"@mdi/js": "^7.4.47",
|
|
46
46
|
"@mdi/react": "^1.6.1",
|
|
47
47
|
"@react-native-vector-icons/common": "^12.0.1",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ActivityIndicator as RNActivityIndicator, View } from 'react-native';
|
|
3
|
+
import { ActivityIndicatorProps } from './types';
|
|
4
|
+
import { activityIndicatorStyles } from './ActivityIndicator.styles';
|
|
5
|
+
|
|
6
|
+
const ActivityIndicator: React.FC<ActivityIndicatorProps> = ({
|
|
7
|
+
animating = true,
|
|
8
|
+
size = 'medium',
|
|
9
|
+
intent = 'primary',
|
|
10
|
+
color,
|
|
11
|
+
style,
|
|
12
|
+
testID,
|
|
13
|
+
hidesWhenStopped = true,
|
|
14
|
+
}) => {
|
|
15
|
+
// Handle numeric size
|
|
16
|
+
const sizeVariant = typeof size === 'number' ? 'medium' : size;
|
|
17
|
+
const customSize = typeof size === 'number' ? size : undefined;
|
|
18
|
+
|
|
19
|
+
// Map our size variants to React Native's size prop
|
|
20
|
+
const rnSize = sizeVariant === 'small' ? 'small' : 'large';
|
|
21
|
+
|
|
22
|
+
activityIndicatorStyles.useVariants({
|
|
23
|
+
size: sizeVariant,
|
|
24
|
+
intent,
|
|
25
|
+
animating,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Get the color from styles or use custom color
|
|
29
|
+
const indicatorColor = color || activityIndicatorStyles.spinner.color;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View
|
|
33
|
+
style={[
|
|
34
|
+
activityIndicatorStyles.container,
|
|
35
|
+
customSize && {
|
|
36
|
+
width: customSize,
|
|
37
|
+
height: customSize,
|
|
38
|
+
},
|
|
39
|
+
style
|
|
40
|
+
]}
|
|
41
|
+
testID={testID}
|
|
42
|
+
>
|
|
43
|
+
<RNActivityIndicator
|
|
44
|
+
animating={animating}
|
|
45
|
+
size={customSize || rnSize}
|
|
46
|
+
color={indicatorColor}
|
|
47
|
+
hidesWhenStopped={hidesWhenStopped}
|
|
48
|
+
/>
|
|
49
|
+
</View>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default ActivityIndicator;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
2
|
+
|
|
3
|
+
export const activityIndicatorStyles = StyleSheet.create((theme) => ({
|
|
4
|
+
container: {
|
|
5
|
+
alignItems: 'center',
|
|
6
|
+
justifyContent: 'center',
|
|
7
|
+
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
small: {
|
|
11
|
+
width: 20,
|
|
12
|
+
height: 20,
|
|
13
|
+
},
|
|
14
|
+
medium: {
|
|
15
|
+
width: 36,
|
|
16
|
+
height: 36,
|
|
17
|
+
},
|
|
18
|
+
large: {
|
|
19
|
+
width: 48,
|
|
20
|
+
height: 48,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
intent: {
|
|
24
|
+
primary: {},
|
|
25
|
+
success: {},
|
|
26
|
+
error: {},
|
|
27
|
+
warning: {},
|
|
28
|
+
neutral: {},
|
|
29
|
+
},
|
|
30
|
+
animating: {
|
|
31
|
+
true: {
|
|
32
|
+
opacity: 1,
|
|
33
|
+
},
|
|
34
|
+
false: {
|
|
35
|
+
opacity: 0,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
spinner: {
|
|
42
|
+
borderRadius: 9999,
|
|
43
|
+
borderStyle: 'solid',
|
|
44
|
+
|
|
45
|
+
// Web-specific animation styles
|
|
46
|
+
_web: {
|
|
47
|
+
borderColor: 'transparent',
|
|
48
|
+
animation: 'spin 1s linear infinite',
|
|
49
|
+
boxSizing: 'border-box',
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
variants: {
|
|
53
|
+
size: {
|
|
54
|
+
small: {
|
|
55
|
+
width: 20,
|
|
56
|
+
height: 20,
|
|
57
|
+
borderWidth: 2,
|
|
58
|
+
},
|
|
59
|
+
medium: {
|
|
60
|
+
width: 36,
|
|
61
|
+
height: 36,
|
|
62
|
+
borderWidth: 3,
|
|
63
|
+
},
|
|
64
|
+
large: {
|
|
65
|
+
width: 48,
|
|
66
|
+
height: 48,
|
|
67
|
+
borderWidth: 4,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
intent: {
|
|
71
|
+
primary: {
|
|
72
|
+
color: theme.intents?.primary?.main || '#3b82f6',
|
|
73
|
+
_web: {
|
|
74
|
+
borderTopColor: theme.intents?.primary?.main || '#3b82f6',
|
|
75
|
+
borderRightColor: theme.intents?.primary?.main || '#3b82f6',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
success: {
|
|
79
|
+
color: theme.intents?.success?.main || '#22c55e',
|
|
80
|
+
_web: {
|
|
81
|
+
borderTopColor: theme.intents?.success?.main || '#22c55e',
|
|
82
|
+
borderRightColor: theme.intents?.success?.main || '#22c55e',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
error: {
|
|
86
|
+
color: theme.intents?.error?.main || '#ef4444',
|
|
87
|
+
_web: {
|
|
88
|
+
borderTopColor: theme.intents?.error?.main || '#ef4444',
|
|
89
|
+
borderRightColor: theme.intents?.error?.main || '#ef4444',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
warning: {
|
|
93
|
+
color: theme.intents?.warning?.main || '#f59e0b',
|
|
94
|
+
_web: {
|
|
95
|
+
borderTopColor: theme.intents?.warning?.main || '#f59e0b',
|
|
96
|
+
borderRightColor: theme.intents?.warning?.main || '#f59e0b',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
neutral: {
|
|
100
|
+
color: theme.intents?.neutral?.main || '#6b7280',
|
|
101
|
+
_web: {
|
|
102
|
+
borderTopColor: theme.intents?.neutral?.main || '#6b7280',
|
|
103
|
+
borderRightColor: theme.intents?.neutral?.main || '#6b7280',
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
animating: {
|
|
108
|
+
true: {},
|
|
109
|
+
false: {},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}));
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
+
import { ActivityIndicatorProps } from './types';
|
|
4
|
+
import { activityIndicatorStyles } from './ActivityIndicator.styles';
|
|
5
|
+
import './ActivityIndicator.web.css';
|
|
6
|
+
|
|
7
|
+
const ActivityIndicator: React.FC<ActivityIndicatorProps> = ({
|
|
8
|
+
animating = true,
|
|
9
|
+
size = 'medium',
|
|
10
|
+
intent = 'primary',
|
|
11
|
+
color,
|
|
12
|
+
style,
|
|
13
|
+
testID,
|
|
14
|
+
hidesWhenStopped = true,
|
|
15
|
+
}) => {
|
|
16
|
+
// Handle numeric size
|
|
17
|
+
const sizeVariant = typeof size === 'number' ? 'medium' : size;
|
|
18
|
+
const customSize = typeof size === 'number' ? size : undefined;
|
|
19
|
+
|
|
20
|
+
// Apply variants using the correct Unistyles 3.0 pattern
|
|
21
|
+
activityIndicatorStyles.useVariants({
|
|
22
|
+
size: sizeVariant as 'small' | 'medium' | 'large',
|
|
23
|
+
intent: intent as 'primary' | 'success' | 'error' | 'warning' | 'neutral',
|
|
24
|
+
animating: animating as boolean,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Don't render if not animating and hidesWhenStopped is true
|
|
28
|
+
if (!animating && hidesWhenStopped) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create the style array following the official documentation pattern
|
|
33
|
+
const containerStyleArray = [
|
|
34
|
+
activityIndicatorStyles.container,
|
|
35
|
+
customSize && {
|
|
36
|
+
width: customSize,
|
|
37
|
+
height: customSize,
|
|
38
|
+
},
|
|
39
|
+
style,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const spinnerStyleArray = [
|
|
43
|
+
activityIndicatorStyles.spinner,
|
|
44
|
+
customSize && {
|
|
45
|
+
width: customSize,
|
|
46
|
+
height: customSize,
|
|
47
|
+
borderWidth: Math.max(2, customSize / 10),
|
|
48
|
+
},
|
|
49
|
+
color && { borderTopColor: color, borderRightColor: color },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Use getWebProps to generate className and ref for web
|
|
53
|
+
const containerProps = getWebProps(containerStyleArray);
|
|
54
|
+
const spinnerProps = getWebProps(spinnerStyleArray);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div {...containerProps} data-testid={testID}>
|
|
58
|
+
<div {...spinnerProps} />
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default ActivityIndicator;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# ActivityIndicator
|
|
2
|
+
|
|
3
|
+
A cross-platform loading indicator component that displays a spinning animation to indicate loading or processing state.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Cross-platform**: Works seamlessly on both React and React Native
|
|
8
|
+
- **Intent-based colors**: Uses semantic color system (primary, neutral, success, error, warning)
|
|
9
|
+
- **Multiple sizes**: Supports small, medium, large, or custom numeric sizes
|
|
10
|
+
- **Customizable**: Override colors and styles as needed
|
|
11
|
+
- **Animation control**: Start/stop animation with `animating` prop
|
|
12
|
+
- **Auto-hide**: Optionally hide when not animating
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { ActivityIndicator } from '@idealyst/components';
|
|
18
|
+
|
|
19
|
+
// Basic usage
|
|
20
|
+
<ActivityIndicator />
|
|
21
|
+
|
|
22
|
+
// With different sizes
|
|
23
|
+
<ActivityIndicator size="small" />
|
|
24
|
+
<ActivityIndicator size="medium" />
|
|
25
|
+
<ActivityIndicator size="large" />
|
|
26
|
+
<ActivityIndicator size={64} /> // Custom size in pixels
|
|
27
|
+
|
|
28
|
+
// With different intents
|
|
29
|
+
<ActivityIndicator intent="primary" />
|
|
30
|
+
<ActivityIndicator intent="success" />
|
|
31
|
+
<ActivityIndicator intent="error" />
|
|
32
|
+
<ActivityIndicator intent="warning" />
|
|
33
|
+
<ActivityIndicator intent="neutral" />
|
|
34
|
+
|
|
35
|
+
// Custom color
|
|
36
|
+
<ActivityIndicator color="#FF5733" />
|
|
37
|
+
|
|
38
|
+
// Control animation
|
|
39
|
+
<ActivityIndicator animating={isLoading} />
|
|
40
|
+
|
|
41
|
+
// Don't hide when stopped
|
|
42
|
+
<ActivityIndicator animating={false} hidesWhenStopped={false} />
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Props
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Description |
|
|
48
|
+
|------|------|---------|-------------|
|
|
49
|
+
| `animating` | `boolean` | `true` | Whether the indicator is animating (visible) |
|
|
50
|
+
| `size` | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | The size of the indicator |
|
|
51
|
+
| `intent` | `'primary' \| 'neutral' \| 'success' \| 'error' \| 'warning'` | `'primary'` | The color intent of the indicator |
|
|
52
|
+
| `color` | `string` | - | Custom color to override intent |
|
|
53
|
+
| `style` | `ViewStyle` | - | Additional styles to apply to the container |
|
|
54
|
+
| `testID` | `string` | - | Test identifier for testing |
|
|
55
|
+
| `hidesWhenStopped` | `boolean` | `true` | Whether to hide the indicator when not animating |
|
|
56
|
+
|
|
57
|
+
## Examples
|
|
58
|
+
|
|
59
|
+
### Loading State
|
|
60
|
+
```tsx
|
|
61
|
+
const LoadingScreen = () => {
|
|
62
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
66
|
+
<ActivityIndicator
|
|
67
|
+
animating={isLoading}
|
|
68
|
+
size="large"
|
|
69
|
+
intent="primary"
|
|
70
|
+
/>
|
|
71
|
+
{isLoading && <Text>Loading data...</Text>}
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Button with Loading
|
|
78
|
+
```tsx
|
|
79
|
+
const SubmitButton = ({ onSubmit }) => {
|
|
80
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
81
|
+
|
|
82
|
+
const handleSubmit = async () => {
|
|
83
|
+
setIsSubmitting(true);
|
|
84
|
+
await onSubmit();
|
|
85
|
+
setIsSubmitting(false);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Button onPress={handleSubmit} disabled={isSubmitting}>
|
|
90
|
+
{isSubmitting ? (
|
|
91
|
+
<ActivityIndicator size="small" color="white" />
|
|
92
|
+
) : (
|
|
93
|
+
'Submit'
|
|
94
|
+
)}
|
|
95
|
+
</Button>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Custom Styled Indicator
|
|
101
|
+
```tsx
|
|
102
|
+
<ActivityIndicator
|
|
103
|
+
size={50}
|
|
104
|
+
color="#8B5CF6"
|
|
105
|
+
style={{
|
|
106
|
+
backgroundColor: 'rgba(139, 92, 246, 0.1)',
|
|
107
|
+
padding: 20,
|
|
108
|
+
borderRadius: 10,
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Platform Differences
|
|
114
|
+
|
|
115
|
+
- **Web**: Uses CSS animations with a custom spinner implementation
|
|
116
|
+
- **Native**: Uses React Native's built-in `ActivityIndicator` component
|
|
117
|
+
- Both platforms support all the same props for consistency
|
|
118
|
+
|
|
119
|
+
## Accessibility
|
|
120
|
+
|
|
121
|
+
The ActivityIndicator component includes:
|
|
122
|
+
- Proper ARIA roles for screen readers on web
|
|
123
|
+
- Visual indication of loading state
|
|
124
|
+
- Support for test IDs for testing
|
|
125
|
+
|
|
126
|
+
## Best Practices
|
|
127
|
+
|
|
128
|
+
1. **Always provide context**: Pair with text to explain what's loading
|
|
129
|
+
2. **Use appropriate sizes**: Small for inline, large for full-screen loading
|
|
130
|
+
3. **Match intent to context**: Use error intent for retry states, success for completion
|
|
131
|
+
4. **Consider animation performance**: Avoid too many simultaneous indicators
|
|
132
|
+
5. **Provide alternative content**: Show skeleton screens or placeholders when appropriate
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface ActivityIndicatorProps {
|
|
4
|
+
/**
|
|
5
|
+
* Whether the indicator is animating (visible)
|
|
6
|
+
* @default true
|
|
7
|
+
*/
|
|
8
|
+
animating?: boolean;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The size of the indicator
|
|
12
|
+
* @default "medium"
|
|
13
|
+
*/
|
|
14
|
+
size?: 'small' | 'medium' | 'large' | number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The color/intent of the indicator
|
|
18
|
+
* @default "primary"
|
|
19
|
+
*/
|
|
20
|
+
intent?: 'primary' | 'neutral' | 'success' | 'error' | 'warning';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Custom color to override intent
|
|
24
|
+
*/
|
|
25
|
+
color?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Additional styles to apply to the container
|
|
29
|
+
*/
|
|
30
|
+
style?: ViewStyle;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Test identifier for testing
|
|
34
|
+
*/
|
|
35
|
+
testID?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether to hide the indicator when not animating
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
hidesWhenStopped?: boolean;
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -44,6 +44,9 @@ export * from './Dialog/types';
|
|
|
44
44
|
export { default as Popover } from './Popover';
|
|
45
45
|
export * from './Popover/types';
|
|
46
46
|
|
|
47
|
+
export { default as ActivityIndicator } from './ActivityIndicator';
|
|
48
|
+
export * from './ActivityIndicator/types';
|
|
49
|
+
|
|
47
50
|
export type { ButtonProps } from './Button/types';
|
|
48
51
|
export type { TextProps } from './Text/types';
|
|
49
52
|
export type { ViewProps } from './View/types';
|
|
@@ -58,6 +61,7 @@ export type { IconProps } from './Icon/types';
|
|
|
58
61
|
export type { SVGImageProps } from './SVGImage/types';
|
|
59
62
|
export type { DialogProps } from './Dialog/types';
|
|
60
63
|
export type { PopoverProps } from './Popover/types';
|
|
64
|
+
export type { ActivityIndicatorProps } from './ActivityIndicator/types';
|
|
61
65
|
|
|
62
66
|
export { breakpoints } from '@idealyst/theme';
|
|
63
67
|
export type { AppTheme } from '@idealyst/theme';
|