react-native-unit-components 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 (89) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +2 -0
  3. package/android/build.gradle +145 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/unitreactnativecomponents/UnitReactNativeComponentsPackage.kt +16 -0
  7. package/android/src/main/java/com/unitreactnativecomponents/UnitReactNativeComponentsViewManager.kt +19 -0
  8. package/ios/UnitReactNativeComponents-Bridging-Header.h +1 -0
  9. package/ios/UnitReactNativeComponents.xcodeproj/project.pbxproj +283 -0
  10. package/ios/UnitReactNativeComponents.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  11. package/ios/UnitReactNativeComponents.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  12. package/ios/UnitReactNativeComponents.xcodeproj/project.xcworkspace/xcuserdata/alonshprung.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  13. package/ios/UnitReactNativeComponents.xcodeproj/xcuserdata/alonshprung.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  14. package/ios/UnitReactNativeComponentsViewManager.m +7 -0
  15. package/ios/UnitReactNativeComponentsViewManager.swift +36 -0
  16. package/lib/commonjs/components/BottomSheet/BottomSheetComponent.js +143 -0
  17. package/lib/commonjs/components/BottomSheet/BottomSheetComponent.js.map +1 -0
  18. package/lib/commonjs/components/Card/CardComponent.js +97 -0
  19. package/lib/commonjs/components/Card/CardComponent.js.map +1 -0
  20. package/lib/commonjs/components/index.js +32 -0
  21. package/lib/commonjs/components/index.js.map +1 -0
  22. package/lib/commonjs/helpers/BottomSheet.js +274 -0
  23. package/lib/commonjs/helpers/BottomSheet.js.map +1 -0
  24. package/lib/commonjs/helpers/EventBus.js +79 -0
  25. package/lib/commonjs/helpers/EventBus.js.map +1 -0
  26. package/lib/commonjs/index.js +19 -0
  27. package/lib/commonjs/index.js.map +1 -0
  28. package/lib/commonjs/nativeComponenet/NativeComponentExample.js +19 -0
  29. package/lib/commonjs/nativeComponenet/NativeComponentExample.js.map +1 -0
  30. package/lib/commonjs/webComponent/WebComponent.js +75 -0
  31. package/lib/commonjs/webComponent/WebComponent.js.map +1 -0
  32. package/lib/commonjs/webComponent/html.js +79 -0
  33. package/lib/commonjs/webComponent/html.js.map +1 -0
  34. package/lib/commonjs/webComponent/webMessages/cardMessage.js +13 -0
  35. package/lib/commonjs/webComponent/webMessages/cardMessage.js.map +1 -0
  36. package/lib/commonjs/webComponent/webMessages/pageMessage.js +14 -0
  37. package/lib/commonjs/webComponent/webMessages/pageMessage.js.map +1 -0
  38. package/lib/commonjs/webComponent/webMessages/unitMessages.js +17 -0
  39. package/lib/commonjs/webComponent/webMessages/unitMessages.js.map +1 -0
  40. package/lib/module/components/BottomSheet/BottomSheetComponent.js +118 -0
  41. package/lib/module/components/BottomSheet/BottomSheetComponent.js.map +1 -0
  42. package/lib/module/components/Card/CardComponent.js +75 -0
  43. package/lib/module/components/Card/CardComponent.js.map +1 -0
  44. package/lib/module/components/index.js +3 -0
  45. package/lib/module/components/index.js.map +1 -0
  46. package/lib/module/helpers/BottomSheet.js +252 -0
  47. package/lib/module/helpers/BottomSheet.js.map +1 -0
  48. package/lib/module/helpers/EventBus.js +69 -0
  49. package/lib/module/helpers/EventBus.js.map +1 -0
  50. package/lib/module/index.js +2 -0
  51. package/lib/module/index.js.map +1 -0
  52. package/lib/module/nativeComponenet/NativeComponentExample.js +10 -0
  53. package/lib/module/nativeComponenet/NativeComponentExample.js.map +1 -0
  54. package/lib/module/webComponent/WebComponent.js +49 -0
  55. package/lib/module/webComponent/WebComponent.js.map +1 -0
  56. package/lib/module/webComponent/html.js +72 -0
  57. package/lib/module/webComponent/html.js.map +1 -0
  58. package/lib/module/webComponent/webMessages/cardMessage.js +6 -0
  59. package/lib/module/webComponent/webMessages/cardMessage.js.map +1 -0
  60. package/lib/module/webComponent/webMessages/pageMessage.js +7 -0
  61. package/lib/module/webComponent/webMessages/pageMessage.js.map +1 -0
  62. package/lib/module/webComponent/webMessages/unitMessages.js +10 -0
  63. package/lib/module/webComponent/webMessages/unitMessages.js.map +1 -0
  64. package/lib/typescript/components/BottomSheet/BottomSheetComponent.d.ts +1 -0
  65. package/lib/typescript/components/Card/CardComponent.d.ts +6 -0
  66. package/lib/typescript/components/index.d.ts +2 -0
  67. package/lib/typescript/helpers/BottomSheet.d.ts +34 -0
  68. package/lib/typescript/helpers/EventBus.d.ts +15 -0
  69. package/lib/typescript/index.d.ts +1 -0
  70. package/lib/typescript/nativeComponenet/NativeComponentExample.d.ts +7 -0
  71. package/lib/typescript/webComponent/WebComponent.d.ts +23 -0
  72. package/lib/typescript/webComponent/html.d.ts +2 -0
  73. package/lib/typescript/webComponent/webMessages/cardMessage.d.ts +7 -0
  74. package/lib/typescript/webComponent/webMessages/pageMessage.d.ts +8 -0
  75. package/lib/typescript/webComponent/webMessages/unitMessages.d.ts +24 -0
  76. package/package.json +152 -0
  77. package/src/components/BottomSheet/BottomSheetComponent.tsx +113 -0
  78. package/src/components/Card/CardComponent.tsx +75 -0
  79. package/src/components/index.ts +2 -0
  80. package/src/helpers/BottomSheet.tsx +277 -0
  81. package/src/helpers/EventBus.ts +58 -0
  82. package/src/index.tsx +1 -0
  83. package/src/nativeComponenet/NativeComponentExample.tsx +26 -0
  84. package/src/webComponent/WebComponent.tsx +75 -0
  85. package/src/webComponent/html.ts +72 -0
  86. package/src/webComponent/webMessages/cardMessage.ts +10 -0
  87. package/src/webComponent/webMessages/pageMessage.ts +11 -0
  88. package/src/webComponent/webMessages/unitMessages.ts +31 -0
  89. package/unit-react-native-components.podspec +35 -0
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { WebView } from 'react-native-webview';
3
+ import type { CardEvent, CardMessage } from './webMessages/cardMessage';
4
+ import type { PageEvent, PageMessage } from './webMessages/pageMessage';
5
+ import { UnitEvent, UnitMessage } from './webMessages/unitMessages';
6
+ export declare type WebViewMessageType = PageMessage | CardMessage | UnitMessage;
7
+ declare type WebViewEvent = PageEvent | UnitEvent | CardEvent;
8
+ export interface WebViewMessage {
9
+ type: WebViewMessageType;
10
+ details?: WebViewEvent;
11
+ }
12
+ export interface WebComponentProps {
13
+ type: WebComponentType;
14
+ params?: string;
15
+ onMessage: (message: WebViewMessage) => void;
16
+ script?: string;
17
+ }
18
+ export declare enum WebComponentType {
19
+ card = "unit-elements-card",
20
+ slot = "unit-elements-sdk-slot"
21
+ }
22
+ export declare const WebComponent: React.ForwardRefExoticComponent<WebComponentProps & React.RefAttributes<WebView<{}>>>;
23
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const htmlText = "\n <!DOCTYPE html>\n <html lang=\"en\">\n\n <head lang=\"en\">\n <title>Unit Web SDK Demo</title>\n <script>\n window.UnitMobileSDK = true\n </script>\n <script src=\"https://unit-white-labeled-banking.netlify.app/client-events.js\"></script>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=0\">\n </html>\n\n <body style=\"margin: 0\">\n <COMPONENT>\n\n <script>\n const postMessageToSDK = (message) => {\n if ('ReactNativeWebView' in window) {\n const messageString = JSON.stringify(message)\n window['ReactNativeWebView'].postMessage(messageString)\n }\n }\n\n const postHeight = () => {\n setTimeout(() => {\n const height = window.document.documentElement.getBoundingClientRect().height + 36\n postMessageToSDK({ type: \"pageHeight\", details: { height } })\n })\n }\n\n window.addEventListener('DOMContentLoaded', e => {\n postMessageToSDK({ type: \"pageLoaded\" })\n });\n\n window.addEventListener(\"unitRequestRendering\", (e) => {\n postMessageToSDK({ type: \"unitRequestRendering\", details: { data: JSON.stringify(e.detail) }})\n });\n\n window.addEventListener(\"unitOnLoad\", (e) => {\n postHeight()\n postMessageToSDK({ type: \"unitOnLoad\" })\n });\n\n window.addEventListener(\"unitRequestCloseFlow\", (e) => {\n postMessageToSDK({ type: \"unitRequestCloseFlow\" })\n });\n\n window.addEventListener(\"unitRequestOpenLink\", (e) => {\n postMessageToSDK({ type: \"unitRequestOpenLink\", details: e.detail })\n });\n\n window.addEventListener(\"unitCardStatusChanged\", (e) => {\n const response = e.detail\n response.then((data) => {\n postMessageToSDK({ type: \"cardStatusChanged\", details: { card: JSON.stringify(data.data) }})\n }).catch((e) => {\n console.log(e)\n })\n });\n\n const dispatchRenderingEvent = (details) => {\n const event = new CustomEvent(\"unitRequestRendering\",{ detail: JSON.parse(details) })\n window.dispatchEvent(event)\n }\n\n <SCRIPT_FROM_NATIVE>\n </script>\n </body>\n";
2
+ export default htmlText;
@@ -0,0 +1,7 @@
1
+ export declare enum CardMessage {
2
+ CARD_STATUS_CHANGE = "cardStatusChanged"
3
+ }
4
+ export interface CardStatusChangedEvent {
5
+ card: any;
6
+ }
7
+ export declare type CardEvent = CardStatusChangedEvent;
@@ -0,0 +1,8 @@
1
+ export declare enum PageMessage {
2
+ PAGE_LOADED = "pageLoaded",
3
+ PAGE_HEIGHT = "pageHeight"
4
+ }
5
+ export interface HeightEvent {
6
+ height: number;
7
+ }
8
+ export declare type PageEvent = HeightEvent;
@@ -0,0 +1,24 @@
1
+ export declare enum UnitMessage {
2
+ UNIT_REQUEST_RENDERING = "unitRequestRendering",
3
+ UNIT_REQUEST_REFRESH = "unitRequestRefresh",
4
+ UNIT_REQUEST_CLOSE_FLOW = "unitRequestCloseFlow",
5
+ UNIT_REQUEST_OPEN_LINK = "unitRequestOpenLink",
6
+ UNIT_ON_LOAD = "unitOnLoad"
7
+ }
8
+ export interface RequestRenderingData {
9
+ nativePlace: string;
10
+ nativeComponent: string;
11
+ }
12
+ export interface RequestRenderingEvent {
13
+ data: string;
14
+ }
15
+ export interface RequestRefreshEvent {
16
+ refEvent: string;
17
+ dependencies: string[];
18
+ resourceId: string;
19
+ }
20
+ export interface RequestOpenLinkEvent {
21
+ href: string;
22
+ target: string;
23
+ }
24
+ export declare type UnitEvent = RequestRenderingEvent | RequestRefreshEvent | RequestOpenLinkEvent;
package/package.json ADDED
@@ -0,0 +1,152 @@
1
+ {
2
+ "name": "react-native-unit-components",
3
+ "version": "0.1.0",
4
+ "description": "Unit React Native components",
5
+ "main": "lib/commonjs/index",
6
+ "module": "lib/module/index",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "src/index",
9
+ "source": "src/index",
10
+ "files": [
11
+ "src",
12
+ "lib",
13
+ "android",
14
+ "ios",
15
+ "cpp",
16
+ "unit-react-native-components.podspec",
17
+ "!lib/typescript/example",
18
+ "!android/build",
19
+ "!ios/build",
20
+ "!**/__tests__",
21
+ "!**/__fixtures__",
22
+ "!**/__mocks__"
23
+ ],
24
+ "scripts": {
25
+ "test": "jest",
26
+ "typescript": "tsc --noEmit",
27
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
28
+ "prepare": "bob build",
29
+ "release": "release-it",
30
+ "example": "yarn --cwd example",
31
+ "bootstrap": "yarn example && yarn && yarn example pods"
32
+ },
33
+ "keywords": [
34
+ "react-native",
35
+ "ios",
36
+ "android"
37
+ ],
38
+ "author": "Alon Shprung <alonshp1@gmail.com> (https://github.com/alonshp)",
39
+ "license": "MIT",
40
+ "homepage": "https://www.unit.co",
41
+ "publishConfig": {
42
+ "registry": "https://registry.npmjs.org/"
43
+ },
44
+ "devDependencies": {
45
+ "@arkweid/lefthook": "^0.7.7",
46
+ "@babel/eslint-parser": "^7.18.2",
47
+ "@commitlint/config-conventional": "^17.0.2",
48
+ "@react-native-community/eslint-config": "^3.0.2",
49
+ "@release-it/conventional-changelog": "^5.0.0",
50
+ "@types/jest": "^28.1.2",
51
+ "@types/react": "~17.0.21",
52
+ "@types/react-native": "0.68.0",
53
+ "commitlint": "^17.0.2",
54
+ "eslint": "^8.4.1",
55
+ "eslint-config-prettier": "^8.5.0",
56
+ "eslint-plugin-prettier": "^4.0.0",
57
+ "jest": "^28.1.1",
58
+ "pod-install": "^0.1.0",
59
+ "prettier": "^2.0.5",
60
+ "react": "17.0.2",
61
+ "react-native": "0.68.2",
62
+ "react-native-builder-bob": "^0.18.3",
63
+ "release-it": "^15.0.0",
64
+ "typescript": "^4.5.2",
65
+ "react-native-webview": "11.23.1"
66
+ },
67
+ "resolutions": {
68
+ "@types/react": "17.0.21"
69
+ },
70
+ "peerDependencies": {
71
+ "react": "*",
72
+ "react-native": "*",
73
+ "react-native-webview": "*"
74
+ },
75
+ "dependencies": {
76
+ "react-native-webview": "11.23.1"
77
+ },
78
+ "jest": {
79
+ "preset": "react-native",
80
+ "modulePathIgnorePatterns": [
81
+ "<rootDir>/example/node_modules",
82
+ "<rootDir>/lib/"
83
+ ]
84
+ },
85
+ "commitlint": {
86
+ "extends": [
87
+ "@commitlint/config-conventional"
88
+ ]
89
+ },
90
+ "release-it": {
91
+ "git": {
92
+ "commitMessage": "chore: release ${version}",
93
+ "tagName": "v${version}"
94
+ },
95
+ "npm": {
96
+ "publish": true
97
+ },
98
+ "github": {
99
+ "release": true
100
+ },
101
+ "plugins": {
102
+ "@release-it/conventional-changelog": {
103
+ "preset": "angular"
104
+ }
105
+ }
106
+ },
107
+ "eslintConfig": {
108
+ "root": true,
109
+ "parser": "@babel/eslint-parser",
110
+ "extends": [
111
+ "@react-native-community",
112
+ "prettier"
113
+ ],
114
+ "rules": {
115
+ "prettier/prettier": [
116
+ "error",
117
+ {
118
+ "quoteProps": "consistent",
119
+ "singleQuote": true,
120
+ "tabWidth": 2,
121
+ "trailingComma": "es5",
122
+ "useTabs": false
123
+ }
124
+ ]
125
+ }
126
+ },
127
+ "eslintIgnore": [
128
+ "node_modules/",
129
+ "lib/"
130
+ ],
131
+ "prettier": {
132
+ "quoteProps": "consistent",
133
+ "singleQuote": true,
134
+ "tabWidth": 2,
135
+ "trailingComma": "es5",
136
+ "useTabs": false
137
+ },
138
+ "react-native-builder-bob": {
139
+ "source": "src",
140
+ "output": "lib",
141
+ "targets": [
142
+ "commonjs",
143
+ "module",
144
+ [
145
+ "typescript",
146
+ {
147
+ "project": "tsconfig.build.json"
148
+ }
149
+ ]
150
+ ]
151
+ }
152
+ }
@@ -0,0 +1,113 @@
1
+ import React, { useEffect, useRef, useState } from "react"
2
+ import type WebView from "react-native-webview"
3
+ import { WebComponent, WebComponentType, WebViewMessage } from "../../webComponent/WebComponent"
4
+ import { Dimensions, Linking, View } from "react-native"
5
+ import BottomSheet from "../../helpers/BottomSheet"
6
+ import EventBus, { BusEvent } from "../../helpers/EventBus"
7
+ import { HeightEvent, PageMessage } from "../../webComponent/webMessages/pageMessage"
8
+ import { RequestOpenLinkEvent, RequestRenderingData, RequestRenderingEvent, UnitMessage } from "../../webComponent/webMessages/unitMessages"
9
+ import { CardMessage, CardStatusChangedEvent } from "../../webComponent/webMessages/cardMessage"
10
+
11
+ export const UNBottomSheetComponent = () => {
12
+ const [isActive, setIsActive] = useState(false)
13
+ const [height, setHeight] = useState(0)
14
+
15
+ const [renderingRequest, setRenderingRequest] = useState<string | null>(null)
16
+
17
+ const [waitForLoadingNewComponent, setWaitForLoadingNewComponent] = useState(false)
18
+ const [newHeightMessage, setNewHeightMessage] = useState<WebViewMessage | null>(null)
19
+
20
+ const webRef = useRef<WebView>(null)
21
+
22
+ const _listener = (data: string) => {
23
+ setRenderingRequest(data)
24
+ setIsActive(true)
25
+ }
26
+
27
+ useEffect(() => {
28
+ if (!webRef.current) return
29
+ if (renderingRequest) {
30
+ webRef.current?.injectJavaScript(`dispatchRenderingEvent('${renderingRequest}')`)
31
+ }
32
+ }, [webRef.current, renderingRequest])
33
+
34
+ useEffect(() => {
35
+ if (!isActive) {
36
+ setHeight(0)
37
+ setRenderingRequest(null)
38
+ }
39
+ }, [isActive])
40
+
41
+ useEffect(() => {
42
+ if (!waitForLoadingNewComponent && newHeightMessage) {
43
+ setWaitForLoadingNewComponent(false)
44
+ setNewHeight(newHeightMessage)
45
+ }
46
+ }, [waitForLoadingNewComponent, newHeightMessage])
47
+
48
+ useEffect(() => {
49
+ EventBus.Instance.addListener(BusEvent.REQUEST_RENDERING, _listener)
50
+
51
+ return (() => {
52
+ EventBus.Instance.removeListener(_listener)
53
+ })
54
+ }, [])
55
+
56
+ const getBottomSheetScript = () => {
57
+ return `
58
+ window.addEventListener("unitRequestRefresh", (e) => {
59
+ postMessageToSDK({ type: "unitRequestRefresh", details: e.detail })
60
+ });
61
+ `
62
+ }
63
+
64
+ const setNewHeight = (message: WebViewMessage) => {
65
+ if (renderingRequest) {
66
+ const nativePlace = (JSON.parse(renderingRequest) as RequestRenderingData).nativePlace;
67
+ if (nativePlace == 'overFullScreen') {
68
+ const windowHeight = Dimensions.get('window').height * 0.94;
69
+ setHeight(windowHeight)
70
+ } else {
71
+ setHeight((message.details as HeightEvent).height)
72
+ }
73
+ }
74
+ }
75
+
76
+ return (
77
+ <>
78
+ <View
79
+ pointerEvents={isActive ? undefined : 'none'}
80
+ style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }}
81
+ >
82
+ <BottomSheet
83
+ isOpen={isActive}
84
+ onClose={() => setIsActive(false)}
85
+ onOpen={() => setIsActive(true)}
86
+ >
87
+ <View style={{ height, paddingTop: height === 0 ? 0 : 10 }}>
88
+ <WebComponent ref={webRef} type={WebComponentType.slot} script={getBottomSheetScript()} onMessage={message => {
89
+ if (renderingRequest && message.type == PageMessage.PAGE_HEIGHT) {
90
+ setNewHeightMessage(message)
91
+ } else if (message.type == UnitMessage.UNIT_REQUEST_RENDERING) {
92
+ const { data } = (message.details! as RequestRenderingEvent)
93
+ if (data != renderingRequest) {
94
+ setHeight(0)
95
+ setRenderingRequest(data)
96
+ setWaitForLoadingNewComponent(true)
97
+ setTimeout(() => setWaitForLoadingNewComponent(false), 400)
98
+ }
99
+ } else if (message.type == CardMessage.CARD_STATUS_CHANGE) {
100
+ EventBus.Instance.event(BusEvent.CARD_STATUS_CHANGED, (message.details as CardStatusChangedEvent).card)
101
+ } else if (message.type == UnitMessage.UNIT_REQUEST_CLOSE_FLOW) {
102
+ setIsActive(false)
103
+ } else if (message.type == UnitMessage.UNIT_REQUEST_OPEN_LINK) {
104
+ const href = (message.details as RequestOpenLinkEvent).href
105
+ Linking.openURL(href)
106
+ }
107
+ }} />
108
+ </View>
109
+ </BottomSheet>
110
+ </View>
111
+ </>
112
+ )
113
+ }
@@ -0,0 +1,75 @@
1
+ import React, { useEffect, useRef, useState } from "react"
2
+ import { View } from "react-native";
3
+ import type WebView from "react-native-webview";
4
+ import { HeightEvent, PageMessage } from "../../webComponent/webMessages/pageMessage";
5
+ import { RequestRefreshEvent, RequestRenderingEvent, UnitMessage } from "../../webComponent/webMessages/unitMessages";
6
+ import EventBus, { BusEvent } from "../../helpers/EventBus"
7
+ import { WebComponent, WebComponentType } from "../../webComponent/WebComponent"
8
+
9
+ export interface UNCardComponentProps {
10
+ cardId: string;
11
+ customerToken: string;
12
+ onStatusChanged?: (card: any) => void; // TODO - UNCard
13
+ }
14
+
15
+ export const UNCardComponent = (props: UNCardComponentProps) => {
16
+ const [height, setHeight] = useState(0)
17
+ const [requestRefreshEvent, setRequestRefreshEvent] = useState<RequestRefreshEvent | null>(null)
18
+
19
+ const webRef = useRef<WebView>(null)
20
+
21
+ useEffect(() => {
22
+ EventBus.Instance.addListener(BusEvent.CARD_STATUS_CHANGED, _cardStatusChanged)
23
+ EventBus.Instance.addListener(BusEvent.REQUEST_REFRESH, _requestRefresh)
24
+ return (() => {
25
+ EventBus.Instance.removeListener(_cardStatusChanged)
26
+ EventBus.Instance.removeListener(_requestRefresh)
27
+ })
28
+ }, [])
29
+
30
+ const _cardStatusChanged = (card: any) => {
31
+ if (props.onStatusChanged) {
32
+ props.onStatusChanged(card)
33
+ }
34
+ }
35
+
36
+ const _requestRefresh = (data: RequestRefreshEvent) => {
37
+ setRequestRefreshEvent(data)
38
+ }
39
+
40
+ useEffect(() => {
41
+ if (requestRefreshEvent && requestRefreshEvent.dependencies.includes(WebComponentType.card.valueOf()) && requestRefreshEvent.resourceId == props.cardId) {
42
+ webRef.current?.injectJavaScript(`dispatchRefreshEvent('${JSON.stringify(requestRefreshEvent)}')`)
43
+ }
44
+ }, [requestRefreshEvent])
45
+
46
+ const getCardParams = () => {
47
+ return `
48
+ cardId="${props.cardId}"
49
+ customerToken="${props.customerToken}"
50
+ src="https://d1xlopvhx2cz8k.cloudfront.net/resources/outlay.json"
51
+ `
52
+ }
53
+
54
+ const getCardScript = () => {
55
+ return `
56
+ const dispatchRefreshEvent = (details) => {
57
+ const event = new CustomEvent("unitRequestRefresh",{ detail: JSON.parse(details) })
58
+ window.dispatchEvent(event)
59
+ }
60
+ `
61
+ }
62
+
63
+ return (
64
+ <View style={{ height }}>
65
+ <WebComponent ref={webRef} type={WebComponentType.card} params={getCardParams()} script={getCardScript()} onMessage={message => {
66
+ if (message.type === UnitMessage.UNIT_REQUEST_RENDERING) {
67
+ EventBus.Instance.event(BusEvent.REQUEST_RENDERING, (message.details as RequestRenderingEvent).data)
68
+ }
69
+ if (message.type == PageMessage.PAGE_HEIGHT) {
70
+ setHeight((message.details as HeightEvent).height)
71
+ }
72
+ }} />
73
+ </View>
74
+ )
75
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Card/CardComponent'
2
+ export * from './BottomSheet/BottomSheetComponent'
@@ -0,0 +1,277 @@
1
+ import React, { ReactNode, useEffect, useRef, useState } from 'react';
2
+ import {
3
+ Animated,
4
+ BackHandler,
5
+ Dimensions,
6
+ Easing,
7
+ // Keyboard,
8
+ LayoutChangeEvent,
9
+ PanResponder,
10
+ StyleSheet,
11
+ TouchableOpacity,
12
+ View,
13
+ } from 'react-native';
14
+ import PropTypes from 'prop-types';
15
+
16
+ export interface BottomSheetProps {
17
+ isOpen: boolean
18
+ children: ReactNode
19
+ sliderMaxHeight: number
20
+ onOpen: () => void
21
+ onClose: () => void
22
+ animationDuration: number,
23
+ expandToMaxHeightEnabled: boolean
24
+ }
25
+
26
+ const BottomSheet = (props: BottomSheetProps) => {
27
+ const panelHeightValue = useRef(new Animated.Value(props.sliderMaxHeight)).current;
28
+ const fadeAnim = useRef(new Animated.Value(0)).current;
29
+
30
+ const [isPanelOpened, setIsPanelOpened] = useState(props.isOpen)
31
+ const [contentHeight, setContentHeight] = useState<number | undefined>(undefined)
32
+
33
+ const [currentHeight, setCurrentHeight] = useState<number>(props.sliderMaxHeight)
34
+
35
+ const [dy, setDy] = useState(0)
36
+
37
+ const _parentPanResponder = PanResponder.create({
38
+ onMoveShouldSetPanResponderCapture: (_, gestureState) => {
39
+ return Math.abs(gestureState.dy) > 10
40
+ },
41
+ onPanResponderMove(_, gestureState) {
42
+ if (currentHeight + gestureState.dy < 0) return
43
+ if (!props.expandToMaxHeightEnabled && currentHeight + gestureState.dy < sliderMaxHeight - contentHeight!) return
44
+ panelHeightValue.setValue(currentHeight + gestureState.dy)
45
+ setDy(gestureState.dy)
46
+ },
47
+ onPanResponderRelease: () => {
48
+ if (dy > 0) {
49
+ collapse()
50
+ } else if (!props.expandToMaxHeightEnabled) {
51
+ setToContentHeight(contentHeight!)
52
+ } else if (dy < 0) {
53
+ expand()
54
+ } else {
55
+ expand()
56
+ }
57
+ }
58
+ });
59
+
60
+ useEffect(() => {
61
+ if (props.isOpen && !isPanelOpened) {
62
+ fadeIn()
63
+ } else if (isPanelOpened) {
64
+ setContentHeight(0)
65
+ _dismiss()
66
+ }
67
+ }, [props.isOpen])
68
+
69
+ useEffect(() => {
70
+ BackHandler.addEventListener('hardwareBackPress', _onBackPress);
71
+ const id = panelHeightValue.addListener(e => setCurrentHeight(e.value))
72
+
73
+ return (() => {
74
+ BackHandler.removeEventListener('hardwareBackPress', _onBackPress);
75
+ panelHeightValue.removeListener(id)
76
+ })
77
+ }, [])
78
+
79
+ const fadeIn = () => {
80
+ Animated.timing(fadeAnim, {
81
+ toValue: 0.3,
82
+ duration: 500,
83
+ useNativeDriver: false
84
+ }).start(() => { setIsPanelOpened(true) });
85
+ };
86
+
87
+ const fadeOut = () => {
88
+ Animated.timing(fadeAnim, {
89
+ toValue: 0,
90
+ duration: 500,
91
+ useNativeDriver: false
92
+ }).start(() => {
93
+ setIsPanelOpened(false)
94
+ props.onClose()
95
+ });
96
+ };
97
+
98
+ const setToContentHeight = (contentHeight: number) => {
99
+ Animated.timing(panelHeightValue, {
100
+ duration: props.animationDuration,
101
+ easing: Easing.quad,
102
+ toValue: sliderMaxHeight - contentHeight!,
103
+ useNativeDriver: false,
104
+ }).start()
105
+ }
106
+
107
+ const expand = () => {
108
+ const { animationDuration } = props;
109
+
110
+ if (currentHeight > (sliderMaxHeight - contentHeight!)) {
111
+ setToContentHeight(contentHeight!)
112
+ return
113
+ }
114
+
115
+ Animated.timing(panelHeightValue, {
116
+ duration: animationDuration,
117
+ easing: Easing.quad,
118
+ toValue: 0,
119
+ useNativeDriver: false,
120
+ }).start()
121
+ }
122
+
123
+ const collapse = () => {
124
+ if (currentHeight > (sliderMaxHeight - contentHeight!)) {
125
+ _dismiss()
126
+ return
127
+ }
128
+
129
+ setToContentHeight(contentHeight!)
130
+
131
+ // Animated.timing(panelHeightValue, {
132
+ // duration: animationDuration,
133
+ // easing: Easing.quad,
134
+ // toValue: sliderMaxHeight - contentHeight!,
135
+ // useNativeDriver: false,
136
+ // }).start(() => {
137
+ // const isPanelOpenedNewValue = !isPanelOpened
138
+ // setIsPanelOpened(isPanelOpenedNewValue)
139
+ // if (isPanelOpenedNewValue) {
140
+ // // onOpen();
141
+ // } else {
142
+ // // onClose();
143
+ // Keyboard.dismiss();
144
+ // }
145
+ // });
146
+ };
147
+
148
+ const _onBackPress = () => {
149
+ isPanelOpened && collapse();
150
+ return isPanelOpened;
151
+ };
152
+
153
+ const _setSize = (e: LayoutChangeEvent) => {
154
+ let newContentHeight = e.nativeEvent.layout.height
155
+ if (newContentHeight !== 0) newContentHeight += 42;
156
+
157
+ setContentHeight(newContentHeight)
158
+ setToContentHeight(newContentHeight)
159
+ };
160
+
161
+ const _dismiss = () => {
162
+ const { animationDuration } = props
163
+ Animated.timing(panelHeightValue, {
164
+ duration: animationDuration,
165
+ easing: Easing.quad,
166
+ toValue: sliderMaxHeight,
167
+ useNativeDriver: false,
168
+ }).start(() => {
169
+ })
170
+ fadeOut()
171
+ }
172
+
173
+ const {
174
+ sliderMaxHeight,
175
+ children,
176
+ } = props;
177
+
178
+ return (
179
+ <>
180
+ <TouchableOpacity
181
+ style={{ position: 'absolute', top: 0, bottom: 0, right: 0, left: 0 }} activeOpacity={1}
182
+ onPress={_dismiss}
183
+ >
184
+ <Animated.View
185
+ style={{ backgroundColor: 'black', flex: 1, opacity: fadeAnim }}
186
+ >
187
+ </Animated.View>
188
+ </TouchableOpacity>
189
+ <Animated.View
190
+ {..._parentPanResponder.panHandlers}
191
+ style={{
192
+ ...styles.container,
193
+ height: sliderMaxHeight,
194
+ transform: [
195
+ { translateY: panelHeightValue },
196
+ ],
197
+ }}
198
+ >
199
+ <View
200
+ style={styles.outerContent}
201
+ >
202
+ <TouchableOpacity activeOpacity={1}>
203
+ <View style={styles.lineContainer}>
204
+ <View style={styles.line} />
205
+ </View>
206
+ </TouchableOpacity>
207
+ <View
208
+ onLayout={_setSize}
209
+ style={[styles.innerContent]}
210
+ >
211
+ {children}
212
+ </View>
213
+ </View>
214
+ </Animated.View>
215
+ </>
216
+ );
217
+ }
218
+
219
+ BottomSheet.propTypes = {
220
+ children: PropTypes.oneOfType([PropTypes.node]),
221
+ isOpen: PropTypes.bool,
222
+ sliderMaxHeight: PropTypes.number,
223
+ animation: PropTypes.func,
224
+ animationDuration: PropTypes.number,
225
+ onOpen: PropTypes.func,
226
+ onClose: PropTypes.func,
227
+ expandToMaxHeightEnabled: PropTypes.bool
228
+ };
229
+
230
+ BottomSheet.defaultProps = {
231
+ children: <View />,
232
+ isOpen: true,
233
+ sliderMaxHeight: Dimensions.get('window').height * 0.95,
234
+ animationDuration: 200,
235
+ onOpen: () => null,
236
+ onClose: () => null,
237
+ expandToMaxHeightEnabled: false
238
+ };
239
+
240
+ const styles = StyleSheet.create({
241
+ container: {
242
+ flex: 1,
243
+ marginTop: 60,
244
+ paddingHorizontal: 12,
245
+ shadowColor: '#000000',
246
+ shadowOffset: {
247
+ width: 0,
248
+ height: 6,
249
+ },
250
+ shadowOpacity: 0.37,
251
+ shadowRadius: 7.49,
252
+ elevation: 12,
253
+ borderTopLeftRadius: 15,
254
+ borderTopRightRadius: 15,
255
+ backgroundColor: '#ffffff',
256
+ },
257
+ lineContainer: {
258
+ borderTopLeftRadius: 30,
259
+ borderTopRightRadius: 30,
260
+ alignItems: 'center'
261
+ },
262
+ line: {
263
+ width: 35,
264
+ height: 4,
265
+ borderRadius: 2,
266
+ marginTop: 18,
267
+ backgroundColor: '#D5DDE0',
268
+ },
269
+ outerContent: {
270
+ flex: -1,
271
+ },
272
+ innerContent: {
273
+ flex: -1,
274
+ },
275
+ });
276
+
277
+ export default BottomSheet;