@webority-technologies/mobile 0.0.21 → 0.0.23

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.
Files changed (187) hide show
  1. package/lib/commonjs/components/Accordion/Accordion.js +4 -2
  2. package/lib/commonjs/components/Avatar/Avatar.js +4 -2
  3. package/lib/commonjs/components/Badge/Badge.js +5 -5
  4. package/lib/commonjs/components/Banner/Banner.js +8 -4
  5. package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +6 -4
  6. package/lib/commonjs/components/BottomSheet/BottomSheet.js +69 -13
  7. package/lib/commonjs/components/BottomSheet/index.js +6 -0
  8. package/lib/commonjs/components/Box/Box.js +162 -0
  9. package/lib/commonjs/components/Box/index.js +37 -0
  10. package/lib/commonjs/components/Button/Button.js +7 -7
  11. package/lib/commonjs/components/Carousel/Carousel.js +4 -2
  12. package/lib/commonjs/components/Checkbox/Checkbox.js +14 -5
  13. package/lib/commonjs/components/DatePicker/DatePicker.js +9 -7
  14. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +5 -2
  15. package/lib/commonjs/components/Dialog/Dialog.js +2 -2
  16. package/lib/commonjs/components/FieldBase/FieldBase.js +8 -4
  17. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +13 -5
  18. package/lib/commonjs/components/FormField/FormField.js +61 -25
  19. package/lib/commonjs/components/Input/Input.js +41 -29
  20. package/lib/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +102 -0
  21. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +13 -0
  22. package/lib/commonjs/components/KeyboardToolbar/KeyboardToolbar.js +130 -0
  23. package/lib/commonjs/components/KeyboardToolbar/index.js +13 -0
  24. package/lib/commonjs/components/Modal/Modal.js +17 -6
  25. package/lib/commonjs/components/NumberInput/NumberInput.js +35 -28
  26. package/lib/commonjs/components/OTPInput/OTPInput.js +33 -18
  27. package/lib/commonjs/components/Radio/Radio.js +7 -5
  28. package/lib/commonjs/components/Radio/RadioGroup.js +10 -3
  29. package/lib/commonjs/components/SearchBar/SearchBar.js +4 -2
  30. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +20 -10
  31. package/lib/commonjs/components/Select/Select.js +33 -32
  32. package/lib/commonjs/components/Skeleton/SkeletonContent.js +5 -2
  33. package/lib/commonjs/components/Slider/Slider.js +42 -26
  34. package/lib/commonjs/components/Spinner/Spinner.js +5 -5
  35. package/lib/commonjs/components/Switch/Switch.js +29 -16
  36. package/lib/commonjs/components/Tabs/Tabs.js +4 -2
  37. package/lib/commonjs/components/Text/Text.js +142 -0
  38. package/lib/commonjs/components/Text/index.js +13 -0
  39. package/lib/commonjs/components/TimePicker/TimePicker.js +10 -7
  40. package/lib/commonjs/components/Toast/Toast.js +22 -10
  41. package/lib/commonjs/components/Tooltip/Tooltip.js +6 -2
  42. package/lib/commonjs/components/index.js +141 -89
  43. package/lib/commonjs/form/FormContext.js +40 -0
  44. package/lib/commonjs/form/index.js +68 -0
  45. package/lib/commonjs/form/path.js +79 -0
  46. package/lib/commonjs/form/rules.js +67 -0
  47. package/lib/commonjs/form/types.js +2 -0
  48. package/lib/commonjs/form/useField.js +54 -0
  49. package/lib/commonjs/form/useForm.js +316 -0
  50. package/lib/commonjs/hooks/index.js +14 -0
  51. package/lib/commonjs/hooks/useControllableState.js +30 -0
  52. package/lib/commonjs/hooks/useReducedMotion.js +31 -0
  53. package/lib/commonjs/index.js +96 -11
  54. package/lib/commonjs/theme/ThemeContext.js +30 -2
  55. package/lib/commonjs/theme/tokens.js +12 -0
  56. package/lib/module/components/Accordion/Accordion.js +4 -2
  57. package/lib/module/components/Avatar/Avatar.js +4 -2
  58. package/lib/module/components/Badge/Badge.js +5 -5
  59. package/lib/module/components/Banner/Banner.js +8 -4
  60. package/lib/module/components/BottomNavigation/BottomNavigation.js +6 -4
  61. package/lib/module/components/BottomSheet/BottomSheet.js +68 -13
  62. package/lib/module/components/BottomSheet/index.js +1 -1
  63. package/lib/module/components/Box/Box.js +156 -0
  64. package/lib/module/components/Box/index.js +4 -0
  65. package/lib/module/components/Button/Button.js +7 -7
  66. package/lib/module/components/Carousel/Carousel.js +4 -2
  67. package/lib/module/components/Checkbox/Checkbox.js +14 -5
  68. package/lib/module/components/DatePicker/DatePicker.js +9 -7
  69. package/lib/module/components/DateRangePicker/DateRangePicker.js +5 -2
  70. package/lib/module/components/Dialog/Dialog.js +2 -2
  71. package/lib/module/components/FieldBase/FieldBase.js +8 -4
  72. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +13 -5
  73. package/lib/module/components/FormField/FormField.js +62 -26
  74. package/lib/module/components/Input/Input.js +41 -29
  75. package/lib/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +98 -0
  76. package/lib/module/components/KeyboardAwareScrollView/index.js +4 -0
  77. package/lib/module/components/KeyboardToolbar/KeyboardToolbar.js +125 -0
  78. package/lib/module/components/KeyboardToolbar/index.js +4 -0
  79. package/lib/module/components/Modal/Modal.js +17 -6
  80. package/lib/module/components/NumberInput/NumberInput.js +30 -23
  81. package/lib/module/components/OTPInput/OTPInput.js +30 -15
  82. package/lib/module/components/Radio/Radio.js +7 -5
  83. package/lib/module/components/Radio/RadioGroup.js +10 -3
  84. package/lib/module/components/SearchBar/SearchBar.js +4 -2
  85. package/lib/module/components/SegmentedControl/SegmentedControl.js +20 -10
  86. package/lib/module/components/Select/Select.js +33 -32
  87. package/lib/module/components/Skeleton/SkeletonContent.js +5 -2
  88. package/lib/module/components/Slider/Slider.js +42 -26
  89. package/lib/module/components/Spinner/Spinner.js +5 -5
  90. package/lib/module/components/Switch/Switch.js +29 -16
  91. package/lib/module/components/Tabs/Tabs.js +4 -2
  92. package/lib/module/components/Text/Text.js +138 -0
  93. package/lib/module/components/Text/index.js +4 -0
  94. package/lib/module/components/TimePicker/TimePicker.js +10 -7
  95. package/lib/module/components/Toast/Toast.js +22 -10
  96. package/lib/module/components/Tooltip/Tooltip.js +6 -2
  97. package/lib/module/components/index.js +5 -1
  98. package/lib/module/form/FormContext.js +32 -0
  99. package/lib/module/form/index.js +12 -0
  100. package/lib/module/form/path.js +72 -0
  101. package/lib/module/form/rules.js +52 -0
  102. package/lib/module/form/types.js +2 -0
  103. package/lib/module/form/useField.js +49 -0
  104. package/lib/module/form/useForm.js +312 -0
  105. package/lib/module/hooks/index.js +2 -0
  106. package/lib/module/hooks/useControllableState.js +26 -0
  107. package/lib/module/hooks/useReducedMotion.js +27 -0
  108. package/lib/module/index.js +3 -1
  109. package/lib/module/theme/ThemeContext.js +30 -2
  110. package/lib/module/theme/tokens.js +12 -0
  111. package/lib/typescript/commonjs/components/BottomNavigation/BottomNavigation.d.ts +1 -1
  112. package/lib/typescript/commonjs/components/BottomSheet/BottomSheet.d.ts +41 -0
  113. package/lib/typescript/commonjs/components/BottomSheet/index.d.ts +2 -2
  114. package/lib/typescript/commonjs/components/Box/Box.d.ts +60 -0
  115. package/lib/typescript/commonjs/components/Box/index.d.ts +3 -0
  116. package/lib/typescript/commonjs/components/Button/Button.d.ts +1 -1
  117. package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +3 -2
  118. package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +3 -3
  119. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +2 -2
  120. package/lib/typescript/commonjs/components/FormField/FormField.d.ts +13 -2
  121. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
  122. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/index.d.ts +3 -0
  123. package/lib/typescript/commonjs/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
  124. package/lib/typescript/commonjs/components/KeyboardToolbar/index.d.ts +3 -0
  125. package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -2
  126. package/lib/typescript/commonjs/components/OTPInput/OTPInput.d.ts +3 -2
  127. package/lib/typescript/commonjs/components/Radio/Radio.d.ts +2 -2
  128. package/lib/typescript/commonjs/components/Radio/RadioGroup.d.ts +3 -2
  129. package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -2
  130. package/lib/typescript/commonjs/components/Slider/Slider.d.ts +6 -4
  131. package/lib/typescript/commonjs/components/Spinner/Spinner.d.ts +1 -1
  132. package/lib/typescript/commonjs/components/Switch/Switch.d.ts +3 -2
  133. package/lib/typescript/commonjs/components/Text/Text.d.ts +25 -0
  134. package/lib/typescript/commonjs/components/Text/index.d.ts +3 -0
  135. package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +3 -3
  136. package/lib/typescript/commonjs/components/index.d.ts +10 -2
  137. package/lib/typescript/commonjs/form/FormContext.d.ts +17 -0
  138. package/lib/typescript/commonjs/form/index.d.ts +9 -0
  139. package/lib/typescript/commonjs/form/path.d.ts +10 -0
  140. package/lib/typescript/commonjs/form/rules.d.ts +31 -0
  141. package/lib/typescript/commonjs/form/types.d.ts +94 -0
  142. package/lib/typescript/commonjs/form/useField.d.ts +27 -0
  143. package/lib/typescript/commonjs/form/useForm.d.ts +10 -0
  144. package/lib/typescript/commonjs/hooks/index.d.ts +3 -0
  145. package/lib/typescript/commonjs/hooks/useControllableState.d.ts +17 -0
  146. package/lib/typescript/commonjs/hooks/useReducedMotion.d.ts +8 -0
  147. package/lib/typescript/commonjs/index.d.ts +4 -2
  148. package/lib/typescript/commonjs/theme/types.d.ts +15 -0
  149. package/lib/typescript/module/components/BottomNavigation/BottomNavigation.d.ts +1 -1
  150. package/lib/typescript/module/components/BottomSheet/BottomSheet.d.ts +41 -0
  151. package/lib/typescript/module/components/BottomSheet/index.d.ts +2 -2
  152. package/lib/typescript/module/components/Box/Box.d.ts +60 -0
  153. package/lib/typescript/module/components/Box/index.d.ts +3 -0
  154. package/lib/typescript/module/components/Button/Button.d.ts +1 -1
  155. package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +3 -2
  156. package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +3 -3
  157. package/lib/typescript/module/components/Dialog/Dialog.d.ts +2 -2
  158. package/lib/typescript/module/components/FormField/FormField.d.ts +13 -2
  159. package/lib/typescript/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
  160. package/lib/typescript/module/components/KeyboardAwareScrollView/index.d.ts +3 -0
  161. package/lib/typescript/module/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
  162. package/lib/typescript/module/components/KeyboardToolbar/index.d.ts +3 -0
  163. package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -2
  164. package/lib/typescript/module/components/OTPInput/OTPInput.d.ts +3 -2
  165. package/lib/typescript/module/components/Radio/Radio.d.ts +2 -2
  166. package/lib/typescript/module/components/Radio/RadioGroup.d.ts +3 -2
  167. package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -2
  168. package/lib/typescript/module/components/Slider/Slider.d.ts +6 -4
  169. package/lib/typescript/module/components/Spinner/Spinner.d.ts +1 -1
  170. package/lib/typescript/module/components/Switch/Switch.d.ts +3 -2
  171. package/lib/typescript/module/components/Text/Text.d.ts +25 -0
  172. package/lib/typescript/module/components/Text/index.d.ts +3 -0
  173. package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +3 -3
  174. package/lib/typescript/module/components/index.d.ts +10 -2
  175. package/lib/typescript/module/form/FormContext.d.ts +17 -0
  176. package/lib/typescript/module/form/index.d.ts +9 -0
  177. package/lib/typescript/module/form/path.d.ts +10 -0
  178. package/lib/typescript/module/form/rules.d.ts +31 -0
  179. package/lib/typescript/module/form/types.d.ts +94 -0
  180. package/lib/typescript/module/form/useField.d.ts +27 -0
  181. package/lib/typescript/module/form/useForm.d.ts +10 -0
  182. package/lib/typescript/module/hooks/index.d.ts +3 -0
  183. package/lib/typescript/module/hooks/useControllableState.d.ts +17 -0
  184. package/lib/typescript/module/hooks/useReducedMotion.d.ts +8 -0
  185. package/lib/typescript/module/index.d.ts +4 -2
  186. package/lib/typescript/module/theme/types.d.ts +15 -0
  187. package/package.json +1 -1
