@parafin/core 2.3.1 → 3.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.
@@ -0,0 +1 @@
1
+ $ tsc
package/index.ts CHANGED
@@ -158,3 +158,139 @@ export const openParafinDashboard = (
158
158
  return () => {} // noop
159
159
  }
160
160
  }
161
+
162
+ export const defaultWidgetStyles = {
163
+ width: '100%',
164
+ height: '258px',
165
+ backgroundColor: '#fff',
166
+ border: '1px solid #E8E8E8',
167
+ borderRadius: '16px',
168
+ transition: 'border 0.2s, border-radius 0.2s',
169
+ boxSizing: 'border-box' as const,
170
+ }
171
+
172
+ export type WidgetEvent =
173
+ | 'opted_in'
174
+ | 'opted_out'
175
+ | 'dashboard_opened'
176
+ | 'widget_loaded'
177
+ | 'widget_error'
178
+
179
+ export type WidgetEventMetadata = {
180
+ timeToLoadInMs: number | null
181
+ }
182
+
183
+ const emptyMetadata: WidgetEventMetadata = {
184
+ timeToLoadInMs: null,
185
+ }
186
+
187
+ export type WidgetProps = {
188
+ token: string
189
+ product: 'capital' | 'spend_card' | 'cash_account'
190
+ externalBusinessId?: string
191
+ onEvent?: (
192
+ eventType: WidgetEvent,
193
+ metadata: WidgetEventMetadata
194
+ ) => Promise<void> | void
195
+ onExit?: () => void
196
+ openInNewTab?: boolean
197
+ onLinkOpened?: (url: string, metadata: LinkOpenedMetadata) => void
198
+ inWebView?: boolean
199
+ }
200
+
201
+ export const initializeParafinWidget = (
202
+ iframe: HTMLIFrameElement,
203
+ props: WidgetProps
204
+ ) => {
205
+ const initStartTime = Date.now()
206
+
207
+ // @ts-ignore
208
+ const url = new URL(props.widgetUrlOverride ?? 'https://widget.parafin.com')
209
+ const query = {
210
+ token: props.token,
211
+ product: props.product,
212
+ host: window.location.origin,
213
+ externalBusinessId: props.externalBusinessId ?? '',
214
+ ...Object.fromEntries(url.searchParams),
215
+ }
216
+ const iframeSrc = `${url.origin}?${new URLSearchParams(query).toString()}`
217
+
218
+ iframe.id = `parafin-${props.product}-widget`
219
+ iframe.src = iframeSrc
220
+
221
+ const sendMessage = (message: any) => {
222
+ iframe.contentWindow?.postMessage(message, url.origin)
223
+ }
224
+
225
+ const messageListener = async ({ data, origin }: MessageEvent) => {
226
+ if (origin === url.origin && data?.product === props.product) {
227
+ switch (data?.message) {
228
+ case 'set-border':
229
+ if (data?.borderColor) {
230
+ iframe.style.border = `1px solid ${data.borderColor}`
231
+ }
232
+ if (data?.borderRadius) {
233
+ iframe.style.borderRadius = data.borderRadius
234
+ }
235
+ break
236
+ case 'open-dashboard':
237
+ if (props.onEvent) {
238
+ props.onEvent('dashboard_opened', emptyMetadata)
239
+ }
240
+ openParafinDashboard({
241
+ ...props,
242
+ route: data?.route,
243
+ onExit: () => {
244
+ iframe.src = iframeSrc
245
+ props.onExit?.()
246
+ },
247
+ })
248
+ break
249
+ case 'person-opt-in':
250
+ if (props.onEvent) {
251
+ try {
252
+ await props.onEvent('opted_in', emptyMetadata)
253
+ sendMessage({ message: 'person-opt-in', state: 'success' })
254
+ } catch {
255
+ sendMessage({ message: 'person-opt-in', state: 'error' })
256
+ }
257
+ } else {
258
+ sendMessage({ message: 'person-opt-in', state: 'noop' })
259
+ }
260
+ break
261
+ case 'person-opt-out':
262
+ if (props.onEvent) {
263
+ try {
264
+ await props.onEvent('opted_out', emptyMetadata)
265
+ sendMessage({ message: 'person-opt-out', state: 'success' })
266
+ } catch {
267
+ sendMessage({ message: 'person-opt-out', state: 'error' })
268
+ }
269
+ } else {
270
+ sendMessage({ message: 'person-opt-out', state: 'noop' })
271
+ }
272
+ break
273
+ case 'set-height':
274
+ if (data?.height) {
275
+ iframe.style.height = data.height
276
+ }
277
+ break
278
+ case 'widget-error':
279
+ if (props.onEvent) {
280
+ props.onEvent('widget_error', emptyMetadata)
281
+ }
282
+ break
283
+ case 'widget-load-complete':
284
+ if (props.onEvent) {
285
+ const timeToLoadInMs = Date.now() - initStartTime
286
+ props.onEvent('widget_loaded', { timeToLoadInMs })
287
+ }
288
+ break
289
+ }
290
+ }
291
+ }
292
+
293
+ window.addEventListener('message', messageListener)
294
+
295
+ return () => window.removeEventListener('message', messageListener)
296
+ }
package/out/index.d.ts CHANGED
@@ -25,4 +25,28 @@ type BNPLProps = {
25
25
  onExit?: (id?: string) => Promise<void> | void;
26
26
  };
27
27
  export declare const openParafinDashboard: (props: BaseProps & (CapitalOrSpendProps | BNPLProps)) => () => void;
