@jobber/components-native 0.38.0 → 0.38.1-migrate-au.2

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 (215) hide show
  1. package/dist/src/AtlantisContext/AtlantisContext.js +2 -0
  2. package/dist/src/AutoLink/AutoLink.js +23 -0
  3. package/dist/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.js +19 -0
  4. package/dist/src/AutoLink/components/Link/Link.js +8 -0
  5. package/dist/src/AutoLink/components/index.js +2 -0
  6. package/dist/src/AutoLink/hooks/useCreateLinkedText.js +24 -0
  7. package/dist/src/AutoLink/hooks/useTokenGenerator.js +10 -0
  8. package/dist/src/AutoLink/index.js +1 -0
  9. package/dist/src/AutoLink/messages.js +18 -0
  10. package/dist/src/AutoLink/types.js +1 -0
  11. package/dist/src/AutoLink/utils.js +53 -0
  12. package/dist/src/Form/Form.js +187 -0
  13. package/dist/src/Form/Form.style.js +33 -0
  14. package/dist/src/Form/components/FormActionBar/FormActionBar.js +21 -0
  15. package/dist/src/Form/components/FormActionBar/FormActionBar.style.js +5 -0
  16. package/dist/src/Form/components/FormActionBar/index.js +1 -0
  17. package/dist/src/Form/components/FormBody/FormBody.js +20 -0
  18. package/dist/src/Form/components/FormBody/FormBody.style.js +26 -0
  19. package/dist/src/Form/components/FormBody/index.js +1 -0
  20. package/dist/src/Form/components/FormCache/FormCache.js +34 -0
  21. package/dist/src/Form/components/FormErrorBanner/FormErrorBanner.js +21 -0
  22. package/dist/src/Form/components/FormErrorBanner/index.js +1 -0
  23. package/dist/src/Form/components/FormErrorBanner/messages.js +13 -0
  24. package/dist/src/Form/components/FormMask/FormMask.js +11 -0
  25. package/dist/src/Form/components/FormMask/FormMask.style.js +15 -0
  26. package/dist/src/Form/components/FormMask/index.js +1 -0
  27. package/dist/src/Form/components/FormMessage/FormMessage.js +48 -0
  28. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.js +28 -0
  29. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.style.js +17 -0
  30. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/index.js +1 -0
  31. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/messages.js +8 -0
  32. package/dist/src/Form/components/FormMessage/index.js +1 -0
  33. package/dist/src/Form/components/FormMessageBanner/FormMessageBanner.js +15 -0
  34. package/dist/src/Form/components/FormMessageBanner/index.js +1 -0
  35. package/dist/src/Form/components/FormSaveButton/FormSaveButton.js +69 -0
  36. package/dist/src/Form/components/FormSaveButton/index.js +1 -0
  37. package/dist/src/Form/components/FormSaveButton/messages.js +8 -0
  38. package/dist/src/Form/constants.js +2 -0
  39. package/dist/src/Form/context/AtlantisFormContext.js +16 -0
  40. package/dist/src/Form/context/index.js +1 -0
  41. package/dist/src/Form/context/types.js +1 -0
  42. package/dist/src/Form/hooks/useFormViewRefs.js +14 -0
  43. package/dist/src/Form/hooks/useInternalForm.js +37 -0
  44. package/dist/src/Form/hooks/useOfflineHandler.js +24 -0
  45. package/dist/src/Form/hooks/useSaveButtonPosition.js +25 -0
  46. package/dist/src/Form/hooks/useScreenInformation.js +15 -0
  47. package/dist/src/Form/hooks/useScrollToError/index.js +1 -0
  48. package/dist/src/Form/hooks/useScrollToError/useScrollToError.js +63 -0
  49. package/dist/src/Form/index.js +4 -0
  50. package/dist/src/Form/messages.js +28 -0
  51. package/dist/src/Form/types.js +10 -0
  52. package/dist/src/InputDate/InputDate.js +76 -0
  53. package/dist/src/InputDate/index.js +1 -0
  54. package/dist/src/InputDate/messages.js +8 -0
  55. package/dist/src/Menu/Menu.js +67 -0
  56. package/dist/src/Menu/Menu.style.js +6 -0
  57. package/dist/src/Menu/components/MenuOption/MenuOption.js +25 -0
  58. package/dist/src/Menu/components/MenuOption/MenuOption.style.js +10 -0
  59. package/dist/src/Menu/components/MenuOption/index.js +1 -0
  60. package/dist/src/Menu/components/Overlay/Overlay.js +9 -0
  61. package/dist/src/Menu/components/Overlay/Overlay.style.js +6 -0
  62. package/dist/src/Menu/components/Overlay/index.js +1 -0
  63. package/dist/src/Menu/index.js +1 -0
  64. package/dist/src/Menu/messages.js +8 -0
  65. package/dist/src/Menu/types.js +1 -0
  66. package/dist/src/Menu/utils.js +84 -0
  67. package/dist/src/index.js +4 -0
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/dist/types/src/AtlantisContext/AtlantisContext.d.ts +7 -1
  70. package/dist/types/src/AutoLink/AutoLink.d.ts +3 -0
  71. package/dist/types/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.d.ts +3 -0
  72. package/dist/types/src/AutoLink/components/Link/Link.d.ts +8 -0
  73. package/dist/types/src/AutoLink/components/index.d.ts +2 -0
  74. package/dist/types/src/AutoLink/hooks/useCreateLinkedText.d.ts +12 -0
  75. package/dist/types/src/AutoLink/hooks/useTokenGenerator.d.ts +1 -0
  76. package/dist/types/src/AutoLink/index.d.ts +1 -0
  77. package/dist/types/src/AutoLink/messages.d.ts +17 -0
  78. package/dist/types/src/AutoLink/types.d.ts +32 -0
  79. package/dist/types/src/AutoLink/utils.d.ts +8 -0
  80. package/dist/types/src/Form/Form.d.ts +4 -0
  81. package/dist/types/src/Form/Form.style.d.ts +31 -0
  82. package/dist/types/src/Form/components/FormActionBar/FormActionBar.d.ts +13 -0
  83. package/dist/types/src/Form/components/FormActionBar/FormActionBar.style.d.ts +15 -0
  84. package/dist/types/src/Form/components/FormActionBar/index.d.ts +2 -0
  85. package/dist/types/src/Form/components/FormBody/FormBody.d.ts +10 -0
  86. package/dist/types/src/Form/components/FormBody/FormBody.style.d.ts +24 -0
  87. package/dist/types/src/Form/components/FormBody/index.d.ts +1 -0
  88. package/dist/types/src/Form/components/FormCache/FormCache.d.ts +10 -0
  89. package/dist/types/src/Form/components/FormErrorBanner/FormErrorBanner.d.ts +3 -0
  90. package/dist/types/src/Form/components/FormErrorBanner/index.d.ts +1 -0
  91. package/dist/types/src/Form/components/FormErrorBanner/messages.d.ts +12 -0
  92. package/dist/types/src/Form/components/FormMask/FormMask.d.ts +2 -0
  93. package/dist/types/src/Form/components/FormMask/FormMask.style.d.ts +13 -0
  94. package/dist/types/src/Form/components/FormMask/index.d.ts +1 -0
  95. package/dist/types/src/Form/components/FormMessage/FormMessage.d.ts +19 -0
  96. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.d.ts +8 -0
  97. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.style.d.ts +20 -0
  98. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/index.d.ts +1 -0
  99. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/messages.d.ts +7 -0
  100. package/dist/types/src/Form/components/FormMessage/index.d.ts +1 -0
  101. package/dist/types/src/Form/components/FormMessageBanner/FormMessageBanner.d.ts +7 -0
  102. package/dist/types/src/Form/components/FormMessageBanner/index.d.ts +1 -0
  103. package/dist/types/src/Form/components/FormSaveButton/FormSaveButton.d.ts +3 -0
  104. package/dist/types/src/Form/components/FormSaveButton/index.d.ts +1 -0
  105. package/dist/types/src/Form/components/FormSaveButton/messages.d.ts +7 -0
  106. package/dist/types/src/Form/constants.d.ts +2 -0
  107. package/dist/types/src/Form/context/AtlantisFormContext.d.ts +12 -0
  108. package/dist/types/src/Form/context/index.d.ts +2 -0
  109. package/dist/types/src/Form/context/types.d.ts +26 -0
  110. package/dist/types/src/Form/hooks/useFormViewRefs.d.ts +10 -0
  111. package/dist/types/src/Form/hooks/useInternalForm.d.ts +19 -0
  112. package/dist/types/src/Form/hooks/useOfflineHandler.d.ts +1 -0
  113. package/dist/types/src/Form/hooks/useSaveButtonPosition.d.ts +12 -0
  114. package/dist/types/src/Form/hooks/useScreenInformation.d.ts +8 -0
  115. package/dist/types/src/Form/hooks/useScrollToError/index.d.ts +1 -0
  116. package/dist/types/src/Form/hooks/useScrollToError/useScrollToError.d.ts +10 -0
  117. package/dist/types/src/Form/index.d.ts +5 -0
  118. package/dist/types/src/Form/messages.d.ts +27 -0
  119. package/dist/types/src/Form/types.d.ts +199 -0
  120. package/dist/types/src/InputDate/InputDate.d.ts +74 -0
  121. package/dist/types/src/InputDate/index.d.ts +1 -0
  122. package/dist/types/src/InputDate/messages.d.ts +7 -0
  123. package/dist/types/src/InputNumber/InputNumber.d.ts +1 -1
  124. package/dist/types/src/Menu/Menu.d.ts +3 -0
  125. package/dist/types/src/Menu/Menu.style.d.ts +18 -0
  126. package/dist/types/src/Menu/components/MenuOption/MenuOption.d.ts +3 -0
  127. package/dist/types/src/Menu/components/MenuOption/MenuOption.style.d.ts +8 -0
  128. package/dist/types/src/Menu/components/MenuOption/index.d.ts +1 -0
  129. package/dist/types/src/Menu/components/Overlay/Overlay.d.ts +3 -0
  130. package/dist/types/src/Menu/components/Overlay/Overlay.style.d.ts +12 -0
  131. package/dist/types/src/Menu/components/Overlay/index.d.ts +1 -0
  132. package/dist/types/src/Menu/index.d.ts +2 -0
  133. package/dist/types/src/Menu/messages.d.ts +7 -0
  134. package/dist/types/src/Menu/types.d.ts +22 -0
  135. package/dist/types/src/Menu/utils.d.ts +10 -0
  136. package/dist/types/src/index.d.ts +4 -0
  137. package/package.json +5 -2
  138. package/src/AtlantisContext/AtlantisContext.tsx +10 -1
  139. package/src/AutoLink/AutoLink.tsx +36 -0
  140. package/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.tsx +38 -0
  141. package/src/AutoLink/components/Link/Link.test.tsx +30 -0
  142. package/src/AutoLink/components/Link/Link.tsx +22 -0
  143. package/src/AutoLink/components/index.ts +2 -0
  144. package/src/AutoLink/hooks/useCreateLinkedText.ts +35 -0
  145. package/src/AutoLink/hooks/useTokenGenerator.ts +11 -0
  146. package/src/AutoLink/index.ts +1 -0
  147. package/src/AutoLink/messages.ts +19 -0
  148. package/src/AutoLink/types.ts +39 -0
  149. package/src/AutoLink/utils.ts +76 -0
  150. package/src/Form/Form.style.ts +34 -0
  151. package/src/Form/Form.test.tsx +588 -0
  152. package/src/Form/Form.tsx +296 -0
  153. package/src/Form/components/FormActionBar/FormActionBar.style.ts +11 -0
  154. package/src/Form/components/FormActionBar/FormActionBar.tsx +63 -0
  155. package/src/Form/components/FormActionBar/index.ts +2 -0
  156. package/src/Form/components/FormBody/FormBody.style.ts +27 -0
  157. package/src/Form/components/FormBody/FormBody.tsx +62 -0
  158. package/src/Form/components/FormBody/index.ts +1 -0
  159. package/src/Form/components/FormCache/FormCache.tsx +50 -0
  160. package/src/Form/components/FormErrorBanner/FormErrorBanner.test.tsx +124 -0
  161. package/src/Form/components/FormErrorBanner/FormErrorBanner.tsx +34 -0
  162. package/src/Form/components/FormErrorBanner/index.ts +1 -0
  163. package/src/Form/components/FormErrorBanner/messages.ts +14 -0
  164. package/src/Form/components/FormMask/FormMask.style.tsx +16 -0
  165. package/src/Form/components/FormMask/FormMask.tsx +19 -0
  166. package/src/Form/components/FormMask/index.ts +1 -0
  167. package/src/Form/components/FormMessage/FormMessage.test.tsx +72 -0
  168. package/src/Form/components/FormMessage/FormMessage.tsx +63 -0
  169. package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.style.ts +18 -0
  170. package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.tsx +55 -0
  171. package/src/Form/components/FormMessage/components/InternalFormMessage/index.ts +1 -0
  172. package/src/Form/components/FormMessage/components/InternalFormMessage/messages.ts +10 -0
  173. package/src/Form/components/FormMessage/index.ts +1 -0
  174. package/src/Form/components/FormMessageBanner/FormMessageBanner.test.tsx +27 -0
  175. package/src/Form/components/FormMessageBanner/FormMessageBanner.tsx +33 -0
  176. package/src/Form/components/FormMessageBanner/index.ts +1 -0
  177. package/src/Form/components/FormSaveButton/FormSaveButton.test.tsx +159 -0
  178. package/src/Form/components/FormSaveButton/FormSaveButton.tsx +103 -0
  179. package/src/Form/components/FormSaveButton/index.ts +1 -0
  180. package/src/Form/components/FormSaveButton/messages.ts +9 -0
  181. package/src/Form/constants.ts +2 -0
  182. package/src/Form/context/AtlantisFormContext.test.tsx +45 -0
  183. package/src/Form/context/AtlantisFormContext.tsx +21 -0
  184. package/src/Form/context/index.ts +5 -0
  185. package/src/Form/context/types.ts +34 -0
  186. package/src/Form/hooks/useFormViewRefs.ts +23 -0
  187. package/src/Form/hooks/useInternalForm.ts +99 -0
  188. package/src/Form/hooks/useOfflineHandler.ts +36 -0
  189. package/src/Form/hooks/useSaveButtonPosition.ts +52 -0
  190. package/src/Form/hooks/useScreenInformation.ts +25 -0
  191. package/src/Form/hooks/useScrollToError/index.ts +1 -0
  192. package/src/Form/hooks/useScrollToError/useScrollToError.test.tsx +103 -0
  193. package/src/Form/hooks/useScrollToError/useScrollToError.ts +102 -0
  194. package/src/Form/index.ts +13 -0
  195. package/src/Form/messages.ts +33 -0
  196. package/src/Form/types.ts +255 -0
  197. package/src/InputDate/InputDate.test.tsx +295 -0
  198. package/src/InputDate/InputDate.tsx +231 -0
  199. package/src/InputDate/index.ts +1 -0
  200. package/src/InputDate/messages.ts +9 -0
  201. package/src/InputNumber/InputNumber.tsx +1 -1
  202. package/src/Menu/Menu.style.ts +16 -0
  203. package/src/Menu/Menu.test.tsx +201 -0
  204. package/src/Menu/Menu.tsx +116 -0
  205. package/src/Menu/components/MenuOption/MenuOption.style.tsx +11 -0
  206. package/src/Menu/components/MenuOption/MenuOption.tsx +63 -0
  207. package/src/Menu/components/MenuOption/index.ts +1 -0
  208. package/src/Menu/components/Overlay/Overlay.style.ts +13 -0
  209. package/src/Menu/components/Overlay/Overlay.tsx +16 -0
  210. package/src/Menu/components/Overlay/index.ts +1 -0
  211. package/src/Menu/index.ts +6 -0
  212. package/src/Menu/messages.ts +9 -0
  213. package/src/Menu/types.ts +25 -0
  214. package/src/Menu/utils.ts +151 -0
  215. package/src/index.ts +4 -0
