@bacano/sdk 1.1.19 → 1.2.1

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/react/forms.cjs CHANGED
@@ -21,10 +21,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var forms_exports = {};
22
22
  __export(forms_exports, {
23
23
  BacanoForm: () => BacanoForm,
24
- useBacanoForm: () => useBacanoForm
24
+ useBacanoForm: () => useBacanoForm,
25
+ useBacanoFormTracking: () => useBacanoFormTracking
25
26
  });
26
27
  module.exports = __toCommonJS(forms_exports);
27
- var import_react3 = require("react");
28
+ var import_react4 = require("react");
29
+
30
+ // libs/sdk/src/errors.ts
31
+ var WebsiteError = class extends Error {
32
+ code;
33
+ status;
34
+ retryAfterSeconds;
35
+ constructor(message, code = "WEBSITE_ERROR", options) {
36
+ super(message);
37
+ this.name = "WebsiteError";
38
+ this.code = code;
39
+ this.status = options?.status ?? null;
40
+ this.retryAfterSeconds = options?.retryAfterSeconds ?? null;
41
+ }
42
+ };
28
43
 
29
44
  // libs/sdk/src/react/form-field-control.tsx
30
45
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -114,9 +129,111 @@ function BacanoFormFieldControl(props) {
114
129
  );
115
130
  }
116
131
 
117
- // libs/sdk/src/react/hooks.ts
132
+ // libs/sdk/src/react/form-sections.tsx
133
+ var import_jsx_runtime2 = require("react/jsx-runtime");
134
+ function FormFields(props) {
135
+ return props.fields.map((field) => {
136
+ const controlId = `${props.idPrefix}-${field.key}`;
137
+ const value = props.values[field.key] ?? (field.type === "checkbox" ? false : field.type === "multi_select" ? [] : "");
138
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: props.classNames?.field, children: [
139
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
140
+ "label",
141
+ {
142
+ id: `${controlId}-label`,
143
+ htmlFor: field.type === "radio" ? void 0 : controlId,
144
+ className: props.classNames?.label,
145
+ children: field.label
146
+ }
147
+ ),
148
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
149
+ BacanoFormFieldControl,
150
+ {
151
+ field,
152
+ id: controlId,
153
+ value,
154
+ className: props.classNames?.input,
155
+ onChange: (nextValue) => props.onChange(field.key, nextValue)
156
+ }
157
+ ),
158
+ field.helpText ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: props.classNames?.helpText, children: field.helpText }) : null
159
+ ] }, field.key);
160
+ });
161
+ }
162
+ function FormSections(props) {
163
+ return props.sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: props.classNames?.section, children: [
164
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: props.classNames?.sectionTitle, children: section.title }),
165
+ section.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: props.classNames?.sectionDescription, children: section.description }) : null,
166
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
167
+ FormFields,
168
+ {
169
+ fields: section.fields,
170
+ idPrefix: props.idPrefix,
171
+ values: props.values,
172
+ classNames: props.classNames,
173
+ onChange: props.onChange
174
+ }
175
+ )
176
+ ] }, section.key));
177
+ }
178
+
179
+ // libs/sdk/src/react/form-tracking.ts
118
180
  var import_react2 = require("react");
119
181
 
