@whop/elements-react-native 0.1.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 (339) hide show
  1. package/README.md +179 -0
  2. package/android/build.gradle +64 -0
  3. package/android/consumer-rules.pro +3 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/java/com/whop/elements/WhopAddressSuggestionsModule.kt +102 -0
  6. package/android/src/main/java/com/whop/elements/WhopAuthSessionModule.kt +126 -0
  7. package/android/src/main/java/com/whop/elements/WhopElementsPackage.kt +39 -0
  8. package/android/src/main/java/com/whop/elements/WhopPayButtonManager.kt +74 -0
  9. package/android/src/main/java/com/whop/elements/WhopPayButtonPressEvent.kt +17 -0
  10. package/android/src/main/java/com/whop/elements/WhopPayButtonView.kt +178 -0
  11. package/android/src/main/java/com/whop/elements/WhopPlatformPayModule.kt +212 -0
  12. package/assets/whop-wordmark-dark.png +0 -0
  13. package/assets/whop-wordmark-dark@2x.png +0 -0
  14. package/assets/whop-wordmark-dark@3x.png +0 -0
  15. package/assets/whop-wordmark-light.png +0 -0
  16. package/assets/whop-wordmark-light@2x.png +0 -0
  17. package/assets/whop-wordmark-light@3x.png +0 -0
  18. package/ios/Package.swift +59 -0
  19. package/ios/Sources/ElementsReactNative/WhopAddressSuggestionsImpl.swift +189 -0
  20. package/ios/Sources/ElementsReactNative/WhopAuthSessionImpl.swift +106 -0
  21. package/ios/Sources/ElementsReactNative/WhopPayButtonImpl.swift +89 -0
  22. package/ios/Sources/ElementsReactNative/WhopPlatformPayImpl.swift +181 -0
  23. package/ios/generated/WhopAddressSuggestionsModule.h +11 -0
  24. package/ios/generated/WhopAddressSuggestionsModule.mm +46 -0
  25. package/ios/generated/WhopAuthSessionModule.h +11 -0
  26. package/ios/generated/WhopAuthSessionModule.mm +45 -0
  27. package/ios/generated/WhopElementsShims.h +35 -0
  28. package/ios/generated/WhopPayButtonComponentView.h +12 -0
  29. package/ios/generated/WhopPayButtonComponentView.mm +82 -0
  30. package/ios/generated/WhopPlatformPayModule.h +11 -0
  31. package/ios/generated/WhopPlatformPayModule.mm +51 -0
  32. package/lib/module/api/client.js +139 -0
  33. package/lib/module/api/client.js.map +1 -0
  34. package/lib/module/api/client.test.js +137 -0
  35. package/lib/module/api/client.test.js.map +1 -0
  36. package/lib/module/api/types.js +2 -0
  37. package/lib/module/api/types.js.map +1 -0
  38. package/lib/module/controller/actions.js +148 -0
  39. package/lib/module/controller/actions.js.map +1 -0
  40. package/lib/module/controller/actions.test.js +102 -0
  41. package/lib/module/controller/actions.test.js.map +1 -0
  42. package/lib/module/controller/store.js +158 -0
  43. package/lib/module/controller/store.js.map +1 -0
  44. package/lib/module/core/address/country-data.js +1370 -0
  45. package/lib/module/core/address/country-data.js.map +1 -0
  46. package/lib/module/core/address/country-name.js +38 -0
  47. package/lib/module/core/address/country-name.js.map +1 -0
  48. package/lib/module/core/address/country-names.generated.js +249 -0
  49. package/lib/module/core/address/country-names.generated.js.map +1 -0
  50. package/lib/module/core/address/engine.js +245 -0
  51. package/lib/module/core/address/engine.js.map +1 -0
  52. package/lib/module/core/address/engine.test.js +216 -0
  53. package/lib/module/core/address/engine.test.js.map +1 -0
  54. package/lib/module/core/address/form-state.js +409 -0
  55. package/lib/module/core/address/form-state.js.map +1 -0
  56. package/lib/module/core/address/form-state.test.js +516 -0
  57. package/lib/module/core/address/form-state.test.js.map +1 -0
  58. package/lib/module/core/address/places.js +36 -0
  59. package/lib/module/core/address/places.js.map +1 -0
  60. package/lib/module/core/address/required-coverage.test.js +57 -0
  61. package/lib/module/core/address/required-coverage.test.js.map +1 -0
  62. package/lib/module/core/address/suggest.js +102 -0
  63. package/lib/module/core/address/suggest.js.map +1 -0
  64. package/lib/module/core/address/suggest.test.js +156 -0
  65. package/lib/module/core/address/suggest.test.js.map +1 -0
  66. package/lib/module/core/address/use-suggestions.js +33 -0
  67. package/lib/module/core/address/use-suggestions.js.map +1 -0
  68. package/lib/module/core/payments/address-change.js +91 -0
  69. package/lib/module/core/payments/address-change.js.map +1 -0
  70. package/lib/module/core/payments/billing-merge.js +62 -0
  71. package/lib/module/core/payments/billing-merge.js.map +1 -0
  72. package/lib/module/core/payments/browser-info.js +42 -0
  73. package/lib/module/core/payments/browser-info.js.map +1 -0
  74. package/lib/module/core/payments/bt.js +151 -0
  75. package/lib/module/core/payments/bt.js.map +1 -0
  76. package/lib/module/core/payments/bt.test.js +23 -0
  77. package/lib/module/core/payments/bt.test.js.map +1 -0
  78. package/lib/module/core/payments/currency.js +37 -0
  79. package/lib/module/core/payments/currency.js.map +1 -0
  80. package/lib/module/core/payments/detail.js +159 -0
  81. package/lib/module/core/payments/detail.js.map +1 -0
  82. package/lib/module/core/payments/detail.test.js +225 -0
  83. package/lib/module/core/payments/detail.test.js.map +1 -0
  84. package/lib/module/core/payments/errors.js +115 -0
  85. package/lib/module/core/payments/errors.js.map +1 -0
  86. package/lib/module/core/payments/mint.js +89 -0
  87. package/lib/module/core/payments/mint.js.map +1 -0
  88. package/lib/module/core/payments/plan.js +131 -0
  89. package/lib/module/core/payments/plan.js.map +1 -0
  90. package/lib/module/core/payments/refusals.js +17 -0
  91. package/lib/module/core/payments/refusals.js.map +1 -0
  92. package/lib/module/core/payments/secure-fields.js +87 -0
  93. package/lib/module/core/payments/secure-fields.js.map +1 -0
  94. package/lib/module/core/payments/tax-id-catalog.js +616 -0
  95. package/lib/module/core/payments/tax-id-catalog.js.map +1 -0
  96. package/lib/module/core/payments/wallet-contact.js +42 -0
  97. package/lib/module/core/payments/wallet-contact.js.map +1 -0
  98. package/lib/module/core/payments/wallet-contact.test.js +77 -0
  99. package/lib/module/core/payments/wallet-contact.test.js.map +1 -0
  100. package/lib/module/elements/address.js +467 -0
  101. package/lib/module/elements/address.js.map +1 -0
  102. package/lib/module/elements/branding.js +114 -0
  103. package/lib/module/elements/branding.js.map +1 -0
  104. package/lib/module/elements/card-fields/context.js +254 -0
  105. package/lib/module/elements/card-fields/context.js.map +1 -0
  106. package/lib/module/elements/card-fields/fields.js +309 -0
  107. package/lib/module/elements/card-fields/fields.js.map +1 -0
  108. package/lib/module/elements/card-fields/index.js +30 -0
  109. package/lib/module/elements/card-fields/index.js.map +1 -0
  110. package/lib/module/elements/card.js +107 -0
  111. package/lib/module/elements/card.js.map +1 -0
  112. package/lib/module/elements/email.js +84 -0
  113. package/lib/module/elements/email.js.map +1 -0
  114. package/lib/module/elements/payment/declared-fields.js +186 -0
  115. package/lib/module/elements/payment/declared-fields.js.map +1 -0
  116. package/lib/module/elements/payment/index.js +448 -0
  117. package/lib/module/elements/payment/index.js.map +1 -0
  118. package/lib/module/elements/payment/wallet.js +132 -0
  119. package/lib/module/elements/payment/wallet.js.map +1 -0
  120. package/lib/module/elements/tax-id.js +100 -0
  121. package/lib/module/elements/tax-id.js.map +1 -0
  122. package/lib/module/i18n/catalogs.js +103 -0
  123. package/lib/module/i18n/catalogs.js.map +1 -0
  124. package/lib/module/i18n/index.js +45 -0
  125. package/lib/module/i18n/index.js.map +1 -0
  126. package/lib/module/index.js +27 -0
  127. package/lib/module/index.js.map +1 -0
  128. package/lib/module/namespace.js +174 -0
  129. package/lib/module/namespace.js.map +1 -0
  130. package/lib/module/native/index.js +128 -0
  131. package/lib/module/native/index.js.map +1 -0
  132. package/lib/module/package.json +1 -0
  133. package/lib/module/provider.js +56 -0
  134. package/lib/module/provider.js.map +1 -0
  135. package/lib/module/specs/NativeWhopAddressSuggestions.js +6 -0
  136. package/lib/module/specs/NativeWhopAddressSuggestions.js.map +1 -0
  137. package/lib/module/specs/NativeWhopAuthSession.js +10 -0
  138. package/lib/module/specs/NativeWhopAuthSession.js.map +1 -0
  139. package/lib/module/specs/NativeWhopPlatformPay.js +10 -0
  140. package/lib/module/specs/NativeWhopPlatformPay.js.map +1 -0
  141. package/lib/module/specs/WhopPayButtonNativeComponent.ts +22 -0
  142. package/lib/module/theme/appearance.js +85 -0
  143. package/lib/module/theme/appearance.js.map +1 -0
  144. package/lib/module/theme/appearance.test.js +124 -0
  145. package/lib/module/theme/appearance.test.js.map +1 -0
  146. package/lib/module/theme/provider.js +25 -0
  147. package/lib/module/theme/provider.js.map +1 -0
  148. package/lib/module/theme/resolve.js +69 -0
  149. package/lib/module/theme/resolve.js.map +1 -0
  150. package/lib/module/theme/scales.generated.js +419 -0
  151. package/lib/module/theme/scales.generated.js.map +1 -0
  152. package/lib/module/ui/element-frame.js +83 -0
  153. package/lib/module/ui/element-frame.js.map +1 -0
  154. package/lib/module/ui/payment-method-icon.js +69 -0
  155. package/lib/module/ui/payment-method-icon.js.map +1 -0
  156. package/lib/module/ui/primitives.js +266 -0
  157. package/lib/module/ui/primitives.js.map +1 -0
  158. package/lib/module/ui/select.js +281 -0
  159. package/lib/module/ui/select.js.map +1 -0
  160. package/lib/module/ui/suggestions.js +71 -0
  161. package/lib/module/ui/suggestions.js.map +1 -0
  162. package/lib/module/ui/whop-logo.js +42 -0
  163. package/lib/module/ui/whop-logo.js.map +1 -0
  164. package/lib/typescript/package.json +1 -0
  165. package/lib/typescript/src/api/client.d.ts +87 -0
  166. package/lib/typescript/src/api/client.d.ts.map +1 -0
  167. package/lib/typescript/src/api/types.d.ts +175 -0
  168. package/lib/typescript/src/api/types.d.ts.map +1 -0
  169. package/lib/typescript/src/controller/actions.d.ts +42 -0
  170. package/lib/typescript/src/controller/actions.d.ts.map +1 -0
  171. package/lib/typescript/src/controller/store.d.ts +90 -0
  172. package/lib/typescript/src/controller/store.d.ts.map +1 -0
  173. package/lib/typescript/src/core/address/country-data.d.ts +4 -0
  174. package/lib/typescript/src/core/address/country-data.d.ts.map +1 -0
  175. package/lib/typescript/src/core/address/country-name.d.ts +2 -0
  176. package/lib/typescript/src/core/address/country-name.d.ts.map +1 -0
  177. package/lib/typescript/src/core/address/country-names.generated.d.ts +2 -0
  178. package/lib/typescript/src/core/address/country-names.generated.d.ts.map +1 -0
  179. package/lib/typescript/src/core/address/engine.d.ts +97 -0
  180. package/lib/typescript/src/core/address/engine.d.ts.map +1 -0
  181. package/lib/typescript/src/core/address/form-state.d.ts +144 -0
  182. package/lib/typescript/src/core/address/form-state.d.ts.map +1 -0
  183. package/lib/typescript/src/core/address/places.d.ts +23 -0
  184. package/lib/typescript/src/core/address/places.d.ts.map +1 -0
  185. package/lib/typescript/src/core/address/suggest.d.ts +47 -0
  186. package/lib/typescript/src/core/address/suggest.d.ts.map +1 -0
  187. package/lib/typescript/src/core/address/use-suggestions.d.ts +3 -0
  188. package/lib/typescript/src/core/address/use-suggestions.d.ts.map +1 -0
  189. package/lib/typescript/src/core/payments/address-change.d.ts +52 -0
  190. package/lib/typescript/src/core/payments/address-change.d.ts.map +1 -0
  191. package/lib/typescript/src/core/payments/billing-merge.d.ts +39 -0
  192. package/lib/typescript/src/core/payments/billing-merge.d.ts.map +1 -0
  193. package/lib/typescript/src/core/payments/browser-info.d.ts +12 -0
  194. package/lib/typescript/src/core/payments/browser-info.d.ts.map +1 -0
  195. package/lib/typescript/src/core/payments/bt.d.ts +55 -0
  196. package/lib/typescript/src/core/payments/bt.d.ts.map +1 -0
  197. package/lib/typescript/src/core/payments/currency.d.ts +7 -0
  198. package/lib/typescript/src/core/payments/currency.d.ts.map +1 -0
  199. package/lib/typescript/src/core/payments/detail.d.ts +53 -0
  200. package/lib/typescript/src/core/payments/detail.d.ts.map +1 -0
  201. package/lib/typescript/src/core/payments/errors.d.ts +62 -0
  202. package/lib/typescript/src/core/payments/errors.d.ts.map +1 -0
  203. package/lib/typescript/src/core/payments/mint.d.ts +74 -0
  204. package/lib/typescript/src/core/payments/mint.d.ts.map +1 -0
  205. package/lib/typescript/src/core/payments/plan.d.ts +69 -0
  206. package/lib/typescript/src/core/payments/plan.d.ts.map +1 -0
  207. package/lib/typescript/src/core/payments/refusals.d.ts +12 -0
  208. package/lib/typescript/src/core/payments/refusals.d.ts.map +1 -0
  209. package/lib/typescript/src/core/payments/secure-fields.d.ts +33 -0
  210. package/lib/typescript/src/core/payments/secure-fields.d.ts.map +1 -0
  211. package/lib/typescript/src/core/payments/tax-id-catalog.d.ts +481 -0
  212. package/lib/typescript/src/core/payments/tax-id-catalog.d.ts.map +1 -0
  213. package/lib/typescript/src/core/payments/wallet-contact.d.ts +40 -0
  214. package/lib/typescript/src/core/payments/wallet-contact.d.ts.map +1 -0
  215. package/lib/typescript/src/elements/address.d.ts +55 -0
  216. package/lib/typescript/src/elements/address.d.ts.map +1 -0
  217. package/lib/typescript/src/elements/branding.d.ts +18 -0
  218. package/lib/typescript/src/elements/branding.d.ts.map +1 -0
  219. package/lib/typescript/src/elements/card-fields/context.d.ts +67 -0
  220. package/lib/typescript/src/elements/card-fields/context.d.ts.map +1 -0
  221. package/lib/typescript/src/elements/card-fields/fields.d.ts +15 -0
  222. package/lib/typescript/src/elements/card-fields/fields.d.ts.map +1 -0
  223. package/lib/typescript/src/elements/card-fields/index.d.ts +21 -0
  224. package/lib/typescript/src/elements/card-fields/index.d.ts.map +1 -0
  225. package/lib/typescript/src/elements/card.d.ts +21 -0
  226. package/lib/typescript/src/elements/card.d.ts.map +1 -0
  227. package/lib/typescript/src/elements/email.d.ts +31 -0
  228. package/lib/typescript/src/elements/email.d.ts.map +1 -0
  229. package/lib/typescript/src/elements/payment/declared-fields.d.ts +43 -0
  230. package/lib/typescript/src/elements/payment/declared-fields.d.ts.map +1 -0
  231. package/lib/typescript/src/elements/payment/index.d.ts +37 -0
  232. package/lib/typescript/src/elements/payment/index.d.ts.map +1 -0
  233. package/lib/typescript/src/elements/payment/wallet.d.ts +39 -0
  234. package/lib/typescript/src/elements/payment/wallet.d.ts.map +1 -0
  235. package/lib/typescript/src/elements/tax-id.d.ts +41 -0
  236. package/lib/typescript/src/elements/tax-id.d.ts.map +1 -0
  237. package/lib/typescript/src/i18n/catalogs.d.ts +7 -0
  238. package/lib/typescript/src/i18n/catalogs.d.ts.map +1 -0
  239. package/lib/typescript/src/i18n/index.d.ts +17 -0
  240. package/lib/typescript/src/i18n/index.d.ts.map +1 -0
  241. package/lib/typescript/src/index.d.ts +42 -0
  242. package/lib/typescript/src/index.d.ts.map +1 -0
  243. package/lib/typescript/src/namespace.d.ts +36 -0
  244. package/lib/typescript/src/namespace.d.ts.map +1 -0
  245. package/lib/typescript/src/native/index.d.ts +62 -0
  246. package/lib/typescript/src/native/index.d.ts.map +1 -0
  247. package/lib/typescript/src/provider.d.ts +37 -0
  248. package/lib/typescript/src/provider.d.ts.map +1 -0
  249. package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts +13 -0
  250. package/lib/typescript/src/specs/NativeWhopAddressSuggestions.d.ts.map +1 -0
  251. package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts +20 -0
  252. package/lib/typescript/src/specs/NativeWhopAuthSession.d.ts.map +1 -0
  253. package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts +24 -0
  254. package/lib/typescript/src/specs/NativeWhopPlatformPay.d.ts.map +1 -0
  255. package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts +21 -0
  256. package/lib/typescript/src/specs/WhopPayButtonNativeComponent.d.ts.map +1 -0
  257. package/lib/typescript/src/theme/appearance.d.ts +71 -0
  258. package/lib/typescript/src/theme/appearance.d.ts.map +1 -0
  259. package/lib/typescript/src/theme/provider.d.ts +11 -0
  260. package/lib/typescript/src/theme/provider.d.ts.map +1 -0
  261. package/lib/typescript/src/theme/resolve.d.ts +19 -0
  262. package/lib/typescript/src/theme/resolve.d.ts.map +1 -0
  263. package/lib/typescript/src/theme/scales.generated.d.ts +18 -0
  264. package/lib/typescript/src/theme/scales.generated.d.ts.map +1 -0
  265. package/lib/typescript/src/ui/element-frame.d.ts +32 -0
  266. package/lib/typescript/src/ui/element-frame.d.ts.map +1 -0
  267. package/lib/typescript/src/ui/payment-method-icon.d.ts +21 -0
  268. package/lib/typescript/src/ui/payment-method-icon.d.ts.map +1 -0
  269. package/lib/typescript/src/ui/primitives.d.ts +94 -0
  270. package/lib/typescript/src/ui/primitives.d.ts.map +1 -0
  271. package/lib/typescript/src/ui/select.d.ts +25 -0
  272. package/lib/typescript/src/ui/select.d.ts.map +1 -0
  273. package/lib/typescript/src/ui/suggestions.d.ts +15 -0
  274. package/lib/typescript/src/ui/suggestions.d.ts.map +1 -0
  275. package/lib/typescript/src/ui/whop-logo.d.ts +4 -0
  276. package/lib/typescript/src/ui/whop-logo.d.ts.map +1 -0
  277. package/package.json +121 -0
  278. package/scripts/check-swift.sh +27 -0
  279. package/scripts/codegen-shims.mjs +409 -0
  280. package/scripts/gen-country-names.mjs +58 -0
  281. package/scripts/gen-theme.mjs +124 -0
  282. package/scripts/gen-wordmark.mjs +68 -0
  283. package/scripts/mirror.mjs +112 -0
  284. package/src/api/client.ts +202 -0
  285. package/src/api/types.ts +166 -0
  286. package/src/controller/actions.ts +197 -0
  287. package/src/controller/store.ts +201 -0
  288. package/src/core/address/country-data.ts +927 -0
  289. package/src/core/address/country-name.ts +36 -0
  290. package/src/core/address/country-names.generated.ts +246 -0
  291. package/src/core/address/engine.ts +349 -0
  292. package/src/core/address/form-state.ts +479 -0
  293. package/src/core/address/places.ts +39 -0
  294. package/src/core/address/suggest.ts +153 -0
  295. package/src/core/address/use-suggestions.ts +37 -0
  296. package/src/core/payments/address-change.ts +121 -0
  297. package/src/core/payments/billing-merge.ts +91 -0
  298. package/src/core/payments/browser-info.ts +43 -0
  299. package/src/core/payments/bt.ts +176 -0
  300. package/src/core/payments/currency.ts +54 -0
  301. package/src/core/payments/detail.ts +204 -0
  302. package/src/core/payments/errors.ts +171 -0
  303. package/src/core/payments/mint.ts +130 -0
  304. package/src/core/payments/plan.ts +198 -0
  305. package/src/core/payments/refusals.ts +24 -0
  306. package/src/core/payments/secure-fields.ts +85 -0
  307. package/src/core/payments/tax-id-catalog.ts +320 -0
  308. package/src/core/payments/wallet-contact.ts +56 -0
  309. package/src/elements/address.tsx +570 -0
  310. package/src/elements/branding.tsx +87 -0
  311. package/src/elements/card-fields/context.tsx +310 -0
  312. package/src/elements/card-fields/fields.tsx +281 -0
  313. package/src/elements/card-fields/index.tsx +37 -0
  314. package/src/elements/card.tsx +101 -0
  315. package/src/elements/email.tsx +100 -0
  316. package/src/elements/payment/declared-fields.tsx +238 -0
  317. package/src/elements/payment/index.tsx +497 -0
  318. package/src/elements/payment/wallet.tsx +155 -0
  319. package/src/elements/tax-id.tsx +143 -0
  320. package/src/i18n/catalogs.ts +100 -0
  321. package/src/i18n/index.tsx +46 -0
  322. package/src/index.ts +68 -0
  323. package/src/namespace.tsx +231 -0
  324. package/src/native/index.ts +173 -0
  325. package/src/provider.tsx +76 -0
  326. package/src/specs/NativeWhopAddressSuggestions.ts +14 -0
  327. package/src/specs/NativeWhopAuthSession.ts +21 -0
  328. package/src/specs/NativeWhopPlatformPay.ts +25 -0
  329. package/src/specs/WhopPayButtonNativeComponent.ts +22 -0
  330. package/src/theme/appearance.ts +150 -0
  331. package/src/theme/provider.tsx +28 -0
  332. package/src/theme/resolve.ts +68 -0
  333. package/src/theme/scales.generated.ts +427 -0
  334. package/src/ui/element-frame.tsx +94 -0
  335. package/src/ui/payment-method-icon.tsx +55 -0
  336. package/src/ui/primitives.tsx +269 -0
  337. package/src/ui/select.tsx +255 -0
  338. package/src/ui/suggestions.tsx +69 -0
  339. package/src/ui/whop-logo.tsx +37 -0
