@captello/ulc-webview-sdk 0.4.0 → 0.5.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.
package/README.md CHANGED
@@ -105,14 +105,83 @@ try {
105
105
 
106
106
  ## React — `@captello/ulc-webview-sdk/react`
107
107
 
108
- The React adapter is the smoothest way to integrate. `useCaptelloWebview` owns a
109
- `CaptelloWebview` for the iframe's lifetime: it builds the embed URL, creates the client
110
- when the iframe mounts, wires outbound messages to typed callbacks, tracks readiness,
111
- and destroys the client on unmount. You get back `iframeProps` to spread, an `isReady`
112
- flag, and stable senders so a typical form is just the hook plus an `<iframe>`.
108
+ The React adapter is the smoothest way to integrate, in two flavors:
109
+
110
+ - **`<CaptelloForm>`** a turnkey component. Drop it in with an `embedUrl` and message
111
+ callbacks; it renders the `<iframe>`, shows your loading / error overlays, and forwards
112
+ the senders on a `ref`. The shortest path.
113
+ - **`useCaptelloWebview`** — the underlying hook, for when you'd rather own the markup.
114
+
115
+ Both own one `CaptelloWebview` for the iframe's lifetime: they build the embed URL, create
116
+ the client when the iframe mounts, wire outbound messages to typed callbacks, track
117
+ readiness, and destroy the client on unmount.
113
118
 
114
119
  `react` is an optional peer dependency (React 18+).
115
120
 
121
+ ### `<CaptelloForm>` — the turnkey component
122
+
123
+ ```tsx
124
+ import { useRef } from "react";
125
+ import { FormMode, LauncherType, ActionButtonPosition, type SubmissionBody } from "@captello/ulc-webview-sdk";
126
+ import { CaptelloForm, type CaptelloFormHandle } from "@captello/ulc-webview-sdk/react";
127
+
128
+ function UlcForm({
129
+ eventWebAccessToken,
130
+ onSubmitted,
131
+ }: {
132
+ eventWebAccessToken: string;
133
+ onSubmitted: (body: SubmissionBody) => void;
134
+ }) {
135
+ const form = useRef<CaptelloFormHandle>(null);
136
+
137
+ return (
138
+ <CaptelloForm
139
+ ref={form}
140
+ style={{ height: 600 }} // an iframe has no intrinsic height — size the form here
141
+ embedUrl={{
142
+ baseUrl: "https://capture.captello.com",
143
+ eventWebAccessToken,
144
+ actionButtonPosition: ActionButtonPosition.Hidden, // b=2
145
+ mode: FormMode.Submit,
146
+ launcher: LauncherType.EventGenWeb,
147
+ }}
148
+ onSubmissionBody={(m) => onSubmitted(m.data)}
149
+ loading={<Spinner />}
150
+ error={(message) => <ErrorBanner>{message}</ErrorBanner>}
151
+ >
152
+ {({ isReady }) => (
153
+ <button disabled={!isReady} onClick={() => form.current?.submit()}>
154
+ Submit
155
+ </button>
156
+ )}
157
+ </CaptelloForm>
158
+ );
159
+ }
160
+ ```
161
+
162
+ Props are the hook options (`embedUrl` or `targetOrigin`, every message callback, and the
163
+ client options) plus a few rendering conveniences:
164
+
165
+ - **`className` / `style` / `id`** — applied to the wrapper element. Size the form here; the
166
+ iframe fills it.
167
+ - **`iframeProps`** — attributes spread onto the `<iframe>` (`title`, `allow`, `sandbox`,
168
+ `name`, …). Defaults: `title="Captello form"`, `allow="camera; microphone; geolocation"`.
169
+ When you drive the URL yourself (no `embedUrl`), set `src` here.
170
+ - **`loading`** — a node shown, centered over the iframe, until `form_load_complete`. The
171
+ iframe stays mounted underneath so it keeps loading.
172
+ - **`error`** — a node (or `(message) => node`) shown when the form reports
173
+ `form_error_message`; the function form receives the translated, display-ready text.
174
+ - **`children`** — inline controls rendered after the form. A function receives the live api
175
+ (status + senders), so a submit button needs no separate `ref`.
176
+ - **`ref`** — a `CaptelloFormHandle`: the senders (`submit`, `reset`, `updateDraft`,
177
+ `triggerValidation`, `prefill`, `submitAndWait`) plus `status` / `isReady`, `getIframe()`,
178
+ and `getClient()`. Use it to drive the form from a parent without lifting state.
179
+
180
+ ### `useCaptelloWebview` — the hook
181
+
182
+ Prefer to own the markup? The hook returns `iframeProps` to spread, an `isReady` flag, and
183
+ stable senders — so a typical form is just the hook plus an `<iframe>`.
184
+
116
185
  ```tsx
117
186
  import { FormMode, LauncherType, ActionButtonPosition, type SubmissionBody } from "@captello/ulc-webview-sdk";
118
187
  import { useCaptelloWebview } from "@captello/ulc-webview-sdk/react";
@@ -124,7 +193,7 @@ function UlcForm({
124
193
  eventWebAccessToken: string;
125
194
  onSubmitted: (body: SubmissionBody) => void;
126
195
  }) {
127
- const { iframeProps, isReady, submit, prefillInfo } = useCaptelloWebview({
196
+ const { iframeProps, isReady, submit, prefill } = useCaptelloWebview({
128
197
  embedUrl: {
129
198
  baseUrl: "https://capture.captello.com",
130
199
  eventWebAccessToken,
@@ -146,7 +215,7 @@ function UlcForm({
146
215
  }
147
216
  ```
148
217
 
149
- What the hook handles for you:
218
+ What the hook (and the component built on it) handle for you:
150
219
 
151
220
  - **URL + origin.** Pass `embedUrl: { baseUrl, ...EmbedUrlOptions }` and the hook builds
152
221
  the URL, derives `targetOrigin`, and returns it as `iframeProps.src` — no separate
@@ -155,14 +224,14 @@ What the hook handles for you:
155
224
  - **Readiness.** `isReady` and `status` (`"loading" | "ready" | "error"`) — no manual
156
225
  `useState` + `onFormLoadComplete` for a spinner.
157
226
  - **Prefill timing.** Sends made before the form loads are queued and flushed on
158
- `form_load_complete`, so you can `prefillInfo(...)` as soon as you have data — no
227
+ `form_load_complete`, so you can `prefill(...)` as soon as you have data — no
159
228
  gating on readiness, and no silently-dropped messages.
160
229
  - **No memoization.** Callbacks are read fresh via a ref, so inline arrow functions
161
230
  won't re-subscribe or re-create the client. The client is recreated only when
162
231
  `targetOrigin` (or `embedUrl`) / `hostWindow` / `matchSource` / `queueUntilReady` change.
163
- - **Stable senders** (`submit`, `reset`, `updateDraft`, `triggerValidation`,
164
- `prefillInfo`, `prefillSubmission`, `prefillSubmissionAndInfo`, `submitAndWait`) safe
165
- in deps or passed to children. `getClient()` returns the live client for escape hatches.
232
+ - **Stable senders** (`submit`, `reset`, `updateDraft`, `triggerValidation`, `prefill`,
233
+ `submitAndWait`) safe in deps or passed to children. `getClient()` returns the live
234
+ client for escape hatches.
166
235
 
167
236
  ### Callback props
168
237
 
@@ -200,24 +269,26 @@ point of view.
200
269
 
201
270
  ### Inbound — host → webview (you send)
202
271
 
203
- | `type` | Method | Notes |
204
- | -------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------- |
205
- | `submit_form` | `webview.submit()` | Submit as if the user pressed the button. |
206
- | `reset_form` | `webview.reset()` | Clear all entered values. |
207
- | `update_draft` | `webview.updateDraft()` | Switch to draft-update mode. |
208
- | `trigger_validation` | `webview.triggerValidation(target)` | `target`: `"email" \| "invitation_code" \| "all"`. |
209
- | `form_prefill` | `webview.prefillSubmission(...)` / `prefillInfo(...)` / `prefillSubmissionAndInfo(...)` | Three payload shapes (see below). |
272
+ | `type` | Method | Notes |
273
+ | -------------------- | ----------------------------------------- | -------------------------------------------------- |
274
+ | `submit_form` | `webview.submit()` | Submit as if the user pressed the button. |
275
+ | `reset_form` | `webview.reset()` | Clear all entered values. |
276
+ | `update_draft` | `webview.updateDraft()` | Switch to draft-update mode. |
277
+ | `trigger_validation` | `webview.triggerValidation(target)` | `target`: `"email" \| "invitation_code" \| "all"`. |
278
+ | `form_prefill` | `webview.prefill({ submission?, info? })` | Submission body, transcription items, or both. |
279
+
280
+ ### Prefill (`form_prefill`)
210
281
 
211
- ### Prefill variants (`form_prefill`)
282
+ A single `prefill({ submission?, info? })` carries either or both payloads:
212
283
 
213
- `form_prefill` carries a `data_type` selecting the payload shape:
284
+ - `submission` a `SubmissionPrefill` (a received `SubmissionBody` round-tripped, or a
285
+ partial `{ data?, ... }` you assemble).
286
+ - `info` — a list of `PrefillInfoItem`. The webview matches each item by
287
+ `ll_field_unique_identifier` (e.g. `"FirstName"`, `"Email"`); `ll_field_id` is optional
288
+ metadata (number or string) and `value` may be a string or boolean.
214
289
 
215
- - `PrefillDataType.UlcSubmission` `prefillSubmission(submission)` a `SubmissionPrefill`
216
- (a received `SubmissionBody` round-tripped, or a partial `{ data?, ...} ` you assemble).
217
- - `PrefillDataType.Info` → `prefillInfo(items)` — a list of `PrefillInfoItem`. The webview
218
- matches each item by `ll_field_unique_identifier` (e.g. `"FirstName"`, `"Email"`);
219
- `ll_field_id` is optional metadata (number or string) and `value` may be a string or boolean.
220
- - `PrefillDataType.UlcSubmissionAndInfo` → `prefillSubmissionAndInfo({ submission, info })`.
290
+ Both ride the same wire `data_type` (`ulc_submission_and_info`); pass just the key(s) you
291
+ have.
221
292
 
222
293
  ## Rendering a submission
223
294
 
@@ -310,13 +381,13 @@ new CaptelloWebview(iframe, {
310
381
  - `onAny(listener) => unsubscribe` — every outbound message.
311
382
  - `submit()`, `reset()`, `updateDraft()`, `triggerValidation(target)` — inbound helpers.
312
383
  - `submitAndWait(timeoutMs?)` — submit and await `submission_body` / `form_error_message` (see above).
313
- - `prefillSubmission(...)`, `prefillInfo(...)`, `prefillSubmissionAndInfo(...)`.
384
+ - `prefill({ submission?, info? })` — pre-fill from a submission body, transcription items, or both.
314
385
  - `send(message)` — low-level escape hatch for any `InboundMessage`.
315
386
  - `destroy()` — detach the listener and drop subscriptions (idempotent).
316
387
 
317
388
  **Send queueing.** With `queueUntilReady` (default `true`), any send before the webview
318
389
  reports `form_load_complete` is buffered and flushed, in order, on load — so calling
319
- `prefillInfo(...)` right after mount won't be silently dropped. A client that attaches
390
+ `prefill(...)` right after mount won't be silently dropped. A client that attaches
320
391
  _after_ the form already loaded won't observe `form_load_complete`; either create the
321
392
  client with the iframe, or pass `queueUntilReady: false` to send immediately. The
322
393
  one-shot `/promises` helpers set `queueUntilReady: false` automatically.
@@ -375,8 +446,8 @@ short keys, and ad-hoc `window.addEventListener("message")` / `iframe.contentWin
375
446
  calls. Replace them as follows.
376
447
 
377
448
  **1. Message-type constants → SDK enums.** Delete local copies (e.g. `UlcFormActionTypeSent`,
378
- `UlcFormActionTypeReceived`, `UlcFormDataType`) and import `InboundMessageType`,
379
- `OutboundMessageType`, `PrefillDataType`.
449
+ `UlcFormActionTypeReceived`, `UlcFormDataType`) and import `InboundMessageType` /
450
+ `OutboundMessageType` (the `form_prefill` `data_type` is set for you by `prefill(...)`).
380
451
 
381
452
  **2. Manual URL building → `buildEmbedUrl`.**
382
453
 
@@ -408,12 +479,12 @@ const body = await client.submitAndWait(); // throws SubmissionError on form_err
408
479
 
409
480
  **5. `document.querySelector("#ulcForm").contentWindow.postMessage(...)` → client methods.**
410
481
  Hold the `CaptelloWebview` instance in a ref/context and call `submit()` / `reset()` /
411
- `prefillInfo()` instead of re-querying the DOM and stringifying messages by hand.
482
+ `prefill()` instead of re-querying the DOM and stringifying messages by hand.
412
483
 
413
- **Prefill notes for migrators.** `prefillInfo` items are matched by
484
+ **Prefill notes for migrators.** `prefill({ info })` items are matched by
414
485
  `ll_field_unique_identifier`; `ll_field_id` is optional (number or string) and `value`
415
486
  may be a string or boolean — so existing payloads with numeric ids and boolean values
416
- type-check as-is. `prefillSubmission` accepts a loose `SubmissionPrefill`, so a
487
+ type-check as-is. `prefill({ submission })` accepts a loose `SubmissionPrefill`, so a
417
488
  previously-received `SubmissionBody` (or a partial `{ email?, data?, ... }`) can be passed
418
489
  back directly.
419
490
 
package/dist/react.d.ts CHANGED
@@ -1,47 +1,9 @@
1
- import { RefCallback } from 'react';
2
- import { a as OutboundMessageMap, O as OutboundMessageType, d as OutboundMessage, C as CaptelloWebviewOptions, e as CaptelloWebview, V as ValidationTarget, f as SubmissionPrefill, P as PrefillInfoItem, S as SubmissionBody } from './client-Dik-Mjid.js';
1
+ import * as react from 'react';
2
+ import { CSSProperties, IframeHTMLAttributes, ReactNode, RefCallback } from 'react';
3
+ import { C as CaptelloWebviewOptions, a as OutboundMessageMap, O as OutboundMessageType, d as OutboundMessage, e as CaptelloWebview, V as ValidationTarget, f as SubmissionPrefill, P as PrefillInfoItem, S as SubmissionBody } from './client-Dik-Mjid.js';
3
4
  export { b as SubmissionError, c as SubmissionTimeoutError, U as Unsubscribe } from './client-Dik-Mjid.js';
4
5
  import { EmbedUrlOptions } from './index.js';
5
6
 
6
- /**
7
- * React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.
8
- *
9
- * {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an
10
- * iframe: it creates the client once the iframe mounts, wires the outbound messages
11
- * you care about to typed callbacks, tracks readiness, and destroys the client on
12
- * unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),
13
- * an `isReady` flag, and stable senders (`submit`, `reset`, `prefill`, …).
14
- *
15
- * Sends made before the form loads are queued by the client and flushed on
16
- * `form_load_complete`, so you can call `prefill(...)` as soon as you have data —
17
- * no need to gate on readiness yourself.
18
- *
19
- * Callbacks are held in a ref and always called fresh, so you do NOT need to memoize
20
- * them — passing inline arrow functions will not re-subscribe or re-create the client.
21
- *
22
- * `react` is an optional peer dependency; importing this entry point requires React 18+.
23
- *
24
- * @example
25
- * function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {
26
- * const { iframeProps, isReady, submit } = useCaptelloWebview({
27
- * embedUrl: {
28
- * baseUrl: "https://capture.captello.com",
29
- * eventWebAccessToken: token,
30
- * mode: FormMode.Submit,
31
- * launcher: LauncherType.EventGenWeb,
32
- * },
33
- * onSubmissionBody: (m) => onSubmitted(m.data),
34
- * });
35
- * return (
36
- * <>
37
- * {!isReady && <Spinner />}
38
- * <iframe {...iframeProps} title="UlcForm" allow="camera; microphone" />
39
- * <button onClick={submit}>Submit</button>
40
- * </>
41
- * );
42
- * }
43
- */
44
-
45
7
  /** Per-message-type callback props accepted by {@link useCaptelloWebview}. */
46
8
  interface CaptelloWebviewCallbacks {
47
9
  onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;
@@ -106,5 +68,81 @@ interface UseCaptelloWebviewResult {
106
68
  * Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.
107
69
  */
108
70
  declare function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult;
71
+ /**
72
+ * Imperative handle exposed on {@link CaptelloForm}'s `ref` — the same senders the hook
73
+ * returns, plus the current status and the underlying `<iframe>` node. Lets a parent
74
+ * drive the form (e.g. an external submit button) without lifting state.
75
+ */
76
+ interface CaptelloFormHandle extends Pick<UseCaptelloWebviewResult, "submit" | "reset" | "updateDraft" | "triggerValidation" | "prefill" | "submitAndWait" | "getClient"> {
77
+ /** Current readiness: `"loading" | "ready" | "error"`. */
78
+ readonly status: CaptelloWebviewStatus;
79
+ /** `true` once the form has reported `form_load_complete`. */
80
+ readonly isReady: boolean;
81
+ /** The underlying `<iframe>` DOM node, or `null` before it mounts. */
82
+ getIframe: () => HTMLIFrameElement | null;
83
+ }
84
+ /**
85
+ * Props for {@link CaptelloForm}: every {@link useCaptelloWebviewOptions} option (embed
86
+ * config + message callbacks + client options) plus rendering conveniences.
87
+ */
88
+ interface CaptelloFormProps extends UseCaptelloWebviewOptions {
89
+ /** `className` for the wrapper element. */
90
+ className?: string;
91
+ /** `style` for the wrapper element — size the form here. The component adds `position: relative`; your values win. */
92
+ style?: CSSProperties;
93
+ /** `id` for the wrapper element. */
94
+ id?: string;
95
+ /**
96
+ * Attributes spread onto the `<iframe>` — `title`, `allow`, `sandbox`, `name`, etc.
97
+ * Defaults: `title="Captello form"`, `allow="camera; microphone; geolocation"`. When you
98
+ * drive the URL yourself (no `embedUrl`), set `src` here.
99
+ */
100
+ iframeProps?: Omit<IframeHTMLAttributes<HTMLIFrameElement>, "ref">;
101
+ /** Rendered, centered over the iframe, while it is loading. The iframe stays mounted underneath. */
102
+ loading?: ReactNode;
103
+ /**
104
+ * Rendered, centered over the iframe, when the form reports `form_error_message`.
105
+ * Pass a function to receive the translated, display-ready error text.
106
+ */
107
+ error?: ReactNode | ((message: string | undefined) => ReactNode);
108
+ /**
109
+ * Inline controls rendered after the form. A function receives the live api
110
+ * (status + senders), so you can wire a submit button without a `ref`.
111
+ */
112
+ children?: ReactNode | ((api: UseCaptelloWebviewResult) => ReactNode);
113
+ }
114
+ /**
115
+ * Turnkey component for embedding a Captello capture form — the shortest path to a
116
+ * working integration. Renders the `<iframe>`, wires {@link useCaptelloWebview} to it,
117
+ * shows your `loading` / `error` overlays, and forwards a {@link CaptelloFormHandle} on
118
+ * `ref` so a parent can `submit()` / `prefill()` without lifting state.
119
+ *
120
+ * Pass `embedUrl` and the form fills its wrapper — size the form via `className` / `style`
121
+ * (an iframe has no intrinsic height). Reach for {@link useCaptelloWebview} instead when
122
+ * you need to own the markup.
123
+ *
124
+ * @example
125
+ * function UlcForm({ token }: { token: string }) {
126
+ * const ref = useRef<CaptelloFormHandle>(null);
127
+ * return (
128
+ * <CaptelloForm
129
+ * ref={ref}
130
+ * style={{ height: 600 }}
131
+ * embedUrl={{
132
+ * baseUrl: "https://capture.captello.com",
133
+ * eventWebAccessToken: token,
134
+ * mode: FormMode.Submit,
135
+ * launcher: LauncherType.EventGenWeb,
136
+ * }}
137
+ * onSubmissionBody={(m) => save(m.data)}
138
+ * loading={<Spinner />}
139
+ * error={(msg) => <ErrorBanner>{msg}</ErrorBanner>}
140
+ * >
141
+ * {({ isReady }) => <button disabled={!isReady} onClick={() => ref.current?.submit()}>Submit</button>}
142
+ * </CaptelloForm>
143
+ * );
144
+ * }
145
+ */
146
+ declare const CaptelloForm: react.ForwardRefExoticComponent<CaptelloFormProps & react.RefAttributes<CaptelloFormHandle>>;
109
147
 
110
- export { type CaptelloIframeProps, CaptelloWebview, type CaptelloWebviewCallbacks, type CaptelloWebviewStatus, type EmbedUrlConfig, type UseCaptelloWebviewOptions, type UseCaptelloWebviewResult, useCaptelloWebview };
148
+ export { CaptelloForm, type CaptelloFormHandle, type CaptelloFormProps, type CaptelloIframeProps, CaptelloWebview, type CaptelloWebviewCallbacks, type CaptelloWebviewStatus, type EmbedUrlConfig, type UseCaptelloWebviewOptions, type UseCaptelloWebviewResult, useCaptelloWebview };
package/dist/react.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { buildEmbedUrl } from './chunk-4E7OW4RJ.js';
2
2
  import { CaptelloWebview, OutboundMessageType } from './chunk-PFFBCSJ2.js';
3
3
  export { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from './chunk-PFFBCSJ2.js';
4
- import { useRef, useState, useCallback, useEffect } from 'react';
4
+ import { forwardRef, useState, useRef, useCallback, useImperativeHandle, useEffect } from 'react';
5
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
6
 
6
7
  var CALLBACK_BY_TYPE = {
7
8
  ["form_load_complete" /* FormLoadComplete */]: "onFormLoadComplete",
@@ -99,7 +100,94 @@ function useCaptelloWebview(options) {
99
100
  submitAndWait
100
101
  };
101
102
  }
103
+ var DEFAULT_ALLOW = "camera; microphone; geolocation";
104
+ var WRAPPER_STYLE = { position: "relative" };
105
+ var IFRAME_STYLE = { display: "block", width: "100%", height: "100%", border: 0 };
106
+ var OVERLAY_STYLE = {
107
+ position: "absolute",
108
+ inset: 0,
109
+ display: "flex",
110
+ alignItems: "center",
111
+ justifyContent: "center"
112
+ };
113
+ function CaptelloFormImpl(props, ref) {
114
+ const {
115
+ className,
116
+ style,
117
+ id,
118
+ iframeProps,
119
+ loading,
120
+ error,
121
+ children,
122
+ onFormLoadComplete,
123
+ onFormErrorMessage,
124
+ ...options
125
+ } = props;
126
+ const [errorMessage, setErrorMessage] = useState(void 0);
127
+ const api = useCaptelloWebview({
128
+ ...options,
129
+ // Wrap the two status-bearing callbacks to track the error text, then forward to
130
+ // the caller's handler. The hook reads callbacks fresh, so these inline wrappers
131
+ // don't re-subscribe or re-create the client.
132
+ onFormLoadComplete: (message) => {
133
+ setErrorMessage(void 0);
134
+ onFormLoadComplete?.(message);
135
+ },
136
+ onFormErrorMessage: (message) => {
137
+ setErrorMessage(message.data);
138
+ onFormErrorMessage?.(message);
139
+ }
140
+ });
141
+ const nodeRef = useRef(null);
142
+ const hookRef = api.iframeProps.ref;
143
+ const setIframe = useCallback(
144
+ (node) => {
145
+ nodeRef.current = node;
146
+ hookRef(node);
147
+ },
148
+ [hookRef]
149
+ );
150
+ useImperativeHandle(
151
+ ref,
152
+ () => ({
153
+ submit: api.submit,
154
+ reset: api.reset,
155
+ updateDraft: api.updateDraft,
156
+ triggerValidation: api.triggerValidation,
157
+ prefill: api.prefill,
158
+ submitAndWait: api.submitAndWait,
159
+ getClient: api.getClient,
160
+ getIframe: () => nodeRef.current,
161
+ status: api.status,
162
+ isReady: api.isReady
163
+ }),
164
+ [api]
165
+ );
166
+ const src = api.iframeProps.src ?? iframeProps?.src;
167
+ const showLoading = api.status === "loading" && loading != null;
168
+ const showError = api.status === "error" && error != null;
169
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
170
+ /* @__PURE__ */ jsxs("div", { className, id, style: { ...WRAPPER_STYLE, ...style }, children: [
171
+ /* @__PURE__ */ jsx(
172
+ "iframe",
173
+ {
174
+ title: "Captello form",
175
+ allow: DEFAULT_ALLOW,
176
+ ...iframeProps,
177
+ ref: setIframe,
178
+ src,
179
+ style: { ...IFRAME_STYLE, ...iframeProps?.style }
180
+ }
181
+ ),
182
+ showLoading ? /* @__PURE__ */ jsx("div", { style: OVERLAY_STYLE, children: loading }) : null,
183
+ showError ? /* @__PURE__ */ jsx("div", { style: OVERLAY_STYLE, children: typeof error === "function" ? error(errorMessage) : error }) : null
184
+ ] }),
185
+ typeof children === "function" ? children(api) : children
186
+ ] });
187
+ }
188
+ var CaptelloForm = forwardRef(CaptelloFormImpl);
189
+ CaptelloForm.displayName = "CaptelloForm";
102
190
 
103
- export { useCaptelloWebview };
191
+ export { CaptelloForm, useCaptelloWebview };
104
192
  //# sourceMappingURL=react.js.map
105
193
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["/**\n * React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.\n *\n * {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an\n * iframe: it creates the client once the iframe mounts, wires the outbound messages\n * you care about to typed callbacks, tracks readiness, and destroys the client on\n * unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),\n * an `isReady` flag, and stable senders (`submit`, `reset`, `prefill`, …).\n *\n * Sends made before the form loads are queued by the client and flushed on\n * `form_load_complete`, so you can call `prefill(...)` as soon as you have data —\n * no need to gate on readiness yourself.\n *\n * Callbacks are held in a ref and always called fresh, so you do NOT need to memoize\n * them — passing inline arrow functions will not re-subscribe or re-create the client.\n *\n * `react` is an optional peer dependency; importing this entry point requires React 18+.\n *\n * @example\n * function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {\n * const { iframeProps, isReady, submit } = useCaptelloWebview({\n * embedUrl: {\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * },\n * onSubmissionBody: (m) => onSubmitted(m.data),\n * });\n * return (\n * <>\n * {!isReady && <Spinner />}\n * <iframe {...iframeProps} title=\"UlcForm\" allow=\"camera; microphone\" />\n * <button onClick={submit}>Submit</button>\n * </>\n * );\n * }\n */\n\nimport { useCallback, useEffect, useRef, useState, type RefCallback } from \"react\";\n\nimport { CaptelloWebview } from \"./client\";\nimport type { CaptelloWebviewOptions, Unsubscribe } from \"./client\";\nimport { buildEmbedUrl } from \"./embed-url\";\nimport type { EmbedUrlOptions } from \"./embed-url\";\nimport { OutboundMessageType } from \"./messages\";\nimport type {\n OutboundMessage,\n OutboundMessageMap,\n PrefillInfoItem,\n SubmissionBody,\n SubmissionPrefill,\n ValidationTarget,\n} from \"./messages\";\n\n/** Per-message-type callback props accepted by {@link useCaptelloWebview}. */\nexport interface CaptelloWebviewCallbacks {\n onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;\n onFormErrorMessage?: (message: OutboundMessageMap[OutboundMessageType.FormErrorMessage]) => void;\n onSubmissionBody?: (message: OutboundMessageMap[OutboundMessageType.SubmissionBody]) => void;\n onFormSubmitSuccess?: (message: OutboundMessageMap[OutboundMessageType.FormSubmitSuccess]) => void;\n onConnexionsProfileRedirect?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsProfileRedirect]) => void;\n onConnexionsDownloadVcard?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsDownloadVcard]) => void;\n /** Catch-all: called for every outbound message, after the specific handler above. */\n onAnyMessage?: (message: OutboundMessage) => void;\n}\n\n/** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */\nexport interface EmbedUrlConfig extends EmbedUrlOptions {\n /**\n * The capture **origin**, e.g. `\"https://capture.captello.com\"`. The SDK appends the\n * capture path for you, so the origin, a trailing slash, or the full\n * `…/capture/submission` URL all work — see {@link buildEmbedUrl}.\n */\n baseUrl: string;\n}\n\n/**\n * Options for {@link useCaptelloWebview}.\n *\n * Provide **either** `embedUrl` (the hook builds the URL and derives `targetOrigin`,\n * returning `iframeProps.src`) **or** your own `targetOrigin` (you set the iframe `src`\n * yourself). Plus message callbacks and the usual client options.\n */\nexport interface UseCaptelloWebviewOptions extends CaptelloWebviewOptions, CaptelloWebviewCallbacks {\n /** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */\n embedUrl?: EmbedUrlConfig;\n}\n\n/** Readiness of the embedded form. */\nexport type CaptelloWebviewStatus = \"loading\" | \"ready\" | \"error\";\n\n/** Props to spread onto the `<iframe>`. `src` is present only when `embedUrl` is given. */\nexport interface CaptelloIframeProps {\n ref: RefCallback<HTMLIFrameElement | null>;\n src?: string;\n}\n\n/** What {@link useCaptelloWebview} returns. */\nexport interface UseCaptelloWebviewResult {\n /** Spread onto your iframe: `<iframe {...iframeProps} />`. Includes `src` if `embedUrl` was given. */\n iframeProps: CaptelloIframeProps;\n /** The iframe ref callback (same as `iframeProps.ref`), if you'd rather set `src` yourself. */\n ref: RefCallback<HTMLIFrameElement | null>;\n /** `true` once the form has reported `form_load_complete`. */\n isReady: boolean;\n /** `\"loading\"` → `\"ready\"`; flips to `\"error\"` if a `form_error_message` arrives. */\n status: CaptelloWebviewStatus;\n /** The live client, or `null` before the iframe mounts. For escape-hatch use. */\n getClient: () => CaptelloWebview | null;\n submit: () => void;\n reset: () => void;\n updateDraft: () => void;\n triggerValidation: (target: ValidationTarget) => void;\n prefill: (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => void;\n submitAndWait: (timeoutMs?: number) => Promise<SubmissionBody>;\n}\n\nconst CALLBACK_BY_TYPE: Record<OutboundMessageType, keyof CaptelloWebviewCallbacks> = {\n [OutboundMessageType.FormLoadComplete]: \"onFormLoadComplete\",\n [OutboundMessageType.FormErrorMessage]: \"onFormErrorMessage\",\n [OutboundMessageType.SubmissionBody]: \"onSubmissionBody\",\n [OutboundMessageType.FormSubmitSuccess]: \"onFormSubmitSuccess\",\n [OutboundMessageType.ConnexionsProfileRedirect]: \"onConnexionsProfileRedirect\",\n [OutboundMessageType.ConnexionsDownloadVcard]: \"onConnexionsDownloadVcard\",\n};\n\n/**\n * Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.\n */\nexport function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult {\n const { embedUrl, matchSource, queueUntilReady, hostWindow } = options;\n\n // Resolve the URL + the targetOrigin to use. embedUrl wins; otherwise use the\n // explicit targetOrigin. Recompute only when the URL-affecting inputs change.\n const src = embedUrl ? buildEmbedUrl(embedUrl.baseUrl, embedUrl) : undefined;\n const targetOrigin = src ? new URL(src).origin : options.targetOrigin;\n\n // Latest options/callbacks, read fresh inside listeners so callers needn't memoize.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const clientRef = useRef<CaptelloWebview | null>(null);\n const frameRef = useRef<HTMLIFrameElement | null>(null);\n const teardown = useRef<(() => void) | null>(null);\n\n const [status, setStatus] = useState<CaptelloWebviewStatus>(\"loading\");\n\n const attach = useCallback(\n (frame: HTMLIFrameElement | null) => {\n // Tear down any previous client (ref changed or unmounting).\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n frameRef.current = frame;\n setStatus(\"loading\");\n\n if (!frame) return;\n\n const client = new CaptelloWebview(frame, {\n targetOrigin,\n hostWindow: optionsRef.current.hostWindow,\n matchSource: optionsRef.current.matchSource,\n queueUntilReady: optionsRef.current.queueUntilReady,\n });\n clientRef.current = client;\n\n const offs: Unsubscribe[] = [];\n for (const type of Object.values(OutboundMessageType)) {\n offs.push(\n client.on(type, (message) => {\n if (type === OutboundMessageType.FormLoadComplete) setStatus(\"ready\");\n else if (type === OutboundMessageType.FormErrorMessage) setStatus(\"error\");\n\n const handler = optionsRef.current[CALLBACK_BY_TYPE[type]] as\n | ((m: typeof message) => void)\n | undefined;\n handler?.(message);\n optionsRef.current.onAnyMessage?.(message);\n }),\n );\n }\n\n teardown.current = () => {\n for (const off of offs) off();\n client.destroy();\n };\n },\n // Re-create the client only when connection-level inputs change.\n // Callbacks are read via optionsRef, so they intentionally aren't deps.\n [targetOrigin, matchSource, queueUntilReady, hostWindow],\n );\n\n useEffect(() => {\n if (frameRef.current) attach(frameRef.current);\n return () => {\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n };\n }, [attach]);\n\n const getClient = useCallback(() => clientRef.current, []);\n\n const submit = useCallback(() => clientRef.current?.submit(), []);\n const reset = useCallback(() => clientRef.current?.reset(), []);\n const updateDraft = useCallback(() => clientRef.current?.updateDraft(), []);\n const triggerValidation = useCallback(\n (target: ValidationTarget) => clientRef.current?.triggerValidation(target),\n [],\n );\n const prefill = useCallback(\n (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) =>\n clientRef.current?.prefill(data),\n [],\n );\n const submitAndWait = useCallback((timeoutMs?: number) => {\n const client = clientRef.current;\n if (!client) {\n return Promise.reject(new Error(\"CaptelloWebview: iframe is not mounted yet.\"));\n }\n return client.submitAndWait(timeoutMs);\n }, []);\n\n const iframeProps: CaptelloIframeProps = src ? { ref: attach, src } : { ref: attach };\n\n return {\n iframeProps,\n ref: attach,\n isReady: status === \"ready\",\n status,\n getClient,\n submit,\n reset,\n updateDraft,\n triggerValidation,\n prefill,\n submitAndWait,\n };\n}\n\nexport { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from \"./client\";\nexport type { Unsubscribe } from \"./client\";\n"]}
1
+ {"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["/**\n * React adapter for the Captello webview SDK — `@captello/ulc-webview-sdk/react`.\n *\n * Two entry points, same engine:\n * - {@link CaptelloForm} — a turnkey `<iframe>` component. Drop it in with an `embedUrl`\n * and message callbacks; it renders the frame, shows your `loading` / `error` overlays,\n * and exposes the senders via an imperative `ref`. This is the shortest path.\n * - {@link useCaptelloWebview} — the underlying hook, for when you want to own the markup.\n *\n * {@link useCaptelloWebview} owns a {@link CaptelloWebview} for the lifetime of an\n * iframe: it creates the client once the iframe mounts, wires the outbound messages\n * you care about to typed callbacks, tracks readiness, and destroys the client on\n * unmount. You get back `iframeProps` to spread onto your `<iframe>` (or a bare `ref`),\n * an `isReady` flag, and stable senders (`submit`, `reset`, `prefill`, …).\n *\n * Sends made before the form loads are queued by the client and flushed on\n * `form_load_complete`, so you can call `prefill(...)` as soon as you have data —\n * no need to gate on readiness yourself.\n *\n * Callbacks are held in a ref and always called fresh, so you do NOT need to memoize\n * them — passing inline arrow functions will not re-subscribe or re-create the client.\n *\n * `react` is an optional peer dependency; importing this entry point requires React 18+.\n *\n * @example\n * function UlcForm({ token, onSubmitted }: { token: string; onSubmitted: (b: SubmissionBody) => void }) {\n * const { iframeProps, isReady, submit } = useCaptelloWebview({\n * embedUrl: {\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * },\n * onSubmissionBody: (m) => onSubmitted(m.data),\n * });\n * return (\n * <>\n * {!isReady && <Spinner />}\n * <iframe {...iframeProps} title=\"UlcForm\" allow=\"camera; microphone\" />\n * <button onClick={submit}>Submit</button>\n * </>\n * );\n * }\n */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type IframeHTMLAttributes,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefCallback,\n} from \"react\";\n\nimport { CaptelloWebview } from \"./client\";\nimport type { CaptelloWebviewOptions, Unsubscribe } from \"./client\";\nimport { buildEmbedUrl } from \"./embed-url\";\nimport type { EmbedUrlOptions } from \"./embed-url\";\nimport { OutboundMessageType } from \"./messages\";\nimport type {\n OutboundMessage,\n OutboundMessageMap,\n PrefillInfoItem,\n SubmissionBody,\n SubmissionPrefill,\n ValidationTarget,\n} from \"./messages\";\n\n/** Per-message-type callback props accepted by {@link useCaptelloWebview}. */\nexport interface CaptelloWebviewCallbacks {\n onFormLoadComplete?: (message: OutboundMessageMap[OutboundMessageType.FormLoadComplete]) => void;\n onFormErrorMessage?: (message: OutboundMessageMap[OutboundMessageType.FormErrorMessage]) => void;\n onSubmissionBody?: (message: OutboundMessageMap[OutboundMessageType.SubmissionBody]) => void;\n onFormSubmitSuccess?: (message: OutboundMessageMap[OutboundMessageType.FormSubmitSuccess]) => void;\n onConnexionsProfileRedirect?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsProfileRedirect]) => void;\n onConnexionsDownloadVcard?: (message: OutboundMessageMap[OutboundMessageType.ConnexionsDownloadVcard]) => void;\n /** Catch-all: called for every outbound message, after the specific handler above. */\n onAnyMessage?: (message: OutboundMessage) => void;\n}\n\n/** Embed-URL config: a base URL plus {@link EmbedUrlOptions}. */\nexport interface EmbedUrlConfig extends EmbedUrlOptions {\n /**\n * The capture **origin**, e.g. `\"https://capture.captello.com\"`. The SDK appends the\n * capture path for you, so the origin, a trailing slash, or the full\n * `…/capture/submission` URL all work — see {@link buildEmbedUrl}.\n */\n baseUrl: string;\n}\n\n/**\n * Options for {@link useCaptelloWebview}.\n *\n * Provide **either** `embedUrl` (the hook builds the URL and derives `targetOrigin`,\n * returning `iframeProps.src`) **or** your own `targetOrigin` (you set the iframe `src`\n * yourself). Plus message callbacks and the usual client options.\n */\nexport interface UseCaptelloWebviewOptions extends CaptelloWebviewOptions, CaptelloWebviewCallbacks {\n /** Build the iframe URL and derive `targetOrigin` from it. Sets `iframeProps.src`. */\n embedUrl?: EmbedUrlConfig;\n}\n\n/** Readiness of the embedded form. */\nexport type CaptelloWebviewStatus = \"loading\" | \"ready\" | \"error\";\n\n/** Props to spread onto the `<iframe>`. `src` is present only when `embedUrl` is given. */\nexport interface CaptelloIframeProps {\n ref: RefCallback<HTMLIFrameElement | null>;\n src?: string;\n}\n\n/** What {@link useCaptelloWebview} returns. */\nexport interface UseCaptelloWebviewResult {\n /** Spread onto your iframe: `<iframe {...iframeProps} />`. Includes `src` if `embedUrl` was given. */\n iframeProps: CaptelloIframeProps;\n /** The iframe ref callback (same as `iframeProps.ref`), if you'd rather set `src` yourself. */\n ref: RefCallback<HTMLIFrameElement | null>;\n /** `true` once the form has reported `form_load_complete`. */\n isReady: boolean;\n /** `\"loading\"` → `\"ready\"`; flips to `\"error\"` if a `form_error_message` arrives. */\n status: CaptelloWebviewStatus;\n /** The live client, or `null` before the iframe mounts. For escape-hatch use. */\n getClient: () => CaptelloWebview | null;\n submit: () => void;\n reset: () => void;\n updateDraft: () => void;\n triggerValidation: (target: ValidationTarget) => void;\n prefill: (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => void;\n submitAndWait: (timeoutMs?: number) => Promise<SubmissionBody>;\n}\n\nconst CALLBACK_BY_TYPE: Record<OutboundMessageType, keyof CaptelloWebviewCallbacks> = {\n [OutboundMessageType.FormLoadComplete]: \"onFormLoadComplete\",\n [OutboundMessageType.FormErrorMessage]: \"onFormErrorMessage\",\n [OutboundMessageType.SubmissionBody]: \"onSubmissionBody\",\n [OutboundMessageType.FormSubmitSuccess]: \"onFormSubmitSuccess\",\n [OutboundMessageType.ConnexionsProfileRedirect]: \"onConnexionsProfileRedirect\",\n [OutboundMessageType.ConnexionsDownloadVcard]: \"onConnexionsDownloadVcard\",\n};\n\n/**\n * Binds a {@link CaptelloWebview} to an iframe's lifecycle. See the module doc for usage.\n */\nexport function useCaptelloWebview(options: UseCaptelloWebviewOptions): UseCaptelloWebviewResult {\n const { embedUrl, matchSource, queueUntilReady, hostWindow } = options;\n\n // Resolve the URL + the targetOrigin to use. embedUrl wins; otherwise use the\n // explicit targetOrigin. Recompute only when the URL-affecting inputs change.\n const src = embedUrl ? buildEmbedUrl(embedUrl.baseUrl, embedUrl) : undefined;\n const targetOrigin = src ? new URL(src).origin : options.targetOrigin;\n\n // Latest options/callbacks, read fresh inside listeners so callers needn't memoize.\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const clientRef = useRef<CaptelloWebview | null>(null);\n const frameRef = useRef<HTMLIFrameElement | null>(null);\n const teardown = useRef<(() => void) | null>(null);\n\n const [status, setStatus] = useState<CaptelloWebviewStatus>(\"loading\");\n\n const attach = useCallback(\n (frame: HTMLIFrameElement | null) => {\n // Tear down any previous client (ref changed or unmounting).\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n frameRef.current = frame;\n setStatus(\"loading\");\n\n if (!frame) return;\n\n const client = new CaptelloWebview(frame, {\n targetOrigin,\n hostWindow: optionsRef.current.hostWindow,\n matchSource: optionsRef.current.matchSource,\n queueUntilReady: optionsRef.current.queueUntilReady,\n });\n clientRef.current = client;\n\n const offs: Unsubscribe[] = [];\n for (const type of Object.values(OutboundMessageType)) {\n offs.push(\n client.on(type, (message) => {\n if (type === OutboundMessageType.FormLoadComplete) setStatus(\"ready\");\n else if (type === OutboundMessageType.FormErrorMessage) setStatus(\"error\");\n\n const handler = optionsRef.current[CALLBACK_BY_TYPE[type]] as\n | ((m: typeof message) => void)\n | undefined;\n handler?.(message);\n optionsRef.current.onAnyMessage?.(message);\n }),\n );\n }\n\n teardown.current = () => {\n for (const off of offs) off();\n client.destroy();\n };\n },\n // Re-create the client only when connection-level inputs change.\n // Callbacks are read via optionsRef, so they intentionally aren't deps.\n [targetOrigin, matchSource, queueUntilReady, hostWindow],\n );\n\n useEffect(() => {\n if (frameRef.current) attach(frameRef.current);\n return () => {\n teardown.current?.();\n teardown.current = null;\n clientRef.current = null;\n };\n }, [attach]);\n\n const getClient = useCallback(() => clientRef.current, []);\n\n const submit = useCallback(() => clientRef.current?.submit(), []);\n const reset = useCallback(() => clientRef.current?.reset(), []);\n const updateDraft = useCallback(() => clientRef.current?.updateDraft(), []);\n const triggerValidation = useCallback(\n (target: ValidationTarget) => clientRef.current?.triggerValidation(target),\n [],\n );\n const prefill = useCallback(\n (data: { submission?: SubmissionPrefill; info?: PrefillInfoItem[] }) => clientRef.current?.prefill(data),\n [],\n );\n const submitAndWait = useCallback((timeoutMs?: number) => {\n const client = clientRef.current;\n if (!client) {\n return Promise.reject(new Error(\"CaptelloWebview: iframe is not mounted yet.\"));\n }\n return client.submitAndWait(timeoutMs);\n }, []);\n\n const iframeProps: CaptelloIframeProps = src ? { ref: attach, src } : { ref: attach };\n\n return {\n iframeProps,\n ref: attach,\n isReady: status === \"ready\",\n status,\n getClient,\n submit,\n reset,\n updateDraft,\n triggerValidation,\n prefill,\n submitAndWait,\n };\n}\n\n/* ------------------------------------------------------------------ *\n * <CaptelloForm /> — the turnkey component\n * ------------------------------------------------------------------ */\n\n/** Default iframe permissions for a capture form (business-card camera scan, mic, geo). */\nconst DEFAULT_ALLOW = \"camera; microphone; geolocation\";\n\n/** Wrapper is the positioning context for the loading / error overlays. */\nconst WRAPPER_STYLE: CSSProperties = { position: \"relative\" };\n\n/** The iframe fills the wrapper; size the component, not this. */\nconst IFRAME_STYLE: CSSProperties = { display: \"block\", width: \"100%\", height: \"100%\", border: 0 };\n\n/** Centers the `loading` / `error` node over the iframe. */\nconst OVERLAY_STYLE: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\n/**\n * Imperative handle exposed on {@link CaptelloForm}'s `ref` — the same senders the hook\n * returns, plus the current status and the underlying `<iframe>` node. Lets a parent\n * drive the form (e.g. an external submit button) without lifting state.\n */\nexport interface CaptelloFormHandle\n extends Pick<\n UseCaptelloWebviewResult,\n \"submit\" | \"reset\" | \"updateDraft\" | \"triggerValidation\" | \"prefill\" | \"submitAndWait\" | \"getClient\"\n > {\n /** Current readiness: `\"loading\" | \"ready\" | \"error\"`. */\n readonly status: CaptelloWebviewStatus;\n /** `true` once the form has reported `form_load_complete`. */\n readonly isReady: boolean;\n /** The underlying `<iframe>` DOM node, or `null` before it mounts. */\n getIframe: () => HTMLIFrameElement | null;\n}\n\n/**\n * Props for {@link CaptelloForm}: every {@link useCaptelloWebviewOptions} option (embed\n * config + message callbacks + client options) plus rendering conveniences.\n */\nexport interface CaptelloFormProps extends UseCaptelloWebviewOptions {\n /** `className` for the wrapper element. */\n className?: string;\n /** `style` for the wrapper element — size the form here. The component adds `position: relative`; your values win. */\n style?: CSSProperties;\n /** `id` for the wrapper element. */\n id?: string;\n /**\n * Attributes spread onto the `<iframe>` — `title`, `allow`, `sandbox`, `name`, etc.\n * Defaults: `title=\"Captello form\"`, `allow=\"camera; microphone; geolocation\"`. When you\n * drive the URL yourself (no `embedUrl`), set `src` here.\n */\n iframeProps?: Omit<IframeHTMLAttributes<HTMLIFrameElement>, \"ref\">;\n /** Rendered, centered over the iframe, while it is loading. The iframe stays mounted underneath. */\n loading?: ReactNode;\n /**\n * Rendered, centered over the iframe, when the form reports `form_error_message`.\n * Pass a function to receive the translated, display-ready error text.\n */\n error?: ReactNode | ((message: string | undefined) => ReactNode);\n /**\n * Inline controls rendered after the form. A function receives the live api\n * (status + senders), so you can wire a submit button without a `ref`.\n */\n children?: ReactNode | ((api: UseCaptelloWebviewResult) => ReactNode);\n}\n\nfunction CaptelloFormImpl(props: CaptelloFormProps, ref: Ref<CaptelloFormHandle>): ReactElement {\n const {\n className,\n style,\n id,\n iframeProps,\n loading,\n error,\n children,\n onFormLoadComplete,\n onFormErrorMessage,\n ...options\n } = props;\n\n // The translated error text from the last form_error_message, for the `error` render.\n const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);\n\n const api = useCaptelloWebview({\n ...options,\n // Wrap the two status-bearing callbacks to track the error text, then forward to\n // the caller's handler. The hook reads callbacks fresh, so these inline wrappers\n // don't re-subscribe or re-create the client.\n onFormLoadComplete: (message) => {\n setErrorMessage(undefined);\n onFormLoadComplete?.(message);\n },\n onFormErrorMessage: (message) => {\n setErrorMessage(message.data);\n onFormErrorMessage?.(message);\n },\n });\n\n // Merge the hook's iframe ref with our own node ref so getIframe() can return the DOM node.\n const nodeRef = useRef<HTMLIFrameElement | null>(null);\n const hookRef = api.iframeProps.ref;\n const setIframe = useCallback<RefCallback<HTMLIFrameElement | null>>(\n (node) => {\n nodeRef.current = node;\n hookRef(node);\n },\n [hookRef],\n );\n\n useImperativeHandle(\n ref,\n () => ({\n submit: api.submit,\n reset: api.reset,\n updateDraft: api.updateDraft,\n triggerValidation: api.triggerValidation,\n prefill: api.prefill,\n submitAndWait: api.submitAndWait,\n getClient: api.getClient,\n getIframe: () => nodeRef.current,\n status: api.status,\n isReady: api.isReady,\n }),\n [api],\n );\n\n // embedUrl-derived src wins; otherwise use a src the caller passed via iframeProps.\n const src = api.iframeProps.src ?? iframeProps?.src;\n const showLoading = api.status === \"loading\" && loading != null;\n const showError = api.status === \"error\" && error != null;\n\n return (\n <>\n <div className={className} id={id} style={{ ...WRAPPER_STYLE, ...style }}>\n <iframe\n title=\"Captello form\"\n allow={DEFAULT_ALLOW}\n {...iframeProps}\n ref={setIframe}\n src={src}\n style={{ ...IFRAME_STYLE, ...iframeProps?.style }}\n />\n {showLoading ? <div style={OVERLAY_STYLE}>{loading}</div> : null}\n {showError ? (\n <div style={OVERLAY_STYLE}>{typeof error === \"function\" ? error(errorMessage) : error}</div>\n ) : null}\n </div>\n {typeof children === \"function\" ? children(api) : children}\n </>\n );\n}\n\n/**\n * Turnkey component for embedding a Captello capture form — the shortest path to a\n * working integration. Renders the `<iframe>`, wires {@link useCaptelloWebview} to it,\n * shows your `loading` / `error` overlays, and forwards a {@link CaptelloFormHandle} on\n * `ref` so a parent can `submit()` / `prefill()` without lifting state.\n *\n * Pass `embedUrl` and the form fills its wrapper — size the form via `className` / `style`\n * (an iframe has no intrinsic height). Reach for {@link useCaptelloWebview} instead when\n * you need to own the markup.\n *\n * @example\n * function UlcForm({ token }: { token: string }) {\n * const ref = useRef<CaptelloFormHandle>(null);\n * return (\n * <CaptelloForm\n * ref={ref}\n * style={{ height: 600 }}\n * embedUrl={{\n * baseUrl: \"https://capture.captello.com\",\n * eventWebAccessToken: token,\n * mode: FormMode.Submit,\n * launcher: LauncherType.EventGenWeb,\n * }}\n * onSubmissionBody={(m) => save(m.data)}\n * loading={<Spinner />}\n * error={(msg) => <ErrorBanner>{msg}</ErrorBanner>}\n * >\n * {({ isReady }) => <button disabled={!isReady} onClick={() => ref.current?.submit()}>Submit</button>}\n * </CaptelloForm>\n * );\n * }\n */\nexport const CaptelloForm = forwardRef(CaptelloFormImpl);\nCaptelloForm.displayName = \"CaptelloForm\";\n\nexport { CaptelloWebview, SubmissionError, SubmissionTimeoutError } from \"./client\";\nexport type { Unsubscribe } from \"./client\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@captello/ulc-webview-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Typed SDK for embedding the Captello capture webview: message protocol, host client, and embed-URL builder.",
5
5
  "author": "Lead Liaison",
6
6
  "license": "MIT",