182
+ // libs/sdk/src/marketing/interactions.ts
183
+ function newMarketingEventId() {
184
+ return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `evt_${Date.now()}_${Math.random().toString(16).slice(2)}`;
185
+ }
186
+
187
+ // libs/sdk/src/marketing/form-interactions.ts
188
+ function createFormInteractionTracker(getRuntime, formKey) {
189
+ let visible = false;
190
+ let started = false;
191
+ let attempt = null;
192
+ const completedSteps = /* @__PURE__ */ new Set();
193
+ function emit(eventName, context = {}) {
194
+ try {
195
+ getRuntime().track({ eventName, formKey, ...context });
196
+ } catch {
197
+ }
198
+ }
199
+ return {
200
+ setVisible(next) {
201
+ if (next && !visible && getRuntime().enabled) emit("form_open");
202
+ visible = next;
203
+ },
204
+ start() {
205
+ if (started || !getRuntime().enabled) return;
206
+ started = true;
207
+ emit("form_start");
208
+ },
209
+ stepComplete(stepKey) {
210
+ if (completedSteps.has(stepKey) || !getRuntime().enabled) return;
211
+ completedSteps.add(stepKey);
212
+ emit("form_step_complete", { stepKey });
213
+ },
214
+ beginSubmit() {
215
+ if (attempt) return false;
216
+ attempt = newMarketingEventId();
217
+ emit("form_submit_attempt", { eventId: attempt });
218
+ return true;
219
+ },
220
+ fail(errorCategory) {
221
+ if (!attempt) return;
222
+ emit("form_submit_error", { eventId: attempt, errorCategory });
223
+ attempt = null;
224
+ },
225
+ complete(result) {
226
+ if (!attempt) return;
227
+ if (result?.accepted && typeof result.responseId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
228
+ result.responseId
229
+ )) {
230
+ emit("form_submit_success", { eventId: result.responseId });
231
+ }
232
+ attempt = null;
233
+ }
234
+ };
235
+ }
236
+
120
237
  // libs/sdk/src/react/context-registry.ts
121
238
  var import_react = require("react");