@@ -4,6 +4,7 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState }
4
4
  import { Animated, Easing, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
5
5
  import { createAnimatedValue, fontFor, setNativeValue, useTheme } from "../../theme/index.js";
6
6
  import { triggerHaptic } from "../../utils/index.js";
7
+ import { useReducedMotion } from "../../hooks/index.js";
7
8
  import { FieldBase, resolveFieldSize, resolveFieldTextStyle, resolveVariantColors } from "../FieldBase/FieldBase.js";
8
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  // FieldBase carries every dimension except `multilineMinHeight` — Input owns
@@ -79,26 +80,31 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
79
80
 
80
81
  // Floating label animation
81
82
  useEffect(() => {
82
- Animated.timing(labelAnim, {
83
+ const anim = Animated.timing(labelAnim, {
83
84
  toValue: shouldFloat ? 1 : 0,
84
85
  duration: theme.motion.duration.fast,
85
86
  easing: Easing.bezier(...theme.motion.easing.standard),
86
87
  useNativeDriver: false
87
- }).start();
88
+ });
89
+ anim.start();
90
+ return () => anim.stop();
88
91
  }, [shouldFloat, labelAnim, theme.motion.duration.fast, theme.motion.easing.standard]);
89
92
 
90
93
  // Helper / error fade
91
94
  useEffect(() => {
92
- Animated.timing(messageAnim, {
95
+ const anim = Animated.timing(messageAnim, {
93
96
  toValue: hasError || Boolean(helperText) ? 1 : 0,
94
- duration: 150,
97
+ duration: theme.motion.duration.fast,
95
98
  easing: Easing.bezier(...theme.motion.easing.standard),
96
99
  useNativeDriver: true
97
- }).start();
98
- }, [hasError, helperText, messageAnim, theme.motion.easing.standard]);
100
+ });
101
+ anim.start();
102
+ return () => anim.stop();
103
+ }, [hasError, helperText, messageAnim, theme.motion.duration.fast, theme.motion.easing.standard]);
99
104
 
