ferns-ui 0.47.10 → 1.0.0

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 (402) hide show
  1. package/dist/Accordion.d.ts +3 -0
  2. package/dist/Accordion.js +38 -0
  3. package/dist/Accordion.js.map +1 -0
  4. package/dist/ActionSheet.d.ts +9 -10
  5. package/dist/ActionSheet.js +8 -6
  6. package/dist/ActionSheet.js.map +1 -1
  7. package/dist/AddressField.d.ts +3 -0
  8. package/dist/AddressField.js +33 -0
  9. package/dist/AddressField.js.map +1 -0
  10. package/dist/Avatar.d.ts +2 -2
  11. package/dist/Avatar.js +132 -96
  12. package/dist/Avatar.js.map +1 -1
  13. package/dist/Badge.d.ts +1 -1
  14. package/dist/Badge.js +95 -20
  15. package/dist/Badge.js.map +1 -1
  16. package/dist/Banner.d.ts +1 -1
  17. package/dist/Banner.js +89 -51
  18. package/dist/Banner.js.map +1 -1
  19. package/dist/Body.js +4 -4
  20. package/dist/Body.js.map +1 -1
  21. package/dist/BooleanField.d.ts +3 -0
  22. package/dist/BooleanField.js +90 -0
  23. package/dist/BooleanField.js.map +1 -0
  24. package/dist/Box.js +80 -80
  25. package/dist/Box.js.map +1 -1
  26. package/dist/Button.d.ts +2 -2
  27. package/dist/Button.js +87 -109
  28. package/dist/Button.js.map +1 -1
  29. package/dist/Card.js +2 -2
  30. package/dist/Card.js.map +1 -1
  31. package/dist/CheckBox.d.ts +2 -2
  32. package/dist/CheckBox.js +23 -35
  33. package/dist/CheckBox.js.map +1 -1
  34. package/dist/Common.d.ts +1129 -413
  35. package/dist/Common.js +39 -3
  36. package/dist/Common.js.map +1 -1
  37. package/dist/CommonIconTypes.d.ts +3 -0
  38. package/dist/CommonIconTypes.js +2 -0
  39. package/dist/CommonIconTypes.js.map +1 -0
  40. package/dist/CustomSelectField.d.ts +3 -0
  41. package/dist/CustomSelectField.js +64 -0
  42. package/dist/CustomSelectField.js.map +1 -0
  43. package/dist/DateTimeActionSheet.d.ts +1 -1
  44. package/dist/DateTimeActionSheet.js +170 -158
  45. package/dist/DateTimeActionSheet.js.map +1 -1
  46. package/dist/DateTimeField.d.ts +3 -3
  47. package/dist/DateTimeField.js +140 -30
  48. package/dist/DateTimeField.js.map +1 -1
  49. package/dist/DateUtilities.js.map +1 -1
  50. package/dist/DateUtilities.test.js +2 -1
  51. package/dist/DateUtilities.test.js.map +1 -1
  52. package/dist/DecimalRangeActionSheet.js +3 -15
  53. package/dist/DecimalRangeActionSheet.js.map +1 -1
  54. package/dist/DismissButton.d.ts +3 -0
  55. package/dist/DismissButton.js +14 -0
  56. package/dist/DismissButton.js.map +1 -0
  57. package/dist/EmailField.d.ts +3 -0
  58. package/dist/EmailField.js +52 -0
  59. package/dist/EmailField.js.map +1 -0
  60. package/dist/ErrorBoundary.d.ts +1 -1
  61. package/dist/ErrorPage.js +2 -2
  62. package/dist/ErrorPage.js.map +1 -1
  63. package/dist/FernsProvider.js +1 -1
  64. package/dist/FernsProvider.js.map +1 -1
  65. package/dist/Field.d.ts +2 -2
  66. package/dist/Field.js +67 -136
  67. package/dist/Field.js.map +1 -1
  68. package/dist/Heading.js +37 -16
  69. package/dist/Heading.js.map +1 -1
  70. package/dist/HeightActionSheet.js +3 -7
  71. package/dist/HeightActionSheet.js.map +1 -1
  72. package/dist/Hyperlink.js +7 -1
  73. package/dist/Hyperlink.js.map +1 -1
  74. package/dist/Icon.d.ts +1 -2
  75. package/dist/Icon.js +8 -10
  76. package/dist/Icon.js.map +1 -1
  77. package/dist/IconButton.d.ts +2 -2
  78. package/dist/IconButton.js +93 -92
  79. package/dist/IconButton.js.map +1 -1
  80. package/dist/InfoTooltipButton.d.ts +1 -1
  81. package/dist/InfoTooltipButton.js +2 -2
  82. package/dist/InfoTooltipButton.js.map +1 -1
  83. package/dist/Link.d.ts +1 -1
  84. package/dist/Link.js +7 -3
  85. package/dist/Link.js.map +1 -1
  86. package/dist/MobileAddressAutoComplete.js +8 -8
  87. package/dist/MobileAddressAutoComplete.js.map +1 -1
  88. package/dist/Modal.d.ts +2 -2
  89. package/dist/Modal.js +95 -80
  90. package/dist/Modal.js.map +1 -1
  91. package/dist/MultiselectField.d.ts +3 -0
  92. package/dist/MultiselectField.js +49 -0
  93. package/dist/MultiselectField.js.map +1 -0
  94. package/dist/NumberField.d.ts +3 -0
  95. package/dist/NumberField.js +49 -0
  96. package/dist/NumberField.js.map +1 -0
  97. package/dist/NumberPickerActionSheet.js +2 -2
  98. package/dist/NumberPickerActionSheet.js.map +1 -1
  99. package/dist/OpenAPIContext.js.map +1 -1
  100. package/dist/Page.js +6 -6
  101. package/dist/Page.js.map +1 -1
  102. package/dist/Pagination.d.ts +2 -8
  103. package/dist/Pagination.js +107 -13
  104. package/dist/Pagination.js.map +1 -1
  105. package/dist/PasswordField.d.ts +2 -0
  106. package/dist/PasswordField.js +6 -0
  107. package/dist/PasswordField.js.map +1 -0
  108. package/dist/PhoneNumberField.d.ts +3 -0
  109. package/dist/PhoneNumberField.js +79 -0
  110. package/dist/PhoneNumberField.js.map +1 -0
  111. package/dist/PickerSelect.d.ts +6 -67
  112. package/dist/PickerSelect.js +145 -115
  113. package/dist/PickerSelect.js.map +1 -1
  114. package/dist/Radio.d.ts +3 -0
  115. package/dist/Radio.js +21 -0
  116. package/dist/Radio.js.map +1 -0
  117. package/dist/RadioField.d.ts +3 -0
  118. package/dist/RadioField.js +22 -0
  119. package/dist/RadioField.js.map +1 -0
  120. package/dist/SegmentedControl.d.ts +1 -1
  121. package/dist/SegmentedControl.js +37 -68
  122. package/dist/SegmentedControl.js.map +1 -1
  123. package/dist/SelectField.d.ts +3 -0
  124. package/dist/SelectField.js +20 -0
  125. package/dist/SelectField.js.map +1 -0
  126. package/dist/SideDrawer.js +8 -2
  127. package/dist/SideDrawer.js.map +1 -1
  128. package/dist/Signature.d.ts +1 -0
  129. package/dist/Signature.js +8 -7
  130. package/dist/Signature.js.map +1 -1
  131. package/dist/Signature.native.js +8 -7
  132. package/dist/Signature.native.js.map +1 -1
  133. package/dist/SignatureField.d.ts +3 -0
  134. package/dist/SignatureField.js +54 -0
  135. package/dist/SignatureField.js.map +1 -0
  136. package/dist/Spinner.js +17 -2
  137. package/dist/Spinner.js.map +1 -1
  138. package/dist/SplitPage.d.ts +1 -1
  139. package/dist/SplitPage.js +10 -10
  140. package/dist/SplitPage.js.map +1 -1
  141. package/dist/SplitPage.native.js +7 -7
  142. package/dist/SplitPage.native.js.map +1 -1
  143. package/dist/TapToEdit.d.ts +3 -3
  144. package/dist/TapToEdit.js +64 -59
  145. package/dist/TapToEdit.js.map +1 -1
  146. package/dist/Text.d.ts +1 -1
  147. package/dist/Text.js +70 -64
  148. package/dist/Text.js.map +1 -1
  149. package/dist/TextArea.d.ts +1 -1
  150. package/dist/TextArea.js +2 -14
  151. package/dist/TextArea.js.map +1 -1
  152. package/dist/TextField.d.ts +2 -2
  153. package/dist/TextField.js +89 -206
  154. package/dist/TextField.js.map +1 -1
  155. package/dist/TextFieldNumberActionSheet.js +2 -2
  156. package/dist/TextFieldNumberActionSheet.js.map +1 -1
  157. package/dist/Theme.d.ts +87 -3
  158. package/dist/Theme.js +197 -98
  159. package/dist/Theme.js.map +1 -1
  160. package/dist/TimezonePicker.js +3 -5
  161. package/dist/TimezonePicker.js.map +1 -1
  162. package/dist/Toast.d.ts +18 -5
  163. package/dist/Toast.js +130 -31
  164. package/dist/Toast.js.map +1 -1
  165. package/dist/Tooltip.d.ts +2 -2
  166. package/dist/Tooltip.js +177 -80
  167. package/dist/Tooltip.js.map +1 -1
  168. package/dist/UnifiedAddressAutoComplete.js +2 -2
  169. package/dist/UnifiedAddressAutoComplete.js.map +1 -1
  170. package/dist/Unifier.js.map +1 -1
  171. package/dist/Utilities.js +5 -3
  172. package/dist/Utilities.js.map +1 -1
  173. package/dist/WebAddressAutocomplete.js +2 -2
  174. package/dist/WebAddressAutocomplete.js.map +1 -1
  175. package/dist/fieldElements/FieldError.d.ts +6 -0
  176. package/dist/fieldElements/FieldError.js +14 -0
  177. package/dist/fieldElements/FieldError.js.map +1 -0
  178. package/dist/fieldElements/FieldHelperText.d.ts +6 -0
  179. package/dist/fieldElements/FieldHelperText.js +11 -0
  180. package/dist/fieldElements/FieldHelperText.js.map +1 -0
  181. package/dist/fieldElements/FieldTitle.d.ts +6 -0
  182. package/dist/fieldElements/FieldTitle.js +19 -0
  183. package/dist/fieldElements/FieldTitle.js.map +1 -0
  184. package/dist/fieldElements/index.d.ts +3 -0
  185. package/dist/fieldElements/index.js +4 -0
  186. package/dist/fieldElements/index.js.map +1 -0
  187. package/dist/icons/MobileIcon.d.ts +3 -0
  188. package/dist/icons/MobileIcon.js +24 -0
  189. package/dist/icons/MobileIcon.js.map +1 -0
  190. package/dist/icons/OfflineIcon.d.ts +3 -0
  191. package/dist/icons/OfflineIcon.js +23 -0
  192. package/dist/icons/OfflineIcon.js.map +1 -0
  193. package/dist/icons/OnlineIcon.d.ts +3 -0
  194. package/dist/icons/OnlineIcon.js +24 -0
  195. package/dist/icons/OnlineIcon.js.map +1 -0
  196. package/dist/icons/OutOfficeIcon.d.ts +3 -0
  197. package/dist/icons/OutOfficeIcon.js +24 -0
  198. package/dist/icons/OutOfficeIcon.js.map +1 -0
  199. package/dist/icons/index.d.ts +4 -0
  200. package/dist/icons/index.js +5 -0
  201. package/dist/icons/index.js.map +1 -0
  202. package/dist/index.d.ts +28 -14
  203. package/dist/index.js +28 -14
  204. package/dist/index.js.map +1 -1
  205. package/dist/setupTests.d.ts +1 -0
  206. package/dist/setupTests.js +1 -1
  207. package/dist/setupTests.js.map +1 -1
  208. package/dist/{Table.d.ts → table/Table.d.ts} +2 -2
  209. package/dist/{Table.js → table/Table.js} +18 -10
  210. package/dist/table/Table.js.map +1 -0
  211. package/dist/table/TableBadge.d.ts +6 -0
  212. package/dist/table/TableBadge.js +22 -0
  213. package/dist/table/TableBadge.js.map +1 -0
  214. package/dist/table/TableBoolean.d.ts +6 -0
  215. package/dist/table/TableBoolean.js +39 -0
  216. package/dist/table/TableBoolean.js.map +1 -0
  217. package/dist/table/TableDate.d.ts +3 -0
  218. package/dist/table/TableDate.js +26 -0
  219. package/dist/table/TableDate.js.map +1 -0
  220. package/dist/{TableHeader.d.ts → table/TableHeader.d.ts} +1 -1
  221. package/dist/{TableHeader.js → table/TableHeader.js} +2 -2
  222. package/dist/table/TableHeader.js.map +1 -0
  223. package/dist/{TableHeaderCell.d.ts → table/TableHeaderCell.d.ts} +2 -2
  224. package/dist/table/TableHeaderCell.js +58 -0
  225. package/dist/table/TableHeaderCell.js.map +1 -0
  226. package/dist/table/TableIconButton.d.ts +3 -0
  227. package/dist/table/TableIconButton.js +42 -0
  228. package/dist/table/TableIconButton.js.map +1 -0
  229. package/dist/table/TableNumber.d.ts +3 -0
  230. package/dist/table/TableNumber.js +18 -0
  231. package/dist/table/TableNumber.js.map +1 -0
  232. package/dist/{TableRow.d.ts → table/TableRow.d.ts} +1 -1
  233. package/dist/table/TableRow.js +28 -0
  234. package/dist/table/TableRow.js.map +1 -0
  235. package/dist/table/TableText.d.ts +3 -0
  236. package/dist/table/TableText.js +18 -0
  237. package/dist/table/TableText.js.map +1 -0
  238. package/dist/table/TableTitle.d.ts +3 -0
  239. package/dist/table/TableTitle.js +22 -0
  240. package/dist/table/TableTitle.js.map +1 -0
  241. package/dist/{tableContext.d.ts → table/tableContext.d.ts} +1 -1
  242. package/dist/table/tableContext.js.map +1 -0
  243. package/package.json +10 -4
  244. package/src/Accordion.tsx +92 -0
  245. package/src/ActionSheet.tsx +8 -5
  246. package/src/AddressField.tsx +122 -0
  247. package/src/Avatar.tsx +193 -147
  248. package/src/Badge.tsx +117 -49
  249. package/src/Banner.tsx +158 -111
  250. package/src/Body.tsx +4 -4
  251. package/src/BooleanField.tsx +122 -0
  252. package/src/Box.tsx +92 -82
  253. package/src/Button.tsx +151 -167
  254. package/src/Card.tsx +2 -2
  255. package/src/CheckBox.tsx +30 -111
  256. package/src/Common.ts +1380 -2059
  257. package/src/CommonIconTypes.ts +2030 -0
  258. package/src/CustomSelectField.tsx +116 -0
  259. package/src/DateTimeActionSheet.tsx +341 -259
  260. package/src/DateTimeField.tsx +168 -61
  261. package/src/DateUtilities.test.ts +6 -2
  262. package/src/DateUtilities.tsx +1 -1
  263. package/src/DecimalRangeActionSheet.tsx +2 -17
  264. package/src/DismissButton.tsx +31 -0
  265. package/src/EmailField.tsx +70 -0
  266. package/src/ErrorPage.tsx +2 -2
  267. package/src/FernsProvider.tsx +1 -1
  268. package/src/Field.tsx +83 -345
  269. package/src/Heading.tsx +44 -16
  270. package/src/HeightActionSheet.tsx +2 -9
  271. package/src/Hyperlink.tsx +8 -1
  272. package/src/Icon.tsx +22 -14
  273. package/src/IconButton.tsx +188 -156
  274. package/src/InfoTooltipButton.tsx +4 -6
  275. package/src/Link.tsx +14 -5
  276. package/src/MobileAddressAutoComplete.tsx +17 -13
  277. package/src/Modal.tsx +214 -190
  278. package/src/MultiselectField.tsx +103 -0
  279. package/src/NumberField.tsx +55 -0
  280. package/src/NumberPickerActionSheet.tsx +1 -4
  281. package/src/OpenAPIContext.tsx +1 -1
  282. package/src/Page.tsx +9 -13
  283. package/src/Pagination.tsx +171 -36
  284. package/src/PasswordField.tsx +7 -0
  285. package/src/PhoneNumberField.tsx +103 -0
  286. package/src/PickerSelect.tsx +169 -151
  287. package/src/Radio.tsx +33 -0
  288. package/src/RadioField.tsx +43 -0
  289. package/src/SegmentedControl.tsx +46 -97
  290. package/src/SelectField.tsx +41 -0
  291. package/src/SideDrawer.tsx +8 -2
  292. package/src/Signature.native.tsx +16 -9
  293. package/src/Signature.tsx +19 -11
  294. package/src/SignatureField.tsx +92 -0
  295. package/src/Spinner.tsx +16 -2
  296. package/src/SplitPage.native.tsx +9 -7
  297. package/src/SplitPage.tsx +11 -12
  298. package/src/TapToEdit.tsx +122 -109
  299. package/src/Text.tsx +79 -69
  300. package/src/TextArea.tsx +2 -2
  301. package/src/TextField.tsx +133 -285
  302. package/src/TextFieldNumberActionSheet.tsx +1 -4
  303. package/src/Theme.tsx +223 -172
  304. package/src/TimezonePicker.tsx +3 -18
  305. package/src/Toast.tsx +193 -70
  306. package/src/Tooltip.tsx +233 -122
  307. package/src/UnifiedAddressAutoComplete.tsx +3 -3
  308. package/src/Unifier.ts +4 -4
  309. package/src/Utilities.tsx +11 -3
  310. package/src/WebAddressAutocomplete.tsx +2 -3
  311. package/src/fieldElements/FieldError.tsx +24 -0
  312. package/src/fieldElements/FieldHelperText.tsx +20 -0
  313. package/src/fieldElements/FieldTitle.tsx +31 -0
  314. package/src/fieldElements/index.tsx +3 -0
  315. package/src/icons/MobileIcon.tsx +41 -0
  316. package/src/icons/OfflineIcon.tsx +38 -0
  317. package/src/icons/OnlineIcon.tsx +40 -0
  318. package/src/icons/OutOfficeIcon.tsx +37 -0
  319. package/src/icons/index.ts +4 -0
  320. package/src/index.tsx +28 -14
  321. package/src/setupTests.ts +1 -0
  322. package/src/{Table.tsx → table/Table.tsx} +24 -14
  323. package/src/table/TableBadge.tsx +46 -0
  324. package/src/table/TableBoolean.tsx +70 -0
  325. package/src/table/TableDate.tsx +38 -0
  326. package/src/{TableHeader.tsx → table/TableHeader.tsx} +3 -3
  327. package/src/table/TableHeaderCell.tsx +93 -0
  328. package/src/table/TableIconButton.tsx +62 -0
  329. package/src/table/TableNumber.tsx +29 -0
  330. package/src/{TableRow.tsx → table/TableRow.tsx} +28 -25
  331. package/src/table/TableText.tsx +29 -0
  332. package/src/table/TableTitle.tsx +32 -0
  333. package/src/{tableContext.tsx → table/tableContext.tsx} +1 -1
  334. package/dist/BlurBox.d.ts +0 -5
  335. package/dist/BlurBox.js +0 -28
  336. package/dist/BlurBox.js.map +0 -1
  337. package/dist/BlurBox.native.d.ts +0 -6
  338. package/dist/BlurBox.native.js +0 -30
  339. package/dist/BlurBox.native.js.map +0 -1
  340. package/dist/CustomSelect.d.ts +0 -3
  341. package/dist/CustomSelect.js +0 -47
  342. package/dist/CustomSelect.js.map +0 -1
  343. package/dist/DateTimeField.android.d.ts +0 -3
  344. package/dist/DateTimeField.android.js +0 -67
  345. package/dist/DateTimeField.android.js.map +0 -1
  346. package/dist/DateTimeField.ios.d.ts +0 -3
  347. package/dist/DateTimeField.ios.js +0 -49
  348. package/dist/DateTimeField.ios.js.map +0 -1
  349. package/dist/FieldWithLabels.d.ts +0 -3
  350. package/dist/FieldWithLabels.js +0 -8
  351. package/dist/FieldWithLabels.js.map +0 -1
  352. package/dist/Form.d.ts +0 -16
  353. package/dist/Form.js +0 -89
  354. package/dist/Form.js.map +0 -1
  355. package/dist/HeaderButtons.d.ts +0 -31
  356. package/dist/HeaderButtons.js +0 -53
  357. package/dist/HeaderButtons.js.map +0 -1
  358. package/dist/Mask.d.ts +0 -2
  359. package/dist/Mask.js +0 -19
  360. package/dist/Mask.js.map +0 -1
  361. package/dist/Pill.d.ts +0 -3
  362. package/dist/Pill.js +0 -8
  363. package/dist/Pill.js.map +0 -1
  364. package/dist/Pog.d.ts +0 -3
  365. package/dist/Pog.js +0 -48
  366. package/dist/Pog.js.map +0 -1
  367. package/dist/ProgressBar.d.ts +0 -3
  368. package/dist/ProgressBar.js +0 -35
  369. package/dist/ProgressBar.js.map +0 -1
  370. package/dist/SelectList.d.ts +0 -27
  371. package/dist/SelectList.js +0 -56
  372. package/dist/SelectList.js.map +0 -1
  373. package/dist/Switch.d.ts +0 -3
  374. package/dist/Switch.js +0 -20
  375. package/dist/Switch.js.map +0 -1
  376. package/dist/Table.js.map +0 -1
  377. package/dist/TableHeader.js.map +0 -1
  378. package/dist/TableHeaderCell.js +0 -36
  379. package/dist/TableHeaderCell.js.map +0 -1
  380. package/dist/TableRow.js +0 -30
  381. package/dist/TableRow.js.map +0 -1
  382. package/dist/WithLabel.d.ts +0 -3
  383. package/dist/WithLabel.js +0 -15
  384. package/dist/WithLabel.js.map +0 -1
  385. package/dist/tableContext.js.map +0 -1
  386. package/src/BlurBox.native.tsx +0 -40
  387. package/src/BlurBox.tsx +0 -31
  388. package/src/CustomSelect.tsx +0 -80
  389. package/src/DateTimeField.android.tsx +0 -103
  390. package/src/DateTimeField.ios.tsx +0 -85
  391. package/src/FieldWithLabels.tsx +0 -36
  392. package/src/Form.tsx +0 -175
  393. package/src/HeaderButtons.tsx +0 -107
  394. package/src/Mask.tsx +0 -21
  395. package/src/Pill.tsx +0 -22
  396. package/src/Pog.tsx +0 -75
  397. package/src/ProgressBar.tsx +0 -47
  398. package/src/SelectList.tsx +0 -109
  399. package/src/Switch.tsx +0 -18
  400. package/src/TableHeaderCell.tsx +0 -72
  401. package/src/WithLabel.tsx +0 -45
  402. /package/dist/{tableContext.js → table/tableContext.js} +0 -0