@@ -3,6 +3,7 @@ import { createContext, useContext } from "react";
3
3
  import RNLocalize from "react-native-localize";
4
4
  import { DEFAULT_CURRENCY_SYMBOL } from "../InputCurrency/constants";
5
5
  export const defaultValues = {
6
+ dateFormat: "PP",
6
7
  // The system time is "p"
7
8
  timeFormat: "p",
8
9
  timeZone: RNLocalize.getTimeZone(),
@@ -12,6 +13,7 @@ export const defaultValues = {
12
13
  },
13
14
  floatSeparators: { group: ",", decimal: "." },
14
15
  currencySymbol: DEFAULT_CURRENCY_SYMBOL,
16
+ headerHeight: 0,
15
17
  };
16
18
  export const AtlantisContext = createContext(defaultValues);
17
19
  export function useAtlantisContext() {
@@ -0,0 +1,23 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import React from "react";
13
+ import { Text as RNText } from "react-native";
14
+ import { ComposeTextWithLinks } from "./components";
15
+ import { useCreateLinkedText } from "./hooks/useCreateLinkedText";
16
+ import { TypographyGestureDetector } from "../Typography";
17
+ import { tokens } from "../utils/design";
18
+ export function AutoLink(_a) {
19
+ var { children: text = "", bottomTabsVisible = true, selectable = true } = _a, rest = __rest(_a, ["children", "bottomTabsVisible", "selectable"]);
20
+ const { splitText, matches } = useCreateLinkedText(Object.assign({ text }, rest));
21
+ return (React.createElement(TypographyGestureDetector, null,
22
+ React.createElement(RNText, { selectable: selectable, selectionColor: tokens["color-brand--highlight"] }, splitText.map((part, index) => (React.createElement(ComposeTextWithLinks, { key: index, part: part, index: index, match: matches[part], bottomTabsVisible: bottomTabsVisible, selectable: selectable }))))));
23
+ }
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { useIntl } from "react-intl";
3
+ import { Platform } from "react-native";
4
+ import { onLongPressLink, onPressLink } from "../../utils";
5
+ import { Link } from "../Link/Link";
6
+ import { Text } from "../../../Text";
7
+ export function ComposeTextWithLinks({ part, index, match, bottomTabsVisible, selectable = true, }) {
8
+ const { formatMessage } = useIntl();
9
+ const isLink = match === null || match === void 0 ? void 0 : match.getType();
10
+ if (isLink) {
11
+ return (React.createElement(Link, { key: index, onPress: () => onPressLink(match), onLongPress: () => {
12
+ if (selectable && Platform.OS === "android") {
13
+ return;
14
+ }
15
+ onLongPressLink(match, bottomTabsVisible, formatMessage);
16
+ } }, match.getAnchorText()));
17
+ }
18
+ return React.createElement(Text, { key: index }, part);
19
+ }
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ // eslint-disable-next-line no-restricted-imports
3
+ import { Text as RNText } from "react-native";
4
+ import { Text } from "../../../Text";
5
+ export function Link({ children, onPress, onLongPress, }) {
6
+ return (React.createElement(RNText, { onPress: onPress, onLongPress: onLongPress },
7
+ React.createElement(Text, { variation: "interactive" }, children)));
8
+ }
@@ -0,0 +1,2 @@
1
+ export { Link } from "./Link/Link";
2
+ export { ComposeTextWithLinks } from "./ComposeTextWithLinks/ComposeTextWithLinks";
@@ -0,0 +1,24 @@
1
+ import Autolinker from "autolinker";
2
+ import { useTokenGenerator } from "./useTokenGenerator";
3
+ import { shouldIgnoreURL } from "../utils";
4
+ export function useCreateLinkedText({ text = "", email = true, phone = true, urls = true, }) {
5
+ const [generateToken, tokenRegexp] = useTokenGenerator();
6
+ const matches = {};
7
+ const linkedText = Autolinker.link(text, {
8
+ email,
9
+ phone,
10
+ urls,
11
+ replaceFn: match => {
12
+ if (shouldIgnoreURL(text, match))
13
+ return false;
14
+ const token = generateToken();
15
+ matches[token] = match;
16
+ return token;
17
+ },
18
+ });
19
+ const splitText = splitLinkedText(linkedText, tokenRegexp);
20
+ return { splitText, matches };
21
+ }
22
+ function splitLinkedText(linkedText, tokenRegexp) {
23
+ return linkedText.split(tokenRegexp).filter(part => Boolean(part));
24
+ }
@@ -0,0 +1,10 @@
1
+ import { useState } from "react";
2
+ import { v1 } from "react-native-uuid";
3
+ export function useTokenGenerator() {
4
+ let counter = 0;
5
+ const [identifier] = useState(v1());
6
+ return [
7
+ () => `@__ELEMENT-${identifier}-${counter++}__@`,
8
+ new RegExp(`(@__ELEMENT-${identifier}-\\d+__@)`, "g"),
9
+ ];
10
+ }
@@ -0,0 +1 @@
1
+ export * from "./AutoLink";
@@ -0,0 +1,18 @@
1
+ import { defineMessages } from "react-intl";
2
+ export const messages = defineMessages({
3
+ phoneCopied: {
4
+ id: "phoneCopied",
5
+ defaultMessage: "Phone number copied",
6
+ description: "Message shown after copying a phone number",
7
+ },
8
+ emailCopied: {
9
+ id: "emailCopied",
10
+ defaultMessage: "Email copied",
11
+ description: "Message shown after copying an email",
12
+ },
13
+ urlCopied: {
14
+ id: "urlCopied",
15
+ defaultMessage: "URL copied",
16
+ description: "Message shown after copying a URL",
17
+ },
18
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ import Clipboard from "@react-native-clipboard/clipboard";
2
+ import { Linking } from "react-native";
3
+ import { messages } from "./messages";
4
+ import { showToast } from "../Toast";
5
+ function hasPrefix(text, prefixes) {
6
+ return prefixes.some(prefix => text.includes(prefix));
7
+ }
8
+ export function shouldIgnoreURL(text, match) {
9
+ const matchedText = match.getMatchedText().toLocaleLowerCase();
10
+ const urlPrefixes = ["http://", "https://", "www."];
11
+ const ignorePrefixes = ["file://", "ftp://"];
12
+ const previousChar = text.charAt(match.getOffset() - 1);
13
+ if (match.getType() === "url") {
14
+ const hasUrlPrefix = hasPrefix(matchedText, urlPrefixes);
15
+ const hasIgnorePrefix = hasPrefix(matchedText, ignorePrefixes) || previousChar === "/";
16
+ return hasIgnorePrefix && !hasUrlPrefix;
17
+ }
18
+ return false;
19
+ }
20
+ export function getUrl(match, immediateOpen = true) {
21
+ const matchType = match.getType();
22
+ switch (matchType) {
23
+ case "email":
24
+ return immediateOpen
25
+ ? `mailto:${encodeURIComponent(match.getEmail())}`
26
+ : match.getEmail();
27
+ case "phone":
28
+ return immediateOpen
29
+ ? `tel:${match.getNumber()}`
30
+ : match.getNumber();
31
+ default:
32
+ return match.getAnchorHref();
33
+ }
34
+ }
35
+ export function copyTextToClipboard(text, toastConfig) {
36
+ Clipboard.setString(text);
37
+ if (toastConfig) {
38
+ const { message, bottomTabsVisible } = toastConfig;
39
+ showToast({ message, bottomTabsVisible });
40
+ }
41
+ }
42
+ export function onLongPressLink(match, bottomTabsVisible, formatMessage) {
43
+ const linkUrl = getUrl(match, false);
44
+ const toastConfig = {
45
+ message: formatMessage(messages[`${match.getType()}Copied`]),
46
+ bottomTabsVisible,
47
+ };
48
+ copyTextToClipboard(linkUrl, toastConfig);
49
+ }
50
+ export function onPressLink(match) {
51
+ const linkUrl = getUrl(match);
52
+ Linking.openURL(linkUrl);
53
+ }
@@ -0,0 +1,187 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ import React, { useState } from "react";
22
+ import { FormProvider } from "react-hook-form";
23
+ import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
24
+ import { Keyboard, Platform, View, findNodeHandle, } from "react-native";
25
+ import { styles } from "./Form.style";
26
+ import { FormErrorBanner } from "./components/FormErrorBanner";
27
+ import { KEYBOARD_SAVE_BUTTON_DISTANCE } from "./constants";
28
+ import { FormMessageBanner } from "./components/FormMessageBanner";
29
+ import { FormSubmitErrorType, } from "./types";
30
+ import { FormMask } from "./components/FormMask";
31
+ import { useInternalForm } from "./hooks/useInternalForm";
32
+ import { useFormViewRefs } from "./hooks/useFormViewRefs";
33
+ import { useScreenInformation } from "./hooks/useScreenInformation";
34
+ import { FormMessage } from "./components/FormMessage";
35
+ import { FormBody, useBottomPadding } from "./components/FormBody";
36
+ import { useOfflineHandler } from "./hooks/useOfflineHandler";
37
+ import { useScrollToError } from "./hooks/useScrollToError";
38
+ import { FormSaveButton } from "./components/FormSaveButton";
39
+ import { useSaveButtonPosition } from "./hooks/useSaveButtonPosition";
40
+ import { FormCache } from "./components/FormCache/FormCache";
41
+ import { InputAccessoriesProvider } from "../InputText";
42
+ import { tokens } from "../utils/design";
43
+ import { ErrorMessageProvider } from "../ErrorMessageWrapper";
44
+ export function Form(_a) {
45
+ var { initialLoading } = _a, rest = __rest(_a, ["initialLoading"]);
46
+ const child = initialLoading ? React.createElement(FormMask, null) : React.createElement(InternalForm, Object.assign({}, rest));
47
+ return (React.createElement(InputAccessoriesProvider, null,
48
+ React.createElement(ErrorMessageProvider, null, child)));
49
+ }
50
+ // eslint-disable-next-line max-statements
51
+ function InternalForm({ children, onBeforeSubmit, onSubmit, onSubmitError, onSubmitSuccess, bannerErrors, bannerMessages, initialValues, mode = "onTouched", reValidateMode = "onChange", formRef, saveButtonLabel, renderStickySection, localCacheKey, localCacheExclude, localCacheId, secondaryActions, saveButtonOffset, showStickySaveButton = false, renderFooter, }) {
52
+ var _a;
53
+ const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
54
+ const [saveButtonHeight, setSaveButtonHeight] = useState(0);
55
+ const [messageBannerHeight, setMessageBannerHeight] = useState(0);
56
+ const { formMethods, handleSubmit, isSubmitting, removeListenerRef, setLocalCache, } = useInternalForm({
57
+ mode,
58
+ reValidateMode,
59
+ initialValues,
60
+ formRef,
61
+ localCacheKey,
62
+ localCacheExclude,
63
+ localCacheId,
64
+ scrollViewRef,
65
+ saveButtonHeight,
66
+ messageBannerHeight,
67
+ });
68
+ const { windowHeight, headerHeight } = useScreenInformation();
69
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
70
+ const [keyboardScreenY, setKeyboardScreenY] = useState(0);
71
+ const [formContentHeight, setFormContentHeight] = useState(0);
72
+ const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false);
73
+ const paddingBottom = useBottomPadding();
74
+ const { saveButtonPosition } = useSaveButtonPosition({
75
+ formContentHeight,
76
+ isBottomSheetOpen,
77
+ showStickySaveButton,
78
+ keyboardHeight,
79
+ keyboardScreenY,
80
+ });
81
+ const [isSecondaryActionLoading, setIsSecondaryActionLoading] = useState(false);
82
+ const extraViewHeight = paddingBottom + KEYBOARD_SAVE_BUTTON_DISTANCE;
83
+ const calculatedKeyboardHeight = keyboardHeight - extraViewHeight;
84
+ useScrollToError({
85
+ formState: formMethods.formState,
86
+ refNode: findNodeHandle(scrollViewRef.current),
87
+ setFocus: formMethods.setFocus,
88
+ scrollToPosition: (_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollToPosition,
89
+ });
90
+ const handleOfflineSubmit = useOfflineHandler();
91
+ const keyboardProps = Platform.select({
92
+ ios: {
93
+ onKeyboardWillHide: handleKeyboardHide,
94
+ onKeyboardWillShow: handleKeyboardShow,
95
+ },
96
+ android: {
97
+ onKeyboardDidHide: handleKeyboardHide,
98
+ onKeyboardDidShow: handleKeyboardShow,
99
+ },
100
+ });
101
+ const onLayout = (event) => {
102
+ setMessageBannerHeight(event.nativeEvent.layout.height);
103
+ };
104
+ return (React.createElement(FormProvider, Object.assign({}, formMethods),
105
+ React.createElement(React.Fragment, null,
106
+ (isSubmitting || isSecondaryActionLoading) && React.createElement(FormMask, null),
107
+ React.createElement(FormCache, { localCacheKey: localCacheKey, localCacheExclude: localCacheExclude, setLocalCache: setLocalCache }),
108
+ React.createElement(FormBody, { keyboardHeight: calculateSaveButtonOffset(), submit: handleSubmit(internalSubmit), isFormSubmitting: isSubmitting, saveButtonLabel: saveButtonLabel, shouldRenderActionBar: saveButtonPosition === "sticky", renderStickySection: renderStickySection, secondaryActions: secondaryActions, setSecondaryActionLoading: setIsSecondaryActionLoading, setSaveButtonHeight: setSaveButtonHeight, saveButtonOffset: saveButtonOffset },
109
+ React.createElement(KeyboardAwareScrollView, Object.assign({ enableResetScrollToCoords: false, enableAutomaticScroll: true, keyboardOpeningTime: Platform.OS === "ios" ? tokens["timing-slowest"] : 0, keyboardShouldPersistTaps: "handled", ref: scrollViewRef }, keyboardProps, { extraHeight: headerHeight, contentContainerStyle: !keyboardHeight && styles.scrollContentContainer }),
110
+ React.createElement(View, { onLayout: ({ nativeEvent }) => {
111
+ setFormContentHeight(nativeEvent.layout.height);
112
+ } },
113
+ React.createElement(View, { onLayout: onLayout },
114
+ React.createElement(FormMessageBanner, { bannerMessages: bannerMessages }),
115
+ React.createElement(FormErrorBanner, { networkError: bannerErrors === null || bannerErrors === void 0 ? void 0 : bannerErrors.networkError, bannerError: bannerErrors === null || bannerErrors === void 0 ? void 0 : bannerErrors.bannerError })),
116
+ React.createElement(View, { style: styles.formChildContainer },
117
+ React.createElement(React.Fragment, null,
118
+ React.createElement(View, { style: styles.formContent }, children),
119
+ saveButtonPosition === "inline" && (React.createElement(View, { style: styles.fixedSaveButton }, renderStickySection ? (renderStickySection(handleSubmit(internalSubmit), saveButtonLabel, isSubmitting)) : (React.createElement(FormSaveButton, { primaryAction: handleSubmit(internalSubmit), label: saveButtonLabel, loading: isSubmitting, secondaryActions: secondaryActions, setSecondaryActionLoading: setIsSecondaryActionLoading, onOpenBottomSheet: () => setIsBottomSheetOpen(true), onCloseBottomSheet: () => setIsBottomSheetOpen(false) })))),
120
+ renderFooter))),
121
+ React.createElement(View, { style: styles.safeArea, ref: bottomViewRef })))),
122
+ React.createElement(FormMessage, null)));
123
+ function handleKeyboardShow(frames) {
124
+ setKeyboardScreenY(frames.endCoordinates.screenY);
125
+ setKeyboardHeight(frames.endCoordinates.height);
126
+ }
127
+ function handleKeyboardHide() {
128
+ var _a;
129
+ (_a = bottomViewRef === null || bottomViewRef === void 0 ? void 0 : bottomViewRef.current) === null || _a === void 0 ? void 0 : _a.measureInWindow(
130
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
131
+ (_x, y, _width, _height) => {
132
+ var _a;
133
+ // This fixes extra whitespace below the form if it was scrolled down while the keyboard was open
134
+ // i.e. a View below the form is higher than the bottom of the window
135
+ if (y < windowHeight) {
136
+ (_a = scrollViewRef === null || scrollViewRef === void 0 ? void 0 : scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollToEnd();
137
+ }
138
+ });
139
+ setKeyboardHeight(0);
140
+ setKeyboardScreenY(0);
141
+ }
142
+ function internalSubmit(data) {
143
+ return __awaiter(this, void 0, void 0, function* () {
144
+ let performSubmit = true;
145
+ if (onBeforeSubmit) {
146
+ performSubmit = yield onBeforeSubmit(data);
147
+ }
148
+ if (performSubmit) {
149
+ Keyboard.dismiss();
150
+ return onSubmit(data)
151
+ .then((result) => {
152
+ var _a;
153
+ (_a = removeListenerRef.current) === null || _a === void 0 ? void 0 : _a.call(removeListenerRef);
154
+ onSubmitSuccess(result);
155
+ })
156
+ .catch(handleSubmitCatch);
157
+ }
158
+ });
159
+ }
160
+ function handleSubmitCatch(error) {
161
+ // Scroll to top of form to see error
162
+ scrollToTop();
163
+ onSubmitError(error);
164
+ if ((error === null || error === void 0 ? void 0 : error.errorType) === FormSubmitErrorType.NetworkError) {
165
+ // @ts-expect-error We are making the form submission fail so that we can
166
+ // prevent the isSubmitSuccess to be true
167
+ formMethods.setError("offline", "Error saving form.");
168
+ handleOfflineSubmit(handleRetry, clearFormErrors)();
169
+ }
170
+ }
171
+ function clearFormErrors() {
172
+ // @ts-expect-error We are clearing the error that we previously set
173
+ // when the form had no internet connection
174
+ formMethods.clearErrors("offline");
175
+ }
176
+ function handleRetry() {
177
+ clearFormErrors();
178
+ return handleSubmit(internalSubmit)();
179
+ }
180
+ function calculateSaveButtonOffset() {
181
+ if (saveButtonOffset) {
182
+ // Included the space-base because it's the padding of the FormActionBar
183
+ return calculatedKeyboardHeight - saveButtonOffset + tokens["space-base"];
184
+ }
185
+ return calculatedKeyboardHeight;
186
+ }
187
+ }
@@ -0,0 +1,33 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../utils/design";
3
+ export const styles = StyleSheet.create({
4
+ container: {
5
+ flex: 1,
6
+ flexGrow: 1,
7
+ width: "100%",
8
+ },
9
+ safeArea: {
10
+ backgroundColor: tokens["color-surface"],
11
+ },
12
+ scrollContentContainer: {
13
+ flexGrow: 1,
14
+ },
15
+ scrollView: {
16
+ flexGrow: 1,
17
+ },
18
+ formChildContainer: {
19
+ flexGrow: 1,
20
+ justifyContent: "flex-start",
21
+ },
22
+ formContent: {
23
+ paddingVertical: tokens["space-small"],
24
+ },
25
+ fixedSaveButton: {
26
+ padding: tokens["space-base"],
27
+ backgroundColor: tokens["color-surface"],
28
+ },
29
+ activityIndicator: {
30
+ marginVertical: tokens["space-base"],
31
+ flex: 1,
32
+ },
33
+ });
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import Reanimated from "react-native-reanimated";
4
+ import { styles } from "./FormActionBar.style";
5
+ import { FormSaveButton } from "../FormSaveButton";
6
+ const ReanimatedView = Reanimated.createAnimatedComponent(View);
7
+ export function FormActionBar({ keyboardHeight, submit, isFormSubmitting, saveButtonLabel, renderStickySection, setSaveButtonHeight, secondaryActions, setSecondaryActionLoading, }) {
8
+ const buttonStyle = StyleSheet.flatten([
9
+ styles.saveButton,
10
+ {
11
+ position: keyboardHeight > 0 ? "absolute" : "relative",
12
+ bottom: 0,
13
+ },
14
+ ]);
15
+ const onLayout = (event) => {
16
+ setSaveButtonHeight && setSaveButtonHeight(event.nativeEvent.layout.height);
17
+ };
18
+ return (
19
+ //@ts-expect-error tsc-ci
20
+ React.createElement(ReanimatedView, { style: buttonStyle, onLayout: onLayout }, renderStickySection ? (renderStickySection(submit, saveButtonLabel, isFormSubmitting)) : (React.createElement(FormSaveButton, { setSecondaryActionLoading: setSecondaryActionLoading, primaryAction: submit, loading: isFormSubmitting, label: saveButtonLabel, secondaryActions: secondaryActions }))));
21
+ }
@@ -0,0 +1,5 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../../utils/design";
3
+ export const styles = StyleSheet.create({
4
+ saveButton: Object.assign({ padding: tokens["space-base"], backgroundColor: tokens["color-surface"], width: "100%" }, tokens["shadow-high"]),
5
+ });
@@ -0,0 +1 @@
1
+ export { FormActionBar } from "./FormActionBar";
@@ -0,0 +1,20 @@
1
+ import React, { useMemo } from "react";
2
+ import { View } from "react-native";
3
+ import { styles } from "./FormBody.style";
4
+ import { useScreenInformation } from "../../hooks/useScreenInformation";
5
+ import { FormActionBar } from "../FormActionBar";
6
+ import { tokens } from "../../../utils/design";
7
+ export function FormBody({ isFormSubmitting, submit, keyboardHeight, children, saveButtonLabel, renderStickySection, shouldRenderActionBar = true, secondaryActions, setSecondaryActionLoading, setSaveButtonHeight, saveButtonOffset, }) {
8
+ const paddingBottom = useBottomPadding();
9
+ const fullViewPadding = useMemo(() => ({ paddingBottom }), [paddingBottom]);
10
+ return (React.createElement(React.Fragment, null,
11
+ React.createElement(View, { style: [styles.container] },
12
+ children,
13
+ shouldRenderActionBar && (React.createElement(FormActionBar, { setSecondaryActionLoading: setSecondaryActionLoading, keyboardHeight: keyboardHeight, submit: submit, isFormSubmitting: isFormSubmitting, saveButtonLabel: saveButtonLabel, renderStickySection: renderStickySection, secondaryActions: secondaryActions, setSaveButtonHeight: setSaveButtonHeight }))),
14
+ shouldRenderActionBar && !saveButtonOffset && (React.createElement(View, { style: [fullViewPadding, styles.safeArea], testID: "ATL-FormSafeArea" }))));
15
+ }
16
+ export function useBottomPadding() {
17
+ const { insets } = useScreenInformation();
18
+ const extraBottomSpace = insets.bottom - tokens["space-base"];
19
+ return extraBottomSpace >= 0 ? extraBottomSpace : 0;
20
+ }
@@ -0,0 +1,26 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../../utils/design";
3
+ export const styles = StyleSheet.create({
4
+ container: {
5
+ flex: 1,
6
+ flexGrow: 1,
7
+ width: "100%",
8
+ },
9
+ safeArea: {
10
+ backgroundColor: tokens["color-surface"],
11
+ },
12
+ scrollContentContainer: {
13
+ flexGrow: 1,
14
+ },
15
+ scrollView: {
16
+ flexGrow: 1,
17
+ },
18
+ formChildContainer: {
19
+ flexGrow: 1,
20
+ justifyContent: "flex-start",
21
+ },
22
+ activityIndicator: {
23
+ marginVertical: tokens["space-base"],
24
+ flex: 1,
25
+ },
26
+ });
@@ -0,0 +1 @@
1
+ export { FormBody, useBottomPadding } from "./FormBody";
@@ -0,0 +1,34 @@
1
+ import React, { useEffect, useMemo } from "react";
2
+ import { useFormContext, useWatch } from "react-hook-form";
3
+ import omit from "lodash/omit";
4
+ export function FormCache({ localCacheExclude, localCacheKey, setLocalCache, }) {
5
+ const { control, formState } = useFormContext();
6
+ const { isDirty } = formState;
7
+ const formData = useWatch({ control });
8
+ const shouldExclude = useMemo(() => {
9
+ return Array.isArray(localCacheExclude) && localCacheExclude.length > 0;
10
+ }, [localCacheExclude]);
11
+ useEffect(() => {
12
+ !localCacheKey &&
13
+ console.log("No `localCacheKey` specified on Form. Local copy of form data is now disabled.");
14
+ }, [localCacheKey]);
15
+ /**
16
+ * Automatically save form data locally
17
+ */
18
+ useEffect(() => {
19
+ if (!isDirty)
20
+ return;
21
+ if (shouldExclude) {
22
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
23
+ // @ts-ignore The type below is not working. It seems to be an issue with react-hook-form
24
+ // https://github.com/react-hook-form/react-hook-form/issues/2978
25
+ setLocalCache(omit(formData, localCacheExclude));
26
+ }
27
+ else {
28
+ // @ts-expect-error Typescript thinks that the FieldValues defined in useWatch is different
29
+ // from the one in useFormContext
30
+ setLocalCache(formData);
31
+ }
32
+ }, [formData, isDirty, localCacheExclude, setLocalCache, shouldExclude]);
33
+ return React.createElement(React.Fragment, null);
34
+ }
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { useIntl } from "react-intl";
3
+ import { messages } from "./messages";
4
+ import { useAtlantisContext } from "../../../AtlantisContext";
5
+ import { Banner } from "../../../Banner";
6
+ export function FormErrorBanner({ networkError, bannerError, }) {
7
+ const { formatMessage } = useIntl();
8
+ const { isOnline } = useAtlantisContext();
9
+ if (!isOnline) {
10
+ return (React.createElement(Banner, { text: formatMessage(messages.offlineError), type: "error" }));
11
+ }
12
+ else if (networkError) {
13
+ return (React.createElement(Banner, { text: formatMessage(messages.networkError), type: "error" }));
14
+ }
15
+ else if (bannerError) {
16
+ return (React.createElement(Banner, { text: bannerError.title, details: bannerError.messages, type: "error" }));
17
+ }
18
+ else {
19
+ return React.createElement(React.Fragment, null);
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ export { FormErrorBanner } from "./FormErrorBanner";
@@ -0,0 +1,13 @@
1
+ import { defineMessages } from "react-intl";
2
+ export const messages = defineMessages({
3
+ networkError: {
4
+ id: "networkError",
5
+ defaultMessage: "Could not save changes",
6
+ description: "Displayed when a general server error occurs during save",
7
+ },
8
+ offlineError: {
9
+ id: "offlineError",
10
+ defaultMessage: "Currently offline. Check your internet connection.",
11
+ description: "Error message to be shown when the app is offline",
12
+ },
13
+ });
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { useIntl } from "react-intl";
4
+ import { styles } from "./FormMask.style";
5
+ import { ActivityIndicator } from "../../../ActivityIndicator";
6
+ import { messages } from "../../messages";
7
+ export function FormMask() {
8
+ const { formatMessage } = useIntl();
9
+ return (React.createElement(View, { style: styles.mask, accessibilityLabel: formatMessage(messages.loadingA11YLabel) },
10
+ React.createElement(ActivityIndicator, null)));
11
+ }
@@ -0,0 +1,15 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../../utils/design";
3
+ export const styles = StyleSheet.create({
4
+ mask: {
5
+ zIndex: tokens["elevation-modal"],
6
+ width: "100%",
7
+ height: "100%",
8
+ position: "absolute",
9
+ top: 0,
10
+ left: 0,
11
+ backgroundColor: tokens["color-overlay--dimmed"],
12
+ padding: tokens["space-base"],
13
+ justifyContent: "center",
14
+ },
15
+ });
@@ -0,0 +1 @@
1
+ export { FormMask } from "./FormMask";