122
239
  var REACT_CONTEXT_REGISTRY = /* @__PURE__ */ Symbol.for(
@@ -143,6 +260,37 @@ function getOrCreateReactContext(key, defaultValue) {
143
260
  return context;
144
261
  }
145
262
 
263
+ // libs/sdk/src/react/marketing-context.ts
264
+ var NOOP_MARKETING_RUNTIME = {
265
+ enabled: false,
266
+ track: () => void 0,
267
+ captureAttribution: () => null,
268
+ getAttribution: () => null,
269
+ destroy: () => void 0
270
+ };
271
+ var BacanoMarketingContext = getOrCreateReactContext(
272
+ "bacano-marketing-v1",
273
+ NOOP_MARKETING_RUNTIME
274
+ );
275
+
276
+ // libs/sdk/src/react/form-tracking.ts
277
+ function useBacanoFormTracking(formKey, options = {}) {
278
+ const runtime = (0, import_react2.useContext)(BacanoMarketingContext);
279
+ const current = (0, import_react2.useRef)(runtime);
280
+ current.current = runtime;
281
+ const tracker = (0, import_react2.useMemo)(
282
+ () => createFormInteractionTracker(() => current.current, formKey),
283
+ [formKey]
284
+ );
285
+ (0, import_react2.useEffect)(() => {
286
+ tracker.setVisible((options.visible ?? true) && runtime.enabled);
287
+ }, [tracker, options.visible, runtime.enabled]);
288
+ return tracker;
289
+ }
290
+
291
+ // libs/sdk/src/react/hooks.ts
292
+ var import_react3 = require("react");
293
+
146
294
  // libs/sdk/src/react/context.ts
147
295
  var BacanoReactContext = getOrCreateReactContext("bacano-client-v1", {
148
296
  client: null,
@@ -152,11 +300,11 @@ var BacanoReactContext = getOrCreateReactContext("bacano-client-v1", {
152
300
 
153
301
  // libs/sdk/src/react/hooks.ts
154
302
  function useBacanoState() {
155
- return (0, import_react2.useContext)(BacanoReactContext);
303
+ return (0, import_react3.useContext)(BacanoReactContext);
156
304
  }
157
305
 
158
306
  // libs/sdk/src/react/forms.tsx
159
- var import_jsx_runtime2 = require("react/jsx-runtime");
307
+ var import_jsx_runtime3 = require("react/jsx-runtime");
160
308
  var EMPTY_FORM_FIELDS = [];
161
309
  var EMPTY_FORM_SECTIONS = [];
162
310
  var EMPTY_FORM_STEPS = [];
@@ -168,31 +316,35 @@ function useBacanoForm(formKey, options) {
168
316
  } = useBacanoState();
169
317
  const enabled = options?.enabled ?? true;
170
318
  const initialForm = options?.initialForm?.key === formKey ? options.initialForm : null;
171
- const [form, setForm] = (0, import_react3.useState)(initialForm);
172
- const formRef = (0, import_react3.useRef)(initialForm);
173
- const [loading, setLoading] = (0, import_react3.useState)(enabled && !initialForm);
174
- const [error, setError] = (0, import_react3.useState)(null);
175
- const load = (0, import_react3.useCallback)(
319
+ const [form, setForm] = (0, import_react4.useState)(initialForm);
320
+ const formRef = (0, import_react4.useRef)(initialForm);
321
+ const requestVersion = (0, import_react4.useRef)(0);
322
+ const [loading, setLoading] = (0, import_react4.useState)(enabled && !initialForm);
323
+ const [error, setError] = (0, import_react4.useState)(null);
324
+ const load = (0, import_react4.useCallback)(
176
325
  async (force = false) => {
177
326
  if (!enabled || !client) return;
327
+ const version = ++requestVersion.current;
178
328
  setLoading(formRef.current === null);
179
329
  setError(null);
180
330
  try {
181
331
  const nextForm = await client.forms.getDefinition(formKey, { force });
332
+ if (version !== requestVersion.current) return;
182
333
  formRef.current = nextForm;
183
334
  setForm(nextForm);
184
335
  } catch (loadError) {
336
+ if (version !== requestVersion.current) return;
185
337
  if (!formRef.current) setForm(null);
186
338
  setError(
187
339
  loadError instanceof Error ? loadError : new Error(String(loadError))
188
340
  );
189
341
  } finally {
190
- setLoading(false);
342
+ if (version === requestVersion.current) setLoading(false);
191
343
  }
192
344
  },
193
345
  [client, enabled, formKey]
194
346
  );
195
- (0, import_react3.useEffect)(() => {
347
+ (0, import_react4.useEffect)(() => {
196
348
  if (!enabled) {
197
349
  formRef.current = null;
198
350
  setForm(null);
@@ -201,76 +353,38 @@ function useBacanoForm(formKey, options) {
201
353
  return;
202
354
  }
203
355
  if (client) void load();
356
+ return () => {
357
+ requestVersion.current += 1;
358
+ };
204
359
  }, [client, enabled, load]);
205
360
  return {
206
- form,
207
- loading: enabled && form === null && (clientLoading || loading),
361
+ form: enabled ? form?.key === formKey ? form : initialForm : null,
362
+ loading: enabled && form?.key !== formKey && !initialForm && (clientLoading || loading),
208
363
  error: clientError ?? error,
209
364
  refresh: async () => load(true)
210
365
  };
211
366
  }
212
- function FormFields(props) {
213
- return props.fields.map((field) => {
214
- const controlId = `${props.idPrefix}-${field.key}`;
215
- const value = props.values[field.key] ?? (field.type === "checkbox" ? false : field.type === "multi_select" ? [] : "");
216
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: props.classNames?.field, children: [
217
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
218
- "label",
219
- {
220
- id: `${controlId}-label`,
221
- htmlFor: field.type === "radio" ? void 0 : controlId,
222
- className: props.classNames?.label,
223
- children: field.label
224
- }
225
- ),
226
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
227
- BacanoFormFieldControl,
228
- {
229
- field,
230
- id: controlId,
231
- value,
232
- className: props.classNames?.input,
233
- onChange: (nextValue) => props.onChange(field.key, nextValue)
234
- }
235
- ),
236
- field.helpText ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: props.classNames?.helpText, children: field.helpText }) : null
237
- ] }, field.key);
238
- });
239
- }
240
- function FormSections(props) {
241
- return props.sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: props.classNames?.section, children: [
242
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: props.classNames?.sectionTitle, children: section.title }),
243
- section.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: props.classNames?.sectionDescription, children: section.description }) : null,
244
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
245
- FormFields,
246
- {
247
- fields: section.fields,
248
- idPrefix: props.idPrefix,
249
- values: props.values,
250
- classNames: props.classNames,
251
- onChange: props.onChange
252
- }
253
- )
254
- ] }, section.key));
255
- }
256
- function BacanoForm(props) {
257
- const reactId = (0, import_react3.useId)();
367
+ function BacanoFormInstance(props) {
368
+ const reactId = (0, import_react4.useId)();
258
369
  const idPrefix = `${props.formKey}-${reactId.replace(/:/g, "")}`;
259
370
  const { client } = useBacanoState();
260
371
  const { form, loading, error } = useBacanoForm(props.formKey, {
261
372
  initialForm: props.initialForm
262
373
  });
263
- const [values, setValues] = (0, import_react3.useState)({});
264
- const [step, setStep] = (0, import_react3.useState)(0);
265
- const [submitting, setSubmitting] = (0, import_react3.useState)(false);
266
- const [submitted, setSubmitted] = (0, import_react3.useState)(false);
267
- const [submissionError, setSubmissionError] = (0, import_react3.useState)(null);
268
- const [honeypot, setHoneypot] = (0, import_react3.useState)("");
269
- const idempotencyKey = (0, import_react3.useRef)(crypto.randomUUID());
374
+ const [internalValues, setValues] = (0, import_react4.useState)({});
375
+ const [step, setStep] = (0, import_react4.useState)(0);
376
+ const [submitting, setSubmitting] = (0, import_react4.useState)(false);
377
+ const [submitted, setSubmitted] = (0, import_react4.useState)(false);
378
+ const [submissionError, setSubmissionError] = (0, import_react4.useState)(null);
379
+ const [honeypot, setHoneypot] = (0, import_react4.useState)("");
380
+ const tracking = useBacanoFormTracking(props.formKey, {
381
+ visible: (props.visible ?? true) && !loading && form?.status === "ACTIVE" && !submitted
382
+ });
383
+ const idempotencyKey = (0, import_react4.useRef)(crypto.randomUUID());
270
384
  const fields = form?.definition.fields ?? EMPTY_FORM_FIELDS;
271
385
  const sections = form?.definition.sections ?? EMPTY_FORM_SECTIONS;
272
386
  const steps = form?.definition.steps ?? EMPTY_FORM_STEPS;
273
- const fieldKeys = (0, import_react3.useMemo)(
387
+ const fieldKeys = (0, import_react4.useMemo)(
274
388
  () => new Set(
275
389
  [
276
390
  ...fields,
@@ -283,7 +397,12 @@ function BacanoForm(props) {
283
397
  ),
284
398
  [fields, sections, steps]
285
399
  );
286
- (0, import_react3.useEffect)(() => {
400
+ const values = Object.fromEntries(
401
+ Object.entries(props.values ?? internalValues).filter(
402
+ ([key]) => fieldKeys.has(key)
403
+ )
404
+ );
405
+ (0, import_react4.useEffect)(() => {
287
406
  setStep(0);
288
407
  setValues(
289
408
  (current) => Object.fromEntries(
@@ -295,23 +414,37 @@ function BacanoForm(props) {
295
414
  setHoneypot("");
296
415
  idempotencyKey.current = crypto.randomUUID();
297
416
  }, [fieldKeys, form?.version]);
298
- if (loading) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: props.loadingFallback ?? null });
417
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: props.loadingFallback ?? null });
299
418
  if (error && !form)
300
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: props.errorFallback?.(error) ?? props.unavailableFallback ?? null });
301
- if (!form) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: props.unavailableFallback ?? null });
419
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: props.errorFallback?.(error) ?? props.unavailableFallback ?? null });
420
+ if (!form) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: props.unavailableFallback ?? null });
302
421
  if (form.status === "PAUSED") {
303
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: props.classNames?.message, "data-bacano-form-unavailable": true, children: props.unavailableFallback ?? form.definition.unavailableMessage });
422
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: props.classNames?.message, "data-bacano-form-unavailable": true, children: props.unavailableFallback ?? form.definition.unavailableMessage });
304
423
  }
