@wix/headless-bookings 0.0.23 → 0.0.24
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/cjs/dist/react/booking-form/BookingForm.d.ts +156 -0
- package/cjs/dist/react/booking-form/BookingForm.js +126 -0
- package/cjs/dist/react/core/booking-form/BookingForm.d.ts +139 -0
- package/cjs/dist/react/core/booking-form/BookingForm.js +181 -0
- package/cjs/dist/react/index.d.ts +1 -0
- package/cjs/dist/react/index.js +1 -0
- package/cjs/dist/services/booking-form/booking-form.d.ts +140 -0
- package/cjs/dist/services/booking-form/booking-form.js +171 -0
- package/cjs/dist/services/index.d.ts +1 -0
- package/cjs/dist/services/index.js +1 -0
- package/dist/react/booking-form/BookingForm.d.ts +156 -0
- package/dist/react/booking-form/BookingForm.js +126 -0
- package/dist/react/core/booking-form/BookingForm.d.ts +139 -0
- package/dist/react/core/booking-form/BookingForm.js +181 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/services/booking-form/booking-form.d.ts +140 -0
- package/dist/services/booking-form/booking-form.js +171 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BookingForm Service - Manages form submission state for booking flows
|
|
3
|
+
*
|
|
4
|
+
* This service supports two modes:
|
|
5
|
+
* 1. Client-side: Provide formId to load the form dynamically
|
|
6
|
+
* 2. SSR/SSG: Provide a pre-loaded form object for server-side rendering
|
|
7
|
+
*
|
|
8
|
+
* When using a pre-loaded form, it must belong to the bookings namespace.
|
|
9
|
+
*/
|
|
10
|
+
import { type Signal, type ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
|
|
11
|
+
import type { FormValues } from '@wix/form-public';
|
|
12
|
+
import { forms } from '@wix/forms';
|
|
13
|
+
/**
|
|
14
|
+
* The required namespace for booking forms.
|
|
15
|
+
* All booking forms must belong to this namespace.
|
|
16
|
+
*/
|
|
17
|
+
export declare const BOOKING_FORM_NAMESPACE = "wix.bookings.v2.bookings";
|
|
18
|
+
/**
|
|
19
|
+
* Validation result type for form validation.
|
|
20
|
+
* Fields with errors will be marked according to the validation failures.
|
|
21
|
+
*/
|
|
22
|
+
export interface ValidationResult {
|
|
23
|
+
/** Whether the form is valid */
|
|
24
|
+
valid: boolean;
|
|
25
|
+
/** Array of validation failure messages */
|
|
26
|
+
validationFailures: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Configuration for BookingFormService.
|
|
30
|
+
* Supports two modes:
|
|
31
|
+
* 1. Client-side loading: Provide formId to fetch the form dynamically
|
|
32
|
+
* 2. SSR/SSG: Provide a pre-loaded form object (must be from bookings namespace)
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* // Pattern 1: Client-side loading
|
|
37
|
+
* const config = { formId: 'form-123' };
|
|
38
|
+
*
|
|
39
|
+
* // Pattern 2: Pre-loaded form (SSR/SSG)
|
|
40
|
+
* const config = { form: preloadedForm };
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export type BookingFormServiceConfig = {
|
|
44
|
+
/** The form ID to load (client-side loading) */
|
|
45
|
+
formId: string;
|
|
46
|
+
} | {
|
|
47
|
+
/** Pre-loaded form object (SSR/SSG) - must be from bookings namespace */
|
|
48
|
+
form: forms.Form;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* API interface for the BookingForm service.
|
|
52
|
+
* Manages form submission state and validation for booking forms.
|
|
53
|
+
*
|
|
54
|
+
* Supports two modes:
|
|
55
|
+
* - Client-side: formId is provided via config, form is loaded dynamically
|
|
56
|
+
* - SSR/SSG: Pre-loaded form is provided via config
|
|
57
|
+
*/
|
|
58
|
+
export interface BookingFormServiceAPI {
|
|
59
|
+
/** Signal containing the current form values from FormService (live state of form fields) */
|
|
60
|
+
formValues: ReadOnlySignal<FormValues>;
|
|
61
|
+
/** Signal containing form submission data (set before actual submission) */
|
|
62
|
+
formSubmission: Signal<FormValues | null>;
|
|
63
|
+
/** Submitted form ID (won't be supported on first phase) */
|
|
64
|
+
formSubmissionId: ReadOnlySignal<string | null>;
|
|
65
|
+
/** Signal containing the form ID (from config or extracted from form) */
|
|
66
|
+
formId: ReadOnlySignal<string>;
|
|
67
|
+
/** Signal containing the pre-loaded form (if provided via SSR) */
|
|
68
|
+
form: ReadOnlySignal<forms.Form | null>;
|
|
69
|
+
/** Action to store form submission data */
|
|
70
|
+
setFormSubmission: (formValues: FormValues) => void;
|
|
71
|
+
/** Action to validate form - internally delegates to Form.Fields ref */
|
|
72
|
+
validateFormSubmission: () => Promise<ValidationResult>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Internal API interface that extends public API.
|
|
76
|
+
* Contains methods for internal use by core components.
|
|
77
|
+
*/
|
|
78
|
+
export interface BookingFormServiceInternalAPI extends BookingFormServiceAPI {
|
|
79
|
+
/** Internal: Register validate callback (called by core component) */
|
|
80
|
+
_setValidateCallback: (callback: () => Promise<ValidationResult>) => void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Type guard to check if config has formId
|
|
84
|
+
*/
|
|
85
|
+
export declare function hasFormId(config: BookingFormServiceConfig): config is {
|
|
86
|
+
formId: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Type guard to check if config has pre-loaded form
|
|
90
|
+
*/
|
|
91
|
+
export declare function hasForm(config: BookingFormServiceConfig): config is {
|
|
92
|
+
form: forms.Form;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Service definition for BookingForm.
|
|
96
|
+
* Defines the contract that the BookingFormService must implement.
|
|
97
|
+
*/
|
|
98
|
+
export declare const BookingFormServiceDefinition: string & {
|
|
99
|
+
__api: BookingFormServiceAPI;
|
|
100
|
+
__config: {};
|
|
101
|
+
isServiceDefinition?: boolean;
|
|
102
|
+
} & BookingFormServiceAPI;
|
|
103
|
+
/**
|
|
104
|
+
* Implementation of BookingFormService.
|
|
105
|
+
*
|
|
106
|
+
* This service manages form submission state for booking flows.
|
|
107
|
+
* Supports two modes:
|
|
108
|
+
* 1. Client-side: formId is provided, form is loaded dynamically
|
|
109
|
+
* 2. SSR/SSG: Pre-loaded form is provided (must be from bookings namespace)
|
|
110
|
+
*
|
|
111
|
+
* NOTE: The Form.Fields ref is managed internally - the core component passes
|
|
112
|
+
* a validate callback that the service stores and calls when validateFormSubmission() is invoked.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```tsx
|
|
116
|
+
* // Pattern 1: Client-side loading
|
|
117
|
+
* const bookingFormService = useService(
|
|
118
|
+
* BookingFormServiceDefinition.withConfig({ formId: 'form-123' })
|
|
119
|
+
* );
|
|
120
|
+
*
|
|
121
|
+
* // Pattern 2: SSR with pre-loaded form
|
|
122
|
+
* const bookingFormService = useService(
|
|
123
|
+
* BookingFormServiceDefinition.withConfig({ form: preloadedForm })
|
|
124
|
+
* );
|
|
125
|
+
*
|
|
126
|
+
* // Store form submission
|
|
127
|
+
* bookingFormService.setFormSubmission(formValues);
|
|
128
|
+
*
|
|
129
|
+
* // Validate form
|
|
130
|
+
* const result = await bookingFormService.validateFormSubmission();
|
|
131
|
+
* if (result.valid) {
|
|
132
|
+
* // Proceed with booking
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare const BookingFormService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
137
|
+
__api: BookingFormServiceAPI;
|
|
138
|
+
__config: {};
|
|
139
|
+
isServiceDefinition?: boolean;
|
|
140
|
+
} & BookingFormServiceAPI, BookingFormServiceConfig>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BookingForm Service - Manages form submission state for booking flows
|
|
3
|
+
*
|
|
4
|
+
* This service supports two modes:
|
|
5
|
+
* 1. Client-side: Provide formId to load the form dynamically
|
|
6
|
+
* 2. SSR/SSG: Provide a pre-loaded form object for server-side rendering
|
|
7
|
+
*
|
|
8
|
+
* When using a pre-loaded form, it must belong to the bookings namespace.
|
|
9
|
+
*/
|
|
10
|
+
import { defineService, implementService } from '@wix/services-definitions';
|
|
11
|
+
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
12
|
+
import { FormServiceDefinition } from '@wix/headless-forms/services';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* The required namespace for booking forms.
|
|
18
|
+
* All booking forms must belong to this namespace.
|
|
19
|
+
*/
|
|
20
|
+
export const BOOKING_FORM_NAMESPACE = 'wix.bookings.v2.bookings';
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Helper Types
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to check if config has formId
|
|
26
|
+
*/
|
|
27
|
+
export function hasFormId(config) {
|
|
28
|
+
return 'formId' in config;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Type guard to check if config has pre-loaded form
|
|
32
|
+
*/
|
|
33
|
+
export function hasForm(config) {
|
|
34
|
+
return 'form' in config;
|
|
35
|
+
}
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Service Definition
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Service definition for BookingForm.
|
|
41
|
+
* Defines the contract that the BookingFormService must implement.
|
|
42
|
+
*/
|
|
43
|
+
export const BookingFormServiceDefinition = defineService('bookingForm');
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Service Implementation
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Validation Helpers
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Validates that a form belongs to the bookings namespace.
|
|
52
|
+
* @param form The form to validate
|
|
53
|
+
* @throws Error if the form is not from the bookings namespace
|
|
54
|
+
*/
|
|
55
|
+
function validateBookingFormNamespace(form) {
|
|
56
|
+
// Check if form has namespace property and validate it
|
|
57
|
+
// The namespace might be in different locations depending on the form structure
|
|
58
|
+
const formNamespace = form.namespace ||
|
|
59
|
+
form._namespace;
|
|
60
|
+
if (formNamespace && formNamespace !== BOOKING_FORM_NAMESPACE) {
|
|
61
|
+
throw new Error(`BookingFormService: Invalid form namespace. ` +
|
|
62
|
+
`Expected "${BOOKING_FORM_NAMESPACE}", but received "${formNamespace}". ` +
|
|
63
|
+
`Ensure the form is a booking form.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extracts the form ID from either config type.
|
|
68
|
+
* @param config The service config
|
|
69
|
+
* @returns The form ID
|
|
70
|
+
*/
|
|
71
|
+
function extractFormId(config) {
|
|
72
|
+
if ('formId' in config) {
|
|
73
|
+
return config.formId;
|
|
74
|
+
}
|
|
75
|
+
// Extract from form object - forms typically have an 'id' or '_id' property
|
|
76
|
+
const form = config.form;
|
|
77
|
+
const formId = form.id ||
|
|
78
|
+
form._id;
|
|
79
|
+
if (!formId) {
|
|
80
|
+
throw new Error('BookingFormService: Could not extract form ID from provided form object.');
|
|
81
|
+
}
|
|
82
|
+
return formId;
|
|
83
|
+
}
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Service Implementation
|
|
86
|
+
// ============================================================================
|
|
87
|
+
/**
|
|
88
|
+
* Implementation of BookingFormService.
|
|
89
|
+
*
|
|
90
|
+
* This service manages form submission state for booking flows.
|
|
91
|
+
* Supports two modes:
|
|
92
|
+
* 1. Client-side: formId is provided, form is loaded dynamically
|
|
93
|
+
* 2. SSR/SSG: Pre-loaded form is provided (must be from bookings namespace)
|
|
94
|
+
*
|
|
95
|
+
* NOTE: The Form.Fields ref is managed internally - the core component passes
|
|
96
|
+
* a validate callback that the service stores and calls when validateFormSubmission() is invoked.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* // Pattern 1: Client-side loading
|
|
101
|
+
* const bookingFormService = useService(
|
|
102
|
+
* BookingFormServiceDefinition.withConfig({ formId: 'form-123' })
|
|
103
|
+
* );
|
|
104
|
+
*
|
|
105
|
+
* // Pattern 2: SSR with pre-loaded form
|
|
106
|
+
* const bookingFormService = useService(
|
|
107
|
+
* BookingFormServiceDefinition.withConfig({ form: preloadedForm })
|
|
108
|
+
* );
|
|
109
|
+
*
|
|
110
|
+
* // Store form submission
|
|
111
|
+
* bookingFormService.setFormSubmission(formValues);
|
|
112
|
+
*
|
|
113
|
+
* // Validate form
|
|
114
|
+
* const result = await bookingFormService.validateFormSubmission();
|
|
115
|
+
* if (result.valid) {
|
|
116
|
+
* // Proceed with booking
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export const BookingFormService = implementService.withConfig()(BookingFormServiceDefinition, ({ getService, config }) => {
|
|
121
|
+
const signalsService = getService(SignalsServiceDefinition);
|
|
122
|
+
const formService = getService(FormServiceDefinition);
|
|
123
|
+
// Determine if we have a pre-loaded form
|
|
124
|
+
const hasPreloadedForm = 'form' in config;
|
|
125
|
+
// If form is provided, validate namespace
|
|
126
|
+
if (hasPreloadedForm) {
|
|
127
|
+
validateBookingFormNamespace(config.form);
|
|
128
|
+
}
|
|
129
|
+
// Extract formId from config (either directly or from form object)
|
|
130
|
+
const formIdValue = extractFormId(config);
|
|
131
|
+
// Initialize formSubmission signal (stores form data to be submitted)
|
|
132
|
+
const formSubmission = signalsService.signal(null);
|
|
133
|
+
// Initialize submissionId signal (for future use)
|
|
134
|
+
const formSubmissionId = signalsService.signal(null);
|
|
135
|
+
// formId signal - readonly
|
|
136
|
+
const formId = signalsService.signal(formIdValue);
|
|
137
|
+
// form signal - contains pre-loaded form if provided
|
|
138
|
+
const form = signalsService.signal(hasPreloadedForm ? config.form : null);
|
|
139
|
+
// Internal: Store validate callback (set by core component, not exposed publicly)
|
|
140
|
+
let _validateCallback = null;
|
|
141
|
+
// Internal: Register validate callback (called by core component via closure)
|
|
142
|
+
const _setValidateCallback = (callback) => {
|
|
143
|
+
_validateCallback = callback;
|
|
144
|
+
};
|
|
145
|
+
// Action to set form submission data
|
|
146
|
+
const setFormSubmission = (formValues) => {
|
|
147
|
+
formSubmission.set(formValues);
|
|
148
|
+
};
|
|
149
|
+
// Action to validate form - delegates to internally registered callback
|
|
150
|
+
const validateFormSubmission = async () => {
|
|
151
|
+
if (!_validateCallback) {
|
|
152
|
+
console.error('BookingFormService: Validate callback not registered. ' +
|
|
153
|
+
'Ensure BookingForm component is mounted.');
|
|
154
|
+
return { valid: false, validationFailures: ['Form not initialized'] };
|
|
155
|
+
}
|
|
156
|
+
return _validateCallback();
|
|
157
|
+
};
|
|
158
|
+
// Public API (what's exposed in BookingFormServiceAPI interface)
|
|
159
|
+
const publicApi = {
|
|
160
|
+
formValues: formService.formValuesSignal,
|
|
161
|
+
formSubmission,
|
|
162
|
+
formSubmissionId,
|
|
163
|
+
formId,
|
|
164
|
+
form,
|
|
165
|
+
setFormSubmission,
|
|
166
|
+
validateFormSubmission,
|
|
167
|
+
// Internal method for core component access
|
|
168
|
+
_setValidateCallback,
|
|
169
|
+
};
|
|
170
|
+
return publicApi;
|
|
171
|
+
});
|
|
@@ -6,3 +6,4 @@ export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type
|
|
|
6
6
|
export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type Location, type BookingServiceActions, type ServiceSelection, type ServiceSelectionTimeSlot, } from './booking/booking.js';
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type SlotSelection, type TimeSlot, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListFilters, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
9
|
+
export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, type BookingFormServiceInternalAPI, type ValidationResult, } from './booking-form/booking-form.js';
|
|
@@ -6,3 +6,4 @@ export { ServiceService, ServiceServiceDefinition, loadServiceServiceInitialData
|
|
|
6
6
|
export { BookingService, BookingServiceDefinition, } from './booking/booking.js';
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, loadServiceListServiceInitialData, } from './service-list/service-list.js';
|
|
9
|
+
export { BookingFormService, BookingFormServiceDefinition, } from './booking-form/booking-form.js';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BookingForm - High-level component for rendering booking forms
|
|
3
|
+
* Provides components for displaying and managing booking form fields
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { Form } from '@wix/headless-forms/react';
|
|
7
|
+
type FieldMap = Parameters<typeof Form.Fields>[0]['fieldMap'];
|
|
8
|
+
/**
|
|
9
|
+
* Props for BookingForm.Root component
|
|
10
|
+
*/
|
|
11
|
+
export interface RootProps {
|
|
12
|
+
/** The form ID to load (required - standalone mode) */
|
|
13
|
+
formId: string;
|
|
14
|
+
/** Optional additional metadata to pass to FormService */
|
|
15
|
+
additionalMetadata?: Record<string, string | string[]>;
|
|
16
|
+
/** Optional partial field map to override default booking field components */
|
|
17
|
+
fieldMap?: Partial<FieldMap>;
|
|
18
|
+
/** Child components - if not provided, Form.Fields is rendered with default field map */
|
|
19
|
+
children?: React.ReactNode;
|
|
20
|
+
/** Whether to render as a child component */
|
|
21
|
+
asChild?: boolean;
|
|
22
|
+
/** CSS classes to apply to the root element */
|
|
23
|
+
className?: string;
|
|
24
|
+
/** Row gap class name for Form.Fields when using default rendering */
|
|
25
|
+
rowGapClassname?: string;
|
|
26
|
+
/** Column gap class name for Form.Fields when using default rendering */
|
|
27
|
+
columnGapClassname?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Root component for booking form that wraps CoreBookingForm.Root.
|
|
31
|
+
* Automatically loads form and handles submission in standalone mode.
|
|
32
|
+
* Uses DEFAULT_BOOKING_FIELD_MAP for field rendering with optional user overrides.
|
|
33
|
+
*
|
|
34
|
+
* @order 1
|
|
35
|
+
* @component
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* import { BookingForm } from '@wix/headless-bookings/react';
|
|
39
|
+
*
|
|
40
|
+
* // Minimal usage - uses default field components
|
|
41
|
+
* <BookingForm.Root formId="your-form-id" />
|
|
42
|
+
*
|
|
43
|
+
* // With custom styling
|
|
44
|
+
* <BookingForm.Root
|
|
45
|
+
* formId="your-form-id"
|
|
46
|
+
* className="my-form-container"
|
|
47
|
+
* rowGapClassname="gap-y-6"
|
|
48
|
+
* columnGapClassname="gap-x-4"
|
|
49
|
+
* />
|
|
50
|
+
*
|
|
51
|
+
* // With custom field overrides
|
|
52
|
+
* <BookingForm.Root
|
|
53
|
+
* formId="your-form-id"
|
|
54
|
+
* fieldMap={{ TEXT_INPUT: MyCustomTextInput }}
|
|
55
|
+
* />
|
|
56
|
+
*
|
|
57
|
+
* // With full control over form structure
|
|
58
|
+
* <BookingForm.Root formId="your-form-id">
|
|
59
|
+
* <Form.Loading className="flex justify-center p-4" />
|
|
60
|
+
* <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
|
|
61
|
+
* <BookingForm.Fields />
|
|
62
|
+
* <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
|
|
63
|
+
* <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
|
|
64
|
+
* </BookingForm.Root>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare const Root: React.ForwardRefExoticComponent<RootProps & React.RefAttributes<HTMLDivElement>>;
|
|
68
|
+
/**
|
|
69
|
+
* Props for BookingForm.Fields component
|
|
70
|
+
*/
|
|
71
|
+
export interface FieldsProps {
|
|
72
|
+
/** Optional override for row gap class name */
|
|
73
|
+
rowGapClassname?: string;
|
|
74
|
+
/** Optional override for column gap class name */
|
|
75
|
+
columnGapClassname?: string;
|
|
76
|
+
/** CSS classes to apply */
|
|
77
|
+
className?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fields component that renders Form.Fields with the merged field map.
|
|
81
|
+
* Must be used within BookingForm.Root.
|
|
82
|
+
*
|
|
83
|
+
* @component
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* <BookingForm.Root formId="your-form-id">
|
|
87
|
+
* <Form.Loading className="p-4" />
|
|
88
|
+
* <BookingForm.Fields rowGapClassname="gap-y-6" />
|
|
89
|
+
* <Form.Error className="text-destructive" />
|
|
90
|
+
* </BookingForm.Root>
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare const Fields: React.ForwardRefExoticComponent<FieldsProps & React.RefAttributes<HTMLDivElement>>;
|
|
94
|
+
/**
|
|
95
|
+
* Render props for BookingForm.Data component
|
|
96
|
+
*/
|
|
97
|
+
export interface DataRenderProps {
|
|
98
|
+
/** The form ID being used */
|
|
99
|
+
formId: string;
|
|
100
|
+
/** Merged field map (DEFAULT_BOOKING_FIELD_MAP + user overrides) */
|
|
101
|
+
fields: FieldMap;
|
|
102
|
+
/** Current form values from FormService (live state of form fields) */
|
|
103
|
+
formValues: Record<string, unknown>;
|
|
104
|
+
/** Current form submission data */
|
|
105
|
+
formSubmission: Record<string, unknown> | null;
|
|
106
|
+
/** Action to store form submission data */
|
|
107
|
+
setFormSubmission: (formValues: Record<string, unknown>) => void;
|
|
108
|
+
/** Action to validate the form */
|
|
109
|
+
validateFormSubmission: () => Promise<{
|
|
110
|
+
valid: boolean;
|
|
111
|
+
validationFailures: string[];
|
|
112
|
+
}>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Props for BookingForm.Data component
|
|
116
|
+
*/
|
|
117
|
+
export interface DataProps {
|
|
118
|
+
/** The form ID to load (required - standalone mode) */
|
|
119
|
+
formId: string;
|
|
120
|
+
/** Optional additional metadata to pass to FormService */
|
|
121
|
+
additionalMetadata?: Record<string, string | string[]>;
|
|
122
|
+
/** Optional partial field map to override default booking field components */
|
|
123
|
+
fieldMap?: Partial<FieldMap>;
|
|
124
|
+
/** Render prop function that receives form data and actions */
|
|
125
|
+
children: (data: DataRenderProps) => React.ReactNode;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Data component that provides booking form data via render props.
|
|
129
|
+
* Use this when you need programmatic access to form state and actions.
|
|
130
|
+
*
|
|
131
|
+
* @component
|
|
132
|
+
* @example
|
|
133
|
+
* ```tsx
|
|
134
|
+
* <BookingForm.Data formId="your-form-id">
|
|
135
|
+
* {({ formId, fields, formValues, setFormSubmission, validateFormSubmission }) => (
|
|
136
|
+
* <div>
|
|
137
|
+
* <Form.Fields fieldMap={fields} rowGapClassname="gap-y-4" columnGapClassname="gap-x-2" />
|
|
138
|
+
* <button onClick={async () => {
|
|
139
|
+
* const result = await validateFormSubmission();
|
|
140
|
+
* if (result.valid) {
|
|
141
|
+
* // Store current form values before submission
|
|
142
|
+
* setFormSubmission(formValues);
|
|
143
|
+
* }
|
|
144
|
+
* }}>
|
|
145
|
+
* Validate & Submit
|
|
146
|
+
* </button>
|
|
147
|
+
* </div>
|
|
148
|
+
* )}
|
|
149
|
+
* </BookingForm.Data>
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export declare function Data(props: DataProps): React.ReactNode;
|
|
153
|
+
export declare namespace Data {
|
|
154
|
+
var displayName: string;
|
|
155
|
+
}
|
|
156
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* BookingForm - High-level component for rendering booking forms
|
|
4
|
+
* Provides components for displaying and managing booking form fields
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
8
|
+
import { Form } from '@wix/headless-forms/react';
|
|
9
|
+
import * as CoreBookingForm from '../core/booking-form/BookingForm.js';
|
|
10
|
+
var TestIds;
|
|
11
|
+
(function (TestIds) {
|
|
12
|
+
TestIds["bookingFormRoot"] = "booking-form-root";
|
|
13
|
+
TestIds["bookingFormFields"] = "booking-form-fields";
|
|
14
|
+
})(TestIds || (TestIds = {}));
|
|
15
|
+
/**
|
|
16
|
+
* Root component for booking form that wraps CoreBookingForm.Root.
|
|
17
|
+
* Automatically loads form and handles submission in standalone mode.
|
|
18
|
+
* Uses DEFAULT_BOOKING_FIELD_MAP for field rendering with optional user overrides.
|
|
19
|
+
*
|
|
20
|
+
* @order 1
|
|
21
|
+
* @component
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { BookingForm } from '@wix/headless-bookings/react';
|
|
25
|
+
*
|
|
26
|
+
* // Minimal usage - uses default field components
|
|
27
|
+
* <BookingForm.Root formId="your-form-id" />
|
|
28
|
+
*
|
|
29
|
+
* // With custom styling
|
|
30
|
+
* <BookingForm.Root
|
|
31
|
+
* formId="your-form-id"
|
|
32
|
+
* className="my-form-container"
|
|
33
|
+
* rowGapClassname="gap-y-6"
|
|
34
|
+
* columnGapClassname="gap-x-4"
|
|
35
|
+
* />
|
|
36
|
+
*
|
|
37
|
+
* // With custom field overrides
|
|
38
|
+
* <BookingForm.Root
|
|
39
|
+
* formId="your-form-id"
|
|
40
|
+
* fieldMap={{ TEXT_INPUT: MyCustomTextInput }}
|
|
41
|
+
* />
|
|
42
|
+
*
|
|
43
|
+
* // With full control over form structure
|
|
44
|
+
* <BookingForm.Root formId="your-form-id">
|
|
45
|
+
* <Form.Loading className="flex justify-center p-4" />
|
|
46
|
+
* <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
|
|
47
|
+
* <BookingForm.Fields />
|
|
48
|
+
* <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
|
|
49
|
+
* <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
|
|
50
|
+
* </BookingForm.Root>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export const Root = React.forwardRef((props, ref) => {
|
|
54
|
+
const { children, asChild, className, formId, additionalMetadata, fieldMap, rowGapClassname = 'gap-y-4', columnGapClassname = 'gap-x-2', ...otherProps } = props;
|
|
55
|
+
return (_jsx(CoreBookingForm.Root, { formId: formId, additionalMetadata: additionalMetadata, fieldMap: fieldMap, children: ({ fields }) => (_jsx(BookingFormContext.Provider, { value: { fields, rowGapClassname, columnGapClassname }, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.bookingFormRoot, ...otherProps, children: _jsx("div", { children: children ?? (_jsxs(_Fragment, { children: [_jsx(Form.Loading, { className: "flex justify-center p-4 text-foreground" }), _jsx(Form.LoadingError, { className: "bg-destructive/10 border border-destructive/20 text-destructive px-4 py-3 rounded-lg mb-4" }), _jsx(Form.Fields, { fieldMap: fields, rowGapClassname: rowGapClassname, columnGapClassname: columnGapClassname }), _jsx(Form.Error, { className: "mt-4 bg-destructive/10 border border-destructive/20 text-destructive px-4 py-3 rounded-lg" }), _jsx(Form.Submitted, { className: "mt-4 bg-green-500/10 border border-green-500/20 text-green-500 px-4 py-3 rounded-lg" })] })) }) }) })) }));
|
|
56
|
+
});
|
|
57
|
+
Root.displayName = 'BookingForm.Root';
|
|
58
|
+
const BookingFormContext = React.createContext(null);
|
|
59
|
+
/**
|
|
60
|
+
* Hook to access BookingForm context
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
function useBookingFormContext() {
|
|
64
|
+
const context = React.useContext(BookingFormContext);
|
|
65
|
+
if (!context) {
|
|
66
|
+
throw new Error('useBookingFormContext must be used within a BookingForm.Root component');
|
|
67
|
+
}
|
|
68
|
+
return context;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Fields component that renders Form.Fields with the merged field map.
|
|
72
|
+
* Must be used within BookingForm.Root.
|
|
73
|
+
*
|
|
74
|
+
* @component
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* <BookingForm.Root formId="your-form-id">
|
|
78
|
+
* <Form.Loading className="p-4" />
|
|
79
|
+
* <BookingForm.Fields rowGapClassname="gap-y-6" />
|
|
80
|
+
* <Form.Error className="text-destructive" />
|
|
81
|
+
* </BookingForm.Root>
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const Fields = React.forwardRef((props, ref) => {
|
|
85
|
+
const context = useBookingFormContext();
|
|
86
|
+
const { rowGapClassname = context.rowGapClassname, columnGapClassname = context.columnGapClassname, className, ...otherProps } = props;
|
|
87
|
+
return (_jsx("div", { ref: ref, className: className, "data-testid": TestIds.bookingFormFields, ...otherProps, children: _jsx(Form.Fields, { fieldMap: context.fields, rowGapClassname: rowGapClassname, columnGapClassname: columnGapClassname }) }));
|
|
88
|
+
});
|
|
89
|
+
Fields.displayName = 'BookingForm.Fields';
|
|
90
|
+
/**
|
|
91
|
+
* Data component that provides booking form data via render props.
|
|
92
|
+
* Use this when you need programmatic access to form state and actions.
|
|
93
|
+
*
|
|
94
|
+
* @component
|
|
95
|
+
* @example
|
|
96
|
+
* ```tsx
|
|
97
|
+
* <BookingForm.Data formId="your-form-id">
|
|
98
|
+
* {({ formId, fields, formValues, setFormSubmission, validateFormSubmission }) => (
|
|
99
|
+
* <div>
|
|
100
|
+
* <Form.Fields fieldMap={fields} rowGapClassname="gap-y-4" columnGapClassname="gap-x-2" />
|
|
101
|
+
* <button onClick={async () => {
|
|
102
|
+
* const result = await validateFormSubmission();
|
|
103
|
+
* if (result.valid) {
|
|
104
|
+
* // Store current form values before submission
|
|
105
|
+
* setFormSubmission(formValues);
|
|
106
|
+
* }
|
|
107
|
+
* }}>
|
|
108
|
+
* Validate & Submit
|
|
109
|
+
* </button>
|
|
110
|
+
* </div>
|
|
111
|
+
* )}
|
|
112
|
+
* </BookingForm.Data>
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export function Data(props) {
|
|
116
|
+
const { formId, additionalMetadata, fieldMap, children } = props;
|
|
117
|
+
return (_jsx(CoreBookingForm.Root, { formId: formId, additionalMetadata: additionalMetadata, fieldMap: fieldMap, children: ({ formId: id, fields, formValues, formSubmission, setFormSubmission, validateFormSubmission, }) => children({
|
|
118
|
+
formId: id,
|
|
119
|
+
fields,
|
|
120
|
+
formValues,
|
|
121
|
+
formSubmission,
|
|
122
|
+
setFormSubmission,
|
|
123
|
+
validateFormSubmission,
|
|
124
|
+
}) }));
|
|
125
|
+
}
|
|
126
|
+
Data.displayName = 'BookingForm.Data';
|