100
105
  // Error shake — off by default; consumers opt in via
101
106
  // theme.components.input.shakeOnError. The haptic ships with the shake.
107
+ const reduceMotion = useReducedMotion();
102
108
  const shakeOnError = theme.components.input?.shakeOnError ?? false;
103
109
  const prevErrorRef = useRef(hasError);
104
110
  useEffect(() => {
@@ -106,33 +112,39 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
106
112
  prevErrorRef.current = hasError;
107
113
  return;
108
114
  }
115
+ let anim;
109
116
  if (hasError && !prevErrorRef.current) {
110
- setNativeValue(shakeAnim, 0);
111
- Animated.sequence([Animated.timing(shakeAnim, {
112
- toValue: 1,
113
- duration: 50,
114
- useNativeDriver: true
115
- }), Animated.timing(shakeAnim, {
116
- toValue: -1,
117
- duration: 50,
118
- useNativeDriver: true
119
- }), Animated.timing(shakeAnim, {
120
- toValue: 1,
121
- duration: 50,
122
- useNativeDriver: true
123
- }), Animated.timing(shakeAnim, {
124
- toValue: -1,
125
- duration: 50,
126
- useNativeDriver: true
127
- }), Animated.timing(shakeAnim, {
128
- toValue: 0,
129
- duration: 100,
130
- useNativeDriver: true
131
- })]).start();
117
+ // Reduce Motion: keep the error haptic, skip the shake.
118
+ if (!reduceMotion) {
119
+ setNativeValue(shakeAnim, 0);
120
+ anim = Animated.sequence([Animated.timing(shakeAnim, {
121
+ toValue: 1,
122
+ duration: 50,
123
+ useNativeDriver: true
124
+ }), Animated.timing(shakeAnim, {
125
+ toValue: -1,
126
+ duration: 50,
127
+ useNativeDriver: true
128
+ }), Animated.timing(shakeAnim, {
129
+ toValue: 1,
130
+ duration: 50,
131
+ useNativeDriver: true
132
+ }), Animated.timing(shakeAnim, {
133
+ toValue: -1,
134
+ duration: 50,
135
+ useNativeDriver: true
136
+ }), Animated.timing(shakeAnim, {
137
+ toValue: 0,
138
+ duration: 100,
139
+ useNativeDriver: true
140
+ })]);
141
+ anim.start();
142
+ }
132
143
  triggerHaptic('notificationError');
133
144
  }
134
145
  prevErrorRef.current = hasError;
135
- }, [hasError, shakeAnim, shakeOnError]);
146
+ return () => anim?.stop();
147
+ }, [hasError, shakeAnim, shakeOnError, reduceMotion]);
136
148
  const handleFocus = useCallback(e => {
137
149
  setIsFocused(true);
138
150
  onFocus?.(e);
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+
3
+ import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
4
+ import { findNodeHandle, Keyboard, Platform, ScrollView, TextInput } from 'react-native';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ /**
7
+ * A ScrollView that keeps the focused input visible above the keyboard — no
8
+ * native dependency. On keyboard show it pads the content by the keyboard
9
+ * height and scrolls the focused TextInput so its bottom sits `extraScrollHeight`
10
+ * above the keyboard, but only when it would otherwise be covered (so already-
11
+ * visible fields don't jump). The classic measure-and-scroll approach, in pure RN.
12
+ */
13
+ const KeyboardAwareScrollView = /*#__PURE__*/forwardRef((props, ref) => {
14
+ const {
15
+ children,
16
+ extraScrollHeight = 24,
17
+ disabled = false,
18
+ contentContainerStyle,
19
+ onScroll,
20
+ onLayout,
21
+ ...rest
22
+ } = props;
23
+ const scrollRef = useRef(null);
24
+ const scrollYRef = useRef(0);
25
+ const viewportHeightRef = useRef(0);
26
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
27
+ const setRefs = useCallback(node => {
28
+ scrollRef.current = node;
29
+ if (typeof ref === 'function') ref(node);else if (ref) ref.current = node;
30
+ }, [ref]);
31
+ const scrollFocusedIntoView = useCallback(kbHeight => {
32
+ const scroll = scrollRef.current;
33
+ const scrollNode = scroll ? findNodeHandle(scroll) : null;
34
+ const stateApi = TextInput?.State;
35
+ const focused = stateApi?.currentlyFocusedInput?.();
36
+ if (!scroll || scrollNode == null || !focused?.measureLayout) return;
37
+ focused.measureLayout(scrollNode, (_left, top, _width, height) => {
38
+ const scrollY = scrollYRef.current;
39
+ const viewportH = viewportHeightRef.current;
40
+ if (viewportH <= 0) return;
41
+ const keyboardTop = viewportH - kbHeight - extraScrollHeight;
42
+ const fieldBottomInViewport = top - scrollY + height;
43
+ if (fieldBottomInViewport > keyboardTop) {
44
+ const delta = fieldBottomInViewport - keyboardTop;
45
+ scroll.scrollTo({
46
+ y: scrollY + delta,
47
+ animated: true
48
+ });
49
+ }
50
+ }, () => undefined);
51
+ }, [extraScrollHeight]);
52
+ useEffect(() => {
53
+ if (disabled) return undefined;
54
+ // iOS exposes the will* events (smoother); Android only fires did*.
55
+ const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
56
+ const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
57
+ let rafId;
58
+ const showSub = Keyboard.addListener(showEvent, e => {
59
+ const height = e.endCoordinates?.height ?? 0;
60
+ setKeyboardHeight(height);
61
+ // Wait a frame so the padding is applied before we scroll. Tracked so
62
+ // it can be cancelled on unmount (never runs against a torn-down tree).
63
+ if (rafId != null) cancelAnimationFrame(rafId);
64
+ rafId = requestAnimationFrame(() => scrollFocusedIntoView(height));
65
+ });
66
+ const hideSub = Keyboard.addListener(hideEvent, () => setKeyboardHeight(0));
67
+ return () => {
68
+ if (rafId != null) cancelAnimationFrame(rafId);
69
+ showSub.remove();
70
+ hideSub.remove();
71
+ };
72
+ }, [disabled, scrollFocusedIntoView]);
73
+ const handleScroll = useCallback(e => {
74
+ scrollYRef.current = e.nativeEvent.contentOffset.y;
75
+ onScroll?.(e);
76
+ }, [onScroll]);
77
+ const handleLayout = useCallback(e => {
78
+ viewportHeightRef.current = e.nativeEvent.layout.height;
79
+ onLayout?.(e);
80
+ }, [onLayout]);
81
+ return /*#__PURE__*/_jsx(ScrollView, {
82
+ ref: setRefs,
83
+ onScroll: handleScroll,
84
+ onLayout: handleLayout,
85
+ scrollEventThrottle: 16,
86
+ keyboardShouldPersistTaps: "handled",
87
+ keyboardDismissMode: Platform.OS === 'ios' ? 'interactive' : 'on-drag',
88
+ contentContainerStyle: [contentContainerStyle, {
89
+ paddingBottom: keyboardHeight
90
+ }],
91
+ ...rest,
92
+ children: children
93
+ });
94
+ });
95
+ KeyboardAwareScrollView.displayName = 'KeyboardAwareScrollView';
96
+ export { KeyboardAwareScrollView };
97
+ export default KeyboardAwareScrollView;
98
+ //# sourceMappingURL=KeyboardAwareScrollView.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { KeyboardAwareScrollView } from "./KeyboardAwareScrollView.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ import { Keyboard, Platform, Pressable, StyleSheet, View } from 'react-native';
5
+ import { useTheme } from "../../theme/index.js";
6
+ import { useOptionalFormContext } from "../../form/FormContext.js";
7
+ import { Text } from "../Text/index.js";
8
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
+ const NavButton = ({
10
+ label,
11
+ accessibilityLabel,
12
+ onPress,
13
+ theme
14
+ }) => /*#__PURE__*/_jsx(Pressable, {
15
+ onPress: onPress,
16
+ disabled: !onPress,
17
+ accessibilityRole: "button",
18
+ accessibilityLabel: accessibilityLabel,
19
+ hitSlop: 8,
20
+ style: {
21
+ paddingHorizontal: theme.spacing.sm,
22
+ opacity: onPress ? 1 : 0.4
23
+ },
24
+ children: /*#__PURE__*/_jsx(Text, {
25
+ variant: "h2",
26
+ color: onPress ? 'link' : 'disabled',
27
+ children: label
28
+ })
29
+ });
30
+
31
+ /**
32
+ * An accessory bar pinned just above the keyboard — pure RN, no native
33
+ * dependency. Shows a Done (dismiss) button and optional prev/next chevrons
34
+ * that, inside a `<Form>`, walk the registered field order. Render it at the
35
+ * screen root (a `flex: 1` ancestor) so its `bottom: keyboardHeight` offset
36
+ * lands it on top of the keyboard. Renders nothing while the keyboard is hidden.
37
+ */
38
+ const KeyboardToolbar = props => {
39
+ const {
40
+ doneLabel = 'Done',
41
+ showNavigation,
42
+ onDone,
43
+ onNext,
44
+ onPrev,
45
+ leading,
46
+ style,
47
+ testID
48
+ } = props;
49
+ const theme = useTheme();
50
+ const form = useOptionalFormContext();
51
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
52
+ useEffect(() => {
53
+ const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
54
+ const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
55
+ const showSub = Keyboard.addListener(showEvent, e => setKeyboardHeight(e.endCoordinates?.height ?? 0));
56
+ const hideSub = Keyboard.addListener(hideEvent, () => setKeyboardHeight(0));
57
+ return () => {
58
+ showSub.remove();
59
+ hideSub.remove();
60
+ };
61
+ }, []);
62
+ if (keyboardHeight <= 0) return null;
63
+ const handleDone = onDone ?? (() => Keyboard.dismiss());
64
+ const handlePrev = onPrev ?? form?.focusPrev;
65
+ const handleNext = onNext ?? form?.focusNext;
66
+ const navEnabled = showNavigation ?? Boolean(handlePrev || handleNext);
67
+ return /*#__PURE__*/_jsxs(View, {
68
+ testID: testID,
69
+ accessibilityRole: "toolbar",
70
+ style: [styles.bar, {
71
+ bottom: keyboardHeight,
72
+ backgroundColor: theme.colors.background.elevated,
73
+ borderTopColor: theme.colors.border.primary,
74
+ paddingHorizontal: theme.spacing.md
75
+ }, style],
76
+ children: [navEnabled ? /*#__PURE__*/_jsxs(View, {
77
+ style: styles.nav,
78
+ children: [/*#__PURE__*/_jsx(NavButton, {
79
+ label: "\u2039",
80
+ accessibilityLabel: "Previous field",
81
+ onPress: handlePrev,
82
+ theme: theme
83
+ }), /*#__PURE__*/_jsx(NavButton, {
84
+ label: "\u203A",
85
+ accessibilityLabel: "Next field",
86
+ onPress: handleNext,
87
+ theme: theme
88
+ })]
89
+ }) : /*#__PURE__*/_jsx(View, {}), leading, /*#__PURE__*/_jsx(View, {
90
+ style: styles.spacer
91
+ }), /*#__PURE__*/_jsx(Pressable, {
92
+ onPress: handleDone,
93
+ accessibilityRole: "button",
94
+ accessibilityLabel: doneLabel,
95
+ hitSlop: 8,
96
+ children: /*#__PURE__*/_jsx(Text, {
97
+ variant: "body",
98
+ weight: "semibold",
99
+ color: "link",
100
+ children: doneLabel
101
+ })
102
+ })]
103
+ });
104
+ };
105
+ const styles = StyleSheet.create({
106
+ bar: {
107
+ position: 'absolute',
108
+ left: 0,
109
+ right: 0,
110
+ height: 44,
111
+ flexDirection: 'row',
112
+ alignItems: 'center',
113
+ borderTopWidth: StyleSheet.hairlineWidth
114
+ },
115
+ nav: {
116
+ flexDirection: 'row',
117
+ alignItems: 'center'
118
+ },
119
+ spacer: {
120
+ flex: 1
121
+ }
122
+ });
123
+ export { KeyboardToolbar };
124
+ export default KeyboardToolbar;
125
+ //# sourceMappingURL=KeyboardToolbar.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { KeyboardToolbar } from "./KeyboardToolbar.js";
4
+ //# sourceMappingURL=index.js.map
@@ -39,6 +39,7 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
39
39
  const wasVisibleRef = useRef(false);
40
40
  const styles = useMemo(() => buildStyles(theme), [theme]);
41
41
  useEffect(() => {
42
+ const running = [];
42
43
  if (visible) {
43
44
  // Two Fabric quirks force this combination: backdrop View needs
44
45
  // `collapsable={false}` (see render) so flattening doesn't drop it,
@@ -46,21 +47,25 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
46
47
  // Animated.View that also sets backgroundColor inline renders as 0 on
47
48
  // RN 0.85 / Fabric / iOS, leaving the dim layer invisible. Single
48
49
  // 240ms tween — JS thread cost is negligible.
49
- Animated.timing(backdropAnim, {
50
+ const backdrop = Animated.timing(backdropAnim, {
50
51
  toValue: 1,
51
52
  duration,
52
53
  useNativeDriver: false
53
- }).start();
54
+ });
55
+ backdrop.start();
56
+ running.push(backdrop);
54
57
  if (presentation === 'bottom') {
55
- Animated.spring(translateYAnim, {
58
+ const slide = Animated.spring(translateYAnim, {
56
59
  toValue: 0,
57
60
  damping: theme.motion.spring.gentle.damping,
58
61
  stiffness: theme.motion.spring.gentle.stiffness,
59
62
  mass: theme.motion.spring.gentle.mass,
60
63
  useNativeDriver: true
61
- }).start();
64
+ });
65
+ slide.start();
66
+ running.push(slide);
62
67
  } else {
63
- Animated.parallel([Animated.spring(scaleAnim, {
68
+ const enter = Animated.parallel([Animated.spring(scaleAnim, {
64
69
  toValue: 1,
65
70
  damping: theme.motion.spring.gentle.damping,
66
71
  stiffness: theme.motion.spring.gentle.stiffness,
@@ -70,7 +75,9 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
70
75
  toValue: 1,
71
76
  duration,
72
77
  useNativeDriver: true
73
- })]).start();
78
+ })]);
79
+ enter.start();
80
+ running.push(enter);
74
81
  }
75
82
  } else {
76
83
  // backdropAnim is JS-driven (see note above) — reset directly so a
@@ -80,6 +87,10 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
80
87
  setNativeValue(opacityAnim, 0);
81
88
  setNativeValue(translateYAnim, screenHeight);
82
89
  }
90
+ // Stop in-flight tweens on unmount / visibility flip so an enter animation
91
+ // never resolves against a torn-down tree (fixes the jest async leak and
92
+ // the equivalent post-unmount work in the real app).
93
+ return () => running.forEach(animation => animation.stop());
83
94
  }, [visible, presentation, duration, backdropAnim, scaleAnim, opacityAnim, translateYAnim, screenHeight, theme.motion.spring.gentle.damping, theme.motion.spring.gentle.stiffness, theme.motion.spring.gentle.mass, scaleStartValue]);
84
95
 
85
96
  // Accessibility focus trap: when the modal opens, push screen-reader focus
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
5
+ import { useControllableState } from "../../hooks/index.js";
5
6
  import { useTheme } from "../../theme/index.js";
6
7
  import { triggerHaptic } from "../../utils/index.js";
7
8
  import { FieldBase, resolveFieldSize, resolveFieldTextStyle } from "../FieldBase/FieldBase.js";
@@ -43,6 +44,7 @@ const sanitizeTyped = (raw, allowDecimal) => {
43
44
  const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
44
45
  const {
45
46
  value,
47
+ defaultValue,
46
48
  onChange,
47
49
  min = Number.NEGATIVE_INFINITY,
48
50
  max = Number.POSITIVE_INFINITY,
@@ -66,6 +68,11 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
66
68
  textInputProps,
67
69
  testID
68
70
  } = props;
71
+ const [current, setCurrent] = useControllableState({
72
+ value,
73
+ defaultValue: defaultValue ?? props.min ?? 0,
74
+ onChange
75
+ });
69
76
  const theme = useTheme();
70
77
  const sizeTokens = resolveFieldSize(theme, size);
71
78
  const fieldText = resolveFieldTextStyle(theme, {
@@ -76,7 +83,7 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
76
83
  const longPressInterval = theme.components.numberInput?.longPressIntervalMs ?? DEFAULT_LONG_PRESS_INTERVAL;
77
84
  const pressHaptic = theme.components.numberInput?.pressHaptic ?? false;
78
85
  const inputRef = useRef(null);
79
- const [draft, setDraft] = useState(formatValue(value, allowDecimal, precision));
86
+ const [draft, setDraft] = useState(formatValue(current, allowDecimal, precision));
80
87
  const [editing, setEditing] = useState(false);
81
88
  const [focused, setFocused] = useState(false);
82
89
  const repeatTimeoutRef = useRef(null);
@@ -94,41 +101,41 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
94
101
  useEffect(() => clearRepeat, [clearRepeat]);
95
102
  useEffect(() => {
96
103
  if (!editing) {
97
- setDraft(formatValue(value, allowDecimal, precision));
104
+ setDraft(formatValue(current, allowDecimal, precision));
98
105
  }
99
- }, [value, allowDecimal, precision, editing]);
100
- const atMin = value <= min;
101
- const atMax = value >= max;
106
+ }, [current, allowDecimal, precision, editing]);
107
+ const atMin = current <= min;
108
+ const atMax = current >= max;
102
109
  const interactive = !disabled;
103
110
  const setExternal = useCallback(next => {
104
111
  const rounded = roundToPrecision(next, allowDecimal ? precision : 0);
105
112
  const clamped = clamp(rounded, min, max);
106
- if (clamped !== value) {
107
- onChange(clamped);
113
+ if (clamped !== current) {
114
+ setCurrent(clamped);
108
115
  }
109
- }, [allowDecimal, max, min, onChange, precision, value]);
116
+ }, [allowDecimal, max, min, setCurrent, precision, current]);
110
117
  const decrement = useCallback(() => {
111
118
  if (!interactive || atMin) return;
112
119
  if (pressHaptic) triggerHaptic('impactLight');
113
- setExternal(value - step);
114
- }, [atMin, interactive, pressHaptic, setExternal, step, value]);
120
+ setExternal(current - step);
121
+ }, [atMin, interactive, pressHaptic, setExternal, step, current]);
115
122
  const increment = useCallback(() => {
116
123
  if (!interactive || atMax) return;
117
124
  if (pressHaptic) triggerHaptic('impactLight');
118
- setExternal(value + step);
119
- }, [atMax, interactive, pressHaptic, setExternal, step, value]);
125
+ setExternal(current + step);
126
+ }, [atMax, interactive, pressHaptic, setExternal, step, current]);
120
127
  React.useImperativeHandle(ref, () => ({
121
128
  focus: () => inputRef.current?.focus(),
122
129
  blur: () => inputRef.current?.blur(),
123
130
  clear: () => {
124
131
  setDraft('');
125
132
  if (Number.isFinite(min) && min > 0) {
126
- onChange(min);
133
+ setCurrent(min);
127
134
  } else {
128
- onChange(0 < min ? min : 0 > max ? max : 0);
135
+ setCurrent(0 < min ? min : 0 > max ? max : 0);
129
136
  }
130
137
  }
131
- }), [max, min, onChange]);
138
+ }), [max, min, setCurrent]);
132
139
  const startRepeating = useCallback(direction => {
133
140
  if (!interactive) return;
134
141
  clearRepeat();
@@ -148,27 +155,27 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
148
155
  const parsed = Number(cleaned);
149
156
  if (cleaned !== '' && cleaned !== '-' && cleaned !== '.' && Number.isFinite(parsed)) {
150
157
  if (parsed >= min && parsed <= max) {
151
- if (parsed !== value) {
152
- onChange(roundToPrecision(parsed, allowDecimal ? precision : 0));
158
+ if (parsed !== current) {
159
+ setCurrent(roundToPrecision(parsed, allowDecimal ? precision : 0));
153
160
  }
154
161
  }
155
162
  }
156
- }, [allowDecimal, max, min, onChange, precision, value]);
163
+ }, [allowDecimal, max, min, setCurrent, precision, current]);
157
164
  const handleBlur = useCallback(() => {
158
165
  setEditing(false);
159
166
  setFocused(false);
160
167
  const parsed = Number(draft);
161
168
  if (draft === '' || !Number.isFinite(parsed)) {
162
- setDraft(formatValue(value, allowDecimal, precision));
169
+ setDraft(formatValue(current, allowDecimal, precision));
163
170
  return;
164
171
  }
165
172
  const rounded = roundToPrecision(parsed, allowDecimal ? precision : 0);
166
173
  const clamped = clamp(rounded, min, max);
167
- if (clamped !== value) {
168
- onChange(clamped);
174
+ if (clamped !== current) {
175
+ setCurrent(clamped);
169
176
  }
170
177
  setDraft(formatValue(clamped, allowDecimal, precision));
171
- }, [allowDecimal, draft, max, min, onChange, precision, value]);
178
+ }, [allowDecimal, draft, max, min, setCurrent, precision, current]);
172
179
  const handleFocus = useCallback(() => {
173
180
  setEditing(true);
174
181
  setFocused(true);
@@ -232,7 +239,7 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
232
239
  accessibilityRole: "adjustable",
233
240
  accessibilityLabel: accessibleInputLabel,
234
241
  accessibilityValue: {
235
- now: Number.isFinite(value) ? value : 0
242
+ now: Number.isFinite(current) ? current : 0
236
243
  },
237
244
  accessibilityState: {
238
245
  disabled