@@ -0,0 +1,269 @@
1
+ import { type ComponentRef, forwardRef, type ForwardRefExoticComponent, type ReactNode, type RefAttributes, useState } from 'react';
2
+ import {
3
+ Pressable,
4
+ StyleSheet,
5
+ Text,
6
+ TextInput,
7
+ type TextInputFocusEvent,
8
+ type TextInputProps,
9
+ type TextStyle,
10
+ View,
11
+ type ViewStyle,
12
+ } from 'react-native';
13
+
14
+ import type { PartName } from '../theme/appearance';
15
+ import { alphaStep, type ResolvedTheme, step, useTheme } from '../theme/provider';
16
+
17
+ /** Merge the theme's per-part override in last, so a consumer's `parts` entry always wins. */
18
+ export function part(theme: ResolvedTheme, name: PartName): ViewStyle & TextStyle {
19
+ return theme.parts[name] ?? {};
20
+ }
21
+
22
+ export function useFieldStyles() {
23
+ const theme = useTheme();
24
+ const { tokens } = theme;
25
+ return {
26
+ theme,
27
+ container: {
28
+ gap: tokens.spacing,
29
+ } satisfies ViewStyle,
30
+ label: {
31
+ fontSize: tokens.labelFontSize,
32
+ fontFamily: tokens.fontFamily,
33
+ color: step(theme.gray, 11),
34
+ fontWeight: '500',
35
+ } satisfies TextStyle,
36
+ box: (state: { focused: boolean; invalid: boolean; disabled: boolean }): ViewStyle => ({
37
+ minHeight: tokens.controlHeight,
38
+ borderRadius: tokens.radius,
39
+ borderWidth: tokens.borderWidth,
40
+ borderColor: state.invalid
41
+ ? step(theme.danger, 8)
42
+ : state.focused
43
+ ? step(theme.accent, 8)
44
+ : alphaStep(theme.gray, 7),
45
+ backgroundColor: state.disabled ? alphaStep(theme.gray, 2) : step(theme.gray, 1),
46
+ paddingHorizontal: tokens.spacing * 3,
47
+ justifyContent: 'center',
48
+ }),
49
+ input: {
50
+ fontSize: tokens.fontSize,
51
+ fontFamily: tokens.fontFamily,
52
+ color: step(theme.gray, 12),
53
+ paddingVertical: tokens.spacing * 2,
54
+ // Android draws its own underline and vertical padding on TextInput; both fight the box.
55
+ paddingHorizontal: 0,
56
+ } satisfies TextStyle,
57
+ error: {
58
+ fontSize: tokens.labelFontSize,
59
+ fontFamily: tokens.fontFamily,
60
+ color: step(theme.danger, 11),
61
+ } satisfies TextStyle,
62
+ placeholder: alphaStep(theme.gray, 10),
63
+ };
64
+ }
65
+
66
+ export interface FieldProps extends Omit<TextInputProps, 'style'> {
67
+ label?: string;
68
+ error?: string;
69
+ disabled?: boolean;
70
+ trailing?: ReactNode;
71
+ containerStyle?: ViewStyle;
72
+ partName?: PartName;
73
+ }
74
+
75
+ /** React Native's own TextInput instance type is not nameable from a declaration file, so the
76
+ * alias is declared here and the component is annotated with it. */
77
+ export type FieldRef = ComponentRef<typeof TextInput>;
78
+
79
+ export const Field: ForwardRefExoticComponent<FieldProps & RefAttributes<FieldRef>> = forwardRef<
80
+ FieldRef,
81
+ FieldProps
82
+ >(function Field(
83
+ { label, error, disabled, trailing, containerStyle, partName, onFocus, onBlur, ...input },
84
+ ref,
85
+ ) {
86
+ const s = useFieldStyles();
87
+ const [focused, setFocused] = useState(false);
88
+ const invalid = Boolean(error);
89
+
90
+ const handleFocus = (e: TextInputFocusEvent) => {
91
+ setFocused(true);
92
+ onFocus?.(e);
93
+ };
94
+ const handleBlur = (e: TextInputFocusEvent) => {
95
+ setFocused(false);
96
+ onBlur?.(e);
97
+ };
98
+
99
+ return (
100
+ <View style={[s.container, containerStyle]}>
101
+ {label ? <Text style={[s.label, part(s.theme, 'whop-Label')]}>{label}</Text> : null}
102
+ <View
103
+ style={[
104
+ s.box({ focused, invalid, disabled: Boolean(disabled) }),
105
+ trailing ? styles.row : null,
106
+ part(s.theme, 'whop-Input'),
107
+ focused ? part(s.theme, 'whop-InputFocused') : null,
108
+ invalid ? part(s.theme, 'whop-InputInvalid') : null,
109
+ partName ? part(s.theme, partName) : null,
110
+ ]}
111
+ >
112
+ <TextInput
113
+ ref={ref}
114
+ editable={!disabled}
115
+ placeholderTextColor={s.placeholder}
116
+ onFocus={handleFocus}
117
+ onBlur={handleBlur}
118
+ style={[s.input, styles.grow]}
119
+ {...input}
120
+ />
121
+ {trailing}
122
+ </View>
123
+ {error ? <Text style={[s.error, part(s.theme, 'whop-ErrorText')]}>{error}</Text> : null}
124
+ </View>
125
+ );
126
+ });
127
+
128
+ export interface FieldShellProps {
129
+ label?: string;
130
+ error?: string;
131
+ focused?: boolean;
132
+ disabled?: boolean;
133
+ trailing?: ReactNode;
134
+ partName?: PartName;
135
+ boxStyle?: ViewStyle;
136
+ containerStyle?: ViewStyle;
137
+ children: ReactNode;
138
+ }
139
+
140
+ /**
141
+ * The label, box, and error row around an input this package does not own — a Basis Theory hosted
142
+ * field. Shares its chrome with `Field` so an isolated field is visually indistinguishable from a
143
+ * plain one.
144
+ */
145
+ export function FieldShell({
146
+ label,
147
+ error,
148
+ focused = false,
149
+ disabled = false,
150
+ trailing,
151
+ partName,
152
+ boxStyle,
153
+ containerStyle,
154
+ children,
155
+ }: FieldShellProps) {
156
+ const s = useFieldStyles();
157
+ const invalid = Boolean(error);
158
+ return (
159
+ <View style={[s.container, containerStyle]}>
160
+ {label ? <Text style={[s.label, part(s.theme, 'whop-Label')]}>{label}</Text> : null}
161
+ <View
162
+ style={[
163
+ s.box({ focused, invalid, disabled }),
164
+ styles.row,
165
+ part(s.theme, 'whop-Input'),
166
+ focused ? part(s.theme, 'whop-InputFocused') : null,
167
+ invalid ? part(s.theme, 'whop-InputInvalid') : null,
168
+ partName ? part(s.theme, partName) : null,
169
+ boxStyle,
170
+ ]}
171
+ >
172
+ <View style={styles.grow}>{children}</View>
173
+ {trailing}
174
+ </View>
175
+ {error ? <Text style={[s.error, part(s.theme, 'whop-ErrorText')]}>{error}</Text> : null}
176
+ </View>
177
+ );
178
+ }
179
+
180
+ /** The shell every element paints before its first content commit. */
181
+ export function Skeleton({ height, width, style }: { height: number; width?: number | string; style?: ViewStyle }) {
182
+ const theme = useTheme();
183
+ return (
184
+ <View
185
+ accessibilityElementsHidden
186
+ importantForAccessibility="no-hide-descendants"
187
+ style={[
188
+ {
189
+ height,
190
+ width: (width as number | undefined) ?? '100%',
191
+ borderRadius: theme.tokens.radius,
192
+ backgroundColor: alphaStep(theme.gray, 3),
193
+ },
194
+ part(theme, 'whop-Skeleton'),
195
+ style,
196
+ ]}
197
+ />
198
+ );
199
+ }
200
+
201
+ export function FieldSkeleton({ label = true }: { label?: boolean }) {
202
+ const theme = useTheme();
203
+ return (
204
+ <View style={{ gap: theme.tokens.spacing }}>
205
+ {label ? <Skeleton height={12} width={96} /> : null}
206
+ <Skeleton height={theme.tokens.controlHeight} />
207
+ </View>
208
+ );
209
+ }
210
+
211
+ /** Form fields sit top-aligned: one field carrying an error message must not vertically re-centre
212
+ * the field beside it. */
213
+ export function Row({ children, style }: { children: ReactNode; style?: ViewStyle }) {
214
+ const theme = useTheme();
215
+ return (
216
+ <View style={[styles.row, styles.rowTop, { gap: theme.tokens.spacing * 2 }, style]}>{children}</View>
217
+ );
218
+ }
219
+
220
+ export function Stack({ children, style, gap }: { children: ReactNode; style?: ViewStyle; gap?: number }) {
221
+ const theme = useTheme();
222
+ return <View style={[{ gap: gap ?? theme.tokens.spacing * 3 }, style]}>{children}</View>;
223
+ }
224
+
225
+ export function ErrorFace({ message }: { message: string }) {
226
+ const theme = useTheme();
227
+ return (
228
+ <View
229
+ accessibilityRole="alert"
230
+ style={{
231
+ borderRadius: theme.tokens.radius,
232
+ borderWidth: theme.tokens.borderWidth,
233
+ borderColor: alphaStep(theme.danger, 6),
234
+ backgroundColor: alphaStep(theme.danger, 2),
235
+ padding: theme.tokens.spacing * 3,
236
+ }}
237
+ >
238
+ <Text style={{ fontSize: theme.tokens.fontSize, color: step(theme.danger, 11), fontFamily: theme.tokens.fontFamily }}>
239
+ {message}
240
+ </Text>
241
+ </View>
242
+ );
243
+ }
244
+
245
+ export function TextButton({ label, onPress, disabled }: { label: string; onPress: () => void; disabled?: boolean }) {
246
+ const theme = useTheme();
247
+ return (
248
+ <Pressable accessibilityRole="button" disabled={disabled} onPress={onPress} hitSlop={8}>
249
+ {({ pressed }) => (
250
+ <Text
251
+ style={{
252
+ fontSize: theme.tokens.labelFontSize,
253
+ fontFamily: theme.tokens.fontFamily,
254
+ color: step(theme.accent, 11),
255
+ opacity: disabled ? 0.5 : pressed ? 0.7 : 1,
256
+ }}
257
+ >
258
+ {label}
259
+ </Text>
260
+ )}
261
+ </Pressable>
262
+ );
263
+ }
264
+
265
+ const styles = StyleSheet.create({
266
+ row: { flexDirection: 'row', alignItems: 'center' },
267
+ rowTop: { alignItems: 'flex-start' },
268
+ grow: { flex: 1 },
269
+ });
@@ -0,0 +1,255 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import type { LayoutChangeEvent } from 'react-native';
3
+ import {
4
+ Animated,
5
+ Easing,
6
+ FlatList,
7
+ Modal,
8
+ Pressable,
9
+ StyleSheet,
10
+ Text,
11
+ TextInput,
12
+ View,
13
+ useWindowDimensions,
14
+ } from 'react-native';
15
+
16
+ import { useT } from '../i18n';
17
+ import type { PartName } from '../theme/appearance';
18
+ import { alphaStep, step } from '../theme/provider';
19
+ import { part, useFieldStyles } from './primitives';
20
+
21
+ /** Keeps the closed offset and the style cap from drifting apart. */
22
+ const SHEET_MAX_HEIGHT_RATIO = 0.7;
23
+
24
+ export interface SelectOption {
25
+ value: string;
26
+ label: string;
27
+ }
28
+
29
+ export interface SelectProps {
30
+ label?: string;
31
+ value: string | undefined;
32
+ options: SelectOption[];
33
+ onChange: (value: string) => void;
34
+ placeholder?: string;
35
+ error?: string;
36
+ disabled?: boolean;
37
+ /** Long lists (241 countries, 200-odd tax registrations) get a filter row. */
38
+ searchable?: boolean;
39
+ partName?: PartName;
40
+ accessibilityLabel?: string;
41
+ }
42
+
43
+ /**
44
+ * A picker sheet rather than a platform picker: `@react-native-picker/picker` renders as a spinner
45
+ * on iOS and a dropdown on Android, which makes the same element look like two different products
46
+ * and gives the theme nothing to style. One sheet, both platforms.
47
+ */
48
+ export function Select({
49
+ label,
50
+ value,
51
+ options,
52
+ onChange,
53
+ placeholder,
54
+ error,
55
+ disabled,
56
+ searchable,
57
+ partName,
58
+ accessibilityLabel,
59
+ }: SelectProps) {
60
+ const t = useT();
61
+ const s = useFieldStyles();
62
+ const theme = s.theme;
63
+ const { height: windowHeight } = useWindowDimensions();
64
+ const [open, setOpen] = useState(false);
65
+ const [query, setQuery] = useState('');
66
+ const [sheetHeight, setSheetHeight] = useState(0);
67
+ const progress = useRef(new Animated.Value(0)).current;
68
+
69
+ const selected = options.find((option) => option.value === value);
70
+ const filtered = useMemo(() => {
71
+ if (!searchable || !query.trim()) return options;
72
+ const needle = query.trim().toLowerCase();
73
+ return options.filter((option) => option.label.toLowerCase().includes(needle));
74
+ }, [options, query, searchable]);
75
+
76
+ useEffect(() => {
77
+ if (!open) return;
78
+ Animated.timing(progress, {
79
+ toValue: 1,
80
+ duration: 260,
81
+ easing: Easing.out(Easing.cubic),
82
+ useNativeDriver: true,
83
+ }).start();
84
+ }, [open, progress]);
85
+
86
+ const close = useCallback(() => {
87
+ Animated.timing(progress, {
88
+ toValue: 0,
89
+ duration: 200,
90
+ easing: Easing.in(Easing.cubic),
91
+ useNativeDriver: true,
92
+ }).start(({ finished }) => {
93
+ if (!finished) return;
94
+ setOpen(false);
95
+ setQuery('');
96
+ });
97
+ }, [progress]);
98
+
99
+ const onSheetLayout = useCallback((event: LayoutChangeEvent) => {
100
+ const next = event.nativeEvent.layout.height;
101
+ setSheetHeight((previous) => (Math.abs(previous - next) < 1 ? previous : next));
102
+ }, []);
103
+
104
+ const translateY = progress.interpolate({
105
+ inputRange: [0, 1],
106
+ outputRange: [sheetHeight || windowHeight * SHEET_MAX_HEIGHT_RATIO, 0],
107
+ });
108
+
109
+ return (
110
+ <View style={s.container}>
111
+ {label ? <Text style={[s.label, part(theme, 'whop-Label')]}>{label}</Text> : null}
112
+ <Pressable
113
+ accessibilityRole="button"
114
+ accessibilityState={{ disabled: Boolean(disabled), expanded: open }}
115
+ accessibilityLabel={accessibilityLabel ?? label}
116
+ disabled={disabled}
117
+ onPress={() => setOpen(true)}
118
+ style={[
119
+ s.box({ focused: open, invalid: Boolean(error), disabled: Boolean(disabled) }),
120
+ styles.trigger,
121
+ part(theme, 'whop-Select'),
122
+ partName ? part(theme, partName) : null,
123
+ ]}
124
+ >
125
+ <Text
126
+ numberOfLines={1}
127
+ style={[
128
+ s.input,
129
+ styles.grow,
130
+ !selected ? { color: s.placeholder } : null,
131
+ part(theme, 'whop-SelectValue'),
132
+ ]}
133
+ >
134
+ {selected?.label ?? placeholder ?? ''}
135
+ </Text>
136
+ <Chevron color={step(theme.gray, 11)} />
137
+ </Pressable>
138
+ {error ? <Text style={[s.error, part(theme, 'whop-ErrorText')]}>{error}</Text> : null}
139
+
140
+ {/* Not animationType="slide": that translates the whole modal, scrim included, so the dim
141
+ slides up with the sheet instead of fading in place. */}
142
+ <Modal visible={open} transparent animationType="none" onRequestClose={close} statusBarTranslucent>
143
+ <Pressable style={styles.backdrop} onPress={close} accessibilityLabel={t('Close')}>
144
+ <Animated.View style={[styles.scrim, { opacity: progress }]} />
145
+ </Pressable>
146
+ <Animated.View
147
+ onLayout={onSheetLayout}
148
+ style={[
149
+ styles.sheet,
150
+ {
151
+ backgroundColor: step(theme.gray, 1),
152
+ borderTopLeftRadius: theme.tokens.radius * 2,
153
+ borderTopRightRadius: theme.tokens.radius * 2,
154
+ transform: [{ translateY }],
155
+ },
156
+ part(theme, 'whop-Sheet'),
157
+ ]}
158
+ >
159
+ <View style={[styles.grabber, { backgroundColor: alphaStep(theme.gray, 7) }]} />
160
+ {label ? (
161
+ <Text style={[s.label, { paddingHorizontal: theme.tokens.spacing * 4 }]}>{label}</Text>
162
+ ) : null}
163
+ {searchable ? (
164
+ <View style={{ paddingHorizontal: theme.tokens.spacing * 4, paddingTop: theme.tokens.spacing * 2 }}>
165
+ <View style={s.box({ focused: false, invalid: false, disabled: false })}>
166
+ <TextInput
167
+ autoFocus
168
+ value={query}
169
+ onChangeText={setQuery}
170
+ placeholder={t('Search')}
171
+ placeholderTextColor={s.placeholder}
172
+ style={s.input}
173
+ autoCorrect={false}
174
+ autoCapitalize="none"
175
+ />
176
+ </View>
177
+ </View>
178
+ ) : null}
179
+ <FlatList
180
+ data={filtered}
181
+ keyExtractor={(option) => option.value}
182
+ keyboardShouldPersistTaps="handled"
183
+ initialNumToRender={20}
184
+ style={styles.list}
185
+ ListEmptyComponent={
186
+ <Text style={[s.input, { padding: theme.tokens.spacing * 4, color: step(theme.gray, 11) }]}>
187
+ {t('No matches')}
188
+ </Text>
189
+ }
190
+ renderItem={({ item }) => {
191
+ const active = item.value === value;
192
+ return (
193
+ <Pressable
194
+ accessibilityRole="button"
195
+ accessibilityState={{ selected: active }}
196
+ onPress={() => {
197
+ onChange(item.value);
198
+ close();
199
+ }}
200
+ style={({ pressed }) => [
201
+ styles.row,
202
+ {
203
+ paddingHorizontal: theme.tokens.spacing * 4,
204
+ paddingVertical: theme.tokens.spacing * 3,
205
+ backgroundColor: pressed || active ? alphaStep(theme.gray, 3) : 'transparent',
206
+ },
207
+ part(theme, 'whop-SheetRow'),
208
+ ]}
209
+ >
210
+ <Text style={[s.input, styles.grow, { paddingVertical: 0 }]}>{item.label}</Text>
211
+ {active ? <Text style={{ color: step(theme.accent, 11) }}>✓</Text> : null}
212
+ </Pressable>
213
+ );
214
+ }}
215
+ />
216
+ </Animated.View>
217
+ </Modal>
218
+ </View>
219
+ );
220
+ }
221
+
222
+ /** A stroked chevron rather than the "▾" glyph, which renders as a tiny solid triangle whose size
223
+ * and weight are the font's business rather than the theme's. Two borders on a rotated square is
224
+ * the whole shape, so it stays crisp at any size without a vector dependency. */
225
+ function Chevron({ color }: { color: string }) {
226
+ return <View style={[styles.chevron, { borderColor: color }]} />;
227
+ }
228
+
229
+ const styles = StyleSheet.create({
230
+ chevron: {
231
+ width: 7,
232
+ height: 7,
233
+ borderRightWidth: 1.5,
234
+ borderBottomWidth: 1.5,
235
+ borderRadius: 1,
236
+ transform: [{ rotate: '45deg' }],
237
+ marginBottom: 3,
238
+ },
239
+ trigger: { flexDirection: 'row', alignItems: 'center' },
240
+ grow: { flex: 1 },
241
+ backdrop: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 },
242
+ scrim: { flex: 1, backgroundColor: 'rgba(0,0,0,0.4)' },
243
+ sheet: {
244
+ position: 'absolute',
245
+ left: 0,
246
+ right: 0,
247
+ bottom: 0,
248
+ maxHeight: `${SHEET_MAX_HEIGHT_RATIO * 100}%`,
249
+ paddingTop: 8,
250
+ paddingBottom: 24,
251
+ },
252
+ grabber: { alignSelf: 'center', width: 36, height: 4, borderRadius: 2, marginBottom: 12 },
253
+ list: { flexGrow: 0 },
254
+ row: { flexDirection: 'row', alignItems: 'center' },
255
+ });
@@ -0,0 +1,69 @@
1
+ import { Pressable, StyleSheet, Text, View } from 'react-native';
2
+
3
+ import type { AddressSuggestion } from '../core/address/suggest';
4
+ import { alphaStep, step } from '../theme/provider';
5
+ import { part, useFieldStyles } from './primitives';
6
+
7
+ /**
8
+ * The street suggestion list, rendered IN FLOW directly beneath the field rather than floating over
9
+ * the form.
10
+ *
11
+ * An anchored overlay would need the package's first measurement code, its first keyboard
12
+ * avoidance, and an escape from a ScrollView the consumer owns and this SDK cannot configure — and
13
+ * Android clips absolutely-positioned children that leave their parent's bounds regardless of
14
+ * elevation. Pushing the fields below it down costs a little polish and removes all of that.
15
+ */
16
+ export function SuggestionList({
17
+ suggestions,
18
+ onPick,
19
+ }: {
20
+ suggestions: AddressSuggestion[];
21
+ onPick: (suggestion: AddressSuggestion) => void;
22
+ }) {
23
+ const s = useFieldStyles();
24
+ const theme = s.theme;
25
+ if (suggestions.length === 0) return null;
26
+
27
+ return (
28
+ <View
29
+ accessibilityRole="list"
30
+ style={[
31
+ styles.list,
32
+ { borderColor: alphaStep(theme.gray, 6), borderRadius: theme.tokens.radius },
33
+ part(theme, 'whop-AddressSuggestions'),
34
+ ]}
35
+ >
36
+ {suggestions.map((suggestion, index) => (
37
+ <Pressable
38
+ key={`${suggestion.label}:${index}`}
39
+ accessibilityRole="button"
40
+ accessibilityLabel={suggestion.label}
41
+ // onPressIn, not onPress: the field's blur fires first on a tap, and anything that hides
42
+ // this list on blur would unmount the row before its press ever lands.
43
+ onPressIn={() => onPick(suggestion)}
44
+ style={({ pressed }) => [
45
+ styles.row,
46
+ {
47
+ paddingHorizontal: theme.tokens.spacing * 3,
48
+ paddingVertical: theme.tokens.spacing * 3,
49
+ borderTopWidth: index === 0 ? 0 : StyleSheet.hairlineWidth,
50
+ borderTopColor: alphaStep(theme.gray, 5),
51
+ backgroundColor: pressed ? alphaStep(theme.gray, 3) : 'transparent',
52
+ },
53
+ part(theme, 'whop-AddressSuggestion'),
54
+ ]}
55
+ >
56
+ <Text numberOfLines={1} style={[s.input, styles.label, { color: step(theme.gray, 12) }]}>
57
+ {suggestion.label}
58
+ </Text>
59
+ </Pressable>
60
+ ))}
61
+ </View>
62
+ );
63
+ }
64
+
65
+ const styles = StyleSheet.create({
66
+ list: { marginTop: 4, borderWidth: StyleSheet.hairlineWidth, overflow: 'hidden' },
67
+ row: { flexDirection: 'row', alignItems: 'center' },
68
+ label: { flex: 1, paddingVertical: 0 },
69
+ });
@@ -0,0 +1,37 @@
1
+ import { Image } from 'react-native';
2
+
3
+ import { useTheme } from '../theme/provider';
4
+
5
+ /**
6
+ * The Whop wordmark.
7
+ *
8
+ * A bitmap at 1x/2x/3x rather than `react-native-svg`, because a required native peer would have to
9
+ * ship its own `Package.swift` for a Swift-Package-Manager consumer to build at all — and one
10
+ * unbuildable peer blocks every integrator. An optional peer is not an option either: Metro
11
+ * resolves `require` statically, so a missing module is a bundling error rather than something a
12
+ * try/catch can survive.
13
+ *
14
+ * The assets are generated from the same paths the web element draws
15
+ * (frontend/apps/web/src/elements/payments/whop-logo.tsx) by scripts/gen-wordmark.mjs — the mark
16
+ * keeps Whop's orange in both, and only the wordmark's ink changes with the theme.
17
+ */
18
+ const ASSETS = {
19
+ light: require('../../assets/whop-wordmark-light.png'),
20
+ dark: require('../../assets/whop-wordmark-dark.png'),
21
+ };
22
+
23
+ /** The source artwork's aspect ratio; height drives width so the mark never distorts. */
24
+ const ASPECT = 119 / 26;
25
+
26
+ export function WhopLogo({ height = 12 }: { height?: number }) {
27
+ const theme = useTheme();
28
+ return (
29
+ <Image
30
+ source={theme.mode === 'dark' ? ASSETS.dark : ASSETS.light}
31
+ style={{ height, width: height * ASPECT }}
32
+ resizeMode="contain"
33
+ accessibilityRole="image"
34
+ accessibilityLabel="Whop"
35
+ />
36
+ );
37
+ }