@parafin/react 5.1.0 → 6.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/index.d.ts CHANGED
@@ -1,49 +1,4 @@
1
- declare const ParafinWidget: (props: {
2
- token: string;
3
- product: 'capital' | 'spend';
4
- externalBusinessId?: string;
5
- onExit?: () => void;
6
- onOptIn?: () => Promise<OptInFields>;
7
- openInNewTab?: boolean;
8
- } & Record<string, any>) => import("react/jsx-runtime").JSX.Element;
9
- export { OptInFields, ParafinWidget };
10
- type USStates = 'AL' | 'AK' | 'AZ' | 'AR' | 'CA' | 'CO' | 'CT' | 'DE' | 'DC' | 'FL' | 'GA' | 'HI' | 'ID' | 'IL' | 'IN' | 'IA' | 'KS' | 'KY' | 'LA' | 'ME' | 'MD' | 'MA' | 'MI' | 'MN' | 'MS' | 'MO' | 'MT' | 'NE' | 'NV' | 'NH' | 'NJ' | 'NM' | 'NY' | 'NC' | 'ND' | 'OH' | 'OK' | 'OR' | 'PA' | 'RI' | 'SC' | 'SD' | 'TN' | 'TX' | 'UT' | 'VT' | 'VA' | 'WA' | 'WV' | 'WI' | 'WY';
11
- type USTerritories = 'AS' | 'GU' | 'MP' | 'PR' | 'VI';
12
- type CanadianProvinces = 'AB' | 'BC' | 'MB' | 'NB' | 'NL' | 'NS' | 'QC' | 'ON' | 'PE' | 'SK';
13
- type CanadianTerritories = 'NT' | 'NU' | 'YT';
14
- type Address = {
15
- addressLine1: string;
16
- addressLine2?: string;
17
- city: string;
18
- state: USStates | USTerritories | CanadianProvinces | CanadianTerritories;
19
- postalCode: string;
20
- country: 'US' | 'CA';
21
- };
22
- type OptInFields = {
23
- businessExternalId: string;
24
- accountManagers?: {
25
- name: string;
26
- email: string;
27
- }[];
28
- owner: {
29
- firstName: string;
30
- lastName: string;
31
- email: string;
32
- phoneNumber?: string;
33
- /** yyyy-mm-dd */
34
- dateOfBirth?: string;
35
- address?: Address;
36
- };
37
- business: {
38
- legalName: string;
39
- dbaName?: string;
40
- address?: Address;
41
- /** yyyy-mm-dd */
42
- dateEstablished?: string;
43
- };
44
- bank?: {
45
- routingNumber?: string;
46
- accountNumberLastFour?: string;
47
- currencyCode?: 'USD' | 'CAD';
48
- };
49
- };
1
+ import { WidgetProps, OptInFields } from '@parafin/widget';
2
+ declare const ParafinWidget: (props: WidgetProps) => import("react/jsx-runtime").JSX.Element;
3
+ export { ParafinWidget };
4
+ export type { OptInFields };
package/out/index.js CHANGED
@@ -1,12 +1,74 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from 'react';
3
- import { openParafinDashboard } from '@parafin/core';
4
- const ParafinWidget = (props) => {
5
- const frameRef = useRef(null);
6
- const [frameKey, setFrameKey] = useState(0);
7
- const [frameHeight, setFrameHeight] = useState('258px');
8
- const [borderColor, setBorderColor] = useState('#E8E8E8');
9
- const [borderRadius, setBorderRadius] = useState('16px');
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useEffect } from 'react';
3
+
4
+ const openParafinDashboard = (props) => {
5
+ try {
6
+ const { origin, searchParams } = new URL(
7
+ // @ts-ignore
8
+ props.dashboardUrlOverride ?? 'https://app.parafin.com');
9
+ const route = 'route' in props ? props.route : '/';
10
+ const query = {
11
+ product: props.product,
12
+ referrer: 'partner',
13
+ ...('token' in props && { token: props.token }),
14
+ ...('externalBusinessId' in props && {
15
+ externalBusinessId: props.externalBusinessId,
16
+ }),
17
+ ...('orderId' in props && { orderId: props.orderId }),
18
+ ...('orderAmount' in props && {
19
+ orderAmount: props.orderAmount.toString(),
20
+ }),
21
+ ...('additionalQueryParams' in props && props.additionalQueryParams),
22
+ ...Object.fromEntries(searchParams),
23
+ };
24
+ const url = `${origin}${route}?${new URLSearchParams(query).toString()}`;
25
+ if ('openInNewTab' in props && props.openInNewTab) {
26
+ window.open(url, '_blank');
27
+ return;
28
+ }
29
+ const existingParafinDashboard = document.getElementById('parafin-dashboard');
30
+ if (existingParafinDashboard) {
31
+ document.removeChild(existingParafinDashboard);
32
+ }
33
+ const frame = document.createElement('iframe');
34
+ frame.id = 'parafin-dashboard';
35
+ frame.style.position = 'fixed';
36
+ frame.style.top = '0';
37
+ frame.style.left = '0';
38
+ frame.style.width = '100%';
39
+ frame.style.height = '100%';
40
+ frame.style.border = 'none';
41
+ frame.style.zIndex = '2147483647';
42
+ frame.style.backgroundColor = '#fff';
43
+ frame.style.opacity = '0';
44
+ frame.style.transition = 'opacity 0.2s';
45
+ frame.src = url;
46
+ frame.allow = 'accelerometer; gyroscope; clipboard-read; clipboard-write';
47
+ const parse = (message) => (message ? JSON.parse(message) : undefined);
48
+ const closeParafinDashboard = (e) => {
49
+ if (e.origin === origin && e.data?.message === 'close-dashboard') {
50
+ if (props.product === 'bnpl' && props.flow === 'checkout') {
51
+ props.onExit(parse(e.data.order));
52
+ }
53
+ else {
54
+ props.onExit?.();
55
+ }
56
+ window.removeEventListener('message', closeParafinDashboard);
57
+ frame.style.opacity = '0';
58
+ setTimeout(() => document.body.removeChild(frame), 200);
59
+ }
60
+ };
61
+ window.addEventListener('message', closeParafinDashboard);
62
+ document.body.appendChild(frame);
63
+ setTimeout(() => (frame.style.opacity = '1'), 1);
64
+ }
65
+ catch (error) {
66
+ console.error('Error loading Parafin dashboard', error);
67
+ }
68
+ };
69
+
70
+ const initializeWidget = (iframe, props) => {
71
+ // @ts-ignore
10
72
  const url = new URL(props.widgetUrlOverride ?? 'https://widget.parafin.com');
11
73
  const query = {
12
74
  token: props.token,
@@ -16,21 +78,29 @@ const ParafinWidget = (props) => {
16
78
  externalBusinessId: props.externalBusinessId ?? '',
17
79
  ...Object.fromEntries(url.searchParams),
18
80
  };
81
+ iframe.id = `parafin-${props.product}-widget`;
82
+ iframe.src = `${url.origin}?${new URLSearchParams(query).toString()}`;
83
+ const resetIframe = () => {
84
+ iframe.src = `${url.origin}?${new URLSearchParams(query).toString()}`;
85
+ };
86
+ // Message handler
19
87
  const messageListener = async ({ data, origin }) => {
20
88
  if (origin === url.origin && data?.product === props.product) {
21
89
  switch (data?.message) {
22
90
  case 'set-border':
23
- if (data?.borderColor)
24
- setBorderColor(data.borderColor);
25
- if (data?.borderRadius)
26
- setBorderRadius(data.borderRadius);
91
+ if (data?.borderColor) {
92
+ iframe.style.border = `1px solid ${data.borderColor}`;
93
+ }
94
+ if (data?.borderRadius) {
95
+ iframe.style.borderRadius = data.borderRadius;
96
+ }
27
97
  break;
28
98
  case 'open-dashboard':
29
99
  openParafinDashboard({
30
100
  ...props,
31
101
  route: data?.route,
32
102
  onExit: () => {
33
- setFrameKey((key) => key + 1);
103
+ resetIframe();
34
104
  props.onExit?.();
35
105
  },
36
106
  });
@@ -38,28 +108,39 @@ const ParafinWidget = (props) => {
38
108
  case 'opt-in':
39
109
  if (props.onOptIn) {
40
110
  const optInFields = await props.onOptIn();
41
- frameRef?.current?.contentWindow?.postMessage({ message: 'opt-in', optInFields: optInFields }, url.origin);
111
+ iframe.contentWindow?.postMessage({ message: 'opt-in', optInFields }, url.origin);
42
112
  }
43
113
  break;
44
114
  case 'set-height':
45
- if (data?.height)
46
- setFrameHeight(data.height);
115
+ if (data?.height) {
116
+ iframe.style.height = data.height;
117
+ }
47
118
  break;
48
119
  }
49
120
  }
50
121
  };
122
+ window.addEventListener('message', messageListener);
123
+ return () => window.removeEventListener('message', messageListener);
124
+ };
125
+ const defaultWidgetStyles = {
126
+ width: '100%',
127
+ height: '258px',
128
+ backgroundColor: '#fff',
129
+ border: '1px solid #E8E8E8',
130
+ borderRadius: '16px',
131
+ transition: 'border 0.2s, border-radius 0.2s',
132
+ boxSizing: 'border-box',
133
+ };
134
+
135
+ const ParafinWidget = (props) => {
136
+ const frameRef = useRef(null);
51
137
  useEffect(() => {
52
- window.addEventListener('message', messageListener);
53
- return () => window.removeEventListener('message', messageListener);
54
- }, []);
55
- return (_jsx("iframe", { ref: frameRef, id: `parafin-${props.product}-widget`, src: `${url.origin}?${new URLSearchParams(query).toString()}`, style: {
56
- width: '100%',
57
- height: frameHeight,
58
- backgroundColor: '#fff',
59
- border: `1px solid ${borderColor}`,
60
- borderRadius: borderRadius,
61
- transition: 'border 0.2s, border-radius 0.2s',
62
- boxSizing: 'border-box',
63
- } }, frameKey));
138
+ if (frameRef.current) {
139
+ const cleanup = initializeWidget(frameRef.current, props);
140
+ return cleanup;
141
+ }
142
+ }, [props]);
143
+ return jsx("iframe", { ref: frameRef, style: defaultWidgetStyles });
64
144
  };