305
424
  if (submitted) {
306
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: props.classNames?.message, "data-bacano-form-success": true, children: form.definition.successMessage });
425
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: props.classNames?.message, "data-bacano-form-success": true, children: form.definition.successMessage });
307
426
  }
308
427
  const isMultiStep = steps.length > 0;
309
428
  const activeStep = isMultiStep ? steps[step] : null;
310
429
  const isLastStep = !isMultiStep || step === steps.length - 1;
311
- const setFieldValue = (key, value) => setValues((current) => ({ ...current, [key]: value }));
430
+ const setFieldValue = (key, value) => {
431
+ if (value !== "" && value !== false && (!Array.isArray(value) || value.length > 0))
432
+ tracking.start();
433
+ const nextValues = { ...values, [key]: value };
434
+ setValues(nextValues);
435
+ props.onValuesChange?.(nextValues);
436
+ };
312
437
  const submit = async (event) => {
313
438
  event.preventDefault();
439
+ if (submitting || submitted) return;
440
+ if (isLastStep && !tracking.beginSubmit()) return;
441
+ if (!event.currentTarget.checkValidity()) {
442
+ if (isLastStep) tracking.fail("validation");
443
+ event.currentTarget.reportValidity();
444
+ return;
445
+ }
314
446
  if (!isLastStep) {
447
+ if (activeStep) tracking.stepComplete(activeStep.key);
315
448
  setStep((current) => current + 1);
316
449
  return;
317
450
  }
@@ -319,8 +452,12 @@ function BacanoForm(props) {
319
452
  setSubmissionError(null);
320
453
  try {
321
454
  let responseId = null;
455
+ let officialResult;
322
456
  if (props.onSubmit) {
323
- await props.onSubmit(values, form);
457
+ officialResult = await props.onSubmit(values, form);
458
+ if (officialResult && !officialResult.accepted)
459
+ throw new Error(officialResult.message);
460
+ responseId = officialResult?.responseId ?? null;
324
461
  } else {
325
462
  if (!client) throw new Error("Bacano client is not ready");
326
463
  const result = await client.forms.submitResponse({
@@ -332,11 +469,16 @@ function BacanoForm(props) {
332
469
  honeypot
333
470
  });
334
471
  if (!result.accepted) throw new Error(result.message);
472
+ officialResult = result;
335
473
  responseId = result.responseId;
336
474
  }
475
+ tracking.complete(officialResult);
337
476
  setSubmitted(true);
338
477
  props.onSubmitted?.(responseId);
339
478
  } catch (submitError) {
479
+ tracking.fail(
480
+ submitError instanceof WebsiteError && submitError.code === "NETWORK_ERROR" ? "network" : "rejected"
481
+ );
340
482
  setSubmissionError(
341
483
  submitError instanceof Error ? submitError : new Error(String(submitError))
342
484
  );
@@ -344,15 +486,16 @@ function BacanoForm(props) {
344
486
  setSubmitting(false);
345
487
  }
346
488
  };
347
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
489
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
348
490
  "form",
349
491
  {
492
+ noValidate: true,
350
493
  className: props.classNames?.root,
351
494
  "data-bacano-form": form.key,
352
495
  "data-bacano-form-version": form.version,
353
496
  onSubmit: (event) => void submit(event),
354
497
  children: [
355
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
498
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
356
499
  "div",
357
500
  {
358
501
  "aria-hidden": "true",
@@ -364,8 +507,8 @@ function BacanoForm(props) {
364
507
  clip: "rect(0 0 0 0)"
365
508
  },
366
509
  children: [
367
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: `${form.key}-company-website`, children: "Empresa" }),
368
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
510
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: `${form.key}-company-website`, children: "Empresa" }),
511
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
369
512
  "input",
370
513
  {
371
514
  id: `${form.key}-company-website`,
@@ -379,12 +522,12 @@ function BacanoForm(props) {
379
522
  ]
380
523
  }
381
524
  ),
382
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: props.classNames?.title, children: form.name }),
383
- form.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: props.classNames?.description, children: form.description }) : null,
384
- activeStep ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: props.classNames?.step, "data-bacano-form-step": true, children: [
385
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: props.classNames?.stepTitle, children: activeStep.title }),
386
- activeStep.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: props.classNames?.stepDescription, children: activeStep.description }) : null,
387
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
525
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: props.classNames?.title, children: form.name }),
526
+ form.description ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: props.classNames?.description, children: form.description }) : null,
527
+ activeStep ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: props.classNames?.step, "data-bacano-form-step": true, children: [
528
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: props.classNames?.stepTitle, children: activeStep.title }),
529
+ activeStep.description ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: props.classNames?.stepDescription, children: activeStep.description }) : null,
530
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
388
531
  FormFields,
