@idealyst/components 1.2.49 → 1.2.51
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": "@idealyst/components",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.51",
|
|
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.51",
|
|
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.51",
|
|
111
111
|
"@idealyst/tooling": "^1.2.30",
|
|
112
112
|
"@mdi/react": "^1.6.1",
|
|
113
113
|
"@types/react": "^19.1.0",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useEffect, forwardRef, useMemo } from 'react';
|
|
2
|
-
import { Modal, View, Text, TouchableOpacity, TouchableWithoutFeedback, BackHandler,
|
|
1
|
+
import { useEffect, forwardRef, useMemo, useState } from 'react';
|
|
2
|
+
import { Modal, View, Text, TouchableOpacity, TouchableWithoutFeedback, BackHandler, Platform, Keyboard, useWindowDimensions } from 'react-native';
|
|
3
3
|
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
4
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
5
|
import { DialogProps } from './types';
|
|
5
6
|
import { dialogStyles } from './Dialog.styles';
|
|
6
7
|
import { getNativeInteractiveAccessibilityProps } from '../utils/accessibility';
|
|
@@ -16,6 +17,8 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
16
17
|
closeOnBackdropClick = true,
|
|
17
18
|
animationType: _animationType = 'fade',
|
|
18
19
|
avoidKeyboard = false,
|
|
20
|
+
padding: paddingProp = 20,
|
|
21
|
+
maxContentHeight,
|
|
19
22
|
style,
|
|
20
23
|
testID,
|
|
21
24
|
id,
|
|
@@ -42,6 +45,33 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
42
45
|
const containerScale = useSharedValue(0.9);
|
|
43
46
|
const containerOpacity = useSharedValue(0);
|
|
44
47
|
|
|
48
|
+
// Get safe area insets
|
|
49
|
+
const insets = useSafeAreaInsets();
|
|
50
|
+
|
|
51
|
+
// Track keyboard height for avoidKeyboard
|
|
52
|
+
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
53
|
+
const { height: screenHeight } = useWindowDimensions();
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!avoidKeyboard || !open) return;
|
|
57
|
+
|
|
58
|
+
const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
|
|
59
|
+
const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
|
|
60
|
+
|
|
61
|
+
const showSub = Keyboard.addListener(showEvent, (e) => {
|
|
62
|
+
setKeyboardHeight(e.endCoordinates.height);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const hideSub = Keyboard.addListener(hideEvent, () => {
|
|
66
|
+
setKeyboardHeight(0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return () => {
|
|
70
|
+
showSub.remove();
|
|
71
|
+
hideSub.remove();
|
|
72
|
+
};
|
|
73
|
+
}, [avoidKeyboard, open]);
|
|
74
|
+
|
|
45
75
|
// Animate in/out when open changes
|
|
46
76
|
useEffect(() => {
|
|
47
77
|
if (open) {
|
|
@@ -109,11 +139,12 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
109
139
|
});
|
|
110
140
|
|
|
111
141
|
const containerAnimatedStyle = useAnimatedStyle(() => {
|
|
142
|
+
'worklet';
|
|
112
143
|
return {
|
|
113
144
|
opacity: containerOpacity.value,
|
|
114
145
|
transform: [
|
|
115
146
|
{ scale: containerScale.value },
|
|
116
|
-
],
|
|
147
|
+
] as { scale: number }[],
|
|
117
148
|
};
|
|
118
149
|
});
|
|
119
150
|
|
|
@@ -124,19 +155,6 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
124
155
|
const titleStyle = (dialogStyles.title as any)({});
|
|
125
156
|
const closeButtonStyle = (dialogStyles.closeButton as any)({});
|
|
126
157
|
const closeButtonTextStyle = (dialogStyles.closeButtonText as any)({});
|
|
127
|
-
const contentStyle = (dialogStyles.content as any)({});
|
|
128
|
-
|
|
129
|
-
// Style for custom backdrop wrapper (no default backdrop styling)
|
|
130
|
-
const customBackdropWrapperStyle = {
|
|
131
|
-
position: 'absolute' as const,
|
|
132
|
-
top: 0,
|
|
133
|
-
left: 0,
|
|
134
|
-
right: 0,
|
|
135
|
-
bottom: 0,
|
|
136
|
-
display: 'flex' as const,
|
|
137
|
-
alignItems: 'center' as const,
|
|
138
|
-
justifyContent: 'center' as const,
|
|
139
|
-
};
|
|
140
158
|
|
|
141
159
|
// Style for custom backdrop component container (fills entire backdrop area)
|
|
142
160
|
const customBackdropContainerStyle = {
|
|
@@ -147,8 +165,32 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
147
165
|
bottom: 0,
|
|
148
166
|
};
|
|
149
167
|
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
// Position offsets for the container view
|
|
169
|
+
// Top: always safe area + padding
|
|
170
|
+
// Bottom: safe area + padding (no keyboard) or keyboard + padding (with keyboard)
|
|
171
|
+
const topOffset = insets.top + paddingProp;
|
|
172
|
+
const bottomOffset = keyboardHeight > 0
|
|
173
|
+
? keyboardHeight + paddingProp
|
|
174
|
+
: insets.bottom + paddingProp;
|
|
175
|
+
|
|
176
|
+
// Max height is the available space (used as a ceiling, children can be smaller)
|
|
177
|
+
const maxAvailableHeight = screenHeight - topOffset - bottomOffset;
|
|
178
|
+
|
|
179
|
+
// Use the smaller of user's preferred max height and available space
|
|
180
|
+
const effectiveMaxHeight = maxContentHeight
|
|
181
|
+
? Math.min(maxContentHeight, maxAvailableHeight)
|
|
182
|
+
: maxAvailableHeight;
|
|
183
|
+
|
|
184
|
+
// Dialog uses the effective max height, with flex: 1 so children can fill it
|
|
185
|
+
const dialogContainerStyle = {
|
|
186
|
+
...containerStyle,
|
|
187
|
+
maxHeight: effectiveMaxHeight,
|
|
188
|
+
height: maxContentHeight ? effectiveMaxHeight : undefined,
|
|
189
|
+
flex: undefined,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const dialogContainer = (
|
|
193
|
+
<Animated.View ref={ref as any} style={[dialogContainerStyle, style, containerAnimatedStyle]} nativeID={id} {...nativeA11yProps}>
|
|
152
194
|
{(title || showCloseButton) && (
|
|
153
195
|
<View style={headerStyle}>
|
|
154
196
|
{title && (
|
|
@@ -168,26 +210,10 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
168
210
|
)}
|
|
169
211
|
</View>
|
|
170
212
|
)}
|
|
171
|
-
|
|
172
|
-
{children}
|
|
173
|
-
</View>
|
|
213
|
+
{children}
|
|
174
214
|
</Animated.View>
|
|
175
215
|
);
|
|
176
216
|
|
|
177
|
-
const dialogContainer = (
|
|
178
|
-
<TouchableWithoutFeedback onPress={(e: GestureResponderEvent) => e.stopPropagation()}>
|
|
179
|
-
{avoidKeyboard ? (
|
|
180
|
-
<KeyboardAvoidingView
|
|
181
|
-
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
182
|
-
>
|
|
183
|
-
{dialogContent}
|
|
184
|
-
</KeyboardAvoidingView>
|
|
185
|
-
) : (
|
|
186
|
-
dialogContent
|
|
187
|
-
)}
|
|
188
|
-
</TouchableWithoutFeedback>
|
|
189
|
-
);
|
|
190
|
-
|
|
191
217
|
return (
|
|
192
218
|
<Modal
|
|
193
219
|
visible={open}
|
|
@@ -197,20 +223,35 @@ const Dialog = forwardRef<View, DialogProps>(({
|
|
|
197
223
|
statusBarTranslucent
|
|
198
224
|
testID={testID}
|
|
199
225
|
>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
226
|
+
{/* Backdrop layer - positioned absolute, full screen */}
|
|
227
|
+
{BackdropComponent ? (
|
|
228
|
+
<TouchableWithoutFeedback onPress={handleBackdropPress}>
|
|
229
|
+
<Animated.View style={[customBackdropContainerStyle, backdropAnimatedStyle]} pointerEvents="auto">
|
|
230
|
+
<View style={{ flex: 1 }} pointerEvents="none">
|
|
204
231
|
<BackdropComponent isVisible={open} />
|
|
205
|
-
</
|
|
206
|
-
{dialogContainer}
|
|
207
|
-
</View>
|
|
208
|
-
) : (
|
|
209
|
-
<Animated.View style={[backdropStyle, backdropAnimatedStyle]}>
|
|
210
|
-
{dialogContainer}
|
|
232
|
+
</View>
|
|
211
233
|
</Animated.View>
|
|
212
|
-
|
|
213
|
-
|
|
234
|
+
</TouchableWithoutFeedback>
|
|
235
|
+
) : (
|
|
236
|
+
<TouchableWithoutFeedback onPress={handleBackdropPress}>
|
|
237
|
+
<Animated.View style={[backdropStyle, backdropAnimatedStyle]} />
|
|
238
|
+
</TouchableWithoutFeedback>
|
|
239
|
+
)}
|
|
240
|
+
{/* Dialog content - positioned absolute, accounts for keyboard and safe areas */}
|
|
241
|
+
<View
|
|
242
|
+
style={{
|
|
243
|
+
position: 'absolute',
|
|
244
|
+
top: topOffset,
|
|
245
|
+
left: 0,
|
|
246
|
+
right: 0,
|
|
247
|
+
bottom: bottomOffset,
|
|
248
|
+
alignItems: 'center',
|
|
249
|
+
justifyContent: 'center',
|
|
250
|
+
}}
|
|
251
|
+
pointerEvents="box-none"
|
|
252
|
+
>
|
|
253
|
+
{dialogContainer}
|
|
254
|
+
</View>
|
|
214
255
|
</Modal>
|
|
215
256
|
);
|
|
216
257
|
});
|
|
@@ -64,6 +64,7 @@ export const dialogStyles = defineStyle('Dialog', (theme: Theme) => ({
|
|
|
64
64
|
shadowRadius: 20,
|
|
65
65
|
elevation: 10,
|
|
66
66
|
maxHeight: '90%',
|
|
67
|
+
flexDirection: 'column' as const,
|
|
67
68
|
...sizeStyles,
|
|
68
69
|
...typeStyles,
|
|
69
70
|
_web: {
|
|
@@ -127,6 +128,7 @@ export const dialogStyles = defineStyle('Dialog', (theme: Theme) => ({
|
|
|
127
128
|
}),
|
|
128
129
|
|
|
129
130
|
content: (_props: DialogDynamicProps) => ({
|
|
131
|
+
flex: 1,
|
|
130
132
|
_web: {
|
|
131
133
|
overflow: 'visible',
|
|
132
134
|
maxHeight: 'none',
|
|
@@ -21,6 +21,7 @@ const Dialog = forwardRef<HTMLDivElement, DialogProps>(({
|
|
|
21
21
|
showCloseButton = true,
|
|
22
22
|
closeOnBackdropClick = true,
|
|
23
23
|
closeOnEscapeKey = true,
|
|
24
|
+
height,
|
|
24
25
|
style,
|
|
25
26
|
testID,
|
|
26
27
|
id,
|
|
@@ -144,14 +145,18 @@ const Dialog = forwardRef<HTMLDivElement, DialogProps>(({
|
|
|
144
145
|
const containerProps = getWebProps([
|
|
145
146
|
(dialogStyles.container as any)({}),
|
|
146
147
|
style as any,
|
|
148
|
+
height !== undefined ? { height, display: 'flex', flexDirection: 'column' } : null,
|
|
147
149
|
isVisible
|
|
148
150
|
? { opacity: 1, transform: 'scale(1) translateY(0px)' }
|
|
149
151
|
: { opacity: 0, transform: 'scale(0.96) translateY(-4px)' }
|
|
150
|
-
]);
|
|
152
|
+
].filter(Boolean));
|
|
151
153
|
const headerProps = getWebProps([(dialogStyles.header as any)({})]);
|
|
152
154
|
const titleProps = getWebProps([(dialogStyles.title as any)({})]);
|
|
153
155
|
const closeButtonProps = getWebProps([(dialogStyles.closeButton as any)({})]);
|
|
154
|
-
const contentProps = getWebProps([
|
|
156
|
+
const contentProps = getWebProps([
|
|
157
|
+
(dialogStyles.content as any)({}),
|
|
158
|
+
height !== undefined ? { flex: 1, overflow: 'auto' } : null,
|
|
159
|
+
].filter(Boolean));
|
|
155
160
|
|
|
156
161
|
const mergedBackdropRef = useMergeRefs(ref, backdropProps.ref);
|
|
157
162
|
|
package/src/Dialog/types.ts
CHANGED
|
@@ -73,6 +73,11 @@ export interface DialogProps extends BaseProps, InteractiveAccessibilityProps {
|
|
|
73
73
|
*/
|
|
74
74
|
animationType?: DialogAnimationType;
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Maximum height for the dialog content area (native only)
|
|
78
|
+
*/
|
|
79
|
+
maxContentHeight?: number;
|
|
80
|
+
|
|
76
81
|
/**
|
|
77
82
|
* Additional styles (platform-specific)
|
|
78
83
|
*/
|
|
@@ -96,4 +101,11 @@ export interface DialogProps extends BaseProps, InteractiveAccessibilityProps {
|
|
|
96
101
|
* @default false
|
|
97
102
|
*/
|
|
98
103
|
avoidKeyboard?: boolean;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Fixed height for the dialog container.
|
|
107
|
+
* Can be a number (pixels) or a string (e.g., '50%', '400px').
|
|
108
|
+
* When set, children can use flex: 1 to fill the available space.
|
|
109
|
+
*/
|
|
110
|
+
height?: number | string;
|
|
99
111
|
}
|
package/src/index.native.ts
CHANGED
|
@@ -16,6 +16,9 @@ export * from './Pressable/types';
|
|
|
16
16
|
export { default as Input } from './Input';
|
|
17
17
|
export * from './Input/types';
|
|
18
18
|
|
|
19
|
+
export { default as TextInput } from './TextInput';
|
|
20
|
+
export * from './TextInput/types';
|
|
21
|
+
|
|
19
22
|
// New primitive components
|
|
20
23
|
export { default as Checkbox } from './Checkbox';
|
|
21
24
|
export * from './Checkbox/types';
|
|
@@ -115,6 +118,7 @@ export type { IconButtonProps } from './IconButton/types';
|
|
|
115
118
|
export type { TextProps } from './Text/types';
|
|
116
119
|
export type { ViewProps } from './View/types';
|
|
117
120
|
export type { InputProps } from './Input/types';
|
|
121
|
+
export type { TextInputProps } from './TextInput/types';
|
|
118
122
|
export type { CheckboxProps } from './Checkbox/types';
|
|
119
123
|
export type { CardProps } from './Card/types';
|
|
120
124
|
export type { DividerProps } from './Divider/types';
|