package/src/Avatar.tsx CHANGED
@@ -1,83 +1,97 @@
1
1
  /* eslint-disable no-console */
2
2
  import {ImageResult, manipulateAsync, SaveFormat} from "expo-image-manipulator";
3
3
  import {launchImageLibraryAsync, MediaTypeOptions} from "expo-image-picker";
4
- import React, {useContext, useEffect, useState} from "react";
5
- import {Image, ImageResizeMode, Platform, Text, View} from "react-native";
4
+ import {LinearGradient} from "expo-linear-gradient";
5
+ import React, {FC, useEffect, useState} from "react";
6
+ import {Image, Pressable, Text, View} from "react-native";
6
7
 
7
- import {Box} from "./Box";
8
- import {AllColors, AvatarProps, IconName, UnsignedUpTo12} from "./Common";
8
+ import {AvatarProps, CustomSvgProps} from "./Common";
9
9
  import {Icon} from "./Icon";
10
+ import {MobileIcon, OfflineIcon, OnlineIcon, OutOfOfficeIcon} from "./icons";
10
11
  import {isMobileDevice} from "./MediaQuery";
11
- import {ThemeContext} from "./Theme";
12
+ import {useTheme} from "./Theme";
12
13
  import {Tooltip} from "./Tooltip";
13
14
 
