@idealyst/components 1.0.94 → 1.0.96
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/Input/Input.native.tsx +8 -0
- package/src/Input/Input.styles.tsx +2 -1
- package/src/Input/Input.web.tsx +25 -6
- package/src/Input/types.ts +6 -0
- package/src/Select/Select.styles.tsx +20 -27
- package/src/Select/Select.web.tsx +74 -25
- package/src/index.native.ts +3 -0
- package/src/index.ts +3 -0
- package/src/utils/events/events.native.ts +212 -0
- package/src/utils/events/events.web.ts +184 -0
- package/src/utils/events/index.ts +46 -0
- package/src/utils/events/index.web.ts +44 -0
- package/src/utils/events/types.ts +231 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/components",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.96",
|
|
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.96",
|
|
45
45
|
"@mdi/js": ">=7.0.0",
|
|
46
46
|
"@mdi/react": ">=1.0.0",
|
|
47
47
|
"@react-native-vector-icons/common": ">=12.0.0",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
}
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
|
-
"@idealyst/theme": "^1.0.
|
|
94
|
+
"@idealyst/theme": "^1.0.96",
|
|
95
95
|
"@mdi/react": "^1.6.1",
|
|
96
96
|
"@types/react": "^19.1.0",
|
|
97
97
|
"react": "^19.1.0",
|
|
@@ -9,6 +9,7 @@ const Input = React.forwardRef<TextInput, InputProps>(({
|
|
|
9
9
|
onChangeText,
|
|
10
10
|
onFocus,
|
|
11
11
|
onBlur,
|
|
12
|
+
onPress,
|
|
12
13
|
placeholder,
|
|
13
14
|
disabled = false,
|
|
14
15
|
inputType = 'text',
|
|
@@ -50,6 +51,12 @@ const Input = React.forwardRef<TextInput, InputProps>(({
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
|
|
54
|
+
const handlePress = () => {
|
|
55
|
+
if (onPress) {
|
|
56
|
+
onPress();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
const handleBlur = () => {
|
|
54
61
|
setIsFocused(false);
|
|
55
62
|
if (onBlur) {
|
|
@@ -121,6 +128,7 @@ const Input = React.forwardRef<TextInput, InputProps>(({
|
|
|
121
128
|
|
|
122
129
|
{/* Input */}
|
|
123
130
|
<TextInput
|
|
131
|
+
onPress={handlePress}
|
|
124
132
|
ref={ref}
|
|
125
133
|
value={value}
|
|
126
134
|
onChangeText={onChangeText}
|
package/src/Input/Input.web.tsx
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { isValidElement, useState } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
|
-
import { InputProps } from './types';
|
|
4
|
-
import { inputStyles } from './Input.styles';
|
|
5
3
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
|
-
import {
|
|
4
|
+
import { isIconName, resolveIconPath } from '../Icon/icon-resolver';
|
|
7
5
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
6
|
+
import { inputStyles } from './Input.styles';
|
|
7
|
+
import { InputProps } from './types';
|
|
8
8
|
|
|
9
9
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(({
|
|
10
10
|
value,
|
|
11
11
|
onChangeText,
|
|
12
12
|
onFocus,
|
|
13
13
|
onBlur,
|
|
14
|
+
onPress,
|
|
14
15
|
placeholder,
|
|
15
16
|
disabled = false,
|
|
16
17
|
inputType = 'text',
|
|
@@ -58,7 +59,18 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({
|
|
|
58
59
|
}
|
|
59
60
|
};
|
|
60
61
|
|
|
61
|
-
const
|
|
62
|
+
const handlePress = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
63
|
+
// For web compatibility, we can trigger onFocus when pressed
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
if (onPress) {
|
|
67
|
+
onPress();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
e.stopPropagation();
|
|
62
74
|
setIsFocused(true);
|
|
63
75
|
if (onFocus) {
|
|
64
76
|
onFocus();
|
|
@@ -97,6 +109,12 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({
|
|
|
97
109
|
// Get input props
|
|
98
110
|
const inputWebProps = getWebProps([inputStyles.input]);
|
|
99
111
|
|
|
112
|
+
const handleContainerPress = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
e.stopPropagation();
|
|
115
|
+
inputWebProps.ref?.current?.focus();
|
|
116
|
+
}
|
|
117
|
+
|
|
100
118
|
// Merge the forwarded ref with unistyles ref for the input
|
|
101
119
|
const mergedInputRef = useMergeRefs(ref, inputWebProps.ref);
|
|
102
120
|
|
|
@@ -154,7 +172,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({
|
|
|
154
172
|
};
|
|
155
173
|
|
|
156
174
|
return (
|
|
157
|
-
<div {...containerProps} data-testid={testID}>
|
|
175
|
+
<div onClick={handleContainerPress} {...containerProps} data-testid={testID}>
|
|
158
176
|
{/* Left Icon */}
|
|
159
177
|
{leftIcon && (
|
|
160
178
|
<span {...leftIconContainerProps}>
|
|
@@ -168,6 +186,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({
|
|
|
168
186
|
ref={mergedInputRef}
|
|
169
187
|
type={getInputType()}
|
|
170
188
|
value={value}
|
|
189
|
+
onClick={handlePress}
|
|
171
190
|
onChange={handleChange}
|
|
172
191
|
onFocus={handleFocus}
|
|
173
192
|
onBlur={handleBlur}
|
package/src/Input/types.ts
CHANGED
|
@@ -113,7 +113,8 @@ function buildDynamicTriggerStyles(theme: Theme) {
|
|
|
113
113
|
true: {
|
|
114
114
|
borderColor: theme.intents.primary.primary,
|
|
115
115
|
_web: {
|
|
116
|
-
border: `
|
|
116
|
+
border: `1px solid ${theme.intents.primary.primary}`,
|
|
117
|
+
boxShadow: `0 0 0 2px ${theme.intents.primary.primary}20`,
|
|
117
118
|
outline: 'none',
|
|
118
119
|
},
|
|
119
120
|
},
|
|
@@ -137,7 +138,6 @@ export const selectStyles = StyleSheet.create((theme: Theme) => {
|
|
|
137
138
|
return {
|
|
138
139
|
container: {
|
|
139
140
|
position: 'relative',
|
|
140
|
-
backgroundColor: theme.colors.surface.primary,
|
|
141
141
|
},
|
|
142
142
|
label: {
|
|
143
143
|
fontSize: 14,
|
|
@@ -256,35 +256,28 @@ export const selectStyles = StyleSheet.create((theme: Theme) => {
|
|
|
256
256
|
flexDirection: 'row',
|
|
257
257
|
alignItems: 'center',
|
|
258
258
|
minHeight: 36,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
false: {},
|
|
259
|
+
_web: {
|
|
260
|
+
display: 'flex',
|
|
261
|
+
cursor: 'pointer',
|
|
262
|
+
_hover: {
|
|
263
|
+
backgroundColor: theme.colors.surface.secondary,
|
|
265
264
|
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
opacity: 0.5,
|
|
269
|
-
_web: {
|
|
270
|
-
cursor: 'not-allowed',
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
false: {
|
|
274
|
-
_web: {
|
|
275
|
-
cursor: 'pointer',
|
|
276
|
-
_hover: {
|
|
277
|
-
backgroundColor: theme.colors.surface.secondary,
|
|
278
|
-
},
|
|
279
|
-
_active: {
|
|
280
|
-
opacity: 0.8,
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
},
|
|
265
|
+
_active: {
|
|
266
|
+
opacity: 0.8,
|
|
284
267
|
},
|
|
285
268
|
},
|
|
269
|
+
},
|
|
270
|
+
optionFocused: {
|
|
271
|
+
backgroundColor: theme.interaction.focusedBackground,
|
|
286
272
|
_web: {
|
|
287
|
-
|
|
273
|
+
outline: `1px solid ${theme.interaction.focusBorder}`,
|
|
274
|
+
outlineOffset: -1,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
optionDisabled: {
|
|
278
|
+
opacity: theme.interaction.opacity.disabled,
|
|
279
|
+
_web: {
|
|
280
|
+
cursor: 'not-allowed',
|
|
288
281
|
},
|
|
289
282
|
},
|
|
290
283
|
optionContent: {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
|
2
2
|
// @ts-ignore - web-specific import
|
|
3
3
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
4
|
-
import { SelectProps, SelectOption } from './types';
|
|
5
|
-
import { selectStyles } from './Select.styles';
|
|
6
4
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
7
5
|
import { resolveIconPath } from '../Icon/icon-resolver';
|
|
8
|
-
import { PositionedPortal } from '../internal/PositionedPortal';
|
|
9
6
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
7
|
+
import { PositionedPortal } from '../internal/PositionedPortal';
|
|
8
|
+
import { selectStyles } from './Select.styles';
|
|
9
|
+
import { SelectOption, SelectProps } from './types';
|
|
10
10
|
|
|
11
11
|
const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
12
12
|
options,
|
|
@@ -45,6 +45,13 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
45
45
|
})
|
|
46
46
|
: options;
|
|
47
47
|
|
|
48
|
+
// Get the index of the currently selected option
|
|
49
|
+
const getSelectedIndex = () => {
|
|
50
|
+
if (!value) return 0;
|
|
51
|
+
const index = filteredOptions.findIndex(option => option.value === value);
|
|
52
|
+
return index >= 0 ? index : 0;
|
|
53
|
+
};
|
|
54
|
+
|
|
48
55
|
// Apply styles with variants
|
|
49
56
|
selectStyles.useVariants({
|
|
50
57
|
type,
|
|
@@ -54,10 +61,17 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
54
61
|
focused: isOpen,
|
|
55
62
|
});
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (!isOpen)
|
|
64
|
+
// Handle keyboard navigation on the trigger button
|
|
65
|
+
const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
|
|
66
|
+
// Handle opening with arrow keys when closed
|
|
67
|
+
if (!isOpen) {
|
|
68
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Enter' || event.key === ' ') {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
setIsOpen(true);
|
|
71
|
+
setFocusedIndex(getSelectedIndex());
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
61
75
|
|
|
62
76
|
switch (event.key) {
|
|
63
77
|
case 'ArrowDown':
|
|
@@ -72,7 +86,34 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
72
86
|
prev > 0 ? prev - 1 : filteredOptions.length - 1
|
|
73
87
|
);
|
|
74
88
|
break;
|
|
89
|
+
case 'Tab':
|
|
90
|
+
if (event.shiftKey) {
|
|
91
|
+
// Shift+Tab: go to previous option or exit
|
|
92
|
+
if (focusedIndex <= 0) {
|
|
93
|
+
setIsOpen(false);
|
|
94
|
+
} else {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
setFocusedIndex(prev => prev - 1);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// Tab: go to next option or exit
|
|
100
|
+
if (focusedIndex >= filteredOptions.length - 1) {
|
|
101
|
+
setIsOpen(false);
|
|
102
|
+
} else {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
setFocusedIndex(prev => prev < 0 ? 0 : prev + 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
75
108
|
case 'Enter':
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
111
|
+
const option = filteredOptions[focusedIndex];
|
|
112
|
+
if (!option.disabled) {
|
|
113
|
+
handleOptionSelect(option);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
76
117
|
case ' ':
|
|
77
118
|
event.preventDefault();
|
|
78
119
|
if (focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
|
|
@@ -82,36 +123,39 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
82
123
|
}
|
|
83
124
|
}
|
|
84
125
|
break;
|
|
126
|
+
case 'Escape':
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
setIsOpen(false);
|
|
129
|
+
break;
|
|
85
130
|
}
|
|
86
131
|
};
|
|
87
132
|
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
if (!isOpen) return;
|
|
90
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
91
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
92
|
-
}, [isOpen, focusedIndex, filteredOptions]);
|
|
93
|
-
|
|
94
133
|
// Focus search input when dropdown opens
|
|
95
134
|
useEffect(() => {
|
|
96
135
|
if (isOpen && searchable && searchInputRef.current) {
|
|
97
|
-
// Delay to ensure dropdown is positioned
|
|
98
136
|
setTimeout(() => {
|
|
99
137
|
searchInputRef.current?.focus();
|
|
100
138
|
}, 50);
|
|
101
139
|
}
|
|
102
140
|
}, [isOpen, searchable]);
|
|
103
141
|
|
|
104
|
-
const handleTriggerClick = () => {
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
const handleTriggerClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
e.stopPropagation();
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const handleTriggerFocus = () => {
|
|
148
|
+
if (!disabled && !isOpen) {
|
|
149
|
+
setIsOpen(true);
|
|
107
150
|
setSearchTerm('');
|
|
108
|
-
|
|
151
|
+
// Focus on selected option, or first option if none selected
|
|
152
|
+
setFocusedIndex(getSelectedIndex());
|
|
109
153
|
}
|
|
110
154
|
};
|
|
111
155
|
|
|
112
156
|
const handleOptionSelect = (option: SelectOption) => {
|
|
113
157
|
if (!option.disabled) {
|
|
114
|
-
onValueChange(option.value);
|
|
158
|
+
onValueChange?.(option.value);
|
|
115
159
|
setIsOpen(false);
|
|
116
160
|
setSearchTerm('');
|
|
117
161
|
triggerRef.current?.focus();
|
|
@@ -151,6 +195,8 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
151
195
|
<button
|
|
152
196
|
{...triggerWebProps}
|
|
153
197
|
onClick={handleTriggerClick}
|
|
198
|
+
onFocus={handleTriggerFocus}
|
|
199
|
+
onKeyDown={handleKeyDown}
|
|
154
200
|
disabled={disabled}
|
|
155
201
|
aria-label={accessibilityLabel || label}
|
|
156
202
|
aria-expanded={isOpen}
|
|
@@ -196,7 +242,6 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
196
242
|
{...getWebProps([selectStyles.dropdown])}
|
|
197
243
|
style={{
|
|
198
244
|
maxHeight: maxHeight,
|
|
199
|
-
// Override positioning since PositionedPortal handles it
|
|
200
245
|
position: 'relative',
|
|
201
246
|
top: 'auto',
|
|
202
247
|
left: 'auto',
|
|
@@ -219,16 +264,20 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
219
264
|
|
|
220
265
|
<div {...getWebProps([selectStyles.optionsList])}>
|
|
221
266
|
{filteredOptions.map((option, index) => {
|
|
222
|
-
const
|
|
267
|
+
const isFocused = index === focusedIndex;
|
|
223
268
|
|
|
224
269
|
return (
|
|
225
270
|
<div
|
|
226
271
|
key={option.value}
|
|
227
272
|
onClick={() => handleOptionSelect(option)}
|
|
228
273
|
role="option"
|
|
229
|
-
aria-selected={
|
|
274
|
+
aria-selected={option.value === value}
|
|
230
275
|
onMouseEnter={() => setFocusedIndex(index)}
|
|
231
|
-
{...getWebProps([
|
|
276
|
+
{...getWebProps([
|
|
277
|
+
selectStyles.option,
|
|
278
|
+
isFocused && selectStyles.optionFocused,
|
|
279
|
+
option.disabled && selectStyles.optionDisabled,
|
|
280
|
+
])}
|
|
232
281
|
>
|
|
233
282
|
<div {...getWebProps([selectStyles.optionContent])}>
|
|
234
283
|
{option.icon && (
|
|
@@ -273,4 +322,4 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
273
322
|
|
|
274
323
|
Select.displayName = 'Select';
|
|
275
324
|
|
|
276
|
-
export default Select;
|
|
325
|
+
export default Select;
|
package/src/index.native.ts
CHANGED
|
@@ -134,4 +134,7 @@ export type { SkeletonProps, SkeletonGroupProps, SkeletonShape, SkeletonAnimatio
|
|
|
134
134
|
export type { ChipProps, ChipSize, ChipIntent } from './Chip/types';
|
|
135
135
|
export type { BreadcrumbProps, BreadcrumbItem } from './Breadcrumb/types';
|
|
136
136
|
|
|
137
|
+
// Event utilities
|
|
138
|
+
export * from './utils/events';
|
|
139
|
+
|
|
137
140
|
export type { AppTheme } from '@idealyst/theme';
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native-specific event wrappers
|
|
3
|
+
*
|
|
4
|
+
* Converts React Native events to standardized cross-platform events.
|
|
5
|
+
* Note: preventDefault() and stopPropagation() are no-ops on native since
|
|
6
|
+
* React Native's event system doesn't have these concepts in the same way.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
GestureResponderEvent,
|
|
11
|
+
NativeSyntheticEvent,
|
|
12
|
+
NativeScrollEvent,
|
|
13
|
+
TextInputFocusEventData,
|
|
14
|
+
TextInputChangeEventData,
|
|
15
|
+
} from 'react-native';
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
PressEvent,
|
|
19
|
+
FocusEvent,
|
|
20
|
+
ChangeEvent,
|
|
21
|
+
TextChangeEvent,
|
|
22
|
+
ToggleEvent,
|
|
23
|
+
KeyboardEvent,
|
|
24
|
+
ScrollEvent,
|
|
25
|
+
SubmitEvent,
|
|
26
|
+
} from './types';
|
|
27
|
+
|
|
28
|
+
// No-op functions for native (these concepts don't apply)
|
|
29
|
+
const noop = () => {};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Wraps a React Native GestureResponderEvent into a standardized PressEvent
|
|
33
|
+
*/
|
|
34
|
+
export function createPressEvent(
|
|
35
|
+
event: GestureResponderEvent,
|
|
36
|
+
type: PressEvent['type'] = 'press'
|
|
37
|
+
): PressEvent {
|
|
38
|
+
return {
|
|
39
|
+
nativeEvent: event.nativeEvent,
|
|
40
|
+
timestamp: event.nativeEvent.timestamp,
|
|
41
|
+
defaultPrevented: false,
|
|
42
|
+
propagationStopped: false,
|
|
43
|
+
type,
|
|
44
|
+
preventDefault: noop,
|
|
45
|
+
stopPropagation: noop,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wraps a React Native focus event into a standardized FocusEvent
|
|
51
|
+
*/
|
|
52
|
+
export function createFocusEvent(
|
|
53
|
+
event: NativeSyntheticEvent<TextInputFocusEventData>,
|
|
54
|
+
type: FocusEvent['type']
|
|
55
|
+
): FocusEvent {
|
|
56
|
+
return {
|
|
57
|
+
nativeEvent: event.nativeEvent,
|
|
58
|
+
timestamp: Date.now(),
|
|
59
|
+
defaultPrevented: false,
|
|
60
|
+
propagationStopped: false,
|
|
61
|
+
type,
|
|
62
|
+
preventDefault: noop,
|
|
63
|
+
stopPropagation: noop,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates a standardized FocusEvent without a native event (for simple focus tracking)
|
|
69
|
+
*/
|
|
70
|
+
export function createSimpleFocusEvent(type: FocusEvent['type']): FocusEvent {
|
|
71
|
+
return {
|
|
72
|
+
nativeEvent: null,
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
defaultPrevented: false,
|
|
75
|
+
propagationStopped: false,
|
|
76
|
+
type,
|
|
77
|
+
preventDefault: noop,
|
|
78
|
+
stopPropagation: noop,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Wraps a value change into a standardized ChangeEvent
|
|
84
|
+
*/
|
|
85
|
+
export function createChangeEvent<V = string>(value: V): ChangeEvent<V> {
|
|
86
|
+
return {
|
|
87
|
+
nativeEvent: null,
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
defaultPrevented: false,
|
|
90
|
+
propagationStopped: false,
|
|
91
|
+
type: 'change',
|
|
92
|
+
value,
|
|
93
|
+
preventDefault: noop,
|
|
94
|
+
stopPropagation: noop,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Wraps a React Native text change event into a standardized TextChangeEvent
|
|
100
|
+
*/
|
|
101
|
+
export function createTextChangeEvent(
|
|
102
|
+
event: NativeSyntheticEvent<TextInputChangeEventData>
|
|
103
|
+
): TextChangeEvent {
|
|
104
|
+
const text = event.nativeEvent.text;
|
|
105
|
+
return {
|
|
106
|
+
nativeEvent: event.nativeEvent,
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
defaultPrevented: false,
|
|
109
|
+
propagationStopped: false,
|
|
110
|
+
type: 'change',
|
|
111
|
+
value: text,
|
|
112
|
+
text,
|
|
113
|
+
preventDefault: noop,
|
|
114
|
+
stopPropagation: noop,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Creates a TextChangeEvent from a simple text value (for onChangeText)
|
|
120
|
+
*/
|
|
121
|
+
export function createSimpleTextChangeEvent(text: string): TextChangeEvent {
|
|
122
|
+
return {
|
|
123
|
+
nativeEvent: null,
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
defaultPrevented: false,
|
|
126
|
+
propagationStopped: false,
|
|
127
|
+
type: 'change',
|
|
128
|
+
value: text,
|
|
129
|
+
text,
|
|
130
|
+
preventDefault: noop,
|
|
131
|
+
stopPropagation: noop,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates a standardized ToggleEvent for switch/checkbox changes
|
|
137
|
+
*/
|
|
138
|
+
export function createToggleEvent(checked: boolean): ToggleEvent {
|
|
139
|
+
return {
|
|
140
|
+
nativeEvent: null,
|
|
141
|
+
timestamp: Date.now(),
|
|
142
|
+
defaultPrevented: false,
|
|
143
|
+
propagationStopped: false,
|
|
144
|
+
type: 'change',
|
|
145
|
+
value: checked,
|
|
146
|
+
checked,
|
|
147
|
+
preventDefault: noop,
|
|
148
|
+
stopPropagation: noop,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Creates a standardized KeyboardEvent
|
|
154
|
+
* Note: React Native's keyboard events are more limited than web
|
|
155
|
+
*/
|
|
156
|
+
export function createKeyboardEvent(
|
|
157
|
+
key: string,
|
|
158
|
+
type: KeyboardEvent['type']
|
|
159
|
+
): KeyboardEvent {
|
|
160
|
+
return {
|
|
161
|
+
nativeEvent: null,
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
defaultPrevented: false,
|
|
164
|
+
propagationStopped: false,
|
|
165
|
+
type,
|
|
166
|
+
key,
|
|
167
|
+
keyCode: 0,
|
|
168
|
+
altKey: false,
|
|
169
|
+
ctrlKey: false,
|
|
170
|
+
metaKey: false,
|
|
171
|
+
shiftKey: false,
|
|
172
|
+
preventDefault: noop,
|
|
173
|
+
stopPropagation: noop,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Wraps a React Native scroll event into a standardized ScrollEvent
|
|
179
|
+
*/
|
|
180
|
+
export function createScrollEvent(
|
|
181
|
+
event: NativeSyntheticEvent<NativeScrollEvent>,
|
|
182
|
+
type: ScrollEvent['type'] = 'scroll'
|
|
183
|
+
): ScrollEvent {
|
|
184
|
+
const { contentOffset, contentSize, layoutMeasurement } = event.nativeEvent;
|
|
185
|
+
return {
|
|
186
|
+
nativeEvent: event.nativeEvent,
|
|
187
|
+
timestamp: Date.now(),
|
|
188
|
+
defaultPrevented: false,
|
|
189
|
+
propagationStopped: false,
|
|
190
|
+
type,
|
|
191
|
+
contentOffset,
|
|
192
|
+
contentSize,
|
|
193
|
+
layoutMeasurement,
|
|
194
|
+
preventDefault: noop,
|
|
195
|
+
stopPropagation: noop,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Creates a standardized SubmitEvent
|
|
201
|
+
*/
|
|
202
|
+
export function createSubmitEvent(): SubmitEvent {
|
|
203
|
+
return {
|
|
204
|
+
nativeEvent: null,
|
|
205
|
+
timestamp: Date.now(),
|
|
206
|
+
defaultPrevented: false,
|
|
207
|
+
propagationStopped: false,
|
|
208
|
+
type: 'submit',
|
|
209
|
+
preventDefault: noop,
|
|
210
|
+
stopPropagation: noop,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-specific event wrappers
|
|
3
|
+
*
|
|
4
|
+
* Converts React web events to standardized cross-platform events
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
PressEvent,
|
|
9
|
+
FocusEvent,
|
|
10
|
+
ChangeEvent,
|
|
11
|
+
TextChangeEvent,
|
|
12
|
+
ToggleEvent,
|
|
13
|
+
KeyboardEvent,
|
|
14
|
+
ScrollEvent,
|
|
15
|
+
SubmitEvent,
|
|
16
|
+
} from './types';
|
|
17
|
+
|
|
18
|
+
type ReactMouseEvent = React.MouseEvent<HTMLElement>;
|
|
19
|
+
type ReactFocusEvent = React.FocusEvent<HTMLElement>;
|
|
20
|
+
type ReactChangeEvent = React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>;
|
|
21
|
+
type ReactKeyboardEvent = React.KeyboardEvent<HTMLElement>;
|
|
22
|
+
type ReactFormEvent = React.FormEvent<HTMLFormElement>;
|
|
23
|
+
type ReactUIEvent = React.UIEvent<HTMLElement>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Wraps a React mouse/click event into a standardized PressEvent
|
|
27
|
+
*/
|
|
28
|
+
export function createPressEvent(
|
|
29
|
+
event: ReactMouseEvent,
|
|
30
|
+
type: PressEvent['type'] = 'press'
|
|
31
|
+
): PressEvent {
|
|
32
|
+
return {
|
|
33
|
+
nativeEvent: event.nativeEvent,
|
|
34
|
+
timestamp: event.timeStamp,
|
|
35
|
+
defaultPrevented: event.defaultPrevented,
|
|
36
|
+
propagationStopped: false,
|
|
37
|
+
type,
|
|
38
|
+
preventDefault: () => event.preventDefault(),
|
|
39
|
+
stopPropagation: () => event.stopPropagation(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Wraps a React focus event into a standardized FocusEvent
|
|
45
|
+
*/
|
|
46
|
+
export function createFocusEvent(
|
|
47
|
+
event: ReactFocusEvent,
|
|
48
|
+
type: FocusEvent['type']
|
|
49
|
+
): FocusEvent {
|
|
50
|
+
return {
|
|
51
|
+
nativeEvent: event.nativeEvent,
|
|
52
|
+
timestamp: event.timeStamp,
|
|
53
|
+
defaultPrevented: event.defaultPrevented,
|
|
54
|
+
propagationStopped: false,
|
|
55
|
+
type,
|
|
56
|
+
preventDefault: () => event.preventDefault(),
|
|
57
|
+
stopPropagation: () => event.stopPropagation(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Wraps a React change event into a standardized ChangeEvent
|
|
63
|
+
*/
|
|
64
|
+
export function createChangeEvent<V = string>(
|
|
65
|
+
event: ReactChangeEvent,
|
|
66
|
+
value: V
|
|
67
|
+
): ChangeEvent<V> {
|
|
68
|
+
return {
|
|
69
|
+
nativeEvent: event.nativeEvent,
|
|
70
|
+
timestamp: event.timeStamp,
|
|
71
|
+
defaultPrevented: event.defaultPrevented,
|
|
72
|
+
propagationStopped: false,
|
|
73
|
+
type: 'change',
|
|
74
|
+
value,
|
|
75
|
+
preventDefault: () => event.preventDefault(),
|
|
76
|
+
stopPropagation: () => event.stopPropagation(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Wraps a React change event for text inputs into a standardized TextChangeEvent
|
|
82
|
+
*/
|
|
83
|
+
export function createTextChangeEvent(event: ReactChangeEvent): TextChangeEvent {
|
|
84
|
+
const value = event.target.value;
|
|
85
|
+
return {
|
|
86
|
+
nativeEvent: event.nativeEvent,
|
|
87
|
+
timestamp: event.timeStamp,
|
|
88
|
+
defaultPrevented: event.defaultPrevented,
|
|
89
|
+
propagationStopped: false,
|
|
90
|
+
type: 'change',
|
|
91
|
+
value,
|
|
92
|
+
text: value,
|
|
93
|
+
preventDefault: () => event.preventDefault(),
|
|
94
|
+
stopPropagation: () => event.stopPropagation(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Wraps a React change event for checkboxes/switches into a standardized ToggleEvent
|
|
100
|
+
*/
|
|
101
|
+
export function createToggleEvent(event: ReactChangeEvent): ToggleEvent {
|
|
102
|
+
const checked = (event.target as HTMLInputElement).checked;
|
|
103
|
+
return {
|
|
104
|
+
nativeEvent: event.nativeEvent,
|
|
105
|
+
timestamp: event.timeStamp,
|
|
106
|
+
defaultPrevented: event.defaultPrevented,
|
|
107
|
+
propagationStopped: false,
|
|
108
|
+
type: 'change',
|
|
109
|
+
value: checked,
|
|
110
|
+
checked,
|
|
111
|
+
preventDefault: () => event.preventDefault(),
|
|
112
|
+
stopPropagation: () => event.stopPropagation(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Wraps a React keyboard event into a standardized KeyboardEvent
|
|
118
|
+
*/
|
|
119
|
+
export function createKeyboardEvent(
|
|
120
|
+
event: ReactKeyboardEvent,
|
|
121
|
+
type: KeyboardEvent['type']
|
|
122
|
+
): KeyboardEvent {
|
|
123
|
+
return {
|
|
124
|
+
nativeEvent: event.nativeEvent,
|
|
125
|
+
timestamp: event.timeStamp,
|
|
126
|
+
defaultPrevented: event.defaultPrevented,
|
|
127
|
+
propagationStopped: false,
|
|
128
|
+
type,
|
|
129
|
+
key: event.key,
|
|
130
|
+
keyCode: event.keyCode,
|
|
131
|
+
altKey: event.altKey,
|
|
132
|
+
ctrlKey: event.ctrlKey,
|
|
133
|
+
metaKey: event.metaKey,
|
|
134
|
+
shiftKey: event.shiftKey,
|
|
135
|
+
preventDefault: () => event.preventDefault(),
|
|
136
|
+
stopPropagation: () => event.stopPropagation(),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Wraps a React scroll event into a standardized ScrollEvent
|
|
142
|
+
*/
|
|
143
|
+
export function createScrollEvent(
|
|
144
|
+
event: ReactUIEvent,
|
|
145
|
+
type: ScrollEvent['type'] = 'scroll'
|
|
146
|
+
): ScrollEvent {
|
|
147
|
+
const target = event.target as HTMLElement;
|
|
148
|
+
return {
|
|
149
|
+
nativeEvent: event.nativeEvent,
|
|
150
|
+
timestamp: event.timeStamp,
|
|
151
|
+
defaultPrevented: event.defaultPrevented,
|
|
152
|
+
propagationStopped: false,
|
|
153
|
+
type,
|
|
154
|
+
contentOffset: {
|
|
155
|
+
x: target.scrollLeft,
|
|
156
|
+
y: target.scrollTop,
|
|
157
|
+
},
|
|
158
|
+
contentSize: {
|
|
159
|
+
width: target.scrollWidth,
|
|
160
|
+
height: target.scrollHeight,
|
|
161
|
+
},
|
|
162
|
+
layoutMeasurement: {
|
|
163
|
+
width: target.clientWidth,
|
|
164
|
+
height: target.clientHeight,
|
|
165
|
+
},
|
|
166
|
+
preventDefault: () => event.preventDefault(),
|
|
167
|
+
stopPropagation: () => event.stopPropagation(),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Wraps a React form submit event into a standardized SubmitEvent
|
|
173
|
+
*/
|
|
174
|
+
export function createSubmitEvent(event: ReactFormEvent): SubmitEvent {
|
|
175
|
+
return {
|
|
176
|
+
nativeEvent: event.nativeEvent,
|
|
177
|
+
timestamp: event.timeStamp,
|
|
178
|
+
defaultPrevented: event.defaultPrevented,
|
|
179
|
+
propagationStopped: false,
|
|
180
|
+
type: 'submit',
|
|
181
|
+
preventDefault: () => event.preventDefault(),
|
|
182
|
+
stopPropagation: () => event.stopPropagation(),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform event utilities
|
|
3
|
+
*
|
|
4
|
+
* This module provides standardized event types and creation functions
|
|
5
|
+
* that work consistently across web and native platforms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Re-export all types
|
|
9
|
+
export type {
|
|
10
|
+
SyntheticEvent,
|
|
11
|
+
PressEvent,
|
|
12
|
+
FocusEvent,
|
|
13
|
+
ChangeEvent,
|
|
14
|
+
TextChangeEvent,
|
|
15
|
+
SelectChangeEvent,
|
|
16
|
+
ToggleEvent,
|
|
17
|
+
KeyboardEvent,
|
|
18
|
+
ScrollEvent,
|
|
19
|
+
SubmitEvent,
|
|
20
|
+
PressEventHandler,
|
|
21
|
+
FocusEventHandler,
|
|
22
|
+
ChangeEventHandler,
|
|
23
|
+
TextChangeEventHandler,
|
|
24
|
+
SelectChangeEventHandler,
|
|
25
|
+
ToggleEventHandler,
|
|
26
|
+
KeyboardEventHandler,
|
|
27
|
+
ScrollEventHandler,
|
|
28
|
+
SubmitEventHandler,
|
|
29
|
+
} from './types';
|
|
30
|
+
|
|
31
|
+
// Re-export base utility
|
|
32
|
+
export { createBaseSyntheticEvent } from './types';
|
|
33
|
+
|
|
34
|
+
// Re-export platform-specific functions (native by default)
|
|
35
|
+
export {
|
|
36
|
+
createPressEvent,
|
|
37
|
+
createFocusEvent,
|
|
38
|
+
createSimpleFocusEvent,
|
|
39
|
+
createChangeEvent,
|
|
40
|
+
createTextChangeEvent,
|
|
41
|
+
createSimpleTextChangeEvent,
|
|
42
|
+
createToggleEvent,
|
|
43
|
+
createKeyboardEvent,
|
|
44
|
+
createScrollEvent,
|
|
45
|
+
createSubmitEvent,
|
|
46
|
+
} from './events.native';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform event utilities (Web)
|
|
3
|
+
*
|
|
4
|
+
* This module provides standardized event types and creation functions
|
|
5
|
+
* that work consistently across web and native platforms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Re-export all types
|
|
9
|
+
export type {
|
|
10
|
+
SyntheticEvent,
|
|
11
|
+
PressEvent,
|
|
12
|
+
FocusEvent,
|
|
13
|
+
ChangeEvent,
|
|
14
|
+
TextChangeEvent,
|
|
15
|
+
SelectChangeEvent,
|
|
16
|
+
ToggleEvent,
|
|
17
|
+
KeyboardEvent,
|
|
18
|
+
ScrollEvent,
|
|
19
|
+
SubmitEvent,
|
|
20
|
+
PressEventHandler,
|
|
21
|
+
FocusEventHandler,
|
|
22
|
+
ChangeEventHandler,
|
|
23
|
+
TextChangeEventHandler,
|
|
24
|
+
SelectChangeEventHandler,
|
|
25
|
+
ToggleEventHandler,
|
|
26
|
+
KeyboardEventHandler,
|
|
27
|
+
ScrollEventHandler,
|
|
28
|
+
SubmitEventHandler,
|
|
29
|
+
} from './types';
|
|
30
|
+
|
|
31
|
+
// Re-export base utility
|
|
32
|
+
export { createBaseSyntheticEvent } from './types';
|
|
33
|
+
|
|
34
|
+
// Re-export platform-specific functions (web)
|
|
35
|
+
export {
|
|
36
|
+
createPressEvent,
|
|
37
|
+
createFocusEvent,
|
|
38
|
+
createChangeEvent,
|
|
39
|
+
createTextChangeEvent,
|
|
40
|
+
createToggleEvent,
|
|
41
|
+
createKeyboardEvent,
|
|
42
|
+
createScrollEvent,
|
|
43
|
+
createSubmitEvent,
|
|
44
|
+
} from './events.web';
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized cross-platform event system
|
|
3
|
+
*
|
|
4
|
+
* Provides unified event interfaces that work consistently across web and native.
|
|
5
|
+
* On native, methods like preventDefault() and stopPropagation() are no-ops since
|
|
6
|
+
* React Native's event system doesn't have these concepts in the same way.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base synthetic event interface that normalizes web and native events
|
|
11
|
+
*/
|
|
12
|
+
export interface SyntheticEvent<T = unknown> {
|
|
13
|
+
/**
|
|
14
|
+
* The native event (platform-specific)
|
|
15
|
+
* - Web: React.SyntheticEvent
|
|
16
|
+
* - Native: GestureResponderEvent or NativeSyntheticEvent
|
|
17
|
+
*/
|
|
18
|
+
nativeEvent: T;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Prevents the default behavior of the event.
|
|
22
|
+
* On native, this is a no-op as there's no default behavior concept.
|
|
23
|
+
*/
|
|
24
|
+
preventDefault: () => void;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Stops the event from propagating to parent elements.
|
|
28
|
+
* On native, this is a no-op.
|
|
29
|
+
*/
|
|
30
|
+
stopPropagation: () => void;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether preventDefault() has been called
|
|
34
|
+
*/
|
|
35
|
+
defaultPrevented: boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether stopPropagation() has been called
|
|
39
|
+
*/
|
|
40
|
+
propagationStopped: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Timestamp of when the event occurred
|
|
44
|
+
*/
|
|
45
|
+
timestamp: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Press/Click event - for buttons, pressables, touchable elements
|
|
50
|
+
*/
|
|
51
|
+
export interface PressEvent extends SyntheticEvent {
|
|
52
|
+
/**
|
|
53
|
+
* The type of press event
|
|
54
|
+
*/
|
|
55
|
+
type: 'press' | 'pressIn' | 'pressOut' | 'longPress';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Focus event - for inputs and focusable elements
|
|
60
|
+
*/
|
|
61
|
+
export interface FocusEvent extends SyntheticEvent {
|
|
62
|
+
/**
|
|
63
|
+
* The type of focus event
|
|
64
|
+
*/
|
|
65
|
+
type: 'focus' | 'blur';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Change event - for inputs, selects, checkboxes, etc.
|
|
70
|
+
*/
|
|
71
|
+
export interface ChangeEvent<V = string> extends SyntheticEvent {
|
|
72
|
+
/**
|
|
73
|
+
* The new value after the change
|
|
74
|
+
*/
|
|
75
|
+
value: V;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The type of change event
|
|
79
|
+
*/
|
|
80
|
+
type: 'change';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Text change event - specifically for text inputs
|
|
85
|
+
*/
|
|
86
|
+
export interface TextChangeEvent extends ChangeEvent<string> {
|
|
87
|
+
/**
|
|
88
|
+
* The text value
|
|
89
|
+
*/
|
|
90
|
+
text: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Selection change event - for selects, dropdowns
|
|
95
|
+
*/
|
|
96
|
+
export interface SelectChangeEvent<V = string> extends ChangeEvent<V> {
|
|
97
|
+
/**
|
|
98
|
+
* The selected value(s)
|
|
99
|
+
*/
|
|
100
|
+
selected: V;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Toggle event - for switches, checkboxes
|
|
105
|
+
*/
|
|
106
|
+
export interface ToggleEvent extends ChangeEvent<boolean> {
|
|
107
|
+
/**
|
|
108
|
+
* Whether the toggle is now checked/on
|
|
109
|
+
*/
|
|
110
|
+
checked: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Keyboard event - for keyboard interactions
|
|
115
|
+
*/
|
|
116
|
+
export interface KeyboardEvent extends SyntheticEvent {
|
|
117
|
+
/**
|
|
118
|
+
* The key that was pressed
|
|
119
|
+
*/
|
|
120
|
+
key: string;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The key code
|
|
124
|
+
*/
|
|
125
|
+
keyCode: number;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Whether the alt/option key was pressed
|
|
129
|
+
*/
|
|
130
|
+
altKey: boolean;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Whether the control key was pressed
|
|
134
|
+
*/
|
|
135
|
+
ctrlKey: boolean;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Whether the meta/command key was pressed
|
|
139
|
+
*/
|
|
140
|
+
metaKey: boolean;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Whether the shift key was pressed
|
|
144
|
+
*/
|
|
145
|
+
shiftKey: boolean;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The type of keyboard event
|
|
149
|
+
*/
|
|
150
|
+
type: 'keyDown' | 'keyUp' | 'keyPress';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Scroll event - for scrollable containers
|
|
155
|
+
*/
|
|
156
|
+
export interface ScrollEvent extends SyntheticEvent {
|
|
157
|
+
/**
|
|
158
|
+
* Current scroll position
|
|
159
|
+
*/
|
|
160
|
+
contentOffset: {
|
|
161
|
+
x: number;
|
|
162
|
+
y: number;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Size of the scrollable content
|
|
167
|
+
*/
|
|
168
|
+
contentSize: {
|
|
169
|
+
width: number;
|
|
170
|
+
height: number;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Size of the visible container
|
|
175
|
+
*/
|
|
176
|
+
layoutMeasurement: {
|
|
177
|
+
width: number;
|
|
178
|
+
height: number;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The type of scroll event
|
|
183
|
+
*/
|
|
184
|
+
type: 'scroll' | 'scrollBegin' | 'scrollEnd';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Submit event - for forms
|
|
189
|
+
*/
|
|
190
|
+
export interface SubmitEvent extends SyntheticEvent {
|
|
191
|
+
/**
|
|
192
|
+
* The type of submit event
|
|
193
|
+
*/
|
|
194
|
+
type: 'submit';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Event handler type aliases for convenience
|
|
198
|
+
export type PressEventHandler = (event: PressEvent) => void;
|
|
199
|
+
export type FocusEventHandler = (event: FocusEvent) => void;
|
|
200
|
+
export type ChangeEventHandler<V = string> = (event: ChangeEvent<V>) => void;
|
|
201
|
+
export type TextChangeEventHandler = (event: TextChangeEvent) => void;
|
|
202
|
+
export type SelectChangeEventHandler<V = string> = (event: SelectChangeEvent<V>) => void;
|
|
203
|
+
export type ToggleEventHandler = (event: ToggleEvent) => void;
|
|
204
|
+
export type KeyboardEventHandler = (event: KeyboardEvent) => void;
|
|
205
|
+
export type ScrollEventHandler = (event: ScrollEvent) => void;
|
|
206
|
+
export type SubmitEventHandler = (event: SubmitEvent) => void;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Creates a base synthetic event object with default implementations
|
|
210
|
+
*/
|
|
211
|
+
export function createBaseSyntheticEvent<T>(nativeEvent: T): SyntheticEvent<T> {
|
|
212
|
+
let defaultPrevented = false;
|
|
213
|
+
let propagationStopped = false;
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
nativeEvent,
|
|
217
|
+
timestamp: Date.now(),
|
|
218
|
+
get defaultPrevented() {
|
|
219
|
+
return defaultPrevented;
|
|
220
|
+
},
|
|
221
|
+
get propagationStopped() {
|
|
222
|
+
return propagationStopped;
|
|
223
|
+
},
|
|
224
|
+
preventDefault() {
|
|
225
|
+
defaultPrevented = true;
|
|
226
|
+
},
|
|
227
|
+
stopPropagation() {
|
|
228
|
+
propagationStopped = true;
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|