28
+ export declare const defaultWidgetStyles: {
29
+ width: string;
30
+ height: string;
31
+ backgroundColor: string;
32
+ border: string;
33
+ borderRadius: string;
34
+ transition: string;
35
+ boxSizing: "border-box";
36
+ };
37
+ export type WidgetEvent = 'opted_in' | 'opted_out' | 'dashboard_opened' | 'widget_loaded' | 'widget_error';
38
+ export type WidgetEventMetadata = {
39
+ timeToLoadInMs: number | null;
40
+ };
41
+ export type WidgetProps = {
42
+ token: string;
43
+ product: 'capital' | 'spend_card' | 'cash_account';
44
+ externalBusinessId?: string;
45
+ onEvent?: (eventType: WidgetEvent, metadata: WidgetEventMetadata) => Promise<void> | void;
46
+ onExit?: () => void;
47
+ openInNewTab?: boolean;
48
+ onLinkOpened?: (url: string, metadata: LinkOpenedMetadata) => void;
49
+ inWebView?: boolean;
50
+ };
51
+ export declare const initializeParafinWidget: (iframe: HTMLIFrameElement, props: WidgetProps) => () => void;
28
52
  export {};
package/out/index.js CHANGED
@@ -118,3 +118,106 @@ export const openParafinDashboard = (props) => {
118
118
  return () => { }; // noop
119
119
  }
120
120
  };
121
+ export const defaultWidgetStyles = {
122
+ width: '100%',
123
+ height: '258px',
124
+ backgroundColor: '#fff',
125
+ border: '1px solid #E8E8E8',
126
+ borderRadius: '16px',
127
+ transition: 'border 0.2s, border-radius 0.2s',
128
+ boxSizing: 'border-box',
129
+ };
130
+ const emptyMetadata = {
131
+ timeToLoadInMs: null,
132
+ };
133
+ export const initializeParafinWidget = (iframe, props) => {
134
+ const initStartTime = Date.now();
135
+ // @ts-ignore
136
+ const url = new URL(props.widgetUrlOverride ?? 'https://widget.parafin.com');
137
+ const query = {
138
+ token: props.token,
139
+ product: props.product,
140
+ host: window.location.origin,
141
+ externalBusinessId: props.externalBusinessId ?? '',
142
+ ...Object.fromEntries(url.searchParams),
143
+ };
144
+ const iframeSrc = `${url.origin}?${new URLSearchParams(query).toString()}`;
145
+ iframe.id = `parafin-${props.product}-widget`;
146
+ iframe.src = iframeSrc;
147
+ const sendMessage = (message) => {
148
+ iframe.contentWindow?.postMessage(message, url.origin);
149
+ };
150
+ const messageListener = async ({ data, origin }) => {
151
+ if (origin === url.origin && data?.product === props.product) {
152
+ switch (data?.message) {
153
+ case 'set-border':
154
+ if (data?.borderColor) {
155
+ iframe.style.border = `1px solid ${data.borderColor}`;
156
+ }
157
+ if (data?.borderRadius) {
158
+ iframe.style.borderRadius = data.borderRadius;
159
+ }
160
+ break;
161
+ case 'open-dashboard':
162
+ if (props.onEvent) {
163
+ props.onEvent('dashboard_opened', emptyMetadata);
164
+ }
165
+ openParafinDashboard({
166
+ ...props,
167
+ route: data?.route,
168
+ onExit: () => {
169
+ iframe.src = iframeSrc;
170
+ props.onExit?.();
171
+ },
172
+ });
173
+ break;
174
+ case 'person-opt-in':
175
+ if (props.onEvent) {
176
+ try {
177
+ await props.onEvent('opted_in', emptyMetadata);
178
+ sendMessage({ message: 'person-opt-in', state: 'success' });
179
+ }
180
+ catch {
181
+ sendMessage({ message: 'person-opt-in', state: 'error' });
182
+ }
183
+ }
184
+ else {
185
+ sendMessage({ message: 'person-opt-in', state: 'noop' });
186
+ }
187
+ break;
188
+ case 'person-opt-out':
189
+ if (props.onEvent) {
190
+ try {
191
+ await props.onEvent('opted_out', emptyMetadata);
192
+ sendMessage({ message: 'person-opt-out', state: 'success' });
193
+ }
194
+ catch {
195
+ sendMessage({ message: 'person-opt-out', state: 'error' });
196
+ }
197
+ }
198
+ else {
199
+ sendMessage({ message: 'person-opt-out', state: 'noop' });
200
+ }
201
+ break;
202
+ case 'set-height':
203
+ if (data?.height) {
204
+ iframe.style.height = data.height;
205
+ }
206
+ break;
207
+ case 'widget-error':
208
+ if (props.onEvent) {
209
+ props.onEvent('widget_error', emptyMetadata);
210
+ }
211
+ break;
212
+ case 'widget-load-complete':
213
+ if (props.onEvent) {
214
+ const timeToLoadInMs = Date.now() - initStartTime;
215
+ props.onEvent('widget_loaded', { timeToLoadInMs });
216
+ }
217
+ break;
218
+ }
219
+ }
220
+ };
221
+ window.addEventListener('message', messageListener);
222
+ return () => window.removeEventListener('message', messageListener);
223
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parafin/core",
3
- "version": "2.3.1",
3
+ "version": "3.1.0",
4
4
  "description": "Parafin embedded core",
5
5
  "author": "Parafin (https://www.parafin.com)",
6
6
  "module": "out/index.js",