@idealyst/components 1.2.70 → 1.2.72
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 +3 -3
- package/src/Card/Card.styles.tsx +1 -2
- package/src/Card/Card.web.tsx +2 -6
- package/src/IconButton/IconButton.native.tsx +15 -14
- package/src/IconButton/IconButton.web.tsx +8 -3
- package/src/TextArea/TextArea.native.tsx +7 -1
- package/src/TextArea/TextArea.web.tsx +15 -0
- package/src/TextArea/types.ts +25 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/components",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.72",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"publish:npm": "npm publish"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@idealyst/theme": "^1.2.
|
|
59
|
+
"@idealyst/theme": "^1.2.72",
|
|
60
60
|
"@mdi/js": ">=7.0.0",
|
|
61
61
|
"@mdi/react": ">=1.0.0",
|
|
62
62
|
"@react-native-vector-icons/common": ">=12.0.0",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@idealyst/blur": "^1.2.40",
|
|
110
|
-
"@idealyst/theme": "^1.2.
|
|
110
|
+
"@idealyst/theme": "^1.2.72",
|
|
111
111
|
"@idealyst/tooling": "^1.2.30",
|
|
112
112
|
"@mdi/react": "^1.6.1",
|
|
113
113
|
"@types/react": "^19.1.0",
|
package/src/Card/Card.styles.tsx
CHANGED
|
@@ -82,9 +82,8 @@ export const cardStyles = defineStyle('Card', (theme: Theme) => ({
|
|
|
82
82
|
true: {
|
|
83
83
|
_web: {
|
|
84
84
|
cursor: 'pointer',
|
|
85
|
-
transition: '
|
|
85
|
+
transition: 'box-shadow 0.2s ease',
|
|
86
86
|
_hover: {
|
|
87
|
-
transform: 'translateY(-2px)',
|
|
88
87
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.06)',
|
|
89
88
|
},
|
|
90
89
|
},
|
package/src/Card/Card.web.tsx
CHANGED
|
@@ -93,21 +93,17 @@ const Card = forwardRef<IdealystElement, CardProps>(({
|
|
|
93
93
|
|
|
94
94
|
const mergedRef = useMergeRefs(ref, webProps.ref, layoutRef);
|
|
95
95
|
|
|
96
|
-
// Use appropriate HTML element based on pressable state
|
|
97
|
-
const Component: any = pressable ? 'button' : 'div';
|
|
98
|
-
|
|
99
96
|
return (
|
|
100
|
-
<
|
|
97
|
+
<div
|
|
101
98
|
{...webProps}
|
|
102
99
|
{...ariaProps}
|
|
103
100
|
ref={mergedRef as any}
|
|
104
101
|
id={id}
|
|
105
102
|
onClick={pressable ? handleClick : undefined}
|
|
106
|
-
disabled={pressable && disabled}
|
|
107
103
|
data-testid={testID}
|
|
108
104
|
>
|
|
109
105
|
{children}
|
|
110
|
-
</
|
|
106
|
+
</div>
|
|
111
107
|
);
|
|
112
108
|
});
|
|
113
109
|
|
|
@@ -2,10 +2,12 @@ import { forwardRef, useMemo } from 'react';
|
|
|
2
2
|
import { ActivityIndicator, StyleSheet as RNStyleSheet, TouchableOpacity, View } from 'react-native';
|
|
3
3
|
import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons';
|
|
4
4
|
import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg';
|
|
5
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
5
6
|
import { iconButtonStyles } from './IconButton.styles';
|
|
6
7
|
import { IconButtonProps } from './types';
|
|
7
8
|
import { getNativeInteractiveAccessibilityProps } from '../utils/accessibility';
|
|
8
9
|
import type { IdealystElement } from '../utils/refTypes';
|
|
10
|
+
import type { Theme } from '@idealyst/theme';
|
|
9
11
|
|
|
10
12
|
const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) => {
|
|
11
13
|
const {
|
|
@@ -34,9 +36,15 @@ const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) =>
|
|
|
34
36
|
accessibilityPressed,
|
|
35
37
|
} = props;
|
|
36
38
|
|
|
39
|
+
// Get theme for icon size
|
|
40
|
+
const { theme } = useUnistyles() as { theme: Theme };
|
|
41
|
+
|
|
37
42
|
// Button is effectively disabled when loading
|
|
38
43
|
const isDisabled = disabled || loading;
|
|
39
44
|
|
|
45
|
+
// Get icon size directly from theme
|
|
46
|
+
const iconSize = theme.sizes.iconButton[size]?.iconSize ?? 24;
|
|
47
|
+
|
|
40
48
|
// Determine the handler to use - onPress takes precedence
|
|
41
49
|
const pressHandler = onPress ?? onClick;
|
|
42
50
|
|
|
@@ -63,9 +71,6 @@ const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) =>
|
|
|
63
71
|
// Gradient is only applicable to contained buttons
|
|
64
72
|
const showGradient = gradient && type === 'contained';
|
|
65
73
|
|
|
66
|
-
// Get icon size from the computed icon style (from theme)
|
|
67
|
-
const iconSize = iconStyle?.width ?? iconStyle?.height ?? 24;
|
|
68
|
-
|
|
69
74
|
// Generate native accessibility props
|
|
70
75
|
const nativeA11yProps = useMemo(() => {
|
|
71
76
|
const computedLabel = accessibilityLabel ?? (typeof icon === 'string' ? icon : undefined);
|
|
@@ -193,19 +198,15 @@ const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) =>
|
|
|
193
198
|
{renderGradientLayer()}
|
|
194
199
|
{/* Centered spinner overlay */}
|
|
195
200
|
{loading && (
|
|
196
|
-
<View style={RNStyleSheet.absoluteFill}>
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
/>
|
|
202
|
-
</View>
|
|
201
|
+
<View style={[RNStyleSheet.absoluteFill, { justifyContent: 'center', alignItems: 'center' }]}>
|
|
202
|
+
<ActivityIndicator
|
|
203
|
+
size="small"
|
|
204
|
+
color={spinnerColor}
|
|
205
|
+
/>
|
|
203
206
|
</View>
|
|
204
207
|
)}
|
|
205
|
-
{/*
|
|
206
|
-
|
|
207
|
-
{renderIcon()}
|
|
208
|
-
</View>
|
|
208
|
+
{/* Icon renders directly - TouchableOpacity has alignItems/justifyContent center */}
|
|
209
|
+
{renderIcon()}
|
|
209
210
|
</TouchableOpacity>
|
|
210
211
|
);
|
|
211
212
|
});
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import React, { isValidElement, forwardRef, useMemo } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
3
4
|
import { IconButtonProps } from './types';
|
|
4
5
|
import { iconButtonStyles } from './IconButton.styles';
|
|
5
6
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
7
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
7
8
|
import { getWebInteractiveAriaProps, generateAccessibilityId } from '../utils/accessibility';
|
|
8
9
|
import type { IdealystElement } from '../utils/refTypes';
|
|
10
|
+
import type { Theme } from '@idealyst/theme';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Circular icon button component with multiple visual variants and sizes.
|
|
@@ -40,9 +42,15 @@ const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) =>
|
|
|
40
42
|
accessibilityHasPopup,
|
|
41
43
|
} = props;
|
|
42
44
|
|
|
45
|
+
// Get theme for icon size
|
|
46
|
+
const { theme } = useUnistyles() as { theme: Theme };
|
|
47
|
+
|
|
43
48
|
// Button is effectively disabled when loading
|
|
44
49
|
const isDisabled = disabled || loading;
|
|
45
50
|
|
|
51
|
+
// Get icon size directly from theme
|
|
52
|
+
const iconSize = theme.sizes.iconButton[size]?.iconSize ?? 24;
|
|
53
|
+
|
|
46
54
|
// Apply variants for size, disabled, gradient
|
|
47
55
|
iconButtonStyles.useVariants({
|
|
48
56
|
size,
|
|
@@ -121,9 +129,6 @@ const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) =>
|
|
|
121
129
|
const iconStyleArray = [iconStyleComputed];
|
|
122
130
|
const iconProps = getWebProps(iconStyleArray);
|
|
123
131
|
|
|
124
|
-
// Extract icon size from computed styles for explicit sizing
|
|
125
|
-
const iconSize = iconStyleComputed?.width ?? iconStyleComputed?.height ?? 24;
|
|
126
|
-
|
|
127
132
|
// Spinner styles that match the icon color
|
|
128
133
|
const spinnerStyleArray = [(iconButtonStyles.spinner as any)(dynamicProps)];
|
|
129
134
|
const spinnerProps = getWebProps(spinnerStyleArray);
|
|
@@ -11,6 +11,7 @@ const TextArea = forwardRef<IdealystElement, TextAreaProps>(({
|
|
|
11
11
|
value: controlledValue,
|
|
12
12
|
defaultValue = '',
|
|
13
13
|
onChange,
|
|
14
|
+
onKeyDown: _onKeyDown, // Web-only, no-op on native
|
|
14
15
|
placeholder,
|
|
15
16
|
disabled = false,
|
|
16
17
|
minHeight,
|
|
@@ -185,7 +186,12 @@ const TextArea = forwardRef<IdealystElement, TextAreaProps>(({
|
|
|
185
186
|
// For autoGrow: don't set height, let it grow naturally with minHeight constraint
|
|
186
187
|
// For fixed height: use rows-based height
|
|
187
188
|
autoGrow
|
|
188
|
-
? {
|
|
189
|
+
? {
|
|
190
|
+
minHeight: minHeight ?? 44,
|
|
191
|
+
maxHeight: maxHeight,
|
|
192
|
+
// Force height to minHeight when empty to ensure shrinking
|
|
193
|
+
...(value === '' ? { height: minHeight ?? 44 } : {}),
|
|
194
|
+
}
|
|
189
195
|
: { height: rows * 24 },
|
|
190
196
|
textareaStyle,
|
|
191
197
|
]}
|
|
@@ -14,6 +14,7 @@ const TextArea = forwardRef<IdealystElement, TextAreaProps>(({
|
|
|
14
14
|
value: controlledValue,
|
|
15
15
|
defaultValue = '',
|
|
16
16
|
onChange,
|
|
17
|
+
onKeyDown,
|
|
17
18
|
placeholder,
|
|
18
19
|
disabled = false,
|
|
19
20
|
rows = 4,
|
|
@@ -204,6 +205,19 @@ const TextArea = forwardRef<IdealystElement, TextAreaProps>(({
|
|
|
204
205
|
setIsFocused(false);
|
|
205
206
|
};
|
|
206
207
|
|
|
208
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
209
|
+
if (onKeyDown) {
|
|
210
|
+
onKeyDown({
|
|
211
|
+
key: e.key,
|
|
212
|
+
ctrlKey: e.ctrlKey,
|
|
213
|
+
shiftKey: e.shiftKey,
|
|
214
|
+
altKey: e.altKey,
|
|
215
|
+
metaKey: e.metaKey,
|
|
216
|
+
preventDefault: () => e.preventDefault(),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
207
221
|
const showFooter = (error || helperText) || (showCharacterCount && maxLength);
|
|
208
222
|
|
|
209
223
|
const computedTextareaProps = getWebProps([
|
|
@@ -234,6 +248,7 @@ const TextArea = forwardRef<IdealystElement, TextAreaProps>(({
|
|
|
234
248
|
onChange={handleChange}
|
|
235
249
|
onFocus={handleFocus}
|
|
236
250
|
onBlur={handleBlur}
|
|
251
|
+
onKeyDown={handleKeyDown}
|
|
237
252
|
placeholder={placeholder}
|
|
238
253
|
disabled={disabled}
|
|
239
254
|
rows={autoGrow ? undefined : rows}
|
package/src/TextArea/types.ts
CHANGED
|
@@ -9,10 +9,35 @@ export type TextAreaSizeVariant = Size;
|
|
|
9
9
|
export type TextAreaResizeVariant = 'none' | 'vertical' | 'horizontal' | 'both';
|
|
10
10
|
export type TextAreaType = 'outlined' | 'filled' | 'bare';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Keyboard event data with modifier key states.
|
|
14
|
+
* Provides a cross-platform API for handling key presses with modifiers.
|
|
15
|
+
*/
|
|
16
|
+
export interface KeyboardEventData {
|
|
17
|
+
/** The key that was pressed (e.g., 'Enter', 'a', 'Escape') */
|
|
18
|
+
key: string;
|
|
19
|
+
/** Whether the Ctrl key (or Cmd on Mac) was held */
|
|
20
|
+
ctrlKey: boolean;
|
|
21
|
+
/** Whether the Shift key was held */
|
|
22
|
+
shiftKey: boolean;
|
|
23
|
+
/** Whether the Alt key (Option on Mac) was held */
|
|
24
|
+
altKey: boolean;
|
|
25
|
+
/** Whether the Meta key (Cmd on Mac, Win on Windows) was held */
|
|
26
|
+
metaKey: boolean;
|
|
27
|
+
/** Prevents the default browser behavior (web only, no-op on native) */
|
|
28
|
+
preventDefault: () => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
12
31
|
export interface TextAreaProps extends FormInputStyleProps, FormAccessibilityProps {
|
|
13
32
|
value?: string;
|
|
14
33
|
defaultValue?: string;
|
|
15
34
|
onChange?: (value: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Called when a key is pressed while the textarea is focused.
|
|
37
|
+
* Includes modifier key states (ctrl, shift, alt, meta).
|
|
38
|
+
* Web only - no-op on native.
|
|
39
|
+
*/
|
|
40
|
+
onKeyDown?: (event: KeyboardEventData) => void;
|
|
16
41
|
placeholder?: string;
|
|
17
42
|
disabled?: boolean;
|
|
18
43
|
rows?: number;
|