@idealyst/components 1.2.30 → 1.2.32
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 +4 -4
- package/plugin/web.js +2 -0
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +1 -0
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +1 -5
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +15 -28
- package/src/Alert/Alert.native.tsx +2 -2
- package/src/Alert/Alert.styles.tsx +88 -63
- package/src/Alert/Alert.web.tsx +26 -27
- package/src/Chip/Chip.styles.tsx +124 -142
- package/src/IconButton/IconButton.native.tsx +219 -0
- package/src/IconButton/IconButton.styles.tsx +127 -0
- package/src/IconButton/IconButton.web.tsx +198 -0
- package/src/IconButton/index.native.ts +5 -0
- package/src/IconButton/index.ts +5 -0
- package/src/IconButton/index.web.ts +5 -0
- package/src/IconButton/types.ts +84 -0
- package/src/Skeleton/Skeleton.web.tsx +1 -1
- package/src/Switch/Switch.styles.tsx +23 -6
- package/src/Switch/Switch.web.tsx +7 -9
- package/src/View/View.styles.tsx +1 -0
- package/src/View/View.web.tsx +7 -1
- package/src/examples/ActivityIndicatorExamples.tsx +177 -0
- package/src/examples/SwitchExamples.tsx +24 -24
- package/src/examples/index.ts +1 -0
- package/src/extensions/types.ts +12 -0
- package/src/index.native.ts +4 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, { isValidElement, forwardRef, useMemo } from 'react';
|
|
2
|
+
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
+
import { IconButtonProps } from './types';
|
|
4
|
+
import { iconButtonStyles } from './IconButton.styles';
|
|
5
|
+
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
+
import useMergeRefs from '../hooks/useMergeRefs';
|
|
7
|
+
import { getWebInteractiveAriaProps, generateAccessibilityId } from '../utils/accessibility';
|
|
8
|
+
import type { IdealystElement } from '../utils/refTypes';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Circular icon button component with multiple visual variants and sizes.
|
|
12
|
+
* Supports contained, outlined, and text styles with customizable intent colors.
|
|
13
|
+
*/
|
|
14
|
+
const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) => {
|
|
15
|
+
const {
|
|
16
|
+
icon,
|
|
17
|
+
onPress,
|
|
18
|
+
onClick,
|
|
19
|
+
disabled = false,
|
|
20
|
+
loading = false,
|
|
21
|
+
type = 'contained',
|
|
22
|
+
intent = 'primary',
|
|
23
|
+
size = 'md',
|
|
24
|
+
gradient,
|
|
25
|
+
style,
|
|
26
|
+
testID,
|
|
27
|
+
id,
|
|
28
|
+
// Accessibility props
|
|
29
|
+
accessibilityLabel,
|
|
30
|
+
accessibilityHint,
|
|
31
|
+
accessibilityDisabled,
|
|
32
|
+
accessibilityHidden,
|
|
33
|
+
accessibilityRole,
|
|
34
|
+
accessibilityLabelledBy,
|
|
35
|
+
accessibilityDescribedBy,
|
|
36
|
+
accessibilityControls,
|
|
37
|
+
accessibilityExpanded,
|
|
38
|
+
accessibilityPressed,
|
|
39
|
+
accessibilityOwns,
|
|
40
|
+
accessibilityHasPopup,
|
|
41
|
+
} = props;
|
|
42
|
+
|
|
43
|
+
// Button is effectively disabled when loading
|
|
44
|
+
const isDisabled = disabled || loading;
|
|
45
|
+
|
|
46
|
+
// Apply variants for size, disabled, gradient
|
|
47
|
+
iconButtonStyles.useVariants({
|
|
48
|
+
size,
|
|
49
|
+
disabled: isDisabled,
|
|
50
|
+
gradient,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Determine the handler to use - onPress takes precedence
|
|
54
|
+
const pressHandler = onPress ?? onClick;
|
|
55
|
+
|
|
56
|
+
// Warn about deprecated onClick usage in development
|
|
57
|
+
if (process.env.NODE_ENV !== 'production' && onClick && !onPress) {
|
|
58
|
+
console.warn(
|
|
59
|
+
'IconButton: onClick prop is deprecated. Use onPress instead for cross-platform compatibility.'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
if (!isDisabled && pressHandler) {
|
|
66
|
+
e.stopPropagation();
|
|
67
|
+
pressHandler();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Generate unique ID for accessibility
|
|
72
|
+
const buttonId = useMemo(() => id || generateAccessibilityId('icon-button'), [id]);
|
|
73
|
+
|
|
74
|
+
// Generate ARIA props - accessibilityLabel is critical for icon-only buttons
|
|
75
|
+
const ariaProps = useMemo(() => {
|
|
76
|
+
const computedLabel = accessibilityLabel ?? (typeof icon === 'string' ? icon : undefined);
|
|
77
|
+
|
|
78
|
+
return getWebInteractiveAriaProps({
|
|
79
|
+
accessibilityLabel: computedLabel,
|
|
80
|
+
accessibilityHint,
|
|
81
|
+
accessibilityDisabled: accessibilityDisabled ?? isDisabled,
|
|
82
|
+
accessibilityHidden,
|
|
83
|
+
accessibilityRole: accessibilityRole ?? 'button',
|
|
84
|
+
accessibilityLabelledBy,
|
|
85
|
+
accessibilityDescribedBy,
|
|
86
|
+
accessibilityControls,
|
|
87
|
+
accessibilityExpanded,
|
|
88
|
+
accessibilityPressed,
|
|
89
|
+
accessibilityOwns,
|
|
90
|
+
accessibilityHasPopup,
|
|
91
|
+
});
|
|
92
|
+
}, [
|
|
93
|
+
accessibilityLabel,
|
|
94
|
+
icon,
|
|
95
|
+
accessibilityHint,
|
|
96
|
+
accessibilityDisabled,
|
|
97
|
+
isDisabled,
|
|
98
|
+
accessibilityHidden,
|
|
99
|
+
accessibilityRole,
|
|
100
|
+
accessibilityLabelledBy,
|
|
101
|
+
accessibilityDescribedBy,
|
|
102
|
+
accessibilityControls,
|
|
103
|
+
accessibilityExpanded,
|
|
104
|
+
accessibilityPressed,
|
|
105
|
+
accessibilityOwns,
|
|
106
|
+
accessibilityHasPopup,
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
// Compute dynamic styles with all props for full flexibility
|
|
110
|
+
const dynamicProps = { intent, type, size, disabled: isDisabled, gradient };
|
|
111
|
+
const buttonStyleArray = [
|
|
112
|
+
(iconButtonStyles.button as any)(dynamicProps),
|
|
113
|
+
style as any,
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
// Use getWebProps to generate className and ref for web
|
|
117
|
+
const webProps = getWebProps(buttonStyleArray);
|
|
118
|
+
|
|
119
|
+
// Icon styles with dynamic function
|
|
120
|
+
const iconStyleArray = [(iconButtonStyles.icon as any)(dynamicProps)];
|
|
121
|
+
const iconProps = getWebProps(iconStyleArray);
|
|
122
|
+
|
|
123
|
+
// Spinner styles that match the icon color
|
|
124
|
+
const spinnerStyleArray = [(iconButtonStyles.spinner as any)(dynamicProps)];
|
|
125
|
+
const spinnerProps = getWebProps(spinnerStyleArray);
|
|
126
|
+
|
|
127
|
+
// Helper to render icon
|
|
128
|
+
const renderIcon = () => {
|
|
129
|
+
if (typeof icon === 'string') {
|
|
130
|
+
return (
|
|
131
|
+
<IconSvg
|
|
132
|
+
name={icon}
|
|
133
|
+
{...iconProps}
|
|
134
|
+
aria-label={icon}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
} else if (isValidElement(icon)) {
|
|
138
|
+
return icon;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Render spinner with inline CSS animation (absolutely centered)
|
|
144
|
+
const renderSpinner = () => (
|
|
145
|
+
<>
|
|
146
|
+
<style>
|
|
147
|
+
{`
|
|
148
|
+
@keyframes icon-button-spin {
|
|
149
|
+
from { transform: rotate(0deg); }
|
|
150
|
+
to { transform: rotate(360deg); }
|
|
151
|
+
}
|
|
152
|
+
`}
|
|
153
|
+
</style>
|
|
154
|
+
<span
|
|
155
|
+
{...spinnerProps}
|
|
156
|
+
style={{
|
|
157
|
+
position: 'absolute',
|
|
158
|
+
display: 'inline-block',
|
|
159
|
+
width: '1em',
|
|
160
|
+
height: '1em',
|
|
161
|
+
border: '2px solid currentColor',
|
|
162
|
+
borderTopColor: 'transparent',
|
|
163
|
+
borderRadius: '50%',
|
|
164
|
+
animation: 'icon-button-spin 0.8s linear infinite',
|
|
165
|
+
}}
|
|
166
|
+
role="status"
|
|
167
|
+
aria-label="Loading"
|
|
168
|
+
/>
|
|
169
|
+
</>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Merge unistyles web ref with forwarded ref
|
|
173
|
+
const mergedRef = useMergeRefs(ref, webProps.ref);
|
|
174
|
+
|
|
175
|
+
// Content opacity - hide when loading but keep for sizing
|
|
176
|
+
const contentStyle = loading ? { opacity: 0 } : undefined;
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<button
|
|
180
|
+
{...webProps}
|
|
181
|
+
{...ariaProps}
|
|
182
|
+
ref={mergedRef}
|
|
183
|
+
id={buttonId}
|
|
184
|
+
onClick={handleClick}
|
|
185
|
+
disabled={isDisabled}
|
|
186
|
+
data-testid={testID}
|
|
187
|
+
aria-busy={loading ? 'true' : undefined}
|
|
188
|
+
style={{ position: 'relative' }}
|
|
189
|
+
>
|
|
190
|
+
{loading && renderSpinner()}
|
|
191
|
+
<span style={contentStyle}>{renderIcon()}</span>
|
|
192
|
+
</button>
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
IconButton.displayName = 'IconButton';
|
|
197
|
+
|
|
198
|
+
export default IconButton;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { IconName } from '../Icon/icon-types';
|
|
4
|
+
import { Intent, Size } from '@idealyst/theme';
|
|
5
|
+
import { BaseProps } from '../utils/viewStyleProps';
|
|
6
|
+
import { InteractiveAccessibilityProps } from '../utils/accessibility';
|
|
7
|
+
|
|
8
|
+
// Component-specific type aliases for future extensibility
|
|
9
|
+
export type IconButtonType = 'contained' | 'outlined' | 'text';
|
|
10
|
+
export type IconButtonIntentVariant = Intent;
|
|
11
|
+
export type IconButtonSizeVariant = Size;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gradient overlay options for icon buttons.
|
|
15
|
+
* Applies a transparent gradient over the intent background color.
|
|
16
|
+
* - 'darken': Transparent to semi-transparent black (darkens one corner)
|
|
17
|
+
* - 'lighten': Transparent to semi-transparent white (lightens one corner)
|
|
18
|
+
*/
|
|
19
|
+
export type IconButtonGradient = 'darken' | 'lighten';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Circular icon button component with multiple visual variants, sizes, and a single icon.
|
|
23
|
+
* Supports contained, outlined, and text styles with customizable intent colors.
|
|
24
|
+
*/
|
|
25
|
+
export interface IconButtonProps extends BaseProps, InteractiveAccessibilityProps {
|
|
26
|
+
/**
|
|
27
|
+
* The icon to display. Can be an icon name string or a custom ReactNode.
|
|
28
|
+
*/
|
|
29
|
+
icon: IconName | ReactNode;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Called when the button is pressed
|
|
33
|
+
*/
|
|
34
|
+
onPress?: () => void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated Use `onPress` instead. This prop exists for web compatibility only.
|
|
38
|
+
* Using onClick will log a deprecation warning in development.
|
|
39
|
+
*/
|
|
40
|
+
onClick?: () => void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Whether the button is disabled
|
|
44
|
+
*/
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The visual style type of the button
|
|
49
|
+
*/
|
|
50
|
+
type?: IconButtonType;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The intent/color scheme of the button
|
|
54
|
+
*/
|
|
55
|
+
intent?: IconButtonIntentVariant;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The size of the button
|
|
59
|
+
*/
|
|
60
|
+
size?: IconButtonSizeVariant;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Apply a gradient background enhancement.
|
|
64
|
+
* Only applies to 'contained' button type.
|
|
65
|
+
*/
|
|
66
|
+
gradient?: IconButtonGradient;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Whether the button is in a loading state.
|
|
70
|
+
* When true, shows a spinner and disables interaction.
|
|
71
|
+
* The spinner color matches the icon color.
|
|
72
|
+
*/
|
|
73
|
+
loading?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Additional styles (platform-specific)
|
|
77
|
+
*/
|
|
78
|
+
style?: StyleProp<ViewStyle>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Test ID for testing
|
|
82
|
+
*/
|
|
83
|
+
testID?: string;
|
|
84
|
+
}
|
|
@@ -39,7 +39,7 @@ const Skeleton: React.FC<SkeletonProps> = ({
|
|
|
39
39
|
animation,
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
const skeletonProps = getWebProps([skeletonStyles.skeleton, style as any]);
|
|
42
|
+
const skeletonProps = getWebProps([skeletonStyles.skeleton({}), style as any]);
|
|
43
43
|
|
|
44
44
|
// Apply custom border radius if provided and shape is 'rounded'
|
|
45
45
|
const customStyles: React.CSSProperties = {
|
|
@@ -48,6 +48,12 @@ export const switchStyles = defineStyle('Switch', (theme: Theme) => ({
|
|
|
48
48
|
|
|
49
49
|
switchContainer: (_props: SwitchDynamicProps) => ({
|
|
50
50
|
justifyContent: 'center' as const,
|
|
51
|
+
variants: {
|
|
52
|
+
disabled: {
|
|
53
|
+
true: { _web: { cursor: 'not-allowed' } },
|
|
54
|
+
false: { _web: { cursor: 'pointer' } },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
51
57
|
_web: {
|
|
52
58
|
border: 'none',
|
|
53
59
|
padding: 0,
|
|
@@ -59,15 +65,13 @@ export const switchStyles = defineStyle('Switch', (theme: Theme) => ({
|
|
|
59
65
|
switchTrack: (_props: SwitchDynamicProps) => ({
|
|
60
66
|
borderRadius: 9999,
|
|
61
67
|
position: 'relative' as const,
|
|
68
|
+
pointerEvents: 'none' as const,
|
|
62
69
|
variants: {
|
|
63
70
|
size: {
|
|
64
71
|
width: theme.sizes.$switch.trackWidth,
|
|
65
72
|
height: theme.sizes.$switch.trackHeight,
|
|
66
73
|
},
|
|
67
74
|
checked: {
|
|
68
|
-
true: {
|
|
69
|
-
backgroundColor: theme.$intents.primary,
|
|
70
|
-
},
|
|
71
75
|
false: {
|
|
72
76
|
backgroundColor: theme.colors.border.secondary,
|
|
73
77
|
},
|
|
@@ -77,6 +81,14 @@ export const switchStyles = defineStyle('Switch', (theme: Theme) => ({
|
|
|
77
81
|
false: { opacity: 1 },
|
|
78
82
|
},
|
|
79
83
|
},
|
|
84
|
+
compoundVariants: [
|
|
85
|
+
{
|
|
86
|
+
checked: true,
|
|
87
|
+
styles: {
|
|
88
|
+
backgroundColor: theme.$intents.primary,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
80
92
|
_web: {
|
|
81
93
|
transition: 'background-color 0.2s ease',
|
|
82
94
|
},
|
|
@@ -125,14 +137,19 @@ export const switchStyles = defineStyle('Switch', (theme: Theme) => ({
|
|
|
125
137
|
height: theme.sizes.$switch.thumbIconSize,
|
|
126
138
|
},
|
|
127
139
|
checked: {
|
|
128
|
-
true: {
|
|
129
|
-
color: theme.$intents.primary,
|
|
130
|
-
},
|
|
131
140
|
false: {
|
|
132
141
|
color: theme.colors.border.secondary,
|
|
133
142
|
},
|
|
134
143
|
},
|
|
135
144
|
},
|
|
145
|
+
compoundVariants: [
|
|
146
|
+
{
|
|
147
|
+
checked: true,
|
|
148
|
+
styles: {
|
|
149
|
+
color: theme.$intents.primary,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
136
153
|
}),
|
|
137
154
|
|
|
138
155
|
label: (_props: SwitchDynamicProps) => ({
|
|
@@ -77,12 +77,15 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
77
77
|
checked,
|
|
78
78
|
]);
|
|
79
79
|
|
|
80
|
+
|
|
80
81
|
// Apply variants using the correct Unistyles v3 pattern
|
|
81
82
|
switchStyles.useVariants({
|
|
82
|
-
size
|
|
83
|
+
size,
|
|
84
|
+
checked,
|
|
83
85
|
disabled: disabled as boolean,
|
|
84
|
-
|
|
86
|
+
labelPosition,
|
|
85
87
|
margin,
|
|
88
|
+
intent,
|
|
86
89
|
marginVertical,
|
|
87
90
|
marginHorizontal,
|
|
88
91
|
});
|
|
@@ -119,13 +122,7 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
119
122
|
|
|
120
123
|
// Computed container props with dynamic styles (for when label exists)
|
|
121
124
|
const computedContainerProps = getWebProps([
|
|
122
|
-
(switchStyles.container as any)({}),
|
|
123
|
-
style as any,
|
|
124
|
-
{
|
|
125
|
-
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
126
|
-
display: 'inline-flex',
|
|
127
|
-
alignItems: 'center',
|
|
128
|
-
}
|
|
125
|
+
(switchStyles.container as any)({ disabled }),
|
|
129
126
|
]);
|
|
130
127
|
|
|
131
128
|
const mergedButtonRef = useMergeRefs(ref as React.Ref<HTMLButtonElement>, computedButtonProps.ref);
|
|
@@ -135,6 +132,7 @@ const Switch = forwardRef<IdealystElement, SwitchProps>(({
|
|
|
135
132
|
<button
|
|
136
133
|
{...computedButtonProps}
|
|
137
134
|
{...ariaProps}
|
|
135
|
+
style={style as any}
|
|
138
136
|
ref={mergedButtonRef}
|
|
139
137
|
onClick={handleClick}
|
|
140
138
|
disabled={disabled}
|
package/src/View/View.styles.tsx
CHANGED
package/src/View/View.web.tsx
CHANGED
|
@@ -73,6 +73,9 @@ const View = forwardRef<IdealystElement, ViewProps>(({
|
|
|
73
73
|
flexShrink,
|
|
74
74
|
flexBasis,
|
|
75
75
|
alignSelf,
|
|
76
|
+
// Flex content alignment - goes to content (for aligning children)
|
|
77
|
+
alignItems,
|
|
78
|
+
justifyContent,
|
|
76
79
|
// Margin - goes to wrapper (positioning in parent)
|
|
77
80
|
margin,
|
|
78
81
|
marginTop,
|
|
@@ -124,6 +127,9 @@ const View = forwardRef<IdealystElement, ViewProps>(({
|
|
|
124
127
|
inset: 0,
|
|
125
128
|
overflow: 'auto',
|
|
126
129
|
boxSizing: 'border-box',
|
|
130
|
+
// Flex alignment for children
|
|
131
|
+
...(alignItems !== undefined && { alignItems }),
|
|
132
|
+
...(justifyContent !== undefined && { justifyContent }),
|
|
127
133
|
// User's visual styles
|
|
128
134
|
...contentStyles,
|
|
129
135
|
}}
|
|
@@ -139,8 +145,8 @@ const View = forwardRef<IdealystElement, ViewProps>(({
|
|
|
139
145
|
|
|
140
146
|
return (
|
|
141
147
|
<div
|
|
142
|
-
style={style as any}
|
|
143
148
|
{...webProps}
|
|
149
|
+
style={style as any}
|
|
144
150
|
ref={mergedRef}
|
|
145
151
|
id={id}
|
|
146
152
|
data-testid={testID}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Screen, View, Text, Button } from '@idealyst/components';
|
|
3
|
+
import ActivityIndicator from '../ActivityIndicator';
|
|
4
|
+
|
|
5
|
+
export const ActivityIndicatorExamples: React.FC = () => {
|
|
6
|
+
const [isAnimating, setIsAnimating] = useState(true);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<Screen background="primary" padding="lg">
|
|
10
|
+
<View gap="lg">
|
|
11
|
+
<Text typography="h3">ActivityIndicator Examples</Text>
|
|
12
|
+
|
|
13
|
+
<View gap="md">
|
|
14
|
+
<Text typography="h5">Basic</Text>
|
|
15
|
+
<View gap="sm" style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
|
|
16
|
+
<ActivityIndicator />
|
|
17
|
+
</View>
|
|
18
|
+
</View>
|
|
19
|
+
|
|
20
|
+
<View gap="md">
|
|
21
|
+
<Text typography="h5">Sizes</Text>
|
|
22
|
+
<View gap="sm" style={{ flexDirection: 'row', alignItems: 'center', gap: 24 }}>
|
|
23
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
24
|
+
<ActivityIndicator size="xs" />
|
|
25
|
+
<Text typography="caption">xs</Text>
|
|
26
|
+
</View>
|
|
27
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
28
|
+
<ActivityIndicator size="sm" />
|
|
29
|
+
<Text typography="caption">sm</Text>
|
|
30
|
+
</View>
|
|
31
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
32
|
+
<ActivityIndicator size="md" />
|
|
33
|
+
<Text typography="caption">md</Text>
|
|
34
|
+
</View>
|
|
35
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
36
|
+
<ActivityIndicator size="lg" />
|
|
37
|
+
<Text typography="caption">lg</Text>
|
|
38
|
+
</View>
|
|
39
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
40
|
+
<ActivityIndicator size="xl" />
|
|
41
|
+
<Text typography="caption">xl</Text>
|
|
42
|
+
</View>
|
|
43
|
+
</View>
|
|
44
|
+
</View>
|
|
45
|
+
|
|
46
|
+
<View gap="md">
|
|
47
|
+
<Text typography="h5">Intent Colors</Text>
|
|
48
|
+
<View gap="sm" style={{ flexDirection: 'row', alignItems: 'center', gap: 24 }}>
|
|
49
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
50
|
+
<ActivityIndicator intent="primary" />
|
|
51
|
+
<Text typography="caption">primary</Text>
|
|
52
|
+
</View>
|
|
53
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
54
|
+
<ActivityIndicator intent="success" />
|
|
55
|
+
<Text typography="caption">success</Text>
|
|
56
|
+
</View>
|
|
57
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
58
|
+
<ActivityIndicator intent="warning" />
|
|
59
|
+
<Text typography="caption">warning</Text>
|
|
60
|
+
</View>
|
|
61
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
62
|
+
<ActivityIndicator intent="danger" />
|
|
63
|
+
<Text typography="caption">danger</Text>
|
|
64
|
+
</View>
|
|
65
|
+
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
66
|
+
<ActivityIndicator intent="neutral" />
|
|
67
|
+
<Text typography="caption">neutral</Text>
|
|
68
|
+
</View>
|
|
69
|
+
</View>
|
|
70
|
+
</View>
|
|
71
|
+
|
|
72
|
+
<View gap="md">
|
|
73
|
+
<Text typography="h5">Custom Color</Text>
|
|
74
|
+
<View gap="sm" style={{ flexDirection: 'row', alignItems: 'center', gap: 24 }}>
|
|
75
|
+
<ActivityIndicator color="#9333ea" />
|
|
76
|
+
<ActivityIndicator color="#ec4899" />
|
|
77
|
+
<ActivityIndicator color="#06b6d4" />
|
|
78
|
+
<ActivityIndicator color="#f97316" />
|
|
79
|
+
</View>
|
|
80
|
+
</View>
|
|
81
|
+
|
|
82
|
+
<View gap="md">
|
|
83
|
+
<Text typography="h5">Toggle Animation</Text>
|
|
84
|
+
<View gap="sm" style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
|
|
85
|
+
<ActivityIndicator animating={isAnimating} hidesWhenStopped={false} />
|
|
86
|
+
<Button size="sm" onPress={() => setIsAnimating(!isAnimating)}>
|
|
87
|
+
{isAnimating ? 'Stop' : 'Start'}
|
|
88
|
+
</Button>
|
|
89
|
+
</View>
|
|
90
|
+
<Text typography="caption" color="secondary">
|
|
91
|
+
hidesWhenStopped=false keeps the indicator visible when stopped
|
|
92
|
+
</Text>
|
|
93
|
+
</View>
|
|
94
|
+
|
|
95
|
+
<View gap="md">
|
|
96
|
+
<Text typography="h5">Hides When Stopped (default)</Text>
|
|
97
|
+
<View gap="sm" style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
|
|
98
|
+
<View style={{ width: 32, height: 32, alignItems: 'center', justifyContent: 'center' }}>
|
|
99
|
+
<ActivityIndicator animating={isAnimating} />
|
|
100
|
+
</View>
|
|
101
|
+
<Text typography="body2">
|
|
102
|
+
{isAnimating ? 'Visible (animating)' : 'Hidden (stopped)'}
|
|
103
|
+
</Text>
|
|
104
|
+
</View>
|
|
105
|
+
</View>
|
|
106
|
+
|
|
107
|
+
<View gap="md">
|
|
108
|
+
<Text typography="h5">Loading States</Text>
|
|
109
|
+
<View gap="sm">
|
|
110
|
+
<View
|
|
111
|
+
style={{
|
|
112
|
+
flexDirection: 'row',
|
|
113
|
+
alignItems: 'center',
|
|
114
|
+
gap: 12,
|
|
115
|
+
padding: 16,
|
|
116
|
+
backgroundColor: 'rgba(0,0,0,0.05)',
|
|
117
|
+
borderRadius: 8,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<ActivityIndicator size="sm" />
|
|
121
|
+
<Text>Loading data...</Text>
|
|
122
|
+
</View>
|
|
123
|
+
<View
|
|
124
|
+
style={{
|
|
125
|
+
alignItems: 'center',
|
|
126
|
+
justifyContent: 'center',
|
|
127
|
+
padding: 32,
|
|
128
|
+
backgroundColor: 'rgba(0,0,0,0.05)',
|
|
129
|
+
borderRadius: 8,
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<ActivityIndicator size="lg" />
|
|
133
|
+
<Text typography="body2" color="secondary" style={{ marginTop: 12 }}>
|
|
134
|
+
Please wait...
|
|
135
|
+
</Text>
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
</View>
|
|
139
|
+
|
|
140
|
+
<View gap="md">
|
|
141
|
+
<Text typography="h5">Inline with Text</Text>
|
|
142
|
+
<View gap="sm">
|
|
143
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
144
|
+
<Text>Saving</Text>
|
|
145
|
+
<ActivityIndicator size="xs" />
|
|
146
|
+
</View>
|
|
147
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
148
|
+
<Text>Processing request</Text>
|
|
149
|
+
<ActivityIndicator size="sm" intent="success" />
|
|
150
|
+
</View>
|
|
151
|
+
</View>
|
|
152
|
+
</View>
|
|
153
|
+
|
|
154
|
+
<View gap="md">
|
|
155
|
+
<Text typography="h5">On Dark Background</Text>
|
|
156
|
+
<View
|
|
157
|
+
style={{
|
|
158
|
+
flexDirection: 'row',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
gap: 24,
|
|
161
|
+
padding: 24,
|
|
162
|
+
backgroundColor: '#1f2937',
|
|
163
|
+
borderRadius: 8,
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<ActivityIndicator color="#ffffff" />
|
|
167
|
+
<ActivityIndicator color="#60a5fa" />
|
|
168
|
+
<ActivityIndicator color="#34d399" />
|
|
169
|
+
<ActivityIndicator color="#fbbf24" />
|
|
170
|
+
</View>
|
|
171
|
+
</View>
|
|
172
|
+
</View>
|
|
173
|
+
</Screen>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export default ActivityIndicatorExamples;
|