145
+
65
146
  export { ParafinWidget };
package/package.json CHANGED
@@ -1,13 +1,19 @@
1
1
  {
2
2
  "name": "@parafin/react",
3
- "version": "5.1.0",
3
+ "version": "6.0.0-alpha.2",
4
4
  "description": "Parafin React widget",
5
5
  "author": "Parafin (https://www.parafin.com)",
6
6
  "module": "out/index.js",
7
7
  "types": "out/index.d.ts",
8
+ "type": "module",
8
9
  "license": "MIT",
10
+ "files": [
11
+ "out"
12
+ ],
9
13
  "scripts": {
10
- "build": "tsc"
14
+ "build": "npx rollup -c",
15
+ "dev": "npx rollup -c -w",
16
+ "prepublishOnly": "yarn build"
11
17
  },
12
18
  "peerDependencies": {
13
19
  "react": ">=16.8.0"
@@ -17,6 +23,6 @@
17
23
  "typescript": "^4.9.5"
18
24
  },
19
25
  "dependencies": {
20
- "@parafin/core": "^1.0.12"
26
+ "@parafin/widget": "*"
21
27
  }
22
28
  }
package/index.tsx DELETED
@@ -1,194 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react'
2
- import { openParafinDashboard } from '@parafin/core'
3
-
4
- const ParafinWidget = (
5
- props: {
6
- token: string
7
- product: 'capital' | 'spend'
8
- externalBusinessId?: string
9
- onExit?: () => void
10
- onOptIn?: () => Promise<OptInFields>
11
- openInNewTab?: boolean
12
- } & Record<string, any>
13
- ) => {
14
- const frameRef = useRef<HTMLIFrameElement>(null)
15
- const [frameKey, setFrameKey] = useState(0)
16
- const [frameHeight, setFrameHeight] = useState('258px')
17
- const [borderColor, setBorderColor] = useState('#E8E8E8')
18
- const [borderRadius, setBorderRadius] = useState('16px')
19
-
20
- const url = new URL(props.widgetUrlOverride ?? 'https://widget.parafin.com')
21
- const query = {
22
- token: props.token,
23
- product: props.product,
24
- hasOptIn: props.onOptIn ? 'true' : 'false',
25
- host: window.location.origin,
26
- externalBusinessId: props.externalBusinessId ?? '',
27
- ...Object.fromEntries(url.searchParams),
28
- }
29
-
30
- const messageListener = async ({ data, origin }: MessageEvent) => {
31
- if (origin === url.origin && data?.product === props.product) {
32
- switch (data?.message) {
33
- case 'set-border':
34
- if (data?.borderColor) setBorderColor(data.borderColor)
35
- if (data?.borderRadius) setBorderRadius(data.borderRadius)
36
- break
37
- case 'open-dashboard':
38
- openParafinDashboard({
39
- ...props,
40
- route: data?.route,
41
- onExit: () => {
42
- setFrameKey((key) => key + 1)
43
- props.onExit?.()
44
- },
45
- })
46
- break
47
- case 'opt-in':
48
- if (props.onOptIn) {
49
- const optInFields = await props.onOptIn()
50
- frameRef?.current?.contentWindow?.postMessage(
51
- { message: 'opt-in', optInFields: optInFields },
52
- url.origin
53
- )
54
- }
55
- break
56
- case 'set-height':
57
- if (data?.height) setFrameHeight(data.height)
58
- break
59
- }
60
- }
61
- }
62
-
63
- useEffect(() => {
64
- window.addEventListener('message', messageListener)
65
- return () => window.removeEventListener('message', messageListener)
66
- }, [])
67
-
68
- return (
69
- <iframe
70
- key={frameKey}
71
- ref={frameRef}
72
- id={`parafin-${props.product}-widget`}
73
- src={`${url.origin}?${new URLSearchParams(query).toString()}`}
74
- style={{
75
- width: '100%',
76
- height: frameHeight,
77
- backgroundColor: '#fff',
78
- border: `1px solid ${borderColor}`,
79
- borderRadius: borderRadius,
80
- transition: 'border 0.2s, border-radius 0.2s',
81
- boxSizing: 'border-box',
82
- }}
83
- />
84
- )
85
- }
86
-
87
- export { OptInFields, ParafinWidget }
88
-
89
- type USStates =
90
- | 'AL'
91
- | 'AK'
92
- | 'AZ'
93
- | 'AR'
94
- | 'CA'
95
- | 'CO'
96
- | 'CT'
97
- | 'DE'
98
- | 'DC'
99
- | 'FL'
100
- | 'GA'
101
- | 'HI'
102
- | 'ID'
103
- | 'IL'
104
- | 'IN'
105
- | 'IA'
106
- | 'KS'
107
- | 'KY'
108
- | 'LA'
109
- | 'ME'
110
- | 'MD'
111
- | 'MA'
112
- | 'MI'
113
- | 'MN'
114
- | 'MS'
115
- | 'MO'
116
- | 'MT'
117
- | 'NE'
118
- | 'NV'
119
- | 'NH'
120
- | 'NJ'
121
- | 'NM'
122
- | 'NY'
123
- | 'NC'
124
- | 'ND'
125
- | 'OH'
126
- | 'OK'
127
- | 'OR'
128
- | 'PA'
129
- | 'RI'
130
- | 'SC'
131
- | 'SD'
132
- | 'TN'
133
- | 'TX'
134
- | 'UT'
135
- | 'VT'
136
- | 'VA'
137
- | 'WA'
138
- | 'WV'
139
- | 'WI'
140
- | 'WY'
141
-
142
- type USTerritories = 'AS' | 'GU' | 'MP' | 'PR' | 'VI'
143
-
144
- type CanadianProvinces =
145
- | 'AB'
146
- | 'BC'
147
- | 'MB'
148
- | 'NB'
149
- | 'NL'
150
- | 'NS'
151
- | 'QC'
152
- | 'ON'
153
- | 'PE'
154
- | 'SK'
155
-
156
- type CanadianTerritories = 'NT' | 'NU' | 'YT'
157
-
158
- type Address = {
159
- addressLine1: string
160
- addressLine2?: string
161
- city: string
162
- state: USStates | USTerritories | CanadianProvinces | CanadianTerritories
163
- postalCode: string
164
- country: 'US' | 'CA'
165
- }
166
-
167
- type OptInFields = {
168
- businessExternalId: string
169
- accountManagers?: {
170
- name: string
171
- email: string
172
- }[]
173
- owner: {
174
- firstName: string
175
- lastName: string
176
- email: string
177
- phoneNumber?: string
178
- /** yyyy-mm-dd */
179
- dateOfBirth?: string
180
- address?: Address
181
- }
182
- business: {
183
- legalName: string
184
- dbaName?: string
185
- address?: Address
186
- /** yyyy-mm-dd */
187
- dateEstablished?: string
188
- }
189
- bank?: {
190
- routingNumber?: string
191
- accountNumberLastFour?: string
192
- currencyCode?: 'USD' | 'CAD'
193
- }
194
- }
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "esnext",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "jsx": "react-jsx",
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "declaration": true,
11
- "outDir": "./out",
12
- "declarationDir": "./out"
13
- },
14
- "include": ["./index.tsx"]
15
- }