14
15
  const sizes = {
15
- xs: 24,
16
- sm: 32,
17
- md: 48,
18
- lg: 64,
16
+ xs: 28,
17
+ sm: 38,
18
+ md: 56,
19
+ lg: 72,
19
20
  xl: 120,
20
21
  };
21
22
 
22
- const sizeIconPadding: {[id: string]: UnsignedUpTo12} = {
23
- xs: 0,
24
- sm: 0,
25
- md: 1,
26
- lg: 1,
27
- xl: 2,
23
+ const initialsFontSizes = {
24
+ xs: 12,
25
+ sm: 16,
26
+ md: 24,
27
+ lg: 32,
28
+ xl: 60,
28
29
  };
29
30
 
30
- const statusIcons: {[id: string]: {icon: IconName; color: AllColors; label: string}} = {
31
- online: {icon: "circle", color: "green", label: "Online"},
32
- offline: {icon: "circle", color: "gray", label: "Offline"},
33
- doNotDisturb: {icon: "minus-circle", color: "red", label: "Do Not Disturb"},
34
- away: {icon: "moon", color: "orange", label: "Away"},
35
- meeting: {icon: "calendar", color: "orange", label: "In a Meeting"},
36
- vacation: {icon: "plane", color: "orange", label: "On Vacation"},
37
- sick: {icon: "clinic-medical", color: "orange", label: "Sick"},
38
- outOfOffice: {icon: "clock", color: "orange", label: "Out of Office"},
39
- commuting: {icon: "car", color: "orange", label: "Commuting"},
31
+ const iconSizeScale = {
32
+ xs: 0.5,
33
+ sm: 0.7,
34
+ md: 0.9,
35
+ lg: 1.1,
36
+ xl: 1.5,
40
37
  };
41
38
 
42
- export const Avatar = (props: AvatarProps): React.ReactElement => {
43
- const {theme} = useContext(ThemeContext);
39
+ const sizeIconPadding = {
40
+ xs: 12,
41
+ sm: 10,
42
+ md: 9,
43
+ lg: 7,
44
+ xl: 0,
45
+ };
44
46
 
47
+ export const Avatar: FC<AvatarProps> = ({
48
+ name,
49
+ hasBorder = true,
50
+ size = "md",
51
+ src,
52
+ onChange,
53
+ status,
54
+ doNotDisturb = false,
55
+ }) => {
56
+ const {theme} = useTheme();
45
57
  const [isImageLoaded, setIsImageLoaded] = useState(true);
46
- const [hovered, setHovered] = useState(false);
47
- const [src, setSrc] = useState(props.src ?? undefined);
48
- const {
49
- name,
50
- initials,
51
- outline,
52
- size = "md",
53
- imageFit = "contain",
54
- editAvatarImage,
55
- onChange,
56
- avatarImageWidth = sizes[size],
57
- avatarImageHeight,
58
- avatarImageFormat = SaveFormat.PNG,
59
- shouldShowEditIconIfNoImage = false,
60
- } = props;
61
- const width = sizes[size];
62
- const height = sizes[size];
63
- const radius = sizes[size] / 2;
64
- const fontSize = sizes[size] / 2;
58
+ const [imgSrc, setImgSrc] = useState(src ?? undefined);
59
+ const avatarImageFormat = SaveFormat.PNG;
60
+ const avatarImageDiameter = sizes[size];
61
+ const showEditIcon = status === "imagePicker";
62
+
63
+ const avatarRadius = avatarImageDiameter / 2;
65
64
  const computedInitials =
66
- initials ??
67
- (name.match(/(^\S\S?|\s\S)?/g) as any)
68
- .map((v: string) => v.trim())
65
+ (name.match(/(^\S\S?|\s\S)?/g) ?? []) // Use nullish coalescing to handle the case where match returns null
66
+ .map((v) => v.trim())
69
67
  .join("")
70
- .match(/(^\S|\S$)?/g)
68
+ .match(/(^\S|\S$)?/g) ??
69
+ [] // Use nullish coalescing to handle the case where match returns null
71
70
  .join("")
72
71
  .toLocaleUpperCase();
72
+ const statusIcons: {
73
+ [id: string]: {
74
+ icon: (props: CustomSvgProps) => React.ReactElement;
75
+ label: string;
76
+ };
77
+ } = {
78
+ online: {icon: OnlineIcon, label: "Online"},
79
+ offline: {icon: OfflineIcon, label: "Offline"},
80
+ outOfOffice: {icon: OutOfOfficeIcon, label: "Out of Office"},
81
+ activeMobile: {
82
+ icon: MobileIcon,
83
+
84
+ label: "Active on Mobile",
85
+ },
86
+ };
73
87
 
74
88
  // If the src changes, update the image.
75
89
  useEffect(() => {
76
- setSrc(props.src);
77
- }, [props]);
90
+ setImgSrc(imgSrc);
91
+ }, [imgSrc]);
78
92
 
79
- if (editAvatarImage && !onChange) {
80
- console.warn("Avatars with the editAvatarImage flag on should also have an onChange property.");
93
+ if (showEditIcon && !onChange) {
94
+ console.warn("Avatars with the status of 'imagePicker' should also have an onChange property.");
81
95
  }
82
96
 
83
97
  const handleImageError = () => {
@@ -95,7 +109,7 @@ export const Avatar = (props: AvatarProps): React.ReactElement => {
95
109
 
96
110
  if (!result.canceled && result.assets) {
97
111
  const resizedImage = await resizeImage(result.assets[0].uri);
98
- setSrc(resizedImage.uri);
112
+ setImgSrc(resizedImage.uri);
99
113
  if (onChange) {
100
114
  onChange({avatarImageFormat, ...resizedImage});
101
115
  }
@@ -105,106 +119,102 @@ export const Avatar = (props: AvatarProps): React.ReactElement => {
105
119
  const resizeImage = async (imageUri: string): Promise<ImageResult> => {
106
120
  return manipulateAsync(
107
121
  imageUri,
108
- [{resize: {width: avatarImageWidth, height: avatarImageHeight}}],
122
+ [{resize: {width: avatarImageDiameter, height: avatarImageDiameter}}],
109
123
  {format: avatarImageFormat}
110
124
  );
111
125
  };
112
126
 
113
- function shouldShowEditIcon() {
114
- if (Platform.OS === "web") {
115
- return (shouldShowEditIconIfNoImage && !src) || (editAvatarImage && hovered);
116
- } else {
117
- return (shouldShowEditIconIfNoImage && !src) || editAvatarImage;
127
+ const renderEditIcon = () => {
128
+ if (size !== "xl") {
129
+ console.error(`Avatar: "imagePicker" status is only supported for size "xl"`);
130
+ return null;
118
131
  }
119
- }
120
132
 
121
- const renderEditIcon = () => {
122
- if (shouldShowEditIcon() && Platform.OS === "web") {
123
- return (
124
- <Box
125
- alignItems="center"
126
- dangerouslySetInlineStyle={{
127
- __style: {backgroundColor: "rgba(255,255,255,0.8)", borderRadius: radius},
133
+ return (
134
+ <Pressable
135
+ accessibilityRole="button"
136
+ style={{
137
+ alignItems: "center",
138
+ backgroundColor: "rgba(255,255,255,0.75)",
139
+ borderRadius: avatarRadius,
140
+ height: avatarImageDiameter,
141
+ justifyContent: "center",
142
+ position: "absolute",
143
+ width: avatarImageDiameter,
144
+ zIndex: 5,
145
+ }}
146
+ onPress={pickImage}
147
+ >
148
+ <Icon color="primary" iconName="pen-to-square" size="2xl" type="regular" />
149
+ <Text
150
+ style={{
151
+ textAlign: "center",
152
+ fontWeight: "bold",
153
+ fontSize: 12,
154
+ marginTop: 10,
128
155
  }}
129
- height={height}
130
- justifyContent="center"
131
- position="absolute"
132
- width={width}
133
- zIndex={5}
134
- onClick={pickImage}
135
- onHoverEnd={() => setHovered(false)}
136
- onHoverStart={() => setHovered(true)}
137
- >
138
- <Icon color="darkGray" name="edit" size={size} />
139
- <Text style={{fontWeight: "bold"}}>Upload Image</Text>
140
- </Box>
141
- );
142
- } else if (shouldShowEditIcon() && Platform.OS !== "web") {
143
- return (
144
- <Box
145
- bottom
146
- left={Boolean(props.status)}
147
- paddingX={sizeIconPadding[size]}
148
- position="absolute"
149
- right={!Boolean(props.status)}
150
- zIndex={5}
151
- onClick={pickImage}
152
156
  >
153
- <Icon color="darkGray" name="edit" size={size} />
154
- </Box>
155
- );
156
- }
157
- return null;
157
+ Upload Image
158
+ </Text>
159
+ </Pressable>
160
+ );
158
161
  };
159
162
 
160
163
  const renderStatusIcon = () => {
161
- if (!props.status) {
164
+ if (!status || showEditIcon) {
162
165
  return null;
163
166
  }
164
- // eslint-disable-next-line prefer-const
165
- let {icon, color} = statusIcons[props.status];
166
- if (
167
- props.statusMobile &&
168
- ["online", "away", "offline", "doNotDisturb"].includes(props.status)
169
- ) {
170
- icon = "mobile-alt";
171
- }
172
- if (!icon || !color) {
173
- console.warn(`Avatar: Invalid status ${props.status}`);
167
+ const {icon} = statusIcons[status];
168
+
169
+ if (!icon) {
170
+ console.warn(`Avatar: Invalid status ${status}`);
174
171
  return null;
175
172
  }
173
+
176
174
  return (
177
- <Box bottom paddingX={sizeIconPadding[size]} position="absolute" right zIndex={5}>
178
- <Icon color={color} name={icon} size={size} />
179
- </Box>
175
+ <View
176
+ style={{
177
+ bottom: 0,
178
+ position: "absolute",
179
+ right: 0,
180
+ zIndex: 5,
181
+ }}
182
+ >
183
+ {icon({
184
+ doNotDisturb,
185
+ transform: [{scale: iconSizeScale[size]}],
186
+ })}
187
+ </View>
180
188
  );
181
189
  };
182
190
 
183
- const avatar = (
184
- <Box height={height} position="relative" width={width}>
185
- <Box
186
- border={outline ? "white" : undefined}
187
- height={height}
188
- overflow="hidden"
189
- position="relative"
190
- rounding="circle"
191
- width={width}
192
- onHoverEnd={() => setHovered(false)}
193
- onHoverStart={() => setHovered(true)}
191
+ let avatar = (
192
+ <View
193
+ accessibilityHint={showEditIcon ? "Opens file explorer" : "Avatar image"}
194
+ accessibilityLabel={`${name}'s avatar`}
195
+ accessibilityRole="image"
196
+ style={{height: avatarImageDiameter, position: "relative", width: avatarImageDiameter}}
197
+ >
198
+ <Pressable
199
+ accessibilityRole="button"
200
+ style={{
201
+ overflow: "hidden",
202
+ position: "relative",
203
+ borderRadius: 1,
204
+ cursor: showEditIcon ? "pointer" : "auto",
205
+ }}
194
206
  >
195
207
  {src && isImageLoaded ? (
196
208
  // TODO: Make our Image component rounding work so that we can use it for Avatar.
197
209
  // Currently it creates an unrounded box around the Image.
198
210
  <Image
199
- resizeMode={imageFit as ImageResizeMode}
211
+ accessibilityIgnoresInvertColors
200
212
  source={{uri: src, cache: "force-cache"}}
201
213
  style={{
202
- borderRadius: radius,
203
- height,
204
- width,
205
- display: "flex",
206
- alignItems: "center",
207
- justifyContent: "center",
214
+ borderRadius: avatarRadius,
215
+ borderWidth: hasBorder && status !== "imagePicker" ? avatarImageDiameter * 0.04 : 0,
216
+ borderColor: hasBorder ? "white" : "transparent",
217
+ height: avatarImageDiameter,
208
218
  overflow: "hidden",
209
219
  }}
210
220
  onError={handleImageError}
@@ -212,43 +222,79 @@ export const Avatar = (props: AvatarProps): React.ReactElement => {
212
222
  ) : (
213
223
  <View
214
224
  style={{
215
- height,
216
- width,
217
- borderRadius: radius,
225
+ height: avatarImageDiameter,
226
+ width: avatarImageDiameter,
227
+ borderRadius: avatarRadius,
228
+ borderWidth: hasBorder && status !== "imagePicker" ? avatarImageDiameter * 0.04 : 0,
229
+ borderColor: hasBorder && status !== "imagePicker" ? "white" : "transparent",
218
230
  display: "flex",
219
231
  alignItems: "center",
220
232
  justifyContent: "center",
221
- backgroundColor: props.backgroundColor ? theme[props.backgroundColor] : theme.gray,
233
+ backgroundColor: theme.surface.secondaryDark,
222
234
  }}
223
235
  >
224
- <Text style={{fontSize, color: props.textColor ?? theme.darkGray}}>
236
+ <Text
237
+ style={{
238
+ fontWeight: 500,
239
+ fontSize: initialsFontSizes[size],
240
+ color: theme.text.inverted,
241
+ }}
242
+ >
225
243
  {computedInitials}
226
244
  </Text>
227
245
  </View>
228
246
  )}
229
- </Box>
247
+ </Pressable>
230
248
  {/* Needs to come after the image so it renders on top. */}
231
- {renderEditIcon()}
232
- {renderStatusIcon()}
233
- </Box>
249
+ {showEditIcon && renderEditIcon()}
250
+ </View>
234
251
  );
235
252
 
236
- let status = props.statusText;
237
- if (!status && props.status) {
238
- status = statusIcons[props.status]?.label;
253
+ if (hasBorder && status !== "imagePicker") {
254
+ const gradientDiameter = avatarImageDiameter * 1.1;
255
+ const gradientStartColor = "#FFC947";
256
+ const gradientEndColor = "#EA9095";
257
+ // Start the first color in the top left corner and end the second color in the bottom
258
+ // right corner.
259
+
260
+ avatar = (
261
+ <LinearGradient
262
+ colors={[gradientStartColor, gradientEndColor]}
263
+ end={{x: 1, y: 1}}
264
+ start={{x: 0, y: 0}}
265
+ style={{
266
+ height: gradientDiameter,
267
+ width: gradientDiameter,
268
+ borderRadius: gradientDiameter / 2,
269
+ alignItems: "center",
270
+ justifyContent: "center",
271
+ }}
272
+ >
273
+ {avatar}
274
+ </LinearGradient>
275
+ );
239
276
  }
240
277
 
241
278
  if (status) {
242
279
  // Need to wrap the tooltip so it doesn't expand to 100% width and render the tooltip off.
243
280
  // Don't show the tooltips on mobile because they intercept the edit avatar clicks.
244
- return (
245
- <Box width={width}>
246
- <Tooltip idealDirection="top" text={isMobileDevice() ? undefined : status}>
281
+ const widthPlusPadding = avatarImageDiameter + sizeIconPadding[size];
282
+
283
+ avatar = (
284
+ <View
285
+ style={{
286
+ width: widthPlusPadding,
287
+ paddingRight: sizeIconPadding[size],
288
+ paddingBottom: sizeIconPadding[size],
289
+ }}
290
+ >
291
+ <Tooltip idealPosition="top" text={isMobileDevice() ? undefined : status}>
247
292
  {avatar}
248
293
  </Tooltip>
249
- </Box>
294
+ {renderStatusIcon()}
295
+ </View>
250
296
  );
251
- } else {
252
- return avatar;
253
297
  }
298
+
299
+ return avatar;
254
300
  };
package/src/Badge.tsx CHANGED
@@ -1,66 +1,134 @@
1
1
  import React from "react";
2
+ import {Text, View} from "react-native";
2
3
 
3
- import {Box} from "./Box";
4
- import {BadgeProps} from "./Common";
4
+ import {BadgeProps, SurfaceTheme, TextTheme} from "./Common";
5
5
  import {Icon} from "./Icon";
6
- import {Text} from "./Text";
6
+ import {useTheme} from "./Theme";
7
7
 
8
8
  export const Badge = ({
9
- title,
10
- position = "middle",
11
- type = "info",
12
- color,
13
- size = "xs",
14
- iconProps,
15
- fontColor = "white",
16
- fontWeight = "bold",
17
- rounding = "pill",
9
+ value,
10
+ iconName,
11
+ status = "info",
12
+ secondary = false,
13
+ variant,
14
+ maxValue = 100,
15
+ customBackgroundColor,
16
+ customTextColor,
17
+ customBorderColor,
18
+ customIconColor,
19
+ customIconName,
18
20
  }: BadgeProps): React.ReactElement => {
19
- if (color && type !== "custom") {
20
- console.warn('Badge color only supported when `type` is set to "custom".');
21
- }
22
- const badgeColor = type === "custom" ? color! : type;
21
+ const {theme} = useTheme();
22
+ const isIconOnly = variant === "iconOnly";
23
23
 
24
- function renderIcon(): React.ReactElement | null {
25
- if (iconProps && iconProps.name) {
26
- return (
27
- <Box marginRight={title ? 1 : 0}>
28
- <Icon color={iconProps?.color ? iconProps.color : fontColor} {...iconProps} size={size} />
29
- </Box>
30
- );
31
- } else {
32
- return null;
24
+ let badgeColor: keyof TextTheme = "inverted";
25
+
26
+ // TODO: Move to theme
27
+ const secondaryBorderColors = {
28
+ error: "#F39E9E",
29
+ warning: "#FCC58F",
30
+ info: "#8FC1D2",
31
+ success: "#7FD898",
32
+ neutral: "#AAAAAA",
33
+ custom: "#AAAAAA",
34
+ };
35
+
36
+ if (secondary) {
37
+ if (status === "error") {
38
+ badgeColor = "error";
39
+ } else if (status === "warning") {
40
+ badgeColor = "warning";
41
+ } else if (status === "info") {
42
+ badgeColor = "secondaryDark";
43
+ } else if (status === "success") {
44
+ badgeColor = "success";
45
+ } else if (status === "neutral") {
46
+ badgeColor = "primary";
33
47
  }
34
48
  }
35
49
 
36
- function renderLabel(): React.ReactElement | null {
37
- if (!title) {
38
- return null;
50
+ let badgeBgColor: keyof SurfaceTheme = "neutralDark";
51
+
52
+ if (status === "error") {
53
+ badgeBgColor = secondary ? "errorLight" : "error";
54
+ } else if (status === "warning") {
55
+ badgeBgColor = secondary ? "warningLight" : "warning";
56
+ } else if (status === "info") {
57
+ badgeBgColor = secondary ? "secondaryLight" : "secondaryDark";
58
+ } else if (status === "success") {
59
+ badgeBgColor = secondary ? "successLight" : "success";
60
+ } else if (status === "neutral") {
61
+ badgeBgColor = secondary ? "neutralLight" : "neutralDark";
62
+ }
63
+
64
+ const backgroundColor = status === "custom" ? customBackgroundColor : theme.surface[badgeBgColor];
65
+ const borderColor = status === "custom" ? customBorderColor : secondaryBorderColors[status];
66
+ const textColor = status === "custom" ? customTextColor : theme.text[badgeColor];
67
+ const iconColor = status === "custom" ? customIconColor : badgeColor;
68
+
69
+ let badgeBorderRadius = theme.radius.default;
70
+ if (isIconOnly) {
71
+ badgeBorderRadius = theme.radius.full;
72
+ } else if (variant === "numberOnly") {
73
+ badgeBorderRadius = theme.radius.rounded;
74
+ }
75
+
76
+ let badgeValue = value;
77
+
78
+ if (variant === "numberOnly") {
79
+ if (!isNaN(Number(value)) && maxValue) {
80
+ const numberValue = Number(value);
81
+ if (numberValue > maxValue) {
82
+ badgeValue = `${maxValue}+`;
83
+ } else {
84
+ badgeValue = numberValue;
85
+ }
86
+ } else {
87
+ console.warn("Warning: Badge value is not a number");
39
88
  }
40
- return (
41
- <Text color={fontColor} size={size} weight={fontWeight}>
42
- {title}
43
- </Text>
44
- );
45
89
  }
46
90
 
47
91
  return (
48
- <Box
49
- alignItems="baseline"
50
- alignSelf={position === "middle" ? "center" : position === "bottom" ? "end" : "start"}
51
- color={badgeColor}
52
- direction="row"
53
- height="min-content"
54
- justifyContent="center"
55
- marginLeft={1}
56
- marginTop={position === "top" ? -1 : 0}
57
- paddingX={(typeof rounding === "number" && rounding >= 4) || rounding === "pill" ? 2 : 1}
58
- paddingY={1}
59
- rounding={rounding}
60
- width="max-content"
92
+ <View
93
+ style={{
94
+ alignItems: "flex-start",
95
+ }}
61
96
  >
62
- {renderIcon()}
63
- {renderLabel()}
64
- </Box>
97
+ <View
98
+ style={[
99
+ {
100
+ justifyContent: "center",
101
+ alignItems: "center",
102
+ paddingVertical: variant === "iconOnly" ? 1 : theme.spacing.xs,
103
+ paddingHorizontal: variant === "iconOnly" ? theme.spacing.xs : theme.spacing.sm,
104
+ flexDirection: "row",
105
+ borderRadius: badgeBorderRadius,
106
+ backgroundColor,
107
+ height: variant === "iconOnly" ? 16 : "auto",
108
+ width: variant === "iconOnly" ? 16 : "auto",
109
+ },
110
+ isIconOnly && {height: 16, width: 16},
111
+ secondary && {borderWidth: 1, borderColor},
112
+ ]}
113
+ >
114
+ {Boolean(variant !== "numberOnly" && iconName) && (
115
+ <View style={{marginRight: variant === "iconOnly" ? 0 : theme.spacing.sm}}>
116
+ <Icon color={iconColor} iconName={customIconName ?? iconName!} size="xs" />
117
+ </View>
118
+ )}
119
+ {Boolean(variant !== "iconOnly") && (
120
+ <Text
121
+ style={{
122
+ color: textColor,
123
+ fontSize: 10,
124
+ fontWeight: "700",
125
+ fontFamily: "text",
126
+ }}
127
+ >
128
+ {badgeValue}
129
+ </Text>
130
+ )}
131
+ </View>
132
+ </View>
65
133
  );
66
134
  };