@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
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
4
- import { Keyboard, Platform, ScrollView, TextInput } from 'react-native';
4
+ import { Keyboard, Platform, RefreshControl, ScrollView, TextInput } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
+ import { useTheme } from "../../theme/index.js";
5
7
  import { jsx as _jsx } from "react/jsx-runtime";
6
8
  /**
7
9
  * A ScrollView that keeps the focused input visible above the keyboard — no
@@ -15,11 +17,20 @@ const KeyboardAwareScrollView = /*#__PURE__*/forwardRef((props, ref) => {
15
17
  children,
16
18
  extraScrollHeight = 24,
17
19
  disabled = false,
20
+ safeAreaEdges = ['bottom'],
21
+ refreshing = false,
22
+ onRefresh,
23
+ keyboardOpeningTime = 250,
24
+ viewIsInsideTabBar = false,
25
+ resetScrollToCoords,
18
26
  contentContainerStyle,
27
+ refreshControl,
19
28
  onScroll,
20
29
  onLayout,
21
30
  ...rest
22
31
  } = props;
32
+ const theme = useTheme();
33
+ const insets = useSafeAreaInsets();
23
34
  const scrollRef = useRef(null);
24
35
  const scrollYRef = useRef(0);
25
36
  const viewportHeightRef = useRef(0);
@@ -63,26 +74,35 @@ const KeyboardAwareScrollView = /*#__PURE__*/forwardRef((props, ref) => {
63
74
  // iOS exposes the will* events (smoother); Android only fires did*.
64
75
  const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
65
76
  const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
66
- let rafId;
77
+ let timeoutId;
67
78
  const showSub = Keyboard.addListener(showEvent, e => {
68
79
  const height = e.endCoordinates?.height ?? 0;
69
80
  setKeyboardHeight(height);
70
- // Wait a frame so the padding is applied before we scroll. Tracked so
71
- // it can be cancelled on unmount (never runs against a torn-down tree).
72
- if (rafId != null) {
73
- cancelAnimationFrame(rafId);
81
+ // Wait for the keyboard's own animation to (roughly) finish before
82
+ // scrolling, so we measure against the settled viewport. Tracked so
83
+ // it can be cancelled on unmount or a second show before it fires.
84
+ if (timeoutId != null) {
85
+ clearTimeout(timeoutId);
86
+ }
87
+ timeoutId = setTimeout(() => scrollFocusedIntoView(height), keyboardOpeningTime);
88
+ });
89
+ const hideSub = Keyboard.addListener(hideEvent, () => {
90
+ setKeyboardHeight(0);
91
+ if (resetScrollToCoords) {
92
+ scrollRef.current?.scrollTo({
93
+ ...resetScrollToCoords,
94
+ animated: true
95
+ });
74
96
  }
75
- rafId = requestAnimationFrame(() => scrollFocusedIntoView(height));
76
97
  });
77
- const hideSub = Keyboard.addListener(hideEvent, () => setKeyboardHeight(0));
78
98
  return () => {
79
- if (rafId != null) {
80
- cancelAnimationFrame(rafId);
99
+ if (timeoutId != null) {
100
+ clearTimeout(timeoutId);
81
101
  }
82
102
  showSub.remove();
83
103
  hideSub.remove();
84
104
  };
85
- }, [disabled, scrollFocusedIntoView]);
105
+ }, [disabled, scrollFocusedIntoView, keyboardOpeningTime, resetScrollToCoords]);
86
106
  const handleLayout = useCallback(e => {
87
107
  viewportHeightRef.current = e.nativeEvent.layout.height;
88
108
  onLayout?.(e);
@@ -91,6 +111,36 @@ const KeyboardAwareScrollView = /*#__PURE__*/forwardRef((props, ref) => {
91
111
  scrollYRef.current = e.nativeEvent.contentOffset.y;
92
112
  onScroll?.(e);
93
113
  }, [onScroll]);
114
+
115
+ // Borrowed from the spacing scale because the theme has no tab-bar height
116
+ // token, and 3xl (48) is the closest honest value: a react-navigation bottom
117
+ // tab bar is 49pt on iOS and 56dp on Android before safe-area padding. If a
118
+ // dedicated token is ever added, this should use it rather than a scale step
119
+ // that happens to be about right.
120
+ const tabBarInset = viewIsInsideTabBar ? theme.spacing['3xl'] : 0;
121
+ const includesBottomSafeArea = safeAreaEdges.includes('bottom');
122
+ // Once the keyboard is up it already extends to the physical bottom of the
123
+ // screen (past the home-indicator safe area), so adding the bottom inset
124
+ // on top of the keyboard height would double-count that strip of space.
125
+ const bottomInset = keyboardHeight > 0 ? keyboardHeight : includesBottomSafeArea ? insets.bottom : 0;
126
+ const safeAreaPadding = {
127
+ paddingBottom: bottomInset + tabBarInset,
128
+ ...(safeAreaEdges.includes('top') ? {
129
+ paddingTop: insets.top
130
+ } : null),
131
+ ...(safeAreaEdges.includes('left') ? {
132
+ paddingLeft: insets.left
133
+ } : null),
134
+ ...(safeAreaEdges.includes('right') ? {
135
+ paddingRight: insets.right
136
+ } : null)
137
+ };
138
+ const resolvedRefreshControl = refreshControl ?? (onRefresh ? /*#__PURE__*/_jsx(RefreshControl, {
139
+ refreshing: refreshing,
140
+ onRefresh: onRefresh,
141
+ tintColor: theme.colors.primary,
142
+ colors: [theme.colors.primary]
143
+ }) : undefined);
94
144
  return /*#__PURE__*/_jsx(ScrollView, {
95
145
  ref: setRefs,
96
146
  onScroll: handleScroll,
@@ -98,9 +148,8 @@ const KeyboardAwareScrollView = /*#__PURE__*/forwardRef((props, ref) => {
98
148
  scrollEventThrottle: 16,
99
149
  keyboardShouldPersistTaps: "handled",
100
150
  keyboardDismissMode: Platform.OS === 'ios' ? 'interactive' : 'on-drag',
101
- contentContainerStyle: [contentContainerStyle, {
102
- paddingBottom: keyboardHeight
103
- }],
151
+ contentContainerStyle: [contentContainerStyle, safeAreaPadding],
152
+ refreshControl: resolvedRefreshControl,
104
153
  ...rest,
105
154
  children: children
106
155
  });
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+
3
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
4
+ import { AccessibilityInfo, Animated, BackHandler, Easing, StyleSheet } from 'react-native';
5
+ import { useReducedMotion } from "../../hooks/useReducedMotion.js";
6
+ import { createAnimatedValue, useTheme } from "../../theme/index.js";
7
+ import { Spinner } from "../Spinner/index.js";
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ const DEFAULT_SHOW_DELAY_MS = 150;
10
+ const DEFAULT_MIN_VISIBLE_MS = 400;
11
+ let state = {
12
+ refCount: 0,
13
+ message: undefined
14
+ };
15
+ const listeners = new Set();
16
+ const notify = () => {
17
+ for (const listener of listeners) {
18
+ listener();
19
+ }
20
+ };
21
+ const subscribe = listener => {
22
+ listeners.add(listener);
23
+ return () => {
24
+ listeners.delete(listener);
25
+ };
26
+ };
27
+ const getState = () => state;
28
+ const show = message => {
29
+ state = {
30
+ refCount: state.refCount + 1,
31
+ message: message ?? state.message
32
+ };
33
+ notify();
34
+ };
35
+ const hide = () => {
36
+ if (state.refCount <= 0) {
37
+ return;
38
+ }
39
+ const refCount = state.refCount - 1;
40
+ state = {
41
+ refCount,
42
+ message: refCount === 0 ? undefined : state.message
43
+ };
44
+ notify();
45
+ };
46
+ const reset = () => {
47
+ if (state.refCount === 0 && state.message === undefined) {
48
+ return;
49
+ }
50
+ state = {
51
+ refCount: 0,
52
+ message: undefined
53
+ };
54
+ notify();
55
+ };
56
+ const isVisible = () => state.refCount > 0;
57
+ const wrap = async (work, message) => {
58
+ show(message);
59
+ try {
60
+ return await (typeof work === 'function' ? work() : work);
61
+ } finally {
62
+ hide();
63
+ }
64
+ };
65
+ export const loader = {
66
+ show,
67
+ hide,
68
+ wrap,
69
+ isVisible,
70
+ reset
71
+ };
72
+ export const useLoader = () => loader;
73
+ export const LoaderOverlay = ({
74
+ renderIndicator,
75
+ backdropColor,
76
+ showDelayMs = DEFAULT_SHOW_DELAY_MS,
77
+ minVisibleMs = DEFAULT_MIN_VISIBLE_MS,
78
+ testID
79
+ }) => {
80
+ const theme = useTheme();
81
+ const reducedMotion = useReducedMotion();
82
+ const [snapshot, setSnapshot] = useState(getState);
83
+ const [visible, setVisible] = useState(false);
84
+ const visibleRef = useRef(false);
85
+ const showTimerRef = useRef(null);
86
+ const hideTimerRef = useRef(null);
87
+ const shownAtRef = useRef(null);
88
+ const fade = useRef(createAnimatedValue(0)).current;
89
+ useEffect(() => subscribe(() => setSnapshot(getState())), []);
90
+ useEffect(() => {
91
+ visibleRef.current = visible;
92
+ }, [visible]);
93
+ const active = snapshot.refCount > 0;
94
+ useEffect(() => {
95
+ if (active) {
96
+ if (hideTimerRef.current !== null) {
97
+ clearTimeout(hideTimerRef.current);
98
+ hideTimerRef.current = null;
99
+ }
100
+ if (!visibleRef.current && showTimerRef.current === null) {
101
+ showTimerRef.current = setTimeout(() => {
102
+ showTimerRef.current = null;
103
+ shownAtRef.current = Date.now();
104
+ setVisible(true);
105
+ }, showDelayMs);
106
+ }
107
+ return;
108
+ }
109
+ if (showTimerRef.current !== null) {
110
+ clearTimeout(showTimerRef.current);
111
+ showTimerRef.current = null;
112
+ return;
113
+ }
114
+ if (visibleRef.current && hideTimerRef.current === null) {
115
+ const elapsed = shownAtRef.current !== null ? Date.now() - shownAtRef.current : minVisibleMs;
116
+ const remaining = Math.max(0, minVisibleMs - elapsed);
117
+ hideTimerRef.current = setTimeout(() => {
118
+ hideTimerRef.current = null;
119
+ shownAtRef.current = null;
120
+ setVisible(false);
121
+ }, remaining);
122
+ }
123
+ }, [active, showDelayMs, minVisibleMs]);
124
+ useEffect(() => () => {
125
+ if (showTimerRef.current !== null) {
126
+ clearTimeout(showTimerRef.current);
127
+ }
128
+ if (hideTimerRef.current !== null) {
129
+ clearTimeout(hideTimerRef.current);
130
+ }
131
+ }, []);
132
+ useEffect(() => {
133
+ if (!visible) {
134
+ fade.setValue(0);
135
+ return;
136
+ }
137
+ if (reducedMotion) {
138
+ fade.setValue(1);
139
+ return;
140
+ }
141
+ Animated.timing(fade, {
142
+ toValue: 1,
143
+ duration: theme.motion.duration.normal,
144
+ easing: Easing.bezier(...theme.motion.easing.standard),
145
+ useNativeDriver: true
146
+ }).start();
147
+ }, [visible, reducedMotion, fade, theme]);
148
+ useEffect(() => {
149
+ if (!visible) {
150
+ return;
151
+ }
152
+ const subscription = BackHandler.addEventListener('hardwareBackPress', () => true);
153
+ return () => {
154
+ subscription.remove();
155
+ };
156
+ }, [visible]);
157
+ const message = snapshot.message;
158
+ useEffect(() => {
159
+ if (visible && message) {
160
+ AccessibilityInfo.announceForAccessibility(message);
161
+ }
162
+ }, [visible, message]);
163
+ const styles = useMemo(() => buildStyles(), []);
164
+ if (!visible) {
165
+ return null;
166
+ }
167
+ return /*#__PURE__*/_jsx(Animated.View, {
168
+ pointerEvents: "auto",
169
+ testID: testID,
170
+ accessible: true,
171
+ accessibilityViewIsModal: true,
172
+ accessibilityLiveRegion: "polite",
173
+ accessibilityLabel: message ?? 'Loading',
174
+ accessibilityRole: "progressbar",
175
+ accessibilityState: {
176
+ busy: true
177
+ },
178
+ style: [styles.overlay, {
179
+ backgroundColor: backdropColor ?? theme.colors.background.overlay,
180
+ opacity: fade
181
+ }],
182
+ children: renderIndicator ? renderIndicator(message) : /*#__PURE__*/_jsx(Spinner, {
183
+ size: "lg",
184
+ message: message
185
+ })
186
+ });
187
+ };
188
+ const buildStyles = () => StyleSheet.create({
189
+ overlay: {
190
+ ...StyleSheet.absoluteFill,
191
+ alignItems: 'center',
192
+ justifyContent: 'center',
193
+ zIndex: 1000
194
+ }
195
+ });
196
+ //# sourceMappingURL=LoaderOverlay.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { LoaderOverlay, loader, useLoader } from "./LoaderOverlay.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { Linking, Platform, StyleSheet, Text, View } from 'react-native';
5
+ import { useTheme } from "../../theme/index.js";
6
+ import { Skeleton } from "../Skeleton/index.js";
7
+ import { parseMarkdown } from "./parse.js";
8
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
+ const renderInline = (nodes, theme, onLinkPress, keyPrefix = 'i') => nodes.map((node, index) => {
10
+ const key = `${keyPrefix}-${index}`;
11
+ switch (node.type) {
12
+ case 'text':
13
+ return /*#__PURE__*/_jsx(Text, {
14
+ children: node.value
15
+ }, key);
16
+ case 'strong':
17
+ return /*#__PURE__*/_jsx(Text, {
18
+ style: {
19
+ fontWeight: theme.typography.fontWeight.bold
20
+ },
21
+ children: renderInline(node.children, theme, onLinkPress, key)
22
+ }, key);
23
+ case 'em':
24
+ return /*#__PURE__*/_jsx(Text, {
25
+ style: {
26
+ fontStyle: 'italic'
27
+ },
28
+ children: renderInline(node.children, theme, onLinkPress, key)
29
+ }, key);
30
+ case 'strike':
31
+ return /*#__PURE__*/_jsx(Text, {
32
+ style: {
33
+ textDecorationLine: 'line-through'
34
+ },
35
+ children: renderInline(node.children, theme, onLinkPress, key)
36
+ }, key);
37
+ case 'code':
38
+ return /*#__PURE__*/_jsx(Text, {
39
+ style: {
40
+ fontFamily: MONO,
41
+ backgroundColor: theme.colors.background.secondary,
42
+ color: theme.colors.text.primary
43
+ },
44
+ children: node.value
45
+ }, key);
46
+ case 'link':
47
+ return /*#__PURE__*/_jsx(Text, {
48
+ accessibilityRole: "link",
49
+ onPress: () => onLinkPress(node.href),
50
+ style: {
51
+ color: theme.colors.text.link,
52
+ textDecorationLine: 'underline'
53
+ },
54
+ children: renderInline(node.children, theme, onLinkPress, key)
55
+ }, key);
56
+ default:
57
+ return null;
58
+ }
59
+ });
60
+
61
+ /** The theme carries no monospace family, and code has to look like code. */
62
+ const MONO = Platform.select({
63
+ ios: 'Menlo',
64
+ android: 'monospace',
65
+ default: 'monospace'
66
+ });
67
+
68
+ /** The theme's scale is xs/sm/base/lg/xl/2xl/3xl/4xl — no md, no xxl. */
69
+ const HEADING_SCALE = {
70
+ 1: '3xl',
71
+ 2: '2xl',
72
+ 3: 'xl',
73
+ 4: 'lg',
74
+ 5: 'base',
75
+ 6: 'sm'
76
+ };
77
+
78
+ /**
79
+ * Renders a small, deliberate subset of markdown using the theme's own type
80
+ * scale and colours, with no dependency.
81
+ *
82
+ * It exists because Expo has no markdown module and the alternative on npm has
83
+ * not shipped since 2022. What it covers is what our content contains: release
84
+ * notes, help text and descriptions written by people. Nested lists, tables and
85
+ * inline HTML are out of scope on purpose — see parse.ts.
86
+ */
87
+ export const Markdown = ({
88
+ children,
89
+ onLinkPress,
90
+ loading = false,
91
+ style,
92
+ textStyle,
93
+ testID
94
+ }) => {
95
+ const theme = useTheme();
96
+ const styles = useMemo(() => createStyles(theme), [theme]);
97
+ const blocks = useMemo(() => parseMarkdown(children ?? ''), [children]);
98
+ const handleLink = href => {
99
+ if (onLinkPress) {
100
+ onLinkPress(href);
101
+ return;
102
+ }
103
+ // Rejections are swallowed: a link the OS cannot open is not a crash, and
104
+ // there is no useful recovery from inside a text run.
105
+ void Linking.openURL(href).catch(() => undefined);
106
+ };
107
+ if (loading) {
108
+ return /*#__PURE__*/_jsx(MarkdownSkeleton, {
109
+ style: style,
110
+ testID: testID
111
+ });
112
+ }
113
+ if (blocks.length === 0) {
114
+ return null;
115
+ }
116
+ return /*#__PURE__*/_jsx(View, {
117
+ style: style,
118
+ testID: testID,
119
+ children: blocks.map((block, index) => /*#__PURE__*/_jsx(MarkdownBlock, {
120
+ block: block,
121
+ theme: theme,
122
+ styles: styles,
123
+ textStyle: textStyle,
124
+ onLinkPress: handleLink
125
+ }, `b-${index}`))
126
+ });
127
+ };
128
+ Markdown.displayName = 'Markdown';
129
+ const MarkdownBlock = ({
130
+ block,
131
+ theme,
132
+ styles,
133
+ textStyle,
134
+ onLinkPress
135
+ }) => {
136
+ switch (block.type) {
137
+ case 'heading':
138
+ return /*#__PURE__*/_jsx(Text, {
139
+ accessibilityRole: "header",
140
+ style: [styles.block, {
141
+ fontSize: theme.typography.fontSize[HEADING_SCALE[block.level]],
142
+ fontWeight: theme.typography.fontWeight.semibold,
143
+ color: theme.colors.text.primary
144
+ }, textStyle],
145
+ children: renderInline(block.children, theme, onLinkPress)
146
+ });
147
+ case 'paragraph':
148
+ return /*#__PURE__*/_jsx(Text, {
149
+ style: [styles.block, styles.body, textStyle],
150
+ children: renderInline(block.children, theme, onLinkPress)
151
+ });
152
+ case 'listItem':
153
+ return /*#__PURE__*/_jsxs(View, {
154
+ style: [styles.listRow, {
155
+ marginLeft: block.depth * theme.spacing.md
156
+ }],
157
+ children: [/*#__PURE__*/_jsx(Text, {
158
+ style: [styles.body, styles.bullet, textStyle],
159
+ children: block.ordered ? `${block.index}.` : '•'
160
+ }), /*#__PURE__*/_jsx(Text, {
161
+ style: [styles.body, styles.listText, textStyle],
162
+ children: renderInline(block.children, theme, onLinkPress)
163
+ })]
164
+ });
165
+ case 'quote':
166
+ return /*#__PURE__*/_jsx(View, {
167
+ style: styles.quote,
168
+ children: /*#__PURE__*/_jsx(Text, {
169
+ style: [styles.body, styles.quoteText, textStyle],
170
+ children: renderInline(block.children, theme, onLinkPress)
171
+ })
172
+ });
173
+ case 'codeBlock':
174
+ return /*#__PURE__*/_jsx(View, {
175
+ style: styles.codeBlock,
176
+ children: /*#__PURE__*/_jsx(Text, {
177
+ style: styles.codeText,
178
+ children: block.value
179
+ })
180
+ });
181
+ case 'rule':
182
+ return /*#__PURE__*/_jsx(View, {
183
+ style: styles.rule
184
+ });
185
+ default:
186
+ return null;
187
+ }
188
+ };
189
+ const MarkdownSkeleton = ({
190
+ style,
191
+ testID
192
+ }) => /*#__PURE__*/_jsxs(View, {
193
+ style: style,
194
+ testID: testID,
195
+ children: [/*#__PURE__*/_jsx(Skeleton, {
196
+ shape: "text",
197
+ width: "70%",
198
+ height: 20,
199
+ style: {
200
+ marginBottom: 8
201
+ }
202
+ }), /*#__PURE__*/_jsx(Skeleton, {
203
+ shape: "text",
204
+ width: "100%",
205
+ height: 14,
206
+ style: {
207
+ marginBottom: 6
208
+ }
209
+ }), /*#__PURE__*/_jsx(Skeleton, {
210
+ shape: "text",
211
+ width: "92%",
212
+ height: 14,
213
+ style: {
214
+ marginBottom: 6
215
+ }
216
+ }), /*#__PURE__*/_jsx(Skeleton, {
217
+ shape: "text",
218
+ width: "60%",
219
+ height: 14
220
+ })]
221
+ });
222
+ const createStyles = theme => StyleSheet.create({
223
+ block: {
224
+ marginBottom: theme.spacing.sm
225
+ },
226
+ body: {
227
+ fontSize: theme.typography.fontSize.base,
228
+ color: theme.colors.text.primary,
229
+ lineHeight: theme.typography.fontSize.base * 1.5
230
+ },
231
+ listRow: {
232
+ flexDirection: 'row',
233
+ marginBottom: theme.spacing.xs
234
+ },
235
+ bullet: {
236
+ width: theme.spacing.lg
237
+ },
238
+ listText: {
239
+ flex: 1
240
+ },
241
+ quote: {
242
+ borderLeftWidth: 3,
243
+ borderLeftColor: theme.colors.border.secondary,
244
+ paddingLeft: theme.spacing.sm,
245
+ marginBottom: theme.spacing.sm
246
+ },
247
+ quoteText: {
248
+ color: theme.colors.text.secondary,
249
+ fontStyle: 'italic'
250
+ },
251
+ codeBlock: {
252
+ backgroundColor: theme.colors.background.secondary,
253
+ borderRadius: theme.radius.sm,
254
+ padding: theme.spacing.sm,
255
+ marginBottom: theme.spacing.sm
256
+ },
257
+ codeText: {
258
+ fontFamily: MONO,
259
+ fontSize: theme.typography.fontSize.sm,
260
+ color: theme.colors.text.primary
261
+ },
262
+ rule: {
263
+ height: StyleSheet.hairlineWidth,
264
+ backgroundColor: theme.colors.border.primary,
265
+ marginVertical: theme.spacing.md
266
+ }
267
+ });
268
+ //# sourceMappingURL=Markdown.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { Markdown } from "./Markdown.js";
4
+ //# sourceMappingURL=index.js.map