@webority-technologies/mobile-ui 0.0.1 → 0.0.3

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 (134) hide show
  1. package/README.md +2 -1
  2. package/lib/commonjs/components/AuthedImage/AuthedImage.js +225 -0
  3. package/lib/commonjs/components/AuthedImage/authedImageCache.js +253 -0
  4. package/lib/commonjs/components/AuthedImage/index.js +45 -0
  5. package/lib/commonjs/components/BottomSheet/BottomSheet.js +10 -4
  6. package/lib/commonjs/components/Drawer/Drawer.js +6 -3
  7. package/lib/commonjs/components/ErrorBoundary/ErrorBoundary.js +136 -0
  8. package/lib/commonjs/components/ErrorBoundary/index.js +27 -0
  9. package/lib/commonjs/components/ErrorBoundary/useErrorHandler.js +24 -0
  10. package/lib/commonjs/components/ErrorBoundary/withErrorBoundary.js +28 -0
  11. package/lib/commonjs/components/FilePicker/FilePicker.js +1 -1
  12. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +14 -1
  13. package/lib/commonjs/components/ImageGallery/ImageGallery.js +13 -0
  14. package/lib/commonjs/components/InlineSelect/InlineSelect.js +1 -1
  15. package/lib/commonjs/components/Input/Input.js +6 -0
  16. package/lib/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +62 -13
  17. package/lib/commonjs/components/LoaderOverlay/LoaderOverlay.js +203 -0
  18. package/lib/commonjs/components/LoaderOverlay/index.js +25 -0
  19. package/lib/commonjs/components/Markdown/Markdown.js +274 -0
  20. package/lib/commonjs/components/Markdown/index.js +13 -0
  21. package/lib/commonjs/components/Markdown/parse.js +229 -0
  22. package/lib/commonjs/components/OTPInput/OTPInput.js +5 -0
  23. package/lib/commonjs/components/OptionSheet/OptionSheet.js +3 -3
  24. package/lib/commonjs/components/PhoneNumberInput/PhoneNumberInput.js +1 -1
  25. package/lib/commonjs/components/Radio/Radio.js +9 -0
  26. package/lib/commonjs/components/Rating/Rating.js +5 -0
  27. package/lib/commonjs/components/RichTextEditor/RichTextEditor.js +311 -0
  28. package/lib/commonjs/components/RichTextEditor/index.js +20 -0
  29. package/lib/commonjs/components/RichTextEditor/markdownActions.js +213 -0
  30. package/lib/commonjs/components/SearchBar/SearchBar.js +7 -0
  31. package/lib/commonjs/components/Select/Select.js +1 -1
  32. package/lib/commonjs/components/Skeleton/Skeleton.js +17 -36
  33. package/lib/commonjs/components/Slider/Slider.js +21 -5
  34. package/lib/commonjs/components/Swipeable/Swipeable.js +5 -2
  35. package/lib/commonjs/components/TimePicker/TimePicker.js +11 -0
  36. package/lib/commonjs/components/index.js +230 -141
  37. package/lib/commonjs/form/FormContext.js +13 -1
  38. package/lib/commonjs/form/useField.js +13 -1
  39. package/lib/commonjs/theme/Gradient.js +44 -41
  40. package/lib/commonjs/theme/index.js +24 -2
  41. package/lib/commonjs/utils/hapticUtils.js +38 -18
  42. package/lib/module/components/AuthedImage/AuthedImage.js +221 -0
  43. package/lib/module/components/AuthedImage/authedImageCache.js +239 -0
  44. package/lib/module/components/AuthedImage/index.js +5 -0
  45. package/lib/module/components/BottomSheet/BottomSheet.js +10 -4
  46. package/lib/module/components/Drawer/Drawer.js +6 -3
  47. package/lib/module/components/ErrorBoundary/ErrorBoundary.js +130 -0
  48. package/lib/module/components/ErrorBoundary/index.js +6 -0
  49. package/lib/module/components/ErrorBoundary/useErrorHandler.js +20 -0
  50. package/lib/module/components/ErrorBoundary/withErrorBoundary.js +23 -0
  51. package/lib/module/components/FilePicker/FilePicker.js +1 -1
  52. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +14 -1
  53. package/lib/module/components/ImageGallery/ImageGallery.js +13 -0
  54. package/lib/module/components/InlineSelect/InlineSelect.js +1 -1
  55. package/lib/module/components/Input/Input.js +6 -0
  56. package/lib/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +63 -14
  57. package/lib/module/components/LoaderOverlay/LoaderOverlay.js +196 -0
  58. package/lib/module/components/LoaderOverlay/index.js +4 -0
  59. package/lib/module/components/Markdown/Markdown.js +268 -0
  60. package/lib/module/components/Markdown/index.js +4 -0
  61. package/lib/module/components/Markdown/parse.js +223 -0
  62. package/lib/module/components/OTPInput/OTPInput.js +5 -0
  63. package/lib/module/components/OptionSheet/OptionSheet.js +3 -3
  64. package/lib/module/components/PhoneNumberInput/PhoneNumberInput.js +1 -1
  65. package/lib/module/components/Radio/Radio.js +9 -0
  66. package/lib/module/components/Rating/Rating.js +5 -0
  67. package/lib/module/components/RichTextEditor/RichTextEditor.js +306 -0
  68. package/lib/module/components/RichTextEditor/index.js +5 -0
  69. package/lib/module/components/RichTextEditor/markdownActions.js +207 -0
  70. package/lib/module/components/SearchBar/SearchBar.js +7 -0
  71. package/lib/module/components/Select/Select.js +1 -1
  72. package/lib/module/components/Skeleton/Skeleton.js +17 -36
  73. package/lib/module/components/Slider/Slider.js +21 -5
  74. package/lib/module/components/Swipeable/Swipeable.js +5 -2
  75. package/lib/module/components/TimePicker/TimePicker.js +11 -0
  76. package/lib/module/components/index.js +5 -0
  77. package/lib/module/form/FormContext.js +13 -1
  78. package/lib/module/form/useField.js +13 -1
  79. package/lib/module/theme/Gradient.js +41 -38
  80. package/lib/module/theme/index.js +17 -1
  81. package/lib/module/utils/hapticUtils.js +37 -18
  82. package/lib/typescript/commonjs/components/AuthedImage/AuthedImage.d.ts +22 -0
  83. package/lib/typescript/commonjs/components/AuthedImage/authedImageCache.d.ts +48 -0
  84. package/lib/typescript/commonjs/components/AuthedImage/index.d.ts +5 -0
  85. package/lib/typescript/commonjs/components/Button/Button.d.ts +1 -3
  86. package/lib/typescript/commonjs/components/Card/Card.d.ts +1 -2
  87. package/lib/typescript/commonjs/components/ErrorBoundary/ErrorBoundary.d.ts +38 -0
  88. package/lib/typescript/commonjs/components/ErrorBoundary/index.d.ts +5 -0
  89. package/lib/typescript/commonjs/components/ErrorBoundary/useErrorHandler.d.ts +9 -0
  90. package/lib/typescript/commonjs/components/ErrorBoundary/withErrorBoundary.d.ts +9 -0
  91. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +15 -0
  92. package/lib/typescript/commonjs/components/LoaderOverlay/LoaderOverlay.d.ts +25 -0
  93. package/lib/typescript/commonjs/components/LoaderOverlay/index.d.ts +3 -0
  94. package/lib/typescript/commonjs/components/Markdown/Markdown.d.ts +29 -0
  95. package/lib/typescript/commonjs/components/Markdown/index.d.ts +3 -0
  96. package/lib/typescript/commonjs/components/Markdown/parse.d.ts +58 -0
  97. package/lib/typescript/commonjs/components/OptionSheet/OptionSheet.d.ts +8 -1
  98. package/lib/typescript/commonjs/components/RichTextEditor/RichTextEditor.d.ts +48 -0
  99. package/lib/typescript/commonjs/components/RichTextEditor/index.d.ts +5 -0
  100. package/lib/typescript/commonjs/components/RichTextEditor/markdownActions.d.ts +35 -0
  101. package/lib/typescript/commonjs/components/Skeleton/Skeleton.d.ts +1 -1
  102. package/lib/typescript/commonjs/components/index.d.ts +10 -0
  103. package/lib/typescript/commonjs/form/FormContext.d.ts +13 -3
  104. package/lib/typescript/commonjs/theme/Gradient.d.ts +18 -3
  105. package/lib/typescript/commonjs/theme/index.d.ts +16 -1
  106. package/lib/typescript/commonjs/theme/types.d.ts +2 -2
  107. package/lib/typescript/commonjs/utils/hapticUtils.d.ts +4 -0
  108. package/lib/typescript/module/components/AuthedImage/AuthedImage.d.ts +22 -0
  109. package/lib/typescript/module/components/AuthedImage/authedImageCache.d.ts +48 -0
  110. package/lib/typescript/module/components/AuthedImage/index.d.ts +5 -0
  111. package/lib/typescript/module/components/Button/Button.d.ts +1 -3
  112. package/lib/typescript/module/components/Card/Card.d.ts +1 -2
  113. package/lib/typescript/module/components/ErrorBoundary/ErrorBoundary.d.ts +38 -0
  114. package/lib/typescript/module/components/ErrorBoundary/index.d.ts +5 -0
  115. package/lib/typescript/module/components/ErrorBoundary/useErrorHandler.d.ts +9 -0
  116. package/lib/typescript/module/components/ErrorBoundary/withErrorBoundary.d.ts +9 -0
  117. package/lib/typescript/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +15 -0
  118. package/lib/typescript/module/components/LoaderOverlay/LoaderOverlay.d.ts +25 -0
  119. package/lib/typescript/module/components/LoaderOverlay/index.d.ts +3 -0
  120. package/lib/typescript/module/components/Markdown/Markdown.d.ts +29 -0
  121. package/lib/typescript/module/components/Markdown/index.d.ts +3 -0
  122. package/lib/typescript/module/components/Markdown/parse.d.ts +58 -0
  123. package/lib/typescript/module/components/OptionSheet/OptionSheet.d.ts +8 -1
  124. package/lib/typescript/module/components/RichTextEditor/RichTextEditor.d.ts +48 -0
  125. package/lib/typescript/module/components/RichTextEditor/index.d.ts +5 -0
  126. package/lib/typescript/module/components/RichTextEditor/markdownActions.d.ts +35 -0
  127. package/lib/typescript/module/components/Skeleton/Skeleton.d.ts +1 -1
  128. package/lib/typescript/module/components/index.d.ts +10 -0
  129. package/lib/typescript/module/form/FormContext.d.ts +13 -3
  130. package/lib/typescript/module/theme/Gradient.d.ts +18 -3
  131. package/lib/typescript/module/theme/index.d.ts +16 -1
  132. package/lib/typescript/module/theme/types.d.ts +2 -2
  133. package/lib/typescript/module/utils/hapticUtils.d.ts +4 -0
  134. package/package.json +20 -13
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ErrorBoundary = void 0;
7
+ var _mobileCore = require("@webority-technologies/mobile-core");
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _reactNative = require("react-native");
10
+ var _index = require("../../theme/index.js");
11
+ var _index2 = require("../EmptyState/index.js");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
+ const COMPONENT_STACK_LIMIT = 500;
15
+ const DEFAULT_TITLE = 'Something went wrong';
16
+ const DEFAULT_MESSAGE = 'An unexpected error occurred. Please try again.';
17
+ const hasResetKeysChanged = (prev, next) => {
18
+ if (prev === next) {
19
+ return false;
20
+ }
21
+ if (!prev || !next || prev.length !== next.length) {
22
+ return true;
23
+ }
24
+ return prev.some((value, index) => value !== next[index]);
25
+ };
26
+ const DefaultFallback = ({
27
+ error,
28
+ reset,
29
+ title,
30
+ message,
31
+ testID
32
+ }) => {
33
+ const theme = (0, _index.useTheme)();
34
+ const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
35
+ const showRawError = typeof __DEV__ !== 'undefined' && __DEV__;
36
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
37
+ style: styles.root,
38
+ testID: testID,
39
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.EmptyState, {
40
+ title: title ?? DEFAULT_TITLE,
41
+ description: message ?? DEFAULT_MESSAGE,
42
+ action: {
43
+ label: 'Try again',
44
+ onPress: reset,
45
+ testID: testID ? `${testID}-retry` : undefined
46
+ }
47
+ }), showRawError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
48
+ style: styles.devError,
49
+ testID: testID ? `${testID}-dev-error` : undefined,
50
+ children: error.message
51
+ }) : null]
52
+ });
53
+ };
54
+ const buildStyles = theme => _reactNative.StyleSheet.create({
55
+ root: {
56
+ flex: 1,
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ backgroundColor: theme.colors.background.primary,
60
+ padding: theme.spacing.lg
61
+ },
62
+ devError: {
63
+ marginTop: theme.spacing.md,
64
+ color: theme.colors.error,
65
+ fontSize: theme.typography.fontSize.sm,
66
+ textAlign: 'center'
67
+ }
68
+ });
69
+
70
+ /**
71
+ * Catches render errors in its subtree and shows a themed fallback instead of
72
+ * a white screen. Mount this once near the navigation root per the fleet
73
+ * convention, and optionally again around any subtree that should recover
74
+ * independently (a single failed card should not blank the whole screen).
75
+ */
76
+ class ErrorBoundary extends _react.default.Component {
77
+ state = {
78
+ error: null
79
+ };
80
+ static getDerivedStateFromError(error) {
81
+ return {
82
+ error
83
+ };
84
+ }
85
+ componentDidUpdate(prevProps) {
86
+ if (this.state.error && hasResetKeysChanged(prevProps.resetKeys, this.props.resetKeys)) {
87
+ this.reset();
88
+ }
89
+ }
90
+ componentDidCatch(error, info) {
91
+ const componentStack = (info.componentStack ?? '').slice(0, COMPONENT_STACK_LIMIT);
92
+ _mobileCore.Logger.recordError(error, {
93
+ errorType: 'RenderError',
94
+ componentStack
95
+ });
96
+ try {
97
+ this.props.onError?.(error, {
98
+ componentStack
99
+ });
100
+ } catch (onErrorError) {
101
+ _mobileCore.Logger.error('[@webority-technologies/mobile-ui] ErrorBoundary onError threw; the boundary itself is unaffected.', onErrorError);
102
+ }
103
+ }
104
+ reset = () => {
105
+ this.setState({
106
+ error: null
107
+ });
108
+ this.props.onReset?.();
109
+ };
110
+ render() {
111
+ const {
112
+ error
113
+ } = this.state;
114
+ if (!error) {
115
+ return this.props.children;
116
+ }
117
+ const {
118
+ fallback,
119
+ title,
120
+ message,
121
+ testID
122
+ } = this.props;
123
+ if (fallback !== undefined) {
124
+ return typeof fallback === 'function' ? fallback(error, this.reset) : fallback;
125
+ }
126
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(DefaultFallback, {
127
+ error: error,
128
+ reset: this.reset,
129
+ title: title,
130
+ message: message,
131
+ testID: testID
132
+ });
133
+ }
134
+ }
135
+ exports.ErrorBoundary = ErrorBoundary;
136
+ //# sourceMappingURL=ErrorBoundary.js.map
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "ErrorBoundary", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _ErrorBoundary.ErrorBoundary;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "useErrorHandler", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _useErrorHandler.useErrorHandler;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "withErrorBoundary", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _withErrorBoundary.withErrorBoundary;
22
+ }
23
+ });
24
+ var _ErrorBoundary = require("./ErrorBoundary.js");
25
+ var _useErrorHandler = require("./useErrorHandler.js");
26
+ var _withErrorBoundary = require("./withErrorBoundary.js");
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useErrorHandler = void 0;
7
+ var _react = require("react");
8
+ /**
9
+ * Returns a stable callback for surfacing an error caught outside React's
10
+ * render/commit cycle (an async callback, a rejected promise, a native event
11
+ * handler) to the nearest ErrorBoundary. React only catches synchronous
12
+ * render errors on its own; re-throwing during a state update is the
13
+ * documented way to route an async error into that same path.
14
+ */
15
+ const useErrorHandler = () => {
16
+ const [, setState] = (0, _react.useState)();
17
+ return (0, _react.useCallback)(error => {
18
+ setState(() => {
19
+ throw error instanceof Error ? error : new Error(String(error));
20
+ });
21
+ }, []);
22
+ };
23
+ exports.useErrorHandler = useErrorHandler;
24
+ //# sourceMappingURL=useErrorHandler.js.map
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.withErrorBoundary = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _ErrorBoundary = require("./ErrorBoundary.js");
9
+ var _jsxRuntime = require("react/jsx-runtime");
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ /**
12
+ * Wraps a component in an `ErrorBoundary`. Prefer this over hand-wrapping a
13
+ * screen at every call site when the same fallback/reset behaviour applies
14
+ * everywhere that component is rendered.
15
+ */
16
+ const withErrorBoundary = (Component, boundaryProps) => {
17
+ const displayName = Component.displayName ?? Component.name ?? 'Component';
18
+ const Wrapped = props => /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorBoundary.ErrorBoundary, {
19
+ ...boundaryProps,
20
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, {
21
+ ...props
22
+ })
23
+ });
24
+ Wrapped.displayName = `withErrorBoundary(${displayName})`;
25
+ return Wrapped;
26
+ };
27
+ exports.withErrorBoundary = withErrorBoundary;
28
+ //# sourceMappingURL=withErrorBoundary.js.map
@@ -610,7 +610,7 @@ const FilePicker = exports.FilePicker = /*#__PURE__*/(0, _react.forwardRef)((pro
610
610
  title: labels.sheetTitle,
611
611
  options: sources,
612
612
  labelOf: s => s.label,
613
- valueOf: s => s.key,
613
+ getValue: s => s.key,
614
614
  disabledOf: () => false,
615
615
  descriptionOf: s => s.description,
616
616
  iconOf: s => s.icon,
@@ -395,7 +395,20 @@ const FloatingActionButtonGroup = props => {
395
395
  importantForAccessibility: "no",
396
396
  testID: testID ? `${testID}-backdrop` : undefined
397
397
  })
398
- }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
398
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View
399
+ // The container is absolutely positioned and sized to the primary FAB,
400
+ // while the secondary actions sit ABOVE it via negative translateY, so
401
+ // they render outside their parent's bounds. Without box-none the
402
+ // container is itself a touch target across its box, and on Android a
403
+ // child drawn outside the parent's bounds does not receive touches at
404
+ // all. box-none makes the container transparent to touches while its
405
+ // children stay interactive, which is what an expanded menu needs.
406
+ //
407
+ // A consumer app carried this as a patch-package fix against two
408
+ // published versions rather than it landing here, which is how the
409
+ // library kept shipping the bug.
410
+ , {
411
+ pointerEvents: "box-none",
399
412
  style: [positionStyle, containerStyle],
400
413
  accessibilityRole: 'menu',
401
414
  testID: testID,
@@ -398,6 +398,15 @@ const ZoomableImage = ({
398
398
  savedScale.value = minScale;
399
399
  savedTranslateX.value = 0;
400
400
  savedTranslateY.value = 0;
401
+ // Mutation-tested equivalent under the jest reanimated mock (withSpring
402
+ // resolves synchronously to its target there): with this return removed,
403
+ // execution falls into `scale.value > maxScale` (always false, since
404
+ // minScale <= maxScale) and then `savedScale.value = scale.value`, which
405
+ // reads back the minScale just assigned above — same final state, so no
406
+ // jest assertion can distinguish the two. On a real device withSpring is
407
+ // asynchronous, so that fallthrough read could observe a stale
408
+ // mid-animation scale instead; kept as an explicit early return for that
409
+ // reason even though it isn't independently testable here.
401
410
  return;
402
411
  }
403
412
  if (scale.value > maxScale) {
@@ -560,6 +569,10 @@ const buildLightboxStyles = theme => _reactNative.StyleSheet.create({
560
569
  paddingHorizontal: 24
561
570
  },
562
571
  captionText: {
572
+ // Fixed white, not a theme token, and deliberately so: the lightbox is an
573
+ // always-dark surface (#000 backdrop with rgba scrims above), so it does
574
+ // not follow the colour scheme. `text.primary` would be dark ink on black
575
+ // in light mode. Do not "themify" this.
563
576
  color: '#FFFFFF',
564
577
  fontSize: 14,
565
578
  lineHeight: 20,
@@ -150,7 +150,7 @@ const InlineSelect = exports.InlineSelect = /*#__PURE__*/(0, _react.forwardRef)(
150
150
  accessibilityLabel: label ?? placeholder ?? 'Options',
151
151
  options: options,
152
152
  labelOf: labelOf,
153
- valueOf: valueOf,
153
+ getValue: valueOf,
154
154
  disabledOf: disabledOf,
155
155
  descriptionOf: descriptionOf,
156
156
  iconOf: iconOf,
@@ -115,6 +115,12 @@ const Input = exports.Input = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =
115
115
  const shakeOnError = theme.components.input?.shakeOnError ?? false;
116
116
  const prevErrorRef = (0, _react.useRef)(hasError);
117
117
  (0, _react.useEffect)(() => {
118
+ // Equivalent-mutant note: removing this `return` cannot change behaviour.
119
+ // The line above it always sets `prevErrorRef.current = hasError` first,
120
+ // so falling through to the check below becomes `hasError &&
121
+ // !prevErrorRef.current` === `hasError && !hasError`, which is always
122
+ // false — the shake/haptic branch can never fire, and the redundant
123
+ // `prevErrorRef.current = hasError` reassignment at the bottom is a no-op.
118
124
  if (!shakeOnError) {
119
125
  prevErrorRef.current = hasError;
120
126
  return;
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = exports.KeyboardAwareScrollView = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
+ var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
10
+ var _index = require("../../theme/index.js");
9
11
  var _jsxRuntime = require("react/jsx-runtime");
10
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
13
  /**
@@ -20,11 +22,20 @@ const KeyboardAwareScrollView = exports.KeyboardAwareScrollView = /*#__PURE__*/(
20
22
  children,
21
23
  extraScrollHeight = 24,
22
24
  disabled = false,
25
+ safeAreaEdges = ['bottom'],
26
+ refreshing = false,
27
+ onRefresh,
28
+ keyboardOpeningTime = 250,
29
+ viewIsInsideTabBar = false,
30
+ resetScrollToCoords,
23
31
  contentContainerStyle,
32
+ refreshControl,
24
33
  onScroll,
25
34
  onLayout,
26
35
  ...rest
27
36
  } = props;
37
+ const theme = (0, _index.useTheme)();
38
+ const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
28
39
  const scrollRef = (0, _react.useRef)(null);
29
40
  const scrollYRef = (0, _react.useRef)(0);
30
41
  const viewportHeightRef = (0, _react.useRef)(0);
@@ -68,26 +79,35 @@ const KeyboardAwareScrollView = exports.KeyboardAwareScrollView = /*#__PURE__*/(
68
79
  // iOS exposes the will* events (smoother); Android only fires did*.
69
80
  const showEvent = _reactNative.Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
70
81
  const hideEvent = _reactNative.Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
71
- let rafId;
82
+ let timeoutId;
72
83
  const showSub = _reactNative.Keyboard.addListener(showEvent, e => {
73
84
  const height = e.endCoordinates?.height ?? 0;
74
85
  setKeyboardHeight(height);
75
- // Wait a frame so the padding is applied before we scroll. Tracked so
76
- // it can be cancelled on unmount (never runs against a torn-down tree).
77
- if (rafId != null) {
78
- cancelAnimationFrame(rafId);
86
+ // Wait for the keyboard's own animation to (roughly) finish before
87
+ // scrolling, so we measure against the settled viewport. Tracked so
88
+ // it can be cancelled on unmount or a second show before it fires.
89
+ if (timeoutId != null) {
90
+ clearTimeout(timeoutId);
91
+ }
92
+ timeoutId = setTimeout(() => scrollFocusedIntoView(height), keyboardOpeningTime);
93
+ });
94
+ const hideSub = _reactNative.Keyboard.addListener(hideEvent, () => {
95
+ setKeyboardHeight(0);
96
+ if (resetScrollToCoords) {
97
+ scrollRef.current?.scrollTo({
98
+ ...resetScrollToCoords,
99
+ animated: true
100
+ });
79
101
  }
80
- rafId = requestAnimationFrame(() => scrollFocusedIntoView(height));
81
102
  });
82
- const hideSub = _reactNative.Keyboard.addListener(hideEvent, () => setKeyboardHeight(0));
83
103
  return () => {
84
- if (rafId != null) {
85
- cancelAnimationFrame(rafId);
104
+ if (timeoutId != null) {
105
+ clearTimeout(timeoutId);
86
106
  }
87
107
  showSub.remove();
88
108
  hideSub.remove();
89
109
  };
90
- }, [disabled, scrollFocusedIntoView]);
110
+ }, [disabled, scrollFocusedIntoView, keyboardOpeningTime, resetScrollToCoords]);
91
111
  const handleLayout = (0, _react.useCallback)(e => {
92
112
  viewportHeightRef.current = e.nativeEvent.layout.height;
93
113
  onLayout?.(e);
@@ -96,6 +116,36 @@ const KeyboardAwareScrollView = exports.KeyboardAwareScrollView = /*#__PURE__*/(
96
116
  scrollYRef.current = e.nativeEvent.contentOffset.y;
97
117
  onScroll?.(e);
98
118
  }, [onScroll]);
119
+
120
+ // Borrowed from the spacing scale because the theme has no tab-bar height
121
+ // token, and 3xl (48) is the closest honest value: a react-navigation bottom
122
+ // tab bar is 49pt on iOS and 56dp on Android before safe-area padding. If a
123
+ // dedicated token is ever added, this should use it rather than a scale step
124
+ // that happens to be about right.
125
+ const tabBarInset = viewIsInsideTabBar ? theme.spacing['3xl'] : 0;
126
+ const includesBottomSafeArea = safeAreaEdges.includes('bottom');
127
+ // Once the keyboard is up it already extends to the physical bottom of the
128
+ // screen (past the home-indicator safe area), so adding the bottom inset
129
+ // on top of the keyboard height would double-count that strip of space.
130
+ const bottomInset = keyboardHeight > 0 ? keyboardHeight : includesBottomSafeArea ? insets.bottom : 0;
131
+ const safeAreaPadding = {
132
+ paddingBottom: bottomInset + tabBarInset,
133
+ ...(safeAreaEdges.includes('top') ? {
134
+ paddingTop: insets.top
135
+ } : null),
136
+ ...(safeAreaEdges.includes('left') ? {
137
+ paddingLeft: insets.left
138
+ } : null),
139
+ ...(safeAreaEdges.includes('right') ? {
140
+ paddingRight: insets.right
141
+ } : null)
142
+ };
143
+ const resolvedRefreshControl = refreshControl ?? (onRefresh ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.RefreshControl, {
144
+ refreshing: refreshing,
145
+ onRefresh: onRefresh,
146
+ tintColor: theme.colors.primary,
147
+ colors: [theme.colors.primary]
148
+ }) : undefined);
99
149
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
100
150
  ref: setRefs,
101
151
  onScroll: handleScroll,
@@ -103,9 +153,8 @@ const KeyboardAwareScrollView = exports.KeyboardAwareScrollView = /*#__PURE__*/(
103
153
  scrollEventThrottle: 16,
104
154
  keyboardShouldPersistTaps: "handled",
105
155
  keyboardDismissMode: _reactNative.Platform.OS === 'ios' ? 'interactive' : 'on-drag',
106
- contentContainerStyle: [contentContainerStyle, {
107
- paddingBottom: keyboardHeight
108
- }],
156
+ contentContainerStyle: [contentContainerStyle, safeAreaPadding],
157
+ refreshControl: resolvedRefreshControl,
109
158
  ...rest,
110
159
  children: children
111
160
  });
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useLoader = exports.loader = exports.LoaderOverlay = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _useReducedMotion = require("../../hooks/useReducedMotion.js");
10
+ var _index = require("../../theme/index.js");
11
+ var _index2 = require("../Spinner/index.js");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
+ const DEFAULT_SHOW_DELAY_MS = 150;
15
+ const DEFAULT_MIN_VISIBLE_MS = 400;
16
+ let state = {
17
+ refCount: 0,
18
+ message: undefined
19
+ };
20
+ const listeners = new Set();
21
+ const notify = () => {
22
+ for (const listener of listeners) {
23
+ listener();
24
+ }
25
+ };
26
+ const subscribe = listener => {
27
+ listeners.add(listener);
28
+ return () => {
29
+ listeners.delete(listener);
30
+ };
31
+ };
32
+ const getState = () => state;
33
+ const show = message => {
34
+ state = {
35
+ refCount: state.refCount + 1,
36
+ message: message ?? state.message
37
+ };
38
+ notify();
39
+ };
40
+ const hide = () => {
41
+ if (state.refCount <= 0) {
42
+ return;
43
+ }
44
+ const refCount = state.refCount - 1;
45
+ state = {
46
+ refCount,
47
+ message: refCount === 0 ? undefined : state.message
48
+ };
49
+ notify();
50
+ };
51
+ const reset = () => {
52
+ if (state.refCount === 0 && state.message === undefined) {
53
+ return;
54
+ }
55
+ state = {
56
+ refCount: 0,
57
+ message: undefined
58
+ };
59
+ notify();
60
+ };
61
+ const isVisible = () => state.refCount > 0;
62
+ const wrap = async (work, message) => {
63
+ show(message);
64
+ try {
65
+ return await (typeof work === 'function' ? work() : work);
66
+ } finally {
67
+ hide();
68
+ }
69
+ };
70
+ const loader = exports.loader = {
71
+ show,
72
+ hide,
73
+ wrap,
74
+ isVisible,
75
+ reset
76
+ };
77
+ const useLoader = () => loader;
78
+ exports.useLoader = useLoader;
79
+ const LoaderOverlay = ({
80
+ renderIndicator,
81
+ backdropColor,
82
+ showDelayMs = DEFAULT_SHOW_DELAY_MS,
83
+ minVisibleMs = DEFAULT_MIN_VISIBLE_MS,
84
+ testID
85
+ }) => {
86
+ const theme = (0, _index.useTheme)();
87
+ const reducedMotion = (0, _useReducedMotion.useReducedMotion)();
88
+ const [snapshot, setSnapshot] = (0, _react.useState)(getState);
89
+ const [visible, setVisible] = (0, _react.useState)(false);
90
+ const visibleRef = (0, _react.useRef)(false);
91
+ const showTimerRef = (0, _react.useRef)(null);
92
+ const hideTimerRef = (0, _react.useRef)(null);
93
+ const shownAtRef = (0, _react.useRef)(null);
94
+ const fade = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
95
+ (0, _react.useEffect)(() => subscribe(() => setSnapshot(getState())), []);
96
+ (0, _react.useEffect)(() => {
97
+ visibleRef.current = visible;
98
+ }, [visible]);
99
+ const active = snapshot.refCount > 0;
100
+ (0, _react.useEffect)(() => {
101
+ if (active) {
102
+ if (hideTimerRef.current !== null) {
103
+ clearTimeout(hideTimerRef.current);
104
+ hideTimerRef.current = null;
105
+ }
106
+ if (!visibleRef.current && showTimerRef.current === null) {
107
+ showTimerRef.current = setTimeout(() => {
108
+ showTimerRef.current = null;
109
+ shownAtRef.current = Date.now();
110
+ setVisible(true);
111
+ }, showDelayMs);
112
+ }
113
+ return;
114
+ }
115
+ if (showTimerRef.current !== null) {
116
+ clearTimeout(showTimerRef.current);
117
+ showTimerRef.current = null;
118
+ return;
119
+ }
120
+ if (visibleRef.current && hideTimerRef.current === null) {
121
+ const elapsed = shownAtRef.current !== null ? Date.now() - shownAtRef.current : minVisibleMs;
122
+ const remaining = Math.max(0, minVisibleMs - elapsed);
123
+ hideTimerRef.current = setTimeout(() => {
124
+ hideTimerRef.current = null;
125
+ shownAtRef.current = null;
126
+ setVisible(false);
127
+ }, remaining);
128
+ }
129
+ }, [active, showDelayMs, minVisibleMs]);
130
+ (0, _react.useEffect)(() => () => {
131
+ if (showTimerRef.current !== null) {
132
+ clearTimeout(showTimerRef.current);
133
+ }
134
+ if (hideTimerRef.current !== null) {
135
+ clearTimeout(hideTimerRef.current);
136
+ }
137
+ }, []);
138
+ (0, _react.useEffect)(() => {
139
+ if (!visible) {
140
+ fade.setValue(0);
141
+ return;
142
+ }
143
+ if (reducedMotion) {
144
+ fade.setValue(1);
145
+ return;
146
+ }
147
+ _reactNative.Animated.timing(fade, {
148
+ toValue: 1,
149
+ duration: theme.motion.duration.normal,
150
+ easing: _reactNative.Easing.bezier(...theme.motion.easing.standard),
151
+ useNativeDriver: true
152
+ }).start();
153
+ }, [visible, reducedMotion, fade, theme]);
154
+ (0, _react.useEffect)(() => {
155
+ if (!visible) {
156
+ return;
157
+ }
158
+ const subscription = _reactNative.BackHandler.addEventListener('hardwareBackPress', () => true);
159
+ return () => {
160
+ subscription.remove();
161
+ };
162
+ }, [visible]);
163
+ const message = snapshot.message;
164
+ (0, _react.useEffect)(() => {
165
+ if (visible && message) {
166
+ _reactNative.AccessibilityInfo.announceForAccessibility(message);
167
+ }
168
+ }, [visible, message]);
169
+ const styles = (0, _react.useMemo)(() => buildStyles(), []);
170
+ if (!visible) {
171
+ return null;
172
+ }
173
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
174
+ pointerEvents: "auto",
175
+ testID: testID,
176
+ accessible: true,
177
+ accessibilityViewIsModal: true,
178
+ accessibilityLiveRegion: "polite",
179
+ accessibilityLabel: message ?? 'Loading',
180
+ accessibilityRole: "progressbar",
181
+ accessibilityState: {
182
+ busy: true
183
+ },
184
+ style: [styles.overlay, {
185
+ backgroundColor: backdropColor ?? theme.colors.background.overlay,
186
+ opacity: fade
187
+ }],
188
+ children: renderIndicator ? renderIndicator(message) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.Spinner, {
189
+ size: "lg",
190
+ message: message
191
+ })
192
+ });
193
+ };
194
+ exports.LoaderOverlay = LoaderOverlay;
195
+ const buildStyles = () => _reactNative.StyleSheet.create({
196
+ overlay: {
197
+ ..._reactNative.StyleSheet.absoluteFill,
198
+ alignItems: 'center',
199
+ justifyContent: 'center',
200
+ zIndex: 1000
201
+ }
202
+ });
203
+ //# sourceMappingURL=LoaderOverlay.js.map
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "LoaderOverlay", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _LoaderOverlay.LoaderOverlay;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "loader", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _LoaderOverlay.loader;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "useLoader", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _LoaderOverlay.useLoader;
22
+ }
23
+ });
24
+ var _LoaderOverlay = require("./LoaderOverlay.js");
25
+ //# sourceMappingURL=index.js.map