389
532
  {
390
533
  fields: activeStep.fields,
@@ -394,7 +537,7 @@ function BacanoForm(props) {
394
537
  onChange: setFieldValue
395
538
  }
396
539
  ),
397
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
540
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
398
541
  FormSections,
399
542
  {
400
543
  sections: activeStep.sections,
@@ -404,8 +547,8 @@ function BacanoForm(props) {
404
547
  onChange: setFieldValue
405
548
  }
406
549
  )
407
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
408
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
550
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
551
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
409
552
  FormFields,
410
553
  {
411
554
  fields,
@@ -415,7 +558,7 @@ function BacanoForm(props) {
415
558
  onChange: setFieldValue
416
559
  }
417
560
  ),
418
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
561
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
419
562
  FormSections,
420
563
  {
421
564
  sections,
@@ -426,8 +569,8 @@ function BacanoForm(props) {
426
569
  }
427
570
  )
428
571
  ] }),
429
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: props.classNames?.actions, children: [
430
- isMultiStep && step > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
572
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: props.classNames?.actions, children: [
573
+ isMultiStep && step > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
431
574
  "button",
432
575
  {
433
576
  type: "button",
@@ -435,7 +578,7 @@ function BacanoForm(props) {
435
578
  children: "Anterior"
436
579
  }
437
580
  ) : null,
438
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
581
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
439
582
  "button",
440
583
  {
441
584
  type: "submit",
@@ -445,7 +588,7 @@ function BacanoForm(props) {
445
588
  }
446
589
  )
447
590
  ] }),
448
- submissionError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
591
+ submissionError ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
449
592
  "div",
450
593
  {
451
594
  role: "alert",
@@ -458,3 +601,6 @@ function BacanoForm(props) {
458
601
  }
459
602
  );
460
603
  }
604
+ function BacanoForm(props) {
605
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BacanoFormInstance, { ...props }, props.formKey);
606
+ }
package/react/forms.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
- import type { WebsitePublishedForm } from "../types";
2
+ import type { WebsiteFormResponseResult, WebsitePublishedForm } from "../types";
3
+ export { useBacanoFormTracking } from "./form-tracking";
3
4
  export type BacanoFormValues = Record<string, string | string[] | boolean>;
4
5
  export interface UseBacanoFormOptions {
5
6
  enabled?: boolean;
@@ -33,17 +34,24 @@ export interface BacanoFormClassNames {
33
34
  }
34
35
  export interface BacanoFormProps {
35
36
  formKey: string;
37
+ /** Pass the actual modal/drawer visibility when it stays mounted while closed. */
38
+ visible?: boolean;
39
+ /** Controlled values. Use together with onValuesChange; option values, not labels. */
40
+ values?: BacanoFormValues;
41
+ /** Receives the complete next values after a visitor edits a field. */
42
+ onValuesChange?(values: BacanoFormValues): void;
36
43
  /** Definition embedded by a server/build; it is revalidated in runtime. */
37
44
  initialForm?: WebsitePublishedForm | null;
38
45
  /**
39
46
  * Optional custom submission. When omitted, the official Bacano response
40
47
  * endpoint is used. Existing storefronts may keep a custom handler.
41
48
  */
42
- onSubmit?(values: BacanoFormValues, form: WebsitePublishedForm): void | Promise<void>;
49
+ onSubmit?(values: BacanoFormValues, form: WebsitePublishedForm): void | WebsiteFormResponseResult | Promise<void | WebsiteFormResponseResult>;
43
50
  onSubmitted?(responseId: string | null): void;
44
51
  classNames?: BacanoFormClassNames;
45
52
  loadingFallback?: ReactNode;
46
53
  unavailableFallback?: ReactNode;
47
54
  errorFallback?: (error: Error) => ReactNode;
48
55
  }
56
+ /** A different form cannot inherit pending submission state from the old key. */
49
57
  export declare function BacanoForm(props: BacanoFormProps): import("react").JSX.Element;