@webority-technologies/mobile 0.0.23 → 0.0.24

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 (125) hide show
  1. package/lib/commonjs/components/Accordion/Accordion.js +5 -5
  2. package/lib/commonjs/components/AnimatePresence/AnimatePresence.js +69 -0
  3. package/lib/commonjs/components/AnimatePresence/index.js +13 -0
  4. package/lib/commonjs/components/AppBar/AppBar.js +9 -6
  5. package/lib/commonjs/components/Banner/Banner.js +12 -2
  6. package/lib/commonjs/components/Card/Card.js +3 -3
  7. package/lib/commonjs/components/Checkbox/Checkbox.js +3 -2
  8. package/lib/commonjs/components/Chip/Chip.js +4 -2
  9. package/lib/commonjs/components/DatePicker/DatePicker.js +23 -18
  10. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +11 -9
  11. package/lib/commonjs/components/Dialog/Dialog.js +4 -2
  12. package/lib/commonjs/components/Drawer/Drawer.js +4 -2
  13. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +10 -8
  14. package/lib/commonjs/components/ImageGallery/ImageGallery.js +17 -15
  15. package/lib/commonjs/components/ListItem/ListItem.js +4 -3
  16. package/lib/commonjs/components/Modal/Modal.js +4 -3
  17. package/lib/commonjs/components/NumberInput/NumberInput.js +7 -5
  18. package/lib/commonjs/components/OTPInput/OTPInput.js +7 -7
  19. package/lib/commonjs/components/Radio/Radio.js +2 -3
  20. package/lib/commonjs/components/Rating/Rating.js +4 -3
  21. package/lib/commonjs/components/SearchBar/SearchBar.js +7 -4
  22. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +4 -3
  23. package/lib/commonjs/components/Select/Select.js +7 -4
  24. package/lib/commonjs/components/Slider/Slider.js +228 -228
  25. package/lib/commonjs/components/Stepper/Stepper.js +6 -5
  26. package/lib/commonjs/components/Swipeable/Swipeable.js +8 -9
  27. package/lib/commonjs/components/Tabs/Tabs.js +4 -3
  28. package/lib/commonjs/components/TimePicker/TimePicker.js +14 -9
  29. package/lib/commonjs/components/index.js +121 -114
  30. package/lib/commonjs/utils/hapticUtils.js +11 -1
  31. package/lib/commonjs/utils/index.js +6 -0
  32. package/lib/module/components/Accordion/Accordion.js +6 -6
  33. package/lib/module/components/AnimatePresence/AnimatePresence.js +63 -0
  34. package/lib/module/components/AnimatePresence/index.js +4 -0
  35. package/lib/module/components/AppBar/AppBar.js +10 -7
  36. package/lib/module/components/Banner/Banner.js +12 -2
  37. package/lib/module/components/Card/Card.js +4 -4
  38. package/lib/module/components/Checkbox/Checkbox.js +4 -3
  39. package/lib/module/components/Chip/Chip.js +5 -3
  40. package/lib/module/components/DatePicker/DatePicker.js +24 -19
  41. package/lib/module/components/DateRangePicker/DateRangePicker.js +12 -10
  42. package/lib/module/components/Dialog/Dialog.js +5 -3
  43. package/lib/module/components/Drawer/Drawer.js +5 -3
  44. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +11 -9
  45. package/lib/module/components/ImageGallery/ImageGallery.js +18 -16
  46. package/lib/module/components/ListItem/ListItem.js +5 -4
  47. package/lib/module/components/Modal/Modal.js +5 -4
  48. package/lib/module/components/NumberInput/NumberInput.js +8 -6
  49. package/lib/module/components/OTPInput/OTPInput.js +8 -8
  50. package/lib/module/components/Radio/Radio.js +3 -4
  51. package/lib/module/components/Rating/Rating.js +5 -4
  52. package/lib/module/components/SearchBar/SearchBar.js +8 -5
  53. package/lib/module/components/SegmentedControl/SegmentedControl.js +5 -4
  54. package/lib/module/components/Select/Select.js +8 -5
  55. package/lib/module/components/Slider/Slider.js +231 -231
  56. package/lib/module/components/Stepper/Stepper.js +7 -6
  57. package/lib/module/components/Swipeable/Swipeable.js +9 -10
  58. package/lib/module/components/Tabs/Tabs.js +5 -4
  59. package/lib/module/components/TimePicker/TimePicker.js +15 -10
  60. package/lib/module/components/index.js +1 -0
  61. package/lib/module/utils/hapticUtils.js +9 -0
  62. package/lib/module/utils/index.js +1 -1
  63. package/lib/typescript/commonjs/components/Accordion/Accordion.d.ts +3 -0
  64. package/lib/typescript/commonjs/components/AnimatePresence/AnimatePresence.d.ts +30 -0
  65. package/lib/typescript/commonjs/components/AnimatePresence/index.d.ts +3 -0
  66. package/lib/typescript/commonjs/components/AppBar/AppBar.d.ts +6 -0
  67. package/lib/typescript/commonjs/components/Banner/Banner.d.ts +3 -0
  68. package/lib/typescript/commonjs/components/Card/Card.d.ts +3 -0
  69. package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +1 -0
  70. package/lib/typescript/commonjs/components/Chip/Chip.d.ts +3 -0
  71. package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +3 -0
  72. package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts +6 -0
  73. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +3 -0
  74. package/lib/typescript/commonjs/components/Drawer/Drawer.d.ts +3 -0
  75. package/lib/typescript/commonjs/components/FloatingActionButton/FloatingActionButton.d.ts +5 -0
  76. package/lib/typescript/commonjs/components/ImageGallery/ImageGallery.d.ts +6 -0
  77. package/lib/typescript/commonjs/components/ListItem/ListItem.d.ts +3 -0
  78. package/lib/typescript/commonjs/components/Modal/Modal.d.ts +6 -0
  79. package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -0
  80. package/lib/typescript/commonjs/components/OTPInput/OTPInput.d.ts +6 -0
  81. package/lib/typescript/commonjs/components/Rating/Rating.d.ts +6 -0
  82. package/lib/typescript/commonjs/components/SearchBar/SearchBar.d.ts +3 -0
  83. package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  84. package/lib/typescript/commonjs/components/Select/Select.d.ts +6 -0
  85. package/lib/typescript/commonjs/components/Slider/Slider.d.ts +3 -0
  86. package/lib/typescript/commonjs/components/Stepper/Stepper.d.ts +6 -0
  87. package/lib/typescript/commonjs/components/Swipeable/Swipeable.d.ts +3 -0
  88. package/lib/typescript/commonjs/components/Tabs/Tabs.d.ts +3 -0
  89. package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +3 -0
  90. package/lib/typescript/commonjs/components/index.d.ts +2 -0
  91. package/lib/typescript/commonjs/theme/types.d.ts +2 -67
  92. package/lib/typescript/commonjs/utils/hapticUtils.d.ts +8 -0
  93. package/lib/typescript/commonjs/utils/index.d.ts +1 -1
  94. package/lib/typescript/module/components/Accordion/Accordion.d.ts +3 -0
  95. package/lib/typescript/module/components/AnimatePresence/AnimatePresence.d.ts +30 -0
  96. package/lib/typescript/module/components/AnimatePresence/index.d.ts +3 -0
  97. package/lib/typescript/module/components/AppBar/AppBar.d.ts +6 -0
  98. package/lib/typescript/module/components/Banner/Banner.d.ts +3 -0
  99. package/lib/typescript/module/components/Card/Card.d.ts +3 -0
  100. package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +1 -0
  101. package/lib/typescript/module/components/Chip/Chip.d.ts +3 -0
  102. package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +3 -0
  103. package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts +6 -0
  104. package/lib/typescript/module/components/Dialog/Dialog.d.ts +3 -0
  105. package/lib/typescript/module/components/Drawer/Drawer.d.ts +3 -0
  106. package/lib/typescript/module/components/FloatingActionButton/FloatingActionButton.d.ts +5 -0
  107. package/lib/typescript/module/components/ImageGallery/ImageGallery.d.ts +6 -0
  108. package/lib/typescript/module/components/ListItem/ListItem.d.ts +3 -0
  109. package/lib/typescript/module/components/Modal/Modal.d.ts +6 -0
  110. package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -0
  111. package/lib/typescript/module/components/OTPInput/OTPInput.d.ts +6 -0
  112. package/lib/typescript/module/components/Rating/Rating.d.ts +6 -0
  113. package/lib/typescript/module/components/SearchBar/SearchBar.d.ts +3 -0
  114. package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -0
  115. package/lib/typescript/module/components/Select/Select.d.ts +6 -0
  116. package/lib/typescript/module/components/Slider/Slider.d.ts +3 -0
  117. package/lib/typescript/module/components/Stepper/Stepper.d.ts +6 -0
  118. package/lib/typescript/module/components/Swipeable/Swipeable.d.ts +3 -0
  119. package/lib/typescript/module/components/Tabs/Tabs.d.ts +3 -0
  120. package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +3 -0
  121. package/lib/typescript/module/components/index.d.ts +2 -0
  122. package/lib/typescript/module/theme/types.d.ts +2 -67
  123. package/lib/typescript/module/utils/hapticUtils.d.ts +8 -0
  124. package/lib/typescript/module/utils/index.d.ts +1 -1
  125. package/package.json +1 -1
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import Animated, { FadeIn, FadeInDown, FadeInUp, FadeOut, FadeOutDown, FadeOutUp, ZoomIn, ZoomOut } from 'react-native-reanimated';
5
+ import { useTheme } from "../../theme/index.js";
6
+ import { useReducedMotion } from "../../hooks/index.js";
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ /**
9
+ * Animates a single child in on mount and out on unmount. Thin, theme-aware
10
+ * wrapper over Reanimated's `entering`/`exiting` — Reanimated keeps the view
11
+ * alive to play the exit, so the parent only toggles a condition. Honors the OS
12
+ * Reduce Motion setting (renders instantly when reduced).
13
+ *
14
+ * For animating items inside a list, use Reanimated's `itemLayoutAnimation`
15
+ * on `Animated.FlatList` instead — this wrapper targets single conditional nodes.
16
+ */
17
+ const AnimatePresence = ({
18
+ children,
19
+ preset = 'fade',
20
+ duration,
21
+ style,
22
+ testID
23
+ }) => {
24
+ const theme = useTheme();
25
+ const reduceMotion = useReducedMotion();
26
+ const visible = children !== null && children !== undefined && children !== false;
27
+ if (!visible) return null;
28
+ const dur = duration ?? theme.motion.duration.normal;
29
+ let entering;
30
+ let exiting;
31
+ if (!reduceMotion) {
32
+ switch (preset) {
33
+ case 'scale':
34
+ entering = ZoomIn.duration(dur);
35
+ exiting = ZoomOut.duration(dur);
36
+ break;
37
+ case 'slide-up':
38
+ entering = FadeInUp.duration(dur);
39
+ exiting = FadeOutDown.duration(dur);
40
+ break;
41
+ case 'slide-down':
42
+ entering = FadeInDown.duration(dur);
43
+ exiting = FadeOutUp.duration(dur);
44
+ break;
45
+ case 'fade':
46
+ default:
47
+ entering = FadeIn.duration(dur);
48
+ exiting = FadeOut.duration(dur);
49
+ break;
50
+ }
51
+ }
52
+ return /*#__PURE__*/_jsx(Animated.View, {
53
+ entering: entering,
54
+ exiting: exiting,
55
+ style: style,
56
+ testID: testID,
57
+ children: children
58
+ });
59
+ };
60
+ AnimatePresence.displayName = 'AnimatePresence';
61
+ export { AnimatePresence };
62
+ export default AnimatePresence;
63
+ //# sourceMappingURL=AnimatePresence.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { AnimatePresence } from "./AnimatePresence.js";
4
+ //# sourceMappingURL=index.js.map
@@ -5,7 +5,7 @@ import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import { useTheme } from "../../theme/index.js";
7
7
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
8
- import { triggerHaptic } from "../../utils/hapticUtils.js";
8
+ import { triggerHaptic, resolveHaptic } from "../../utils/hapticUtils.js";
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  const variantSize = (theme, variant) => {
11
11
  switch (variant) {
@@ -36,7 +36,8 @@ const variantSize = (theme, variant) => {
36
36
  const ActionButton = ({
37
37
  action,
38
38
  side,
39
- badgeStyle
39
+ badgeStyle,
40
+ haptic
40
41
  }) => {
41
42
  const theme = useTheme();
42
43
  const {
@@ -48,9 +49,8 @@ const ActionButton = ({
48
49
  });
49
50
  const styles = useMemo(() => buildActionStyles(theme), [theme]);
50
51
  const onPress = () => {
51
- if (theme.components.appBar?.pressHaptic ?? false) {
52
- triggerHaptic('selection');
53
- }
52
+ const h = resolveHaptic(haptic, 'selection');
53
+ if (h) triggerHaptic(h);
54
54
  action.onPress();
55
55
  };
56
56
  const badgeValue = action.badge;
@@ -108,6 +108,7 @@ const AppBar = /*#__PURE__*/forwardRef((props, ref) => {
108
108
  titleStyle,
109
109
  subtitleStyle,
110
110
  badgeStyle,
111
+ haptic,
111
112
  testID
112
113
  } = props;
113
114
  const theme = useTheme();
@@ -225,7 +226,8 @@ const AppBar = /*#__PURE__*/forwardRef((props, ref) => {
225
226
  style: styles.sideSlot,
226
227
  children: leftAction ? /*#__PURE__*/_jsx(ActionButton, {
227
228
  action: leftAction,
228
- side: "left"
229
+ side: "left",
230
+ haptic: haptic
229
231
  }) : null
230
232
  }), /*#__PURE__*/_jsx(View, {
231
233
  style: [styles.center, titleAlignment === 'center' ? styles.centerAligned : styles.centerLeft],
@@ -235,7 +237,8 @@ const AppBar = /*#__PURE__*/forwardRef((props, ref) => {
235
237
  children: rightActions?.map((action, index) => /*#__PURE__*/_jsx(ActionButton, {
236
238
  action: action,
237
239
  side: "right",
238
- badgeStyle: badgeStyle
240
+ badgeStyle: badgeStyle,
241
+ haptic: haptic
239
242
  }, `appbar-right-${index}`))
240
243
  })]
241
244
  })]
@@ -3,6 +3,7 @@
3
3
  import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useTheme, createAnimatedValue } from "../../theme/index.js";
6
+ import { resolveHaptic, triggerHaptic } from "../../utils/hapticUtils.js";
6
7
  import { Skeleton, SkeletonText } from "../Skeleton/index.js";
7
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
9
  const Banner = /*#__PURE__*/forwardRef((props, ref) => {
@@ -14,6 +15,7 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
14
15
  actions,
15
16
  dismissible = false,
16
17
  onDismiss,
18
+ haptic,
17
19
  visible = true,
18
20
  animateMount = true,
19
21
  loading = false,
@@ -143,7 +145,11 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
143
145
  }), dismissible ? /*#__PURE__*/_jsx(Pressable, {
144
146
  accessibilityRole: "button",
145
147
  accessibilityLabel: "Dismiss banner",
146
- onPress: onDismiss,
148
+ onPress: () => {
149
+ const h = resolveHaptic(haptic, 'selection');
150
+ if (h) triggerHaptic(h);
151
+ onDismiss?.();
152
+ },
147
153
  hitSlop: 8,
148
154
  style: ({
149
155
  pressed
@@ -168,7 +174,11 @@ const Banner = /*#__PURE__*/forwardRef((props, ref) => {
168
174
  return /*#__PURE__*/_jsx(Pressable, {
169
175
  accessibilityRole: "button",
170
176
  accessibilityLabel: action.label,
171
- onPress: action.onPress,
177
+ onPress: () => {
178
+ const h = resolveHaptic(haptic, 'selection');
179
+ if (h) triggerHaptic(h);
180
+ action.onPress();
181
+ },
172
182
  style: ({
173
183
  pressed
174
184
  }) => [styles.actionBtn, {
@@ -4,7 +4,7 @@ import React, { forwardRef, useMemo } from 'react';
4
4
  import { Animated, Image, Pressable, StyleSheet, View } from 'react-native';
5
5
  import { Gradient, useTheme } from "../../theme/index.js";
6
6
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
7
- import { triggerHaptic } from "../../utils/hapticUtils.js";
7
+ import { resolveHaptic, triggerHaptic } from "../../utils/hapticUtils.js";
8
8
  import { Skeleton, SkeletonText } from "../Skeleton/index.js";
9
9
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  const paddingMap = {
@@ -31,6 +31,7 @@ const Card = /*#__PURE__*/forwardRef((props, ref) => {
31
31
  loading = false,
32
32
  gradient,
33
33
  onPress,
34
+ haptic,
34
35
  accessibilityLabel,
35
36
  accessibilityHint,
36
37
  accessibilityRole,
@@ -150,9 +151,8 @@ const Card = /*#__PURE__*/forwardRef((props, ref) => {
150
151
  let rendered;
151
152
  if (isInteractive) {
152
153
  const handlePress = event => {
153
- if (theme.components.card?.pressHaptic ?? false) {
154
- triggerHaptic('selection');
155
- }
154
+ const h = resolveHaptic(haptic, 'selection');
155
+ if (h) triggerHaptic(h);
156
156
  onPress?.(event);
157
157
  };
158
158
  rendered = /*#__PURE__*/_jsx(Animated.View, {
@@ -4,7 +4,7 @@ import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
4
4
  import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useTheme, createAnimatedValue } from "../../theme/index.js";
6
6
  import { useControllableState } from "../../hooks/index.js";
7
- import { triggerHaptic } from "../../utils/hapticUtils.js";
7
+ import { triggerHaptic, resolveHaptic } from "../../utils/hapticUtils.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  const sizeMap = {
10
10
  sm: 16,
@@ -35,7 +35,7 @@ const Checkbox = /*#__PURE__*/forwardRef((props, ref) => {
35
35
  size = 'md',
36
36
  tone = 'primary',
37
37
  accessibilityLabel,
38
- haptic = 'selection',
38
+ haptic,
39
39
  style,
40
40
  boxStyle,
41
41
  checkIconStyle,
@@ -75,7 +75,8 @@ const Checkbox = /*#__PURE__*/forwardRef((props, ref) => {
75
75
  });
76
76
  const handlePress = event => {
77
77
  if (disabled) return;
78
- if (haptic !== false && theme.components.checkbox?.pressHaptic) triggerHaptic(haptic);
78
+ const h = resolveHaptic(haptic, 'selection');
79
+ if (h) triggerHaptic(h);
79
80
  setCurrent(!current);
80
81
  rest.onPressOut?.(event);
81
82
  };
@@ -4,7 +4,7 @@ import React, { forwardRef, useMemo } from 'react';
4
4
  import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { fontFor, useTheme } from "../../theme/index.js";
6
6
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
7
- import { triggerHaptic } from "../../utils/hapticUtils.js";
7
+ import { resolveHaptic, triggerHaptic } from "../../utils/hapticUtils.js";
8
8
  import { Skeleton } from "../Skeleton/index.js";
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  const toneFor = (theme, tone) => {
@@ -99,6 +99,7 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
99
99
  disabled = false,
100
100
  loading = false,
101
101
  accessibilityLabel,
102
+ haptic,
102
103
  style,
103
104
  textStyle,
104
105
  containerStyle,
@@ -145,12 +146,13 @@ const Chip = /*#__PURE__*/forwardRef((props, ref) => {
145
146
  const borderWidth = isFilled ? 0 : theme.colors.border.width;
146
147
  const handlePress = () => {
147
148
  if (!isPressable) return;
148
- if (theme.components.chip?.pressHaptic) triggerHaptic('selection');
149
+ const h = resolveHaptic(haptic, 'selection');
150
+ if (h) triggerHaptic(h);
149
151
  onPress?.();
150
152
  };
151
153
  const handleClose = () => {
152
154
  if (disabled) return;
153
- if (theme.components.chip?.closeHaptic) triggerHaptic('impactLight');
155
+ if (haptic !== false) triggerHaptic('impactLight');
154
156
  onClose?.();
155
157
  };
156
158
  const a11yLabel = accessibilityLabel ?? label;
@@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { AccessibilityInfo, Animated, Easing, Modal, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
7
- import { triggerHaptic } from "../../utils/index.js";
7
+ import { resolveHaptic, triggerHaptic } from "../../utils/index.js";
8
8
  import { PickerTrigger } from "../PickerTrigger/index.js";
9
9
 
10
10
  /**
@@ -115,6 +115,7 @@ const DatePicker = props => {
115
115
  headerLabelStyle,
116
116
  navButtonStyle,
117
117
  footerButtonStyle,
118
+ haptic,
118
119
  testID,
119
120
  label,
120
121
  placeholder,
@@ -230,7 +231,7 @@ const DatePicker = props => {
230
231
  return false;
231
232
  }, [minDay, maxDay]);
232
233
  const animateMonthChange = useCallback(delta => {
233
- if (theme.components.datePicker?.haptic) triggerHaptic('impactLight');
234
+ if (haptic !== false) triggerHaptic('impactLight');
234
235
  const direction = delta > 0 ? 1 : -1;
235
236
  const distance = theme.components.datePicker?.monthSlideDistance ?? 32;
236
237
  const outDuration = theme.components.datePicker?.monthSlideOutDuration ?? theme.motion.duration.fast ?? 140;
@@ -261,31 +262,31 @@ const DatePicker = props => {
261
262
  const next = addMonths(anchor, delta);
262
263
  setAnchor(next);
263
264
  AccessibilityInfo.announceForAccessibility(formatMonthYear(next, locale));
264
- }, [anchor, locale, monthFade, monthSlide, theme.components.datePicker, theme.motion.duration.fast]);
265
+ }, [anchor, haptic, locale, monthFade, monthSlide, theme.components.datePicker, theme.motion.duration.fast]);
265
266
  const goPrev = useCallback(() => {
266
267
  if (viewMode === 'days') {
267
268
  animateMonthChange(-1);
268
269
  return;
269
270
  }
270
- if (theme.components.datePicker?.haptic) triggerHaptic('impactLight');
271
+ if (haptic !== false) triggerHaptic('impactLight');
271
272
  if (viewMode === 'years') {
272
273
  setAnchor(prev => new Date(prev.getFullYear() - GRID_SIZE, prev.getMonth(), 1));
273
274
  } else {
274
275
  setAnchor(prev => new Date(prev.getFullYear() - GRID_SIZE * DECADE_SPAN, prev.getMonth(), 1));
275
276
  }
276
- }, [animateMonthChange, viewMode, theme.components.datePicker]);
277
+ }, [animateMonthChange, viewMode, haptic]);
277
278
  const goNext = useCallback(() => {
278
279
  if (viewMode === 'days') {
279
280
  animateMonthChange(1);
280
281
  return;
281
282
  }
282
- if (theme.components.datePicker?.haptic) triggerHaptic('impactLight');
283
+ if (haptic !== false) triggerHaptic('impactLight');
283
284
  if (viewMode === 'years') {
284
285
  setAnchor(prev => new Date(prev.getFullYear() + GRID_SIZE, prev.getMonth(), 1));
285
286
  } else {
286
287
  setAnchor(prev => new Date(prev.getFullYear() + GRID_SIZE * DECADE_SPAN, prev.getMonth(), 1));
287
288
  }
288
- }, [animateMonthChange, viewMode, theme.components.datePicker]);
289
+ }, [animateMonthChange, viewMode, haptic]);
289
290
 
290
291
  // View-mode transition: fade + scale, native driver.
291
292
  const animateViewTransition = useCallback(() => {
@@ -305,7 +306,8 @@ const DatePicker = props => {
305
306
  })]).start();
306
307
  }, [viewFade, viewScale, theme.components.datePicker, theme.motion.duration.normal]);
307
308
  const cycleViewMode = useCallback(() => {
308
- if (theme.components.datePicker?.haptic) triggerHaptic('selection');
309
+ const h = resolveHaptic(haptic, 'selection');
310
+ if (h) triggerHaptic(h);
309
311
  const pressDur = theme.components.datePicker?.headerPressDuration ?? 80;
310
312
  const releaseDur = theme.components.datePicker?.headerReleaseDuration ?? 100;
311
313
  Animated.sequence([Animated.timing(headerScale, {
@@ -323,23 +325,26 @@ const DatePicker = props => {
323
325
  return 'years';
324
326
  });
325
327
  animateViewTransition();
326
- }, [animateViewTransition, headerScale, theme.components.datePicker]);
328
+ }, [animateViewTransition, headerScale, haptic, theme.components.datePicker]);
327
329
  const pickYear = useCallback(year => {
328
- if (theme.components.datePicker?.haptic) triggerHaptic('selection');
330
+ const h = resolveHaptic(haptic, 'selection');
331
+ if (h) triggerHaptic(h);
329
332
  setAnchor(prev => new Date(year, prev.getMonth(), 1));
330
333
  setViewMode('days');
331
334
  animateViewTransition();
332
- }, [animateViewTransition, theme.components.datePicker]);
335
+ }, [animateViewTransition, haptic]);
333
336
  const pickDecade = useCallback(decadeStart => {
334
- if (theme.components.datePicker?.haptic) triggerHaptic('selection');
337
+ const h = resolveHaptic(haptic, 'selection');
338
+ if (h) triggerHaptic(h);
335
339
  // Anchor mid-decade so the year grid centers nicely on this decade.
336
340
  setAnchor(prev => new Date(decadeStart + 4, prev.getMonth(), 1));
337
341
  setViewMode('years');
338
342
  animateViewTransition();
339
- }, [animateViewTransition, theme.components.datePicker]);
343
+ }, [animateViewTransition, haptic]);
340
344
  const pressDay = useCallback(cell => {
341
345
  if (isDisabled(cell.date)) return;
342
- if (theme.components.datePicker?.haptic) triggerHaptic('selection');
346
+ const h = resolveHaptic(haptic, 'selection');
347
+ if (h) triggerHaptic(h);
343
348
  setPendingDate(cell.date);
344
349
  if (!cell.inMonth) {
345
350
  setAnchor(new Date(cell.date.getFullYear(), cell.date.getMonth(), 1));
@@ -352,7 +357,7 @@ const DatePicker = props => {
352
357
  mass: theme.motion.spring.bouncy.mass,
353
358
  useNativeDriver: true
354
359
  }).start();
355
- }, [isDisabled, selectScale, theme.motion.spring.bouncy, theme.components.datePicker]);
360
+ }, [isDisabled, selectScale, theme.motion.spring.bouncy, haptic]);
356
361
  const finalizeClose = useCallback(() => {
357
362
  if (isControlled) {
358
363
  onClose?.();
@@ -381,15 +386,15 @@ const DatePicker = props => {
381
386
  });
382
387
  }, [backdrop, mode, finalizeClose, sheet, theme.motion.duration.fast]);
383
388
  const handleCancel = useCallback(() => {
384
- if (theme.components.datePicker?.haptic) triggerHaptic('selection');
389
+ if (haptic !== false) triggerHaptic('selection');
385
390
  handleClose();
386
- }, [handleClose, theme.components.datePicker]);
391
+ }, [handleClose, haptic]);
387
392
  const handleConfirm = useCallback(() => {
388
393
  if (!pendingDate) return;
389
- if (theme.components.datePicker?.haptic) triggerHaptic('notificationSuccess');
394
+ if (haptic !== false) triggerHaptic('notificationSuccess');
390
395
  onChange?.(pendingDate);
391
396
  handleClose();
392
- }, [handleClose, onChange, pendingDate, theme.components.datePicker]);
397
+ }, [handleClose, onChange, pendingDate, haptic]);
393
398
  const sheetTranslate = sheet.interpolate({
394
399
  inputRange: [0, 1],
395
400
  outputRange: [320, 0]
@@ -4,7 +4,7 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState }
4
4
  import { AccessibilityInfo, Animated, Easing, Modal, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
7
- import { triggerHaptic } from "../../utils/index.js";
7
+ import { triggerHaptic, resolveHaptic } from "../../utils/index.js";
8
8
  import { PickerTrigger } from "../PickerTrigger/index.js";
9
9
 
10
10
  /**
@@ -103,6 +103,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
103
103
  confirmLabel = 'Confirm',
104
104
  cancelLabel = 'Cancel',
105
105
  maxRange,
106
+ haptic,
106
107
  style,
107
108
  containerStyle,
108
109
  headerLabelStyle,
@@ -184,7 +185,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
184
185
  const headerLabel = useMemo(() => formatMonthYear(anchor, locale), [anchor, locale]);
185
186
  const weekdays = useMemo(() => weekdayLabels(locale, weekStartsOn), [locale, weekStartsOn]);
186
187
  const animateMonthChange = useCallback(delta => {
187
- if (theme.components.dateRangePicker?.haptic) triggerHaptic('impactLight');
188
+ if (haptic !== false) triggerHaptic('impactLight');
188
189
  const direction = delta > 0 ? 1 : -1;
189
190
  const distance = theme.components.dateRangePicker?.monthSlideDistance ?? 32;
190
191
  const outDuration = theme.components.dateRangePicker?.monthSlideOutDuration ?? theme.motion.duration.fast ?? 140;
@@ -215,12 +216,13 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
215
216
  const next = addMonths(anchor, delta);
216
217
  setAnchor(next);
217
218
  AccessibilityInfo.announceForAccessibility(formatMonthYear(next, locale));
218
- }, [anchor, locale, monthFade, monthSlide, theme.components.dateRangePicker, theme.motion.duration.fast]);
219
+ }, [anchor, haptic, locale, monthFade, monthSlide, theme.components.dateRangePicker, theme.motion.duration.fast]);
219
220
  const goPrev = useCallback(() => animateMonthChange(-1), [animateMonthChange]);
220
221
  const goNext = useCallback(() => animateMonthChange(1), [animateMonthChange]);
221
222
  const pressDay = useCallback(cell => {
222
223
  if (isDisabled(cell.date)) return;
223
- if (theme.components.dateRangePicker?.haptic) triggerHaptic('selection');
224
+ const h = resolveHaptic(haptic, 'selection');
225
+ if (h) triggerHaptic(h);
224
226
  const target = cell.date;
225
227
 
226
228
  // First tap: pick start. Or, when there's already a complete range, restart.
@@ -239,7 +241,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
239
241
  } else {
240
242
  if (maxRange && daysBetween(pendingStart, target) + 1 > maxRange) {
241
243
  // Reject ranges longer than maxRange — restart from the new tap.
242
- if (theme.components.dateRangePicker?.haptic) triggerHaptic('notificationWarning');
244
+ if (haptic !== false) triggerHaptic('notificationWarning');
243
245
  setPendingStart(target);
244
246
  setPendingEnd(null);
245
247
  return;
@@ -250,7 +252,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
250
252
  if (!cell.inMonth) {
251
253
  setAnchor(new Date(cell.date.getFullYear(), cell.date.getMonth(), 1));
252
254
  }
253
- }, [isDisabled, pendingStart, pendingEnd, maxRange, theme.components.dateRangePicker]);
255
+ }, [isDisabled, pendingStart, pendingEnd, maxRange, haptic]);
254
256
  const handleCloseModal = useCallback(() => {
255
257
  if (isControlled) {
256
258
  onClose?.();
@@ -274,18 +276,18 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
274
276
  });
275
277
  }, [backdrop, handleCloseModal, sheet, theme.motion.duration.fast]);
276
278
  const handleCancel = useCallback(() => {
277
- if (theme.components.dateRangePicker?.haptic) triggerHaptic('selection');
279
+ if (haptic !== false) triggerHaptic('selection');
278
280
  handleClose();
279
- }, [handleClose, theme.components.dateRangePicker]);
281
+ }, [handleClose, haptic]);
280
282
  const handleConfirm = useCallback(() => {
281
283
  if (!pendingStart || !pendingEnd) return;
282
- if (theme.components.dateRangePicker?.haptic) triggerHaptic('notificationSuccess');
284
+ if (haptic !== false) triggerHaptic('notificationSuccess');
283
285
  onChange?.({
284
286
  start: pendingStart,
285
287
  end: pendingEnd
286
288
  });
287
289
  handleClose();
288
- }, [handleClose, onChange, pendingStart, pendingEnd, theme.components.dateRangePicker]);
290
+ }, [handleClose, onChange, pendingStart, pendingEnd, haptic]);
289
291
  const sheetTranslate = sheet.interpolate({
290
292
  inputRange: [0, 1],
291
293
  outputRange: [320, 0]
@@ -3,7 +3,7 @@
3
3
  import React, { useCallback, useMemo } from 'react';
4
4
  import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { fontFor, useTheme } from "../../theme/index.js";
6
- import { triggerHaptic } from "../../utils/hapticUtils.js";
6
+ import { resolveHaptic, triggerHaptic } from "../../utils/hapticUtils.js";
7
7
  import { Modal } from "../Modal/Modal.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  const Dialog = props => {
@@ -25,6 +25,7 @@ const Dialog = props => {
25
25
  actionsRowStyle,
26
26
  actionButtonStyle,
27
27
  actionTextStyle,
28
+ haptic,
28
29
  testID
29
30
  } = props;
30
31
  const theme = useTheme();
@@ -36,12 +37,13 @@ const Dialog = props => {
36
37
  const actionButtonMinHeight = dialogTokens?.actionButtonMinHeight ?? 44;
37
38
  const handleAction = useCallback(action => {
38
39
  if (action.loading) return;
39
- if (dialogTokens?.actionHaptic) triggerHaptic('selection');
40
+ const h = resolveHaptic(haptic, 'selection');
41
+ if (h) triggerHaptic(h);
40
42
  action.onPress();
41
43
  if (dismissOnAction) {
42
44
  onClose();
43
45
  }
44
- }, [dismissOnAction, onClose, dialogTokens?.actionHaptic]);
46
+ }, [dismissOnAction, onClose, haptic]);
45
47
  return /*#__PURE__*/_jsx(Modal, {
46
48
  visible: visible,
47
49
  onRequestClose: onClose,
@@ -26,7 +26,7 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
26
26
  import Animated, { Extrapolation, interpolate, runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
27
27
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
28
28
  import { useTheme } from "../../theme/index.js";
29
- import { triggerHaptic } from "../../utils/index.js";
29
+ import { resolveHaptic, triggerHaptic } from "../../utils/index.js";
30
30
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
31
31
  const DEFAULT_CLOSE_VELOCITY_THRESHOLD = 1000;
32
32
  const DEFAULT_DRAG_ACTIVATION_OFFSET = 10;
@@ -40,6 +40,7 @@ const Drawer = /*#__PURE__*/forwardRef((props, ref) => {
40
40
  enableSwipe = true,
41
41
  enableBackdropPress = true,
42
42
  backdropOpacity = 0.5,
43
+ haptic,
43
44
  containerStyle,
44
45
  children,
45
46
  accessibilityLabel,
@@ -219,9 +220,10 @@ const Drawer = /*#__PURE__*/forwardRef((props, ref) => {
219
220
  const styles = useMemo(() => buildStyles(theme), [theme]);
220
221
  const handleBackdropPress = useCallback(() => {
221
222
  if (!enableBackdropPress) return;
222
- if (drawerTokens?.backdropPressHaptic) triggerHaptic('selection');
223
+ const h = resolveHaptic(haptic, 'selection');
224
+ if (h) triggerHaptic(h);
223
225
  close();
224
- }, [enableBackdropPress, close, drawerTokens?.backdropPressHaptic]);
226
+ }, [enableBackdropPress, close, haptic]);
225
227
 
226
228
  // Per-side container layout — must be a hook on every render path so we
227
229
  // can't gate it on the lazy-mount early-return below.
@@ -5,7 +5,7 @@ import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-nativ
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
7
7
  import { usePressAnimation } from "../../hooks/usePressAnimation.js";
8
- import { triggerHaptic } from "../../utils/hapticUtils.js";
8
+ import { triggerHaptic, resolveHaptic } from "../../utils/hapticUtils.js";
9
9
 
10
10
  // Local shape mirror — see types.ts FloatingActionButtonTokens for the canonical
11
11
  // definition. Declared here so the component can read tokens before types.ts is
@@ -64,6 +64,7 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
64
64
  isScrolling = false,
65
65
  accessibilityLabel,
66
66
  accessibilityHint,
67
+ haptic,
67
68
  style,
68
69
  containerStyle,
69
70
  labelStyle,
@@ -83,7 +84,6 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
83
84
  const fabTokens = theme.components.floatingActionButton;
84
85
  const edgeOffset = fabTokens?.edgeOffset ?? 24;
85
86
  const defaultBottomOffset = fabTokens?.bottomOffset ?? 24;
86
- const pressHaptic = fabTokens?.pressHaptic ?? false;
87
87
  const hideAnim = useRef(createAnimatedValue(0)).current;
88
88
  useEffect(() => {
89
89
  if (!hideOnScroll) {
@@ -109,7 +109,8 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
109
109
  });
110
110
  const handlePress = _event => {
111
111
  if (disabled) return;
112
- if (pressHaptic) triggerHaptic('impactLight');
112
+ const h = resolveHaptic(haptic, 'impactLight');
113
+ if (h) triggerHaptic(h);
113
114
  onPress();
114
115
  };
115
116
  const renderedIcon = icon;
@@ -178,7 +179,7 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
178
179
  borderRadius: sizeStyles.diameter / 2
179
180
  }, theme.shadows.lg, {
180
181
  backgroundColor,
181
- opacity: disabled ? 0.7 : 1
182
+ opacity: disabled ? 0.55 : 1
182
183
  }],
183
184
  children: [renderedIcon, isExtended ? /*#__PURE__*/_jsx(Text, {
184
185
  style: [styles.label, {
@@ -224,6 +225,7 @@ const FloatingActionButtonGroup = props => {
224
225
  bottomOffset,
225
226
  size = 'md',
226
227
  accessibilityLabel = 'Quick actions',
228
+ haptic,
227
229
  containerStyle,
228
230
  secondaryActionStyle,
229
231
  labelPillStyle,
@@ -239,7 +241,6 @@ const FloatingActionButtonGroup = props => {
239
241
  const defaultBottomOffset = fabTokens?.bottomOffset ?? 24;
240
242
  const secondaryGap = fabTokens?.secondaryGap ?? 16;
241
243
  const staggerMs = fabTokens?.staggerMs ?? 50;
242
- const pressHaptic = fabTokens?.pressHaptic ?? false;
243
244
  const isControlled = typeof controlledOpen === 'boolean';
244
245
  const [internalOpen, setInternalOpen] = useState(defaultOpen);
245
246
  const isOpen = isControlled ? controlledOpen : internalOpen;
@@ -296,20 +297,21 @@ const FloatingActionButtonGroup = props => {
296
297
  };
297
298
  }, [isOpen, progress, theme.motion.duration.normal, theme.motion.easing.standard, actions.length, itemAnimsVersion, staggerMs]);
298
299
  const setOpen = useCallback(next => {
299
- if (pressHaptic) triggerHaptic('impactLight');
300
+ const h = resolveHaptic(haptic, 'impactLight');
301
+ if (h) triggerHaptic(h);
300
302
  if (!isControlled) setInternalOpen(next);
301
303
  onOpenChange?.(next);
302
- }, [isControlled, onOpenChange, pressHaptic]);
304
+ }, [isControlled, onOpenChange, haptic]);
303
305
  const handlePrimaryPress = useCallback(() => {
304
306
  setOpen(!isOpen);
305
307
  }, [isOpen, setOpen]);
306
308
  const handleActionPress = useCallback(action => {
307
- if (pressHaptic) triggerHaptic('selection');
309
+ if (haptic !== false) triggerHaptic('selection');
308
310
  action.onPress();
309
311
  // Close after action runs
310
312
  if (!isControlled) setInternalOpen(false);
311
313
  onOpenChange?.(false);
312
- }, [isControlled, onOpenChange, pressHaptic]);
314
+ }, [isControlled, onOpenChange, haptic]);
313
315
  const handleBackdropPress = useCallback(() => {
314
316
  if (isOpen) setOpen(false);
315
317
  }, [isOpen, setOpen]);