@bookinglab/booking-ui-react 1.0.2 → 1.0.3
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/dist/index.js +37 -35
- package/dist/index.js.map +1 -1
- package/dist/{index.cjs → index.mjs} +37 -39
- package/dist/index.mjs.map +1 -0
- package/package.json +6 -6
- package/dist/index.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
3
5
|
|
|
4
6
|
// src/components/BookingForm.tsx
|
|
5
7
|
var cx = (...classes) => classes.filter(Boolean).join(" ");
|
|
@@ -26,18 +28,18 @@ function FormField({
|
|
|
26
28
|
const checkboxLabelClasses = cx("text-sm", classNames?.label ?? "text-gray-700");
|
|
27
29
|
const renderLabel = () => {
|
|
28
30
|
if (question.detail_type === "heading") return null;
|
|
29
|
-
return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: labelClasses, children: [
|
|
31
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: inputId, className: labelClasses, children: [
|
|
30
32
|
question.name,
|
|
31
|
-
question.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
33
|
+
question.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
32
34
|
] });
|
|
33
35
|
};
|
|
34
36
|
const renderHelpText = () => {
|
|
35
37
|
if (!question.help_text) return null;
|
|
36
|
-
return /* @__PURE__ */ jsx("p", { className: classNames?.helpText ?? defaultHelpText, children: question.help_text });
|
|
38
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: classNames?.helpText ?? defaultHelpText, children: question.help_text });
|
|
37
39
|
};
|
|
38
40
|
const renderError = () => {
|
|
39
41
|
if (!error) return null;
|
|
40
|
-
return /* @__PURE__ */ jsx("p", { id: errorId, className: classNames?.errorText ?? defaultErrorText, role: "alert", children: error });
|
|
42
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: classNames?.errorText ?? defaultErrorText, role: "alert", children: error });
|
|
41
43
|
};
|
|
42
44
|
const ariaProps = {
|
|
43
45
|
"aria-invalid": hasError ? true : void 0,
|
|
@@ -46,11 +48,11 @@ function FormField({
|
|
|
46
48
|
};
|
|
47
49
|
switch (question.detail_type) {
|
|
48
50
|
case "heading":
|
|
49
|
-
return /* @__PURE__ */ jsx("h3", { className: classNames?.heading ?? defaultHeading, children: question.name });
|
|
51
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h3", { className: classNames?.heading ?? defaultHeading, children: question.name });
|
|
50
52
|
case "text_field":
|
|
51
|
-
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
52
54
|
renderLabel(),
|
|
53
|
-
/* @__PURE__ */ jsx(
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54
56
|
"input",
|
|
55
57
|
{
|
|
56
58
|
id: inputId,
|
|
@@ -66,9 +68,9 @@ function FormField({
|
|
|
66
68
|
renderError()
|
|
67
69
|
] });
|
|
68
70
|
case "text_area":
|
|
69
|
-
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
71
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
70
72
|
renderLabel(),
|
|
71
|
-
/* @__PURE__ */ jsx(
|
|
73
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
74
|
"textarea",
|
|
73
75
|
{
|
|
74
76
|
id: inputId,
|
|
@@ -84,9 +86,9 @@ function FormField({
|
|
|
84
86
|
renderError()
|
|
85
87
|
] });
|
|
86
88
|
case "select":
|
|
87
|
-
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
89
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
88
90
|
renderLabel(),
|
|
89
|
-
/* @__PURE__ */ jsxs(
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
90
92
|
"select",
|
|
91
93
|
{
|
|
92
94
|
id: inputId,
|
|
@@ -95,8 +97,8 @@ function FormField({
|
|
|
95
97
|
className: inputClasses,
|
|
96
98
|
...ariaProps,
|
|
97
99
|
children: [
|
|
98
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Select an option" }),
|
|
99
|
-
question.options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.id, children: option.name }, option.id))
|
|
100
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select an option" }),
|
|
101
|
+
question.options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.id, children: option.name }, option.id))
|
|
100
102
|
]
|
|
101
103
|
}
|
|
102
104
|
),
|
|
@@ -104,9 +106,9 @@ function FormField({
|
|
|
104
106
|
renderError()
|
|
105
107
|
] });
|
|
106
108
|
case "date":
|
|
107
|
-
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
109
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
108
110
|
renderLabel(),
|
|
109
|
-
/* @__PURE__ */ jsx(
|
|
111
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
110
112
|
"input",
|
|
111
113
|
{
|
|
112
114
|
id: inputId,
|
|
@@ -123,9 +125,9 @@ function FormField({
|
|
|
123
125
|
renderError()
|
|
124
126
|
] });
|
|
125
127
|
case "number":
|
|
126
|
-
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
128
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
127
129
|
renderLabel(),
|
|
128
|
-
/* @__PURE__ */ jsx(
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
129
131
|
"input",
|
|
130
132
|
{
|
|
131
133
|
id: inputId,
|
|
@@ -143,9 +145,9 @@ function FormField({
|
|
|
143
145
|
renderError()
|
|
144
146
|
] });
|
|
145
147
|
case "check":
|
|
146
|
-
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
147
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
148
|
-
/* @__PURE__ */ jsx(
|
|
148
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
149
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
149
151
|
"input",
|
|
150
152
|
{
|
|
151
153
|
id: inputId,
|
|
@@ -156,9 +158,9 @@ function FormField({
|
|
|
156
158
|
...ariaProps
|
|
157
159
|
}
|
|
158
160
|
),
|
|
159
|
-
/* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: checkboxLabelClasses, children: [
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: inputId, className: checkboxLabelClasses, children: [
|
|
160
162
|
question.name,
|
|
161
|
-
question.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
163
|
+
question.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
162
164
|
] })
|
|
163
165
|
] }),
|
|
164
166
|
renderHelpText(),
|
|
@@ -180,10 +182,10 @@ function BookingForm({
|
|
|
180
182
|
...classNamesProp,
|
|
181
183
|
label: classNamesProp?.label ?? labelClassName
|
|
182
184
|
};
|
|
183
|
-
const [values, setValues] = useState({});
|
|
184
|
-
const [errors, setErrors] = useState({});
|
|
185
|
-
const [touched, setTouched] = useState({});
|
|
186
|
-
const isQuestionVisible = useCallback(
|
|
185
|
+
const [values, setValues] = react.useState({});
|
|
186
|
+
const [errors, setErrors] = react.useState({});
|
|
187
|
+
const [touched, setTouched] = react.useState({});
|
|
188
|
+
const isQuestionVisible = react.useCallback(
|
|
187
189
|
(question) => {
|
|
188
190
|
const { settings } = question;
|
|
189
191
|
if (!settings?.conditional_answers) {
|
|
@@ -246,7 +248,7 @@ function BookingForm({
|
|
|
246
248
|
[values, questions]
|
|
247
249
|
);
|
|
248
250
|
const visibleQuestions = questions.filter(isQuestionVisible);
|
|
249
|
-
const validateField = useCallback((question, value) => {
|
|
251
|
+
const validateField = react.useCallback((question, value) => {
|
|
250
252
|
if (question.detail_type === "heading") return null;
|
|
251
253
|
if (question.required) {
|
|
252
254
|
if (value === void 0 || value === "" || value === null) {
|
|
@@ -267,7 +269,7 @@ function BookingForm({
|
|
|
267
269
|
}
|
|
268
270
|
return null;
|
|
269
271
|
}, []);
|
|
270
|
-
const validateAll = useCallback(() => {
|
|
272
|
+
const validateAll = react.useCallback(() => {
|
|
271
273
|
const newErrors = {};
|
|
272
274
|
let isValid = true;
|
|
273
275
|
visibleQuestions.forEach((question) => {
|
|
@@ -280,7 +282,7 @@ function BookingForm({
|
|
|
280
282
|
setErrors(newErrors);
|
|
281
283
|
return isValid;
|
|
282
284
|
}, [visibleQuestions, values, validateField]);
|
|
283
|
-
useEffect(() => {
|
|
285
|
+
react.useEffect(() => {
|
|
284
286
|
const visibleIds = new Set(visibleQuestions.map((q) => q.id));
|
|
285
287
|
setErrors((prev) => {
|
|
286
288
|
let changed = false;
|
|
@@ -330,8 +332,8 @@ function BookingForm({
|
|
|
330
332
|
}
|
|
331
333
|
};
|
|
332
334
|
const defaultButton = "w-full mt-4 px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors";
|
|
333
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, noValidate: true, children: [
|
|
334
|
-
visibleQuestions.map((question) => /* @__PURE__ */ jsx(
|
|
335
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className, noValidate: true, children: [
|
|
336
|
+
visibleQuestions.map((question) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
335
337
|
FormField,
|
|
336
338
|
{
|
|
337
339
|
question,
|
|
@@ -342,7 +344,7 @@ function BookingForm({
|
|
|
342
344
|
},
|
|
343
345
|
question.id
|
|
344
346
|
)),
|
|
345
|
-
/* @__PURE__ */ jsx(
|
|
347
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
346
348
|
"button",
|
|
347
349
|
{
|
|
348
350
|
type: "submit",
|
|
@@ -353,6 +355,6 @@ function BookingForm({
|
|
|
353
355
|
] });
|
|
354
356
|
}
|
|
355
357
|
|
|
356
|
-
|
|
358
|
+
exports.BookingForm = BookingForm;
|
|
357
359
|
//# sourceMappingURL=index.js.map
|
|
358
360
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/BookingForm.tsx"],"names":[],"mappings":";;;;AAIA,IAAM,EAAA,GAAK,IAAI,OAAA,KAAoC,OAAA,CAAQ,OAAO,OAAO,CAAA,CAAE,KAAK,GAAG,CAAA;AAKnF,SAAS,SAAA,CAAU;AAAA,EACjB,QAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAMG;AACD,EAAA,MAAM,OAAA,GAAU,CAAA,SAAA,EAAY,QAAA,CAAS,EAAE,CAAA,CAAA;AACvC,EAAA,MAAM,OAAA,GAAU,GAAG,OAAO,CAAA,MAAA,CAAA;AAC1B,EAAA,MAAM,QAAA,GAAW,CAAC,CAAC,KAAA;AAGnB,EAAA,MAAM,mBAAA,GAAsB,MAAA;AAC5B,EAAA,MAAM,YAAA,GAAe,8CAAA;AACrB,EAAA,MAAM,cAAA,GAAiB,0DAAA;AACvB,EAAA,MAAM,YAAA,GAAe,4KAAA;AACrB,EAAA,MAAM,iBAAA,GAAoB,mCAAA;AAC1B,EAAA,MAAM,eAAA,GAAkB,wEAAA;AACxB,EAAA,MAAM,eAAA,GAAkB,4BAAA;AACxB,EAAA,MAAM,gBAAA,GAAmB,2BAAA;AAEzB,EAAA,MAAM,YAAA,GAAe,QAAA,GACjB,EAAA,CAAG,UAAA,EAAY,KAAA,IAAS,YAAA,EAAc,UAAA,EAAY,UAAA,IAAc,iBAAiB,CAAA,GACjF,EAAA,CAAG,UAAA,EAAY,SAAS,YAAY,CAAA;AAExC,EAAA,MAAM,YAAA,GAAe,YAAY,KAAA,IAAS,YAAA;AAC1C,EAAA,MAAM,oBAAA,GAAuB,EAAA,CAAG,SAAA,EAAW,UAAA,EAAY,SAAS,eAAe,CAAA;AAE/E,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,QAAA,CAAS,WAAA,KAAgB,SAAA,EAAW,OAAO,IAAA;AAC/C,IAAA,uBACE,IAAA,CAAC,OAAA,EAAA,EAAM,OAAA,EAAS,OAAA,EAAS,WAAW,YAAA,EACjC,QAAA,EAAA;AAAA,MAAA,QAAA,CAAS,IAAA;AAAA,MACT,QAAA,CAAS,4BAAY,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,mBAAA,EAAoB,aAAA,EAAY,QAAO,QAAA,EAAA,GAAA,EAAC;AAAA,KAAA,EAChF,CAAA;AAAA,EAEJ,CAAA;AAEA,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,IAAI,CAAC,QAAA,CAAS,SAAA,EAAW,OAAO,IAAA;AAChC,IAAA,2BAAQ,GAAA,EAAA,EAAE,SAAA,EAAW,YAAY,QAAA,IAAY,eAAA,EAAkB,mBAAS,SAAA,EAAU,CAAA;AAAA,EACpF,CAAA;AAEA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,uBACE,GAAA,CAAC,GAAA,EAAA,EAAE,EAAA,EAAI,OAAA,EAAS,SAAA,EAAW,YAAY,SAAA,IAAa,gBAAA,EAAkB,IAAA,EAAK,OAAA,EACxE,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,EAEJ,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY;AAAA,IAChB,cAAA,EAAgB,WAAW,IAAA,GAAO,MAAA;AAAA,IAClC,kBAAA,EAAoB,WAAW,OAAA,GAAU,MAAA;AAAA,IACzC,eAAA,EAAiB,SAAS,QAAA,IAAY;AAAA,GACxC;AAEA,EAAA,QAAQ,SAAS,WAAA;AAAa,IAC5B,KAAK,SAAA;AACH,MAAA,2BACG,IAAA,EAAA,EAAG,SAAA,EAAW,YAAY,OAAA,IAAW,cAAA,EACnC,mBAAS,IAAA,EACZ,CAAA;AAAA,IAGJ,KAAK,YAAA;AACH,MAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,UAAA,EAAY,gBAAgB,mBAAA,EACzC,QAAA,EAAA;AAAA,QAAA,WAAA,EAAY;AAAA,wBACb,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI,OAAA;AAAA,YACJ,IAAA,EAAK,MAAA;AAAA,YACL,OAAQ,KAAA,IAAoB,EAAA;AAAA,YAC5B,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACxC,WAAA,EAAa,SAAS,QAAA,EAAU,WAAA;AAAA,YAChC,SAAA,EAAW,YAAA;AAAA,YACV,GAAG;AAAA;AAAA,SACN;AAAA,QACC,cAAA,EAAe;AAAA,QACf,WAAA;AAAY,OAAA,EACf,CAAA;AAAA,IAGJ,KAAK,WAAA;AACH,MAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,UAAA,EAAY,gBAAgB,mBAAA,EACzC,QAAA,EAAA;AAAA,QAAA,WAAA,EAAY;AAAA,wBACb,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI,OAAA;AAAA,YACJ,OAAQ,KAAA,IAAoB,EAAA;AAAA,YAC5B,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACxC,WAAA,EAAa,SAAS,QAAA,EAAU,WAAA;AAAA,YAChC,IAAA,EAAM,CAAA;AAAA,YACN,SAAA,EAAW,YAAA;AAAA,YACV,GAAG;AAAA;AAAA,SACN;AAAA,QACC,cAAA,EAAe;AAAA,QACf,WAAA;AAAY,OAAA,EACf,CAAA;AAAA,IAGJ,KAAK,QAAA;AACH,MAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,UAAA,EAAY,gBAAgB,mBAAA,EACzC,QAAA,EAAA;AAAA,QAAA,WAAA,EAAY;AAAA,wBACb,IAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI,OAAA;AAAA,YACJ,OAAQ,KAAA,IAA6B,EAAA;AAAA,YACrC,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACxC,SAAA,EAAW,YAAA;AAAA,YACV,GAAG,SAAA;AAAA,YAEJ,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,EAAA,EAAG,QAAA,EAAA,kBAAA,EAAgB,CAAA;AAAA,cAChC,QAAA,CAAS,OAAA,EAAS,GAAA,CAAI,CAAC,2BACtB,GAAA,CAAC,QAAA,EAAA,EAAuB,KAAA,EAAO,MAAA,CAAO,EAAA,EACnC,QAAA,EAAA,MAAA,CAAO,IAAA,EAAA,EADG,MAAA,CAAO,EAEpB,CACD;AAAA;AAAA;AAAA,SACH;AAAA,QACC,cAAA,EAAe;AAAA,QACf,WAAA;AAAY,OAAA,EACf,CAAA;AAAA,IAGJ,KAAK,MAAA;AACH,MAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,UAAA,EAAY,gBAAgB,mBAAA,EACzC,QAAA,EAAA;AAAA,QAAA,WAAA,EAAY;AAAA,wBACb,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI,OAAA;AAAA,YACJ,IAAA,EAAK,MAAA;AAAA,YACL,OAAQ,KAAA,IAAoB,EAAA;AAAA,YAC5B,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACxC,GAAA,EAAK,QAAA,CAAS,QAAA,EAAU,GAAA,EAAK,QAAA,EAAS;AAAA,YACtC,GAAA,EAAK,QAAA,CAAS,QAAA,EAAU,GAAA,EAAK,QAAA,EAAS;AAAA,YACtC,SAAA,EAAW,YAAA;AAAA,YACV,GAAG;AAAA;AAAA,SACN;AAAA,QACC,cAAA,EAAe;AAAA,QACf,WAAA;AAAY,OAAA,EACf,CAAA;AAAA,IAGJ,KAAK,QAAA;AACH,MAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,UAAA,EAAY,gBAAgB,mBAAA,EACzC,QAAA,EAAA;AAAA,QAAA,WAAA,EAAY;AAAA,wBACb,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAI,OAAA;AAAA,YACJ,IAAA,EAAK,QAAA;AAAA,YACL,OAAQ,KAAA,IAAoB,EAAA;AAAA,YAC5B,QAAA,EAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,MAAA,CAAO,KAAA,GAAQ,MAAA,CAAO,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,GAAI,EAAE,CAAA;AAAA,YACtE,GAAA,EAAK,SAAS,QAAA,EAAU,GAAA;AAAA,YACxB,GAAA,EAAK,SAAS,QAAA,EAAU,GAAA;AAAA,YACxB,WAAA,EAAa,SAAS,QAAA,EAAU,WAAA;AAAA,YAChC,SAAA,EAAW,YAAA;AAAA,YACV,GAAG;AAAA;AAAA,SACN;AAAA,QACC,cAAA,EAAe;AAAA,QACf,WAAA;AAAY,OAAA,EACf,CAAA;AAAA,IAGJ,KAAK,OAAA;AACH,MAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,UAAA,EAAY,gBAAgB,mBAAA,EAC1C,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wBAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI,OAAA;AAAA,cACJ,IAAA,EAAK,UAAA;AAAA,cACL,OAAA,EAAS,CAAC,CAAC,KAAA;AAAA,cACX,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,OAAO,CAAA;AAAA,cAC1C,SAAA,EAAW,YAAY,QAAA,IAAY,eAAA;AAAA,cAClC,GAAG;AAAA;AAAA,WACN;AAAA,0BACA,IAAA,CAAC,OAAA,EAAA,EAAM,OAAA,EAAS,OAAA,EAAS,WAAW,oBAAA,EACjC,QAAA,EAAA;AAAA,YAAA,QAAA,CAAS,IAAA;AAAA,YACT,QAAA,CAAS,4BAAY,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,mBAAA,EAAoB,aAAA,EAAY,QAAO,QAAA,EAAA,GAAA,EAAC;AAAA,WAAA,EAChF;AAAA,SAAA,EACF,CAAA;AAAA,QACC,cAAA,EAAe;AAAA,QACf,WAAA;AAAY,OAAA,EACf,CAAA;AAAA,IAGJ;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;AAmBO,SAAS,WAAA,CAAY;AAAA,EAC1B,SAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA,GAAc,QAAA;AAAA,EACd,SAAA,GAAY,EAAA;AAAA,EACZ,cAAA;AAAA,EACA,UAAA,EAAY;AACd,CAAA,EAAqB;AAEnB,EAAA,MAAM,UAAA,GAAoC;AAAA,IACxC,GAAG,cAAA;AAAA,IACH,KAAA,EAAO,gBAAgB,KAAA,IAAS;AAAA,GAClC;AACA,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA,CAAqB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA,CAAqB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAkC,EAAE,CAAA;AAIlE,EAAA,MAAM,iBAAA,GAAoB,WAAA;AAAA,IACxB,CAAC,QAAA,KAAgC;AAC/B,MAAA,MAAM,EAAE,UAAS,GAAI,QAAA;AACrB,MAAA,IAAI,CAAC,UAAU,mBAAA,EAAqB;AAClC,QAAA,OAAO,IAAA;AAAA,MACT;AAIA,MAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,mBAA8C,CAAA;AAC/F,MAAA,IAAI,gBAAA,CAAiB,WAAW,CAAA,EAAG;AACjC,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,OAAO,iBAAiB,IAAA,CAAK,CAAC,CAAC,aAAA,EAAe,cAAc,CAAA,KAAM;AAChE,QAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,QAAA,MAAM,YAAA,GAAgB,MAAA,CAAmC,MAAA,CAAO,UAAU,CAAC,CAAA;AAE3E,QAAA,IAAI,YAAA,KAAiB,MAAA,IAAa,YAAA,KAAiB,EAAA,IAAM,iBAAiB,IAAA,EAAM;AAC9E,UAAA,OAAO,KAAA;AAAA,QACT;AAGA,QAAA,MAAM,iBAAA,GACJ,OAAO,YAAA,KAAiB,QAAA,IAAY,iBAAiB,IAAA,IAAQ,IAAA,IAAS,eACjE,YAAA,GACD,YAAA;AAIN,QAAA,MAAM,UAAA,uBAAiB,GAAA,EAAY;AAEnC,QAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAe;AACnC,UAAA,IAAI,CAAA,KAAM,MAAA,IAAa,CAAA,KAAM,IAAA,EAAM;AACnC,UAAA,MAAM,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,CAAE,IAAA,EAAK;AACzB,UAAA,IAAI,CAAA,EAAG,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA;AAAA,QACzB,CAAA;AAEA,QAAA,MAAM,iBAAiB,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,UAAU,CAAA;AAEhE,QAAA,IAAI,OAAO,iBAAA,KAAsB,QAAA,IAAY,iBAAA,KAAsB,IAAA,EAAM;AACvE,UAAA,YAAA,CAAc,kBAA0B,EAAE,CAAA;AAC1C,UAAA,YAAA,CAAc,kBAA0B,IAAI,CAAA;AAAA,QAC9C,CAAA,MAAO;AACL,UAAA,YAAA,CAAa,iBAAiB,CAAA;AAAA,QAChC;AAEA,QAAA,IAAI,cAAA,EAAgB,WAAA,KAAgB,QAAA,IAAY,cAAA,CAAe,SAAS,MAAA,EAAQ;AAE9E,UAAA,MAAM,aAAa,CAAC,GAAG,UAAU,CAAA,CAAE,CAAC,CAAA,IAAK,EAAA;AACzC,UAAA,MAAM,UAAA,GAAa,cAAA,CAAe,OAAA,CAAQ,IAAA,CAAK,CAAC,MAAM,MAAA,CAAO,CAAA,CAAE,EAAE,CAAA,KAAM,UAAU,CAAA;AACjF,UAAA,MAAM,YAAA,GAAe,cAAA,CAAe,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,MAAA,CAAO,CAAA,CAAE,IAAI,CAAA,CAAE,IAAA,EAAK,KAAM,UAAU,CAAA;AAC5F,UAAA,MAAM,MAAM,UAAA,IAAc,YAAA;AAC1B,UAAA,IAAI,GAAA,EAAK;AACP,YAAA,YAAA,CAAa,IAAI,EAAE,CAAA;AACnB,YAAA,YAAA,CAAa,IAAI,IAAI,CAAA;AAAA,UACvB;AAAA,QACF;AAEA,QAAA,MAAM,aAAA,GAAgB,CAAC,QAAA,KAAsB;AAC3C,UAAA,MAAM,WAAA,GAAc,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAA,EAAK;AAC1C,UAAA,OAAO,UAAA,CAAW,IAAI,WAAW,CAAA;AAAA,QACnC,CAAA;AAMA,QAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,cAAc,CAAA,EAAG;AACjC,UAAA,OAAO,eAAe,IAAA,CAAK,CAAC,CAAA,KAAM,aAAA,CAAc,CAAC,CAAC,CAAA;AAAA,QACpD;AAEA,QAAA,IAAI,cAAA,IAAkB,OAAO,cAAA,KAAmB,QAAA,EAAU;AACxD,UAAA,MAAM,WAAA,GAAc,cAAA;AACpB,UAAA,KAAA,MAAW,KAAK,UAAA,EAAY;AAC1B,YAAA,IAAI,OAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,WAAA,EAAa,CAAC,CAAA,EAAG;AACxD,cAAA,MAAM,IAAA,GAAO,YAAY,CAAC,CAAA;AAC1B,cAAA,OAAO,IAAA,KAAS,MAAA,GAAY,IAAA,GAAO,CAAC,CAAC,IAAA;AAAA,YACvC;AAAA,UACF;AACA,UAAA,OAAO,KAAA;AAAA,QACT;AAEA,QAAA,OAAO,cAAc,cAAc,CAAA;AAAA,MACrC,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,QAAQ,SAAS;AAAA,GACpB;AAGA,EAAA,MAAM,gBAAA,GAAmB,SAAA,CAAU,MAAA,CAAO,iBAAiB,CAAA;AAI3D,EAAA,MAAM,aAAA,GAAgB,WAAA,CAAY,CAAC,QAAA,EAAoB,KAAA,KAA6C;AAClG,IAAA,IAAI,QAAA,CAAS,WAAA,KAAgB,SAAA,EAAW,OAAO,IAAA;AAE/C,IAAA,IAAI,SAAS,QAAA,EAAU;AACrB,MAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,EAAA,IAAM,UAAU,IAAA,EAAM;AACzD,QAAA,OAAO,CAAA,EAAG,SAAS,IAAI,CAAA,YAAA,CAAA;AAAA,MACzB;AACA,MAAA,IAAI,QAAA,CAAS,WAAA,KAAgB,OAAA,IAAW,CAAC,KAAA,EAAO;AAC9C,QAAA,OAAO,CAAA,EAAG,SAAS,IAAI,CAAA,gBAAA,CAAA;AAAA,MACzB;AAAA,IACF;AAEA,IAAA,IAAI,SAAS,WAAA,KAAgB,QAAA,IAAY,KAAA,KAAU,EAAA,IAAM,UAAU,MAAA,EAAW;AAC5E,MAAA,MAAM,QAAA,GAAW,OAAO,KAAK,CAAA;AAC7B,MAAA,IAAI,SAAS,QAAA,EAAU,GAAA,KAAQ,UAAa,QAAA,GAAW,QAAA,CAAS,SAAS,GAAA,EAAK;AAC5E,QAAA,OAAO,CAAA,iBAAA,EAAoB,QAAA,CAAS,QAAA,CAAS,GAAG,CAAA,CAAA;AAAA,MAClD;AACA,MAAA,IAAI,SAAS,QAAA,EAAU,GAAA,KAAQ,UAAa,QAAA,GAAW,QAAA,CAAS,SAAS,GAAA,EAAK;AAC5E,QAAA,OAAO,CAAA,iBAAA,EAAoB,QAAA,CAAS,QAAA,CAAS,GAAG,CAAA,CAAA;AAAA,MAClD;AAAA,IACF;AAEA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,WAAA,GAAc,YAAY,MAAe;AAC7C,IAAA,MAAM,YAAwB,EAAC;AAC/B,IAAA,IAAI,OAAA,GAAU,IAAA;AAEd,IAAA,gBAAA,CAAiB,OAAA,CAAQ,CAAC,QAAA,KAAa;AACrC,MAAA,MAAM,QAAQ,aAAA,CAAc,QAAA,EAAU,MAAA,CAAO,QAAA,CAAS,EAAE,CAAC,CAAA;AACzD,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,SAAA,CAAU,QAAA,CAAS,EAAE,CAAA,GAAI,KAAA;AACzB,QAAA,OAAA,GAAU,KAAA;AAAA,MACZ;AAAA,IACF,CAAC,CAAA;AAED,IAAA,SAAA,CAAU,SAAS,CAAA;AACnB,IAAA,OAAO,OAAA;AAAA,EACT,CAAA,EAAG,CAAC,gBAAA,EAAkB,MAAA,EAAQ,aAAa,CAAC,CAAA;AAG5C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,gBAAA,CAAiB,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AAE5D,IAAA,SAAA,CAAU,CAAC,IAAA,KAAS;AAClB,MAAA,IAAI,OAAA,GAAU,KAAA;AACd,MAAA,MAAM,IAAA,GAAO,EAAE,GAAG,IAAA,EAAK;AAEvB,MAAA,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,KAAA,KAAU;AACnC,QAAA,MAAM,EAAA,GAAK,OAAO,KAAK,CAAA;AACvB,QAAA,IAAI,CAAC,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA,EAAG;AACvB,UAAA,OAAO,KAAK,EAAE,CAAA;AACd,UAAA,OAAA,GAAU,IAAA;AAAA,QACZ;AAAA,MACF,CAAC,CAAA;AAED,MAAA,OAAO,UAAU,IAAA,GAAO,IAAA;AAAA,IAC1B,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,gBAAgB,CAAC,CAAA;AAErB,EAAA,MAAM,YAAA,GAAe,CAAC,UAAA,EAAoB,KAAA,KAAqC;AAC7E,IAAA,SAAA,CAAU,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,KAAA,EAAM,CAAE,CAAA;AACtD,IAAA,UAAA,CAAW,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAA,EAAK,CAAE,CAAA;AAGtD,IAAA,IAAI,OAAA,CAAQ,UAAU,CAAA,EAAG;AACvB,MAAA,MAAM,WAAW,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,UAAU,CAAA;AAC1D,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,QAAA,EAAU,KAAK,CAAA;AAC3C,QAAA,SAAA,CAAU,CAAC,IAAA,KAAS;AAClB,UAAA,IAAI,KAAA,EAAO;AACT,YAAA,OAAO,EAAE,GAAG,IAAA,EAAM,CAAC,UAAU,GAAG,KAAA,EAAM;AAAA,UACxC;AACA,UAAA,MAAM,EAAE,CAAC,UAAU,GAAG,CAAA,EAAG,GAAG,MAAK,GAAI,IAAA;AACrC,UAAA,OAAO,IAAA;AAAA,QACT,CAAC,CAAA;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAuB;AAC3C,IAAA,CAAA,CAAE,cAAA,EAAe;AAGjB,IAAA,MAAM,aAAsC,EAAC;AAC7C,IAAA,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,KAAM;AAC9B,MAAA,UAAA,CAAW,CAAA,CAAE,EAAE,CAAA,GAAI,IAAA;AAAA,IACrB,CAAC,CAAA;AACD,IAAA,UAAA,CAAW,UAAU,CAAA;AAErB,IAAA,IAAI,aAAY,EAAG;AAEjB,MAAA,MAAM,gBAA4B,EAAC;AACnC,MAAA,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,KAAM;AAC9B,QAAA,IAAI,EAAE,WAAA,KAAgB,SAAA,IAAa,OAAO,CAAA,CAAE,EAAE,MAAM,MAAA,EAAW;AAC7D,UAAA,aAAA,CAAc,CAAA,CAAE,EAAE,CAAA,GAAI,MAAA,CAAO,EAAE,EAAE,CAAA;AAAA,QACnC;AAAA,MACF,CAAC,CAAA;AACD,MAAA,QAAA,CAAS,aAAa,CAAA;AAAA,IACxB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,aAAA,GAAgB,iLAAA;AAEtB,EAAA,4BACG,MAAA,EAAA,EAAK,QAAA,EAAU,YAAA,EAAc,SAAA,EAAsB,YAAU,IAAA,EAC3D,QAAA,EAAA;AAAA,IAAA,gBAAA,CAAiB,GAAA,CAAI,CAAC,QAAA,qBACrB,GAAA;AAAA,MAAC,SAAA;AAAA,MAAA;AAAA,QAEC,QAAA;AAAA,QACA,KAAA,EAAO,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA;AAAA,QACzB,KAAA,EAAO,QAAQ,QAAA,CAAS,EAAE,IAAI,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,GAAI,MAAA;AAAA,QACpD,UAAU,CAAC,KAAA,KAAU,YAAA,CAAa,QAAA,CAAS,IAAI,KAAK,CAAA;AAAA,QACpD;AAAA,OAAA;AAAA,MALK,QAAA,CAAS;AAAA,KAOjB,CAAA;AAAA,oBACD,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,SAAA,EAAW,YAAY,MAAA,IAAU,aAAA;AAAA,QAEhC,QAAA,EAAA;AAAA;AAAA;AACH,GAAA,EACF,CAAA;AAEJ","file":"index.js","sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport type { Question, FormValues, FormErrors, BookingFormProps, BookingFormClassNames } from '../types';\n\n/** Merge base classes with optional custom classes */\nconst cx = (...classes: (string | undefined)[]) => classes.filter(Boolean).join(' ');\n\n/**\n * Renders a single form field based on question type\n */\nfunction FormField({\n question,\n value,\n error,\n onChange,\n classNames,\n}: {\n question: Question;\n value: string | number | boolean | undefined;\n error?: string;\n onChange: (value: string | number | boolean) => void;\n classNames?: BookingFormClassNames;\n}) {\n const inputId = `question-${question.id}`;\n const errorId = `${inputId}-error`;\n const hasError = !!error;\n\n // Default classes (can be overridden via classNames prop)\n const defaultFieldWrapper = 'mb-4';\n const defaultLabel = 'block text-sm font-medium mb-1 text-gray-700';\n const defaultHeading = 'text-lg font-semibold text-gray-900 mt-4 mb-2 first:mt-0';\n const defaultInput = 'w-full px-3 py-2 border rounded-md text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 border-gray-300 focus:ring-blue-500 focus:border-blue-500';\n const defaultInputError = 'border-red-500 focus:ring-red-500';\n const defaultCheckbox = 'mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500';\n const defaultHelpText = 'mt-1 text-xs text-gray-500';\n const defaultErrorText = 'mt-1 text-xs text-red-600';\n\n const inputClasses = hasError\n ? cx(classNames?.input ?? defaultInput, classNames?.inputError ?? defaultInputError)\n : cx(classNames?.input ?? defaultInput);\n\n const labelClasses = classNames?.label ?? defaultLabel;\n const checkboxLabelClasses = cx('text-sm', classNames?.label ?? 'text-gray-700');\n\n const renderLabel = () => {\n if (question.detail_type === 'heading') return null;\n return (\n <label htmlFor={inputId} className={labelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n );\n };\n\n const renderHelpText = () => {\n if (!question.help_text) return null;\n return <p className={classNames?.helpText ?? defaultHelpText}>{question.help_text}</p>;\n };\n\n const renderError = () => {\n if (!error) return null;\n return (\n <p id={errorId} className={classNames?.errorText ?? defaultErrorText} role=\"alert\">\n {error}\n </p>\n );\n };\n\n const ariaProps = {\n 'aria-invalid': hasError ? true : undefined,\n 'aria-describedby': hasError ? errorId : undefined,\n 'aria-required': question.required || undefined,\n };\n\n switch (question.detail_type) {\n case 'heading':\n return (\n <h3 className={classNames?.heading ?? defaultHeading}>\n {question.name}\n </h3>\n );\n\n case 'text_field':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'text_area':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <textarea\n id={inputId}\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n rows={4}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'select':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <select\n id={inputId}\n value={(value as string | number) ?? ''}\n onChange={(e) => onChange(e.target.value)}\n className={inputClasses}\n {...ariaProps}\n >\n <option value=\"\">Select an option</option>\n {question.options?.map((option) => (\n <option key={option.id} value={option.id}>\n {option.name}\n </option>\n ))}\n </select>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'date':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"date\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n min={question.settings?.min?.toString()}\n max={question.settings?.max?.toString()}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'number':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"number\"\n value={(value as number) ?? ''}\n onChange={(e) => onChange(e.target.value ? Number(e.target.value) : '')}\n min={question.settings?.min}\n max={question.settings?.max}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'check':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n <div className=\"flex items-start gap-2\">\n <input\n id={inputId}\n type=\"checkbox\"\n checked={!!value}\n onChange={(e) => onChange(e.target.checked)}\n className={classNames?.checkbox ?? defaultCheckbox}\n {...ariaProps}\n />\n <label htmlFor={inputId} className={checkboxLabelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n </div>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * BookingForm - A dynamic form component for booking questions\n *\n * @example\n * ```tsx\n * const questions: Question[] = [\n * { id: 1, name: 'Personal Details', detail_type: 'heading' },\n * { id: 2, name: 'Full Name', detail_type: 'text_field', required: true },\n * { id: 3, name: 'Email', detail_type: 'text_field', required: true },\n * ];\n *\n * <BookingForm\n * questions={questions}\n * onSubmit={(values) => console.log(values)}\n * />\n * ```\n */\nexport function BookingForm({\n questions,\n onSubmit,\n submitLabel = 'Submit',\n className = '',\n labelClassName,\n classNames: classNamesProp,\n}: BookingFormProps) {\n // Merge deprecated labelClassName into classNames for backwards compatibility\n const classNames: BookingFormClassNames = {\n ...classNamesProp,\n label: classNamesProp?.label ?? labelClassName,\n };\n const [values, setValues] = useState<FormValues>({});\n const [errors, setErrors] = useState<FormErrors>({});\n const [touched, setTouched] = useState<Record<number, boolean>>({});\n\n\n // Check if a question should be visible based on conditional settings\n const isQuestionVisible = useCallback(\n (question: Question): boolean => {\n const { settings } = question;\n if (!settings?.conditional_answers) {\n return true;\n }\n\n // Check all conditions in conditional_answers\n // Each entry is { questionId: expectedAnswerValue }\n const conditionEntries = Object.entries(settings.conditional_answers as Record<string, unknown>);\n if (conditionEntries.length === 0) {\n return true;\n }\n\n // Question is visible if ANY condition is met\n return conditionEntries.some(([questionIdStr, expectedAnswer]) => {\n const questionId = Number(questionIdStr);\n const currentValue = (values as Record<string, unknown>)[String(questionId)];\n\n if (currentValue === undefined || currentValue === '' || currentValue === null) {\n return false;\n }\n\n // Selects typically store the option id (as string), but allow `{ id, name }` objects too\n const normalizedCurrent =\n typeof currentValue === 'object' && currentValue !== null && 'id' in (currentValue as any)\n ? (currentValue as any)\n : currentValue;\n\n // Build a set of candidate strings for comparison.\n // For select questions we support matching by BOTH option id and option name.\n const candidates = new Set<string>();\n\n const addCandidate = (v: unknown) => {\n if (v === undefined || v === null) return;\n const s = String(v).trim();\n if (s) candidates.add(s);\n };\n\n const sourceQuestion = questions.find((q) => q.id === questionId);\n\n if (typeof normalizedCurrent === 'object' && normalizedCurrent !== null) {\n addCandidate((normalizedCurrent as any).id);\n addCandidate((normalizedCurrent as any).name);\n } else {\n addCandidate(normalizedCurrent);\n }\n\n if (sourceQuestion?.detail_type === 'select' && sourceQuestion.options?.length) {\n // Try to resolve the selected option via id or name.\n const currentStr = [...candidates][0] ?? '';\n const optionById = sourceQuestion.options.find((o) => String(o.id) === currentStr);\n const optionByName = sourceQuestion.options.find((o) => String(o.name).trim() === currentStr);\n const opt = optionById ?? optionByName;\n if (opt) {\n addCandidate(opt.id);\n addCandidate(opt.name);\n }\n }\n\n const matchesScalar = (expected: unknown) => {\n const expectedStr = String(expected).trim();\n return candidates.has(expectedStr);\n };\n\n // Support multiple expected formats:\n // - scalar: \"Consultation\" / \"1\" / 1 / true\n // - array: [\"Consultation\", \"Follow-up\"]\n // - map/object: { \"Consultation\": true }\n if (Array.isArray(expectedAnswer)) {\n return expectedAnswer.some((v) => matchesScalar(v));\n }\n\n if (expectedAnswer && typeof expectedAnswer === 'object') {\n const expectedMap = expectedAnswer as Record<string, unknown>;\n for (const c of candidates) {\n if (Object.prototype.hasOwnProperty.call(expectedMap, c)) {\n const flag = expectedMap[c];\n return flag === undefined ? true : !!flag;\n }\n }\n return false;\n }\n\n return matchesScalar(expectedAnswer);\n });\n },\n [values, questions]\n );\n\n // Get visible questions\n const visibleQuestions = questions.filter(isQuestionVisible);\n\n\n // Validate a single field\n const validateField = useCallback((question: Question, value: FormValues[number]): string | null => {\n if (question.detail_type === 'heading') return null;\n\n if (question.required) {\n if (value === undefined || value === '' || value === null) {\n return `${question.name} is required`;\n }\n if (question.detail_type === 'check' && !value) {\n return `${question.name} must be checked`;\n }\n }\n\n if (question.detail_type === 'number' && value !== '' && value !== undefined) {\n const numValue = Number(value);\n if (question.settings?.min !== undefined && numValue < question.settings.min) {\n return `Minimum value is ${question.settings.min}`;\n }\n if (question.settings?.max !== undefined && numValue > question.settings.max) {\n return `Maximum value is ${question.settings.max}`;\n }\n }\n\n return null;\n }, []);\n\n // Validate all visible fields\n const validateAll = useCallback((): boolean => {\n const newErrors: FormErrors = {};\n let isValid = true;\n\n visibleQuestions.forEach((question) => {\n const error = validateField(question, values[question.id]);\n if (error) {\n newErrors[question.id] = error;\n isValid = false;\n }\n });\n\n setErrors(newErrors);\n return isValid;\n }, [visibleQuestions, values, validateField]);\n\n // Clear errors for hidden questions\n useEffect(() => {\n const visibleIds = new Set(visibleQuestions.map((q) => q.id));\n\n setErrors((prev) => {\n let changed = false;\n const next = { ...prev };\n\n Object.keys(next).forEach((idStr) => {\n const id = Number(idStr);\n if (!visibleIds.has(id)) {\n delete next[id];\n changed = true;\n }\n });\n\n return changed ? next : prev;\n });\n }, [visibleQuestions]);\n\n const handleChange = (questionId: number, value: string | number | boolean) => {\n setValues((prev) => ({ ...prev, [questionId]: value }));\n setTouched((prev) => ({ ...prev, [questionId]: true }));\n\n // Clear error on change if touched\n if (touched[questionId]) {\n const question = questions.find((q) => q.id === questionId);\n if (question) {\n const error = validateField(question, value);\n setErrors((prev) => {\n if (error) {\n return { ...prev, [questionId]: error };\n }\n const { [questionId]: _, ...rest } = prev;\n return rest;\n });\n }\n }\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n\n // Mark all fields as touched\n const allTouched: Record<number, boolean> = {};\n visibleQuestions.forEach((q) => {\n allTouched[q.id] = true;\n });\n setTouched(allTouched);\n\n if (validateAll()) {\n // Only include visible question values\n const visibleValues: FormValues = {};\n visibleQuestions.forEach((q) => {\n if (q.detail_type !== 'heading' && values[q.id] !== undefined) {\n visibleValues[q.id] = values[q.id];\n }\n });\n onSubmit(visibleValues);\n }\n };\n\n const defaultButton = 'w-full mt-4 px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors';\n\n return (\n <form onSubmit={handleSubmit} className={className} noValidate>\n {visibleQuestions.map((question) => (\n <FormField\n key={question.id}\n question={question}\n value={values[question.id]}\n error={touched[question.id] ? errors[question.id] : undefined}\n onChange={(value) => handleChange(question.id, value)}\n classNames={classNames}\n />\n ))}\n <button\n type=\"submit\"\n className={classNames?.button ?? defaultButton}\n >\n {submitLabel}\n </button>\n </form>\n );\n}\n\nexport default BookingForm;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/BookingForm.tsx"],"names":["jsxs","jsx","useState","useCallback","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport type { Question, FormValues, FormErrors, BookingFormProps, BookingFormClassNames } from '../types';\n\n/** Merge base classes with optional custom classes */\nconst cx = (...classes: (string | undefined)[]) => classes.filter(Boolean).join(' ');\n\n/**\n * Renders a single form field based on question type\n */\nfunction FormField({\n question,\n value,\n error,\n onChange,\n classNames,\n}: {\n question: Question;\n value: string | number | boolean | undefined;\n error?: string;\n onChange: (value: string | number | boolean) => void;\n classNames?: BookingFormClassNames;\n}) {\n const inputId = `question-${question.id}`;\n const errorId = `${inputId}-error`;\n const hasError = !!error;\n\n // Default classes (can be overridden via classNames prop)\n const defaultFieldWrapper = 'mb-4';\n const defaultLabel = 'block text-sm font-medium mb-1 text-gray-700';\n const defaultHeading = 'text-lg font-semibold text-gray-900 mt-4 mb-2 first:mt-0';\n const defaultInput = 'w-full px-3 py-2 border rounded-md text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 border-gray-300 focus:ring-blue-500 focus:border-blue-500';\n const defaultInputError = 'border-red-500 focus:ring-red-500';\n const defaultCheckbox = 'mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500';\n const defaultHelpText = 'mt-1 text-xs text-gray-500';\n const defaultErrorText = 'mt-1 text-xs text-red-600';\n\n const inputClasses = hasError\n ? cx(classNames?.input ?? defaultInput, classNames?.inputError ?? defaultInputError)\n : cx(classNames?.input ?? defaultInput);\n\n const labelClasses = classNames?.label ?? defaultLabel;\n const checkboxLabelClasses = cx('text-sm', classNames?.label ?? 'text-gray-700');\n\n const renderLabel = () => {\n if (question.detail_type === 'heading') return null;\n return (\n <label htmlFor={inputId} className={labelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n );\n };\n\n const renderHelpText = () => {\n if (!question.help_text) return null;\n return <p className={classNames?.helpText ?? defaultHelpText}>{question.help_text}</p>;\n };\n\n const renderError = () => {\n if (!error) return null;\n return (\n <p id={errorId} className={classNames?.errorText ?? defaultErrorText} role=\"alert\">\n {error}\n </p>\n );\n };\n\n const ariaProps = {\n 'aria-invalid': hasError ? true : undefined,\n 'aria-describedby': hasError ? errorId : undefined,\n 'aria-required': question.required || undefined,\n };\n\n switch (question.detail_type) {\n case 'heading':\n return (\n <h3 className={classNames?.heading ?? defaultHeading}>\n {question.name}\n </h3>\n );\n\n case 'text_field':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'text_area':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <textarea\n id={inputId}\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n rows={4}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'select':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <select\n id={inputId}\n value={(value as string | number) ?? ''}\n onChange={(e) => onChange(e.target.value)}\n className={inputClasses}\n {...ariaProps}\n >\n <option value=\"\">Select an option</option>\n {question.options?.map((option) => (\n <option key={option.id} value={option.id}>\n {option.name}\n </option>\n ))}\n </select>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'date':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"date\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n min={question.settings?.min?.toString()}\n max={question.settings?.max?.toString()}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'number':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"number\"\n value={(value as number) ?? ''}\n onChange={(e) => onChange(e.target.value ? Number(e.target.value) : '')}\n min={question.settings?.min}\n max={question.settings?.max}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'check':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n <div className=\"flex items-start gap-2\">\n <input\n id={inputId}\n type=\"checkbox\"\n checked={!!value}\n onChange={(e) => onChange(e.target.checked)}\n className={classNames?.checkbox ?? defaultCheckbox}\n {...ariaProps}\n />\n <label htmlFor={inputId} className={checkboxLabelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n </div>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * BookingForm - A dynamic form component for booking questions\n *\n * @example\n * ```tsx\n * const questions: Question[] = [\n * { id: 1, name: 'Personal Details', detail_type: 'heading' },\n * { id: 2, name: 'Full Name', detail_type: 'text_field', required: true },\n * { id: 3, name: 'Email', detail_type: 'text_field', required: true },\n * ];\n *\n * <BookingForm\n * questions={questions}\n * onSubmit={(values) => console.log(values)}\n * />\n * ```\n */\nexport function BookingForm({\n questions,\n onSubmit,\n submitLabel = 'Submit',\n className = '',\n labelClassName,\n classNames: classNamesProp,\n}: BookingFormProps) {\n // Merge deprecated labelClassName into classNames for backwards compatibility\n const classNames: BookingFormClassNames = {\n ...classNamesProp,\n label: classNamesProp?.label ?? labelClassName,\n };\n const [values, setValues] = useState<FormValues>({});\n const [errors, setErrors] = useState<FormErrors>({});\n const [touched, setTouched] = useState<Record<number, boolean>>({});\n\n\n // Check if a question should be visible based on conditional settings\n const isQuestionVisible = useCallback(\n (question: Question): boolean => {\n const { settings } = question;\n if (!settings?.conditional_answers) {\n return true;\n }\n\n // Check all conditions in conditional_answers\n // Each entry is { questionId: expectedAnswerValue }\n const conditionEntries = Object.entries(settings.conditional_answers as Record<string, unknown>);\n if (conditionEntries.length === 0) {\n return true;\n }\n\n // Question is visible if ANY condition is met\n return conditionEntries.some(([questionIdStr, expectedAnswer]) => {\n const questionId = Number(questionIdStr);\n const currentValue = (values as Record<string, unknown>)[String(questionId)];\n\n if (currentValue === undefined || currentValue === '' || currentValue === null) {\n return false;\n }\n\n // Selects typically store the option id (as string), but allow `{ id, name }` objects too\n const normalizedCurrent =\n typeof currentValue === 'object' && currentValue !== null && 'id' in (currentValue as any)\n ? (currentValue as any)\n : currentValue;\n\n // Build a set of candidate strings for comparison.\n // For select questions we support matching by BOTH option id and option name.\n const candidates = new Set<string>();\n\n const addCandidate = (v: unknown) => {\n if (v === undefined || v === null) return;\n const s = String(v).trim();\n if (s) candidates.add(s);\n };\n\n const sourceQuestion = questions.find((q) => q.id === questionId);\n\n if (typeof normalizedCurrent === 'object' && normalizedCurrent !== null) {\n addCandidate((normalizedCurrent as any).id);\n addCandidate((normalizedCurrent as any).name);\n } else {\n addCandidate(normalizedCurrent);\n }\n\n if (sourceQuestion?.detail_type === 'select' && sourceQuestion.options?.length) {\n // Try to resolve the selected option via id or name.\n const currentStr = [...candidates][0] ?? '';\n const optionById = sourceQuestion.options.find((o) => String(o.id) === currentStr);\n const optionByName = sourceQuestion.options.find((o) => String(o.name).trim() === currentStr);\n const opt = optionById ?? optionByName;\n if (opt) {\n addCandidate(opt.id);\n addCandidate(opt.name);\n }\n }\n\n const matchesScalar = (expected: unknown) => {\n const expectedStr = String(expected).trim();\n return candidates.has(expectedStr);\n };\n\n // Support multiple expected formats:\n // - scalar: \"Consultation\" / \"1\" / 1 / true\n // - array: [\"Consultation\", \"Follow-up\"]\n // - map/object: { \"Consultation\": true }\n if (Array.isArray(expectedAnswer)) {\n return expectedAnswer.some((v) => matchesScalar(v));\n }\n\n if (expectedAnswer && typeof expectedAnswer === 'object') {\n const expectedMap = expectedAnswer as Record<string, unknown>;\n for (const c of candidates) {\n if (Object.prototype.hasOwnProperty.call(expectedMap, c)) {\n const flag = expectedMap[c];\n return flag === undefined ? true : !!flag;\n }\n }\n return false;\n }\n\n return matchesScalar(expectedAnswer);\n });\n },\n [values, questions]\n );\n\n // Get visible questions\n const visibleQuestions = questions.filter(isQuestionVisible);\n\n\n // Validate a single field\n const validateField = useCallback((question: Question, value: FormValues[number]): string | null => {\n if (question.detail_type === 'heading') return null;\n\n if (question.required) {\n if (value === undefined || value === '' || value === null) {\n return `${question.name} is required`;\n }\n if (question.detail_type === 'check' && !value) {\n return `${question.name} must be checked`;\n }\n }\n\n if (question.detail_type === 'number' && value !== '' && value !== undefined) {\n const numValue = Number(value);\n if (question.settings?.min !== undefined && numValue < question.settings.min) {\n return `Minimum value is ${question.settings.min}`;\n }\n if (question.settings?.max !== undefined && numValue > question.settings.max) {\n return `Maximum value is ${question.settings.max}`;\n }\n }\n\n return null;\n }, []);\n\n // Validate all visible fields\n const validateAll = useCallback((): boolean => {\n const newErrors: FormErrors = {};\n let isValid = true;\n\n visibleQuestions.forEach((question) => {\n const error = validateField(question, values[question.id]);\n if (error) {\n newErrors[question.id] = error;\n isValid = false;\n }\n });\n\n setErrors(newErrors);\n return isValid;\n }, [visibleQuestions, values, validateField]);\n\n // Clear errors for hidden questions\n useEffect(() => {\n const visibleIds = new Set(visibleQuestions.map((q) => q.id));\n\n setErrors((prev) => {\n let changed = false;\n const next = { ...prev };\n\n Object.keys(next).forEach((idStr) => {\n const id = Number(idStr);\n if (!visibleIds.has(id)) {\n delete next[id];\n changed = true;\n }\n });\n\n return changed ? next : prev;\n });\n }, [visibleQuestions]);\n\n const handleChange = (questionId: number, value: string | number | boolean) => {\n setValues((prev) => ({ ...prev, [questionId]: value }));\n setTouched((prev) => ({ ...prev, [questionId]: true }));\n\n // Clear error on change if touched\n if (touched[questionId]) {\n const question = questions.find((q) => q.id === questionId);\n if (question) {\n const error = validateField(question, value);\n setErrors((prev) => {\n if (error) {\n return { ...prev, [questionId]: error };\n }\n const { [questionId]: _, ...rest } = prev;\n return rest;\n });\n }\n }\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n\n // Mark all fields as touched\n const allTouched: Record<number, boolean> = {};\n visibleQuestions.forEach((q) => {\n allTouched[q.id] = true;\n });\n setTouched(allTouched);\n\n if (validateAll()) {\n // Only include visible question values\n const visibleValues: FormValues = {};\n visibleQuestions.forEach((q) => {\n if (q.detail_type !== 'heading' && values[q.id] !== undefined) {\n visibleValues[q.id] = values[q.id];\n }\n });\n onSubmit(visibleValues);\n }\n };\n\n const defaultButton = 'w-full mt-4 px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors';\n\n return (\n <form onSubmit={handleSubmit} className={className} noValidate>\n {visibleQuestions.map((question) => (\n <FormField\n key={question.id}\n question={question}\n value={values[question.id]}\n error={touched[question.id] ? errors[question.id] : undefined}\n onChange={(value) => handleChange(question.id, value)}\n classNames={classNames}\n />\n ))}\n <button\n type=\"submit\"\n className={classNames?.button ?? defaultButton}\n >\n {submitLabel}\n </button>\n </form>\n );\n}\n\nexport default BookingForm;\n"]}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var react = require('react');
|
|
4
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
3
|
|
|
6
4
|
// src/components/BookingForm.tsx
|
|
7
5
|
var cx = (...classes) => classes.filter(Boolean).join(" ");
|
|
@@ -28,18 +26,18 @@ function FormField({
|
|
|
28
26
|
const checkboxLabelClasses = cx("text-sm", classNames?.label ?? "text-gray-700");
|
|
29
27
|
const renderLabel = () => {
|
|
30
28
|
if (question.detail_type === "heading") return null;
|
|
31
|
-
return /* @__PURE__ */
|
|
29
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: labelClasses, children: [
|
|
32
30
|
question.name,
|
|
33
|
-
question.required && /* @__PURE__ */
|
|
31
|
+
question.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
34
32
|
] });
|
|
35
33
|
};
|
|
36
34
|
const renderHelpText = () => {
|
|
37
35
|
if (!question.help_text) return null;
|
|
38
|
-
return /* @__PURE__ */
|
|
36
|
+
return /* @__PURE__ */ jsx("p", { className: classNames?.helpText ?? defaultHelpText, children: question.help_text });
|
|
39
37
|
};
|
|
40
38
|
const renderError = () => {
|
|
41
39
|
if (!error) return null;
|
|
42
|
-
return /* @__PURE__ */
|
|
40
|
+
return /* @__PURE__ */ jsx("p", { id: errorId, className: classNames?.errorText ?? defaultErrorText, role: "alert", children: error });
|
|
43
41
|
};
|
|
44
42
|
const ariaProps = {
|
|
45
43
|
"aria-invalid": hasError ? true : void 0,
|
|
@@ -48,11 +46,11 @@ function FormField({
|
|
|
48
46
|
};
|
|
49
47
|
switch (question.detail_type) {
|
|
50
48
|
case "heading":
|
|
51
|
-
return /* @__PURE__ */
|
|
49
|
+
return /* @__PURE__ */ jsx("h3", { className: classNames?.heading ?? defaultHeading, children: question.name });
|
|
52
50
|
case "text_field":
|
|
53
|
-
return /* @__PURE__ */
|
|
51
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
54
52
|
renderLabel(),
|
|
55
|
-
/* @__PURE__ */
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
56
54
|
"input",
|
|
57
55
|
{
|
|
58
56
|
id: inputId,
|
|
@@ -68,9 +66,9 @@ function FormField({
|
|
|
68
66
|
renderError()
|
|
69
67
|
] });
|
|
70
68
|
case "text_area":
|
|
71
|
-
return /* @__PURE__ */
|
|
69
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
72
70
|
renderLabel(),
|
|
73
|
-
/* @__PURE__ */
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
74
72
|
"textarea",
|
|
75
73
|
{
|
|
76
74
|
id: inputId,
|
|
@@ -86,9 +84,9 @@ function FormField({
|
|
|
86
84
|
renderError()
|
|
87
85
|
] });
|
|
88
86
|
case "select":
|
|
89
|
-
return /* @__PURE__ */
|
|
87
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
90
88
|
renderLabel(),
|
|
91
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ jsxs(
|
|
92
90
|
"select",
|
|
93
91
|
{
|
|
94
92
|
id: inputId,
|
|
@@ -97,8 +95,8 @@ function FormField({
|
|
|
97
95
|
className: inputClasses,
|
|
98
96
|
...ariaProps,
|
|
99
97
|
children: [
|
|
100
|
-
/* @__PURE__ */
|
|
101
|
-
question.options?.map((option) => /* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select an option" }),
|
|
99
|
+
question.options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.id, children: option.name }, option.id))
|
|
102
100
|
]
|
|
103
101
|
}
|
|
104
102
|
),
|
|
@@ -106,9 +104,9 @@ function FormField({
|
|
|
106
104
|
renderError()
|
|
107
105
|
] });
|
|
108
106
|
case "date":
|
|
109
|
-
return /* @__PURE__ */
|
|
107
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
110
108
|
renderLabel(),
|
|
111
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
112
110
|
"input",
|
|
113
111
|
{
|
|
114
112
|
id: inputId,
|
|
@@ -125,9 +123,9 @@ function FormField({
|
|
|
125
123
|
renderError()
|
|
126
124
|
] });
|
|
127
125
|
case "number":
|
|
128
|
-
return /* @__PURE__ */
|
|
126
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
129
127
|
renderLabel(),
|
|
130
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ jsx(
|
|
131
129
|
"input",
|
|
132
130
|
{
|
|
133
131
|
id: inputId,
|
|
@@ -145,9 +143,9 @@ function FormField({
|
|
|
145
143
|
renderError()
|
|
146
144
|
] });
|
|
147
145
|
case "check":
|
|
148
|
-
return /* @__PURE__ */
|
|
149
|
-
/* @__PURE__ */
|
|
150
|
-
/* @__PURE__ */
|
|
146
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
147
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
148
|
+
/* @__PURE__ */ jsx(
|
|
151
149
|
"input",
|
|
152
150
|
{
|
|
153
151
|
id: inputId,
|
|
@@ -158,9 +156,9 @@ function FormField({
|
|
|
158
156
|
...ariaProps
|
|
159
157
|
}
|
|
160
158
|
),
|
|
161
|
-
/* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: checkboxLabelClasses, children: [
|
|
162
160
|
question.name,
|
|
163
|
-
question.required && /* @__PURE__ */
|
|
161
|
+
question.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
164
162
|
] })
|
|
165
163
|
] }),
|
|
166
164
|
renderHelpText(),
|
|
@@ -182,10 +180,10 @@ function BookingForm({
|
|
|
182
180
|
...classNamesProp,
|
|
183
181
|
label: classNamesProp?.label ?? labelClassName
|
|
184
182
|
};
|
|
185
|
-
const [values, setValues] =
|
|
186
|
-
const [errors, setErrors] =
|
|
187
|
-
const [touched, setTouched] =
|
|
188
|
-
const isQuestionVisible =
|
|
183
|
+
const [values, setValues] = useState({});
|
|
184
|
+
const [errors, setErrors] = useState({});
|
|
185
|
+
const [touched, setTouched] = useState({});
|
|
186
|
+
const isQuestionVisible = useCallback(
|
|
189
187
|
(question) => {
|
|
190
188
|
const { settings } = question;
|
|
191
189
|
if (!settings?.conditional_answers) {
|
|
@@ -248,7 +246,7 @@ function BookingForm({
|
|
|
248
246
|
[values, questions]
|
|
249
247
|
);
|
|
250
248
|
const visibleQuestions = questions.filter(isQuestionVisible);
|
|
251
|
-
const validateField =
|
|
249
|
+
const validateField = useCallback((question, value) => {
|
|
252
250
|
if (question.detail_type === "heading") return null;
|
|
253
251
|
if (question.required) {
|
|
254
252
|
if (value === void 0 || value === "" || value === null) {
|
|
@@ -269,7 +267,7 @@ function BookingForm({
|
|
|
269
267
|
}
|
|
270
268
|
return null;
|
|
271
269
|
}, []);
|
|
272
|
-
const validateAll =
|
|
270
|
+
const validateAll = useCallback(() => {
|
|
273
271
|
const newErrors = {};
|
|
274
272
|
let isValid = true;
|
|
275
273
|
visibleQuestions.forEach((question) => {
|
|
@@ -282,7 +280,7 @@ function BookingForm({
|
|
|
282
280
|
setErrors(newErrors);
|
|
283
281
|
return isValid;
|
|
284
282
|
}, [visibleQuestions, values, validateField]);
|
|
285
|
-
|
|
283
|
+
useEffect(() => {
|
|
286
284
|
const visibleIds = new Set(visibleQuestions.map((q) => q.id));
|
|
287
285
|
setErrors((prev) => {
|
|
288
286
|
let changed = false;
|
|
@@ -332,8 +330,8 @@ function BookingForm({
|
|
|
332
330
|
}
|
|
333
331
|
};
|
|
334
332
|
const defaultButton = "w-full mt-4 px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors";
|
|
335
|
-
return /* @__PURE__ */
|
|
336
|
-
visibleQuestions.map((question) => /* @__PURE__ */
|
|
333
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, noValidate: true, children: [
|
|
334
|
+
visibleQuestions.map((question) => /* @__PURE__ */ jsx(
|
|
337
335
|
FormField,
|
|
338
336
|
{
|
|
339
337
|
question,
|
|
@@ -344,7 +342,7 @@ function BookingForm({
|
|
|
344
342
|
},
|
|
345
343
|
question.id
|
|
346
344
|
)),
|
|
347
|
-
/* @__PURE__ */
|
|
345
|
+
/* @__PURE__ */ jsx(
|
|
348
346
|
"button",
|
|
349
347
|
{
|
|
350
348
|
type: "submit",
|
|
@@ -355,6 +353,6 @@ function BookingForm({
|
|
|
355
353
|
] });
|
|
356
354
|
}
|
|
357
355
|
|
|
358
|
-
|
|
359
|
-
//# sourceMappingURL=index.
|
|
360
|
-
//# sourceMappingURL=index.
|
|
356
|
+
export { BookingForm };
|
|
357
|
+
//# sourceMappingURL=index.mjs.map
|
|
358
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/BookingForm.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport type { Question, FormValues, FormErrors, BookingFormProps, BookingFormClassNames } from '../types';\n\n/** Merge base classes with optional custom classes */\nconst cx = (...classes: (string | undefined)[]) => classes.filter(Boolean).join(' ');\n\n/**\n * Renders a single form field based on question type\n */\nfunction FormField({\n question,\n value,\n error,\n onChange,\n classNames,\n}: {\n question: Question;\n value: string | number | boolean | undefined;\n error?: string;\n onChange: (value: string | number | boolean) => void;\n classNames?: BookingFormClassNames;\n}) {\n const inputId = `question-${question.id}`;\n const errorId = `${inputId}-error`;\n const hasError = !!error;\n\n // Default classes (can be overridden via classNames prop)\n const defaultFieldWrapper = 'mb-4';\n const defaultLabel = 'block text-sm font-medium mb-1 text-gray-700';\n const defaultHeading = 'text-lg font-semibold text-gray-900 mt-4 mb-2 first:mt-0';\n const defaultInput = 'w-full px-3 py-2 border rounded-md text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 border-gray-300 focus:ring-blue-500 focus:border-blue-500';\n const defaultInputError = 'border-red-500 focus:ring-red-500';\n const defaultCheckbox = 'mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500';\n const defaultHelpText = 'mt-1 text-xs text-gray-500';\n const defaultErrorText = 'mt-1 text-xs text-red-600';\n\n const inputClasses = hasError\n ? cx(classNames?.input ?? defaultInput, classNames?.inputError ?? defaultInputError)\n : cx(classNames?.input ?? defaultInput);\n\n const labelClasses = classNames?.label ?? defaultLabel;\n const checkboxLabelClasses = cx('text-sm', classNames?.label ?? 'text-gray-700');\n\n const renderLabel = () => {\n if (question.detail_type === 'heading') return null;\n return (\n <label htmlFor={inputId} className={labelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n );\n };\n\n const renderHelpText = () => {\n if (!question.help_text) return null;\n return <p className={classNames?.helpText ?? defaultHelpText}>{question.help_text}</p>;\n };\n\n const renderError = () => {\n if (!error) return null;\n return (\n <p id={errorId} className={classNames?.errorText ?? defaultErrorText} role=\"alert\">\n {error}\n </p>\n );\n };\n\n const ariaProps = {\n 'aria-invalid': hasError ? true : undefined,\n 'aria-describedby': hasError ? errorId : undefined,\n 'aria-required': question.required || undefined,\n };\n\n switch (question.detail_type) {\n case 'heading':\n return (\n <h3 className={classNames?.heading ?? defaultHeading}>\n {question.name}\n </h3>\n );\n\n case 'text_field':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'text_area':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <textarea\n id={inputId}\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n rows={4}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'select':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <select\n id={inputId}\n value={(value as string | number) ?? ''}\n onChange={(e) => onChange(e.target.value)}\n className={inputClasses}\n {...ariaProps}\n >\n <option value=\"\">Select an option</option>\n {question.options?.map((option) => (\n <option key={option.id} value={option.id}>\n {option.name}\n </option>\n ))}\n </select>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'date':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"date\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n min={question.settings?.min?.toString()}\n max={question.settings?.max?.toString()}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'number':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"number\"\n value={(value as number) ?? ''}\n onChange={(e) => onChange(e.target.value ? Number(e.target.value) : '')}\n min={question.settings?.min}\n max={question.settings?.max}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'check':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n <div className=\"flex items-start gap-2\">\n <input\n id={inputId}\n type=\"checkbox\"\n checked={!!value}\n onChange={(e) => onChange(e.target.checked)}\n className={classNames?.checkbox ?? defaultCheckbox}\n {...ariaProps}\n />\n <label htmlFor={inputId} className={checkboxLabelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n </div>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * BookingForm - A dynamic form component for booking questions\n *\n * @example\n * ```tsx\n * const questions: Question[] = [\n * { id: 1, name: 'Personal Details', detail_type: 'heading' },\n * { id: 2, name: 'Full Name', detail_type: 'text_field', required: true },\n * { id: 3, name: 'Email', detail_type: 'text_field', required: true },\n * ];\n *\n * <BookingForm\n * questions={questions}\n * onSubmit={(values) => console.log(values)}\n * />\n * ```\n */\nexport function BookingForm({\n questions,\n onSubmit,\n submitLabel = 'Submit',\n className = '',\n labelClassName,\n classNames: classNamesProp,\n}: BookingFormProps) {\n // Merge deprecated labelClassName into classNames for backwards compatibility\n const classNames: BookingFormClassNames = {\n ...classNamesProp,\n label: classNamesProp?.label ?? labelClassName,\n };\n const [values, setValues] = useState<FormValues>({});\n const [errors, setErrors] = useState<FormErrors>({});\n const [touched, setTouched] = useState<Record<number, boolean>>({});\n\n\n // Check if a question should be visible based on conditional settings\n const isQuestionVisible = useCallback(\n (question: Question): boolean => {\n const { settings } = question;\n if (!settings?.conditional_answers) {\n return true;\n }\n\n // Check all conditions in conditional_answers\n // Each entry is { questionId: expectedAnswerValue }\n const conditionEntries = Object.entries(settings.conditional_answers as Record<string, unknown>);\n if (conditionEntries.length === 0) {\n return true;\n }\n\n // Question is visible if ANY condition is met\n return conditionEntries.some(([questionIdStr, expectedAnswer]) => {\n const questionId = Number(questionIdStr);\n const currentValue = (values as Record<string, unknown>)[String(questionId)];\n\n if (currentValue === undefined || currentValue === '' || currentValue === null) {\n return false;\n }\n\n // Selects typically store the option id (as string), but allow `{ id, name }` objects too\n const normalizedCurrent =\n typeof currentValue === 'object' && currentValue !== null && 'id' in (currentValue as any)\n ? (currentValue as any)\n : currentValue;\n\n // Build a set of candidate strings for comparison.\n // For select questions we support matching by BOTH option id and option name.\n const candidates = new Set<string>();\n\n const addCandidate = (v: unknown) => {\n if (v === undefined || v === null) return;\n const s = String(v).trim();\n if (s) candidates.add(s);\n };\n\n const sourceQuestion = questions.find((q) => q.id === questionId);\n\n if (typeof normalizedCurrent === 'object' && normalizedCurrent !== null) {\n addCandidate((normalizedCurrent as any).id);\n addCandidate((normalizedCurrent as any).name);\n } else {\n addCandidate(normalizedCurrent);\n }\n\n if (sourceQuestion?.detail_type === 'select' && sourceQuestion.options?.length) {\n // Try to resolve the selected option via id or name.\n const currentStr = [...candidates][0] ?? '';\n const optionById = sourceQuestion.options.find((o) => String(o.id) === currentStr);\n const optionByName = sourceQuestion.options.find((o) => String(o.name).trim() === currentStr);\n const opt = optionById ?? optionByName;\n if (opt) {\n addCandidate(opt.id);\n addCandidate(opt.name);\n }\n }\n\n const matchesScalar = (expected: unknown) => {\n const expectedStr = String(expected).trim();\n return candidates.has(expectedStr);\n };\n\n // Support multiple expected formats:\n // - scalar: \"Consultation\" / \"1\" / 1 / true\n // - array: [\"Consultation\", \"Follow-up\"]\n // - map/object: { \"Consultation\": true }\n if (Array.isArray(expectedAnswer)) {\n return expectedAnswer.some((v) => matchesScalar(v));\n }\n\n if (expectedAnswer && typeof expectedAnswer === 'object') {\n const expectedMap = expectedAnswer as Record<string, unknown>;\n for (const c of candidates) {\n if (Object.prototype.hasOwnProperty.call(expectedMap, c)) {\n const flag = expectedMap[c];\n return flag === undefined ? true : !!flag;\n }\n }\n return false;\n }\n\n return matchesScalar(expectedAnswer);\n });\n },\n [values, questions]\n );\n\n // Get visible questions\n const visibleQuestions = questions.filter(isQuestionVisible);\n\n\n // Validate a single field\n const validateField = useCallback((question: Question, value: FormValues[number]): string | null => {\n if (question.detail_type === 'heading') return null;\n\n if (question.required) {\n if (value === undefined || value === '' || value === null) {\n return `${question.name} is required`;\n }\n if (question.detail_type === 'check' && !value) {\n return `${question.name} must be checked`;\n }\n }\n\n if (question.detail_type === 'number' && value !== '' && value !== undefined) {\n const numValue = Number(value);\n if (question.settings?.min !== undefined && numValue < question.settings.min) {\n return `Minimum value is ${question.settings.min}`;\n }\n if (question.settings?.max !== undefined && numValue > question.settings.max) {\n return `Maximum value is ${question.settings.max}`;\n }\n }\n\n return null;\n }, []);\n\n // Validate all visible fields\n const validateAll = useCallback((): boolean => {\n const newErrors: FormErrors = {};\n let isValid = true;\n\n visibleQuestions.forEach((question) => {\n const error = validateField(question, values[question.id]);\n if (error) {\n newErrors[question.id] = error;\n isValid = false;\n }\n });\n\n setErrors(newErrors);\n return isValid;\n }, [visibleQuestions, values, validateField]);\n\n // Clear errors for hidden questions\n useEffect(() => {\n const visibleIds = new Set(visibleQuestions.map((q) => q.id));\n\n setErrors((prev) => {\n let changed = false;\n const next = { ...prev };\n\n Object.keys(next).forEach((idStr) => {\n const id = Number(idStr);\n if (!visibleIds.has(id)) {\n delete next[id];\n changed = true;\n }\n });\n\n return changed ? next : prev;\n });\n }, [visibleQuestions]);\n\n const handleChange = (questionId: number, value: string | number | boolean) => {\n setValues((prev) => ({ ...prev, [questionId]: value }));\n setTouched((prev) => ({ ...prev, [questionId]: true }));\n\n // Clear error on change if touched\n if (touched[questionId]) {\n const question = questions.find((q) => q.id === questionId);\n if (question) {\n const error = validateField(question, value);\n setErrors((prev) => {\n if (error) {\n return { ...prev, [questionId]: error };\n }\n const { [questionId]: _, ...rest } = prev;\n return rest;\n });\n }\n }\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n\n // Mark all fields as touched\n const allTouched: Record<number, boolean> = {};\n visibleQuestions.forEach((q) => {\n allTouched[q.id] = true;\n });\n setTouched(allTouched);\n\n if (validateAll()) {\n // Only include visible question values\n const visibleValues: FormValues = {};\n visibleQuestions.forEach((q) => {\n if (q.detail_type !== 'heading' && values[q.id] !== undefined) {\n visibleValues[q.id] = values[q.id];\n }\n });\n onSubmit(visibleValues);\n }\n };\n\n const defaultButton = 'w-full mt-4 px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors';\n\n return (\n <form onSubmit={handleSubmit} className={className} noValidate>\n {visibleQuestions.map((question) => (\n <FormField\n key={question.id}\n question={question}\n value={values[question.id]}\n error={touched[question.id] ? errors[question.id] : undefined}\n onChange={(value) => handleChange(question.id, value)}\n classNames={classNames}\n />\n ))}\n <button\n type=\"submit\"\n className={classNames?.button ?? defaultButton}\n >\n {submitLabel}\n </button>\n </form>\n );\n}\n\nexport default BookingForm;\n"]}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bookinglab/booking-ui-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React UI components for BookingLab booking journeys",
|
|
6
|
-
"main": "./dist/index.
|
|
7
|
-
"module": "./dist/index.
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"sideEffects": false,
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/BookingForm.tsx"],"names":["jsxs","jsx","useState","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport type { Question, FormValues, FormErrors, BookingFormProps, BookingFormClassNames } from '../types';\n\n/** Merge base classes with optional custom classes */\nconst cx = (...classes: (string | undefined)[]) => classes.filter(Boolean).join(' ');\n\n/**\n * Renders a single form field based on question type\n */\nfunction FormField({\n question,\n value,\n error,\n onChange,\n classNames,\n}: {\n question: Question;\n value: string | number | boolean | undefined;\n error?: string;\n onChange: (value: string | number | boolean) => void;\n classNames?: BookingFormClassNames;\n}) {\n const inputId = `question-${question.id}`;\n const errorId = `${inputId}-error`;\n const hasError = !!error;\n\n // Default classes (can be overridden via classNames prop)\n const defaultFieldWrapper = 'mb-4';\n const defaultLabel = 'block text-sm font-medium mb-1 text-gray-700';\n const defaultHeading = 'text-lg font-semibold text-gray-900 mt-4 mb-2 first:mt-0';\n const defaultInput = 'w-full px-3 py-2 border rounded-md text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-offset-1 border-gray-300 focus:ring-blue-500 focus:border-blue-500';\n const defaultInputError = 'border-red-500 focus:ring-red-500';\n const defaultCheckbox = 'mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500';\n const defaultHelpText = 'mt-1 text-xs text-gray-500';\n const defaultErrorText = 'mt-1 text-xs text-red-600';\n\n const inputClasses = hasError\n ? cx(classNames?.input ?? defaultInput, classNames?.inputError ?? defaultInputError)\n : cx(classNames?.input ?? defaultInput);\n\n const labelClasses = classNames?.label ?? defaultLabel;\n const checkboxLabelClasses = cx('text-sm', classNames?.label ?? 'text-gray-700');\n\n const renderLabel = () => {\n if (question.detail_type === 'heading') return null;\n return (\n <label htmlFor={inputId} className={labelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n );\n };\n\n const renderHelpText = () => {\n if (!question.help_text) return null;\n return <p className={classNames?.helpText ?? defaultHelpText}>{question.help_text}</p>;\n };\n\n const renderError = () => {\n if (!error) return null;\n return (\n <p id={errorId} className={classNames?.errorText ?? defaultErrorText} role=\"alert\">\n {error}\n </p>\n );\n };\n\n const ariaProps = {\n 'aria-invalid': hasError ? true : undefined,\n 'aria-describedby': hasError ? errorId : undefined,\n 'aria-required': question.required || undefined,\n };\n\n switch (question.detail_type) {\n case 'heading':\n return (\n <h3 className={classNames?.heading ?? defaultHeading}>\n {question.name}\n </h3>\n );\n\n case 'text_field':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"text\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'text_area':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <textarea\n id={inputId}\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={question.settings?.placeholder}\n rows={4}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'select':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <select\n id={inputId}\n value={(value as string | number) ?? ''}\n onChange={(e) => onChange(e.target.value)}\n className={inputClasses}\n {...ariaProps}\n >\n <option value=\"\">Select an option</option>\n {question.options?.map((option) => (\n <option key={option.id} value={option.id}>\n {option.name}\n </option>\n ))}\n </select>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'date':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"date\"\n value={(value as string) || ''}\n onChange={(e) => onChange(e.target.value)}\n min={question.settings?.min?.toString()}\n max={question.settings?.max?.toString()}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'number':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n {renderLabel()}\n <input\n id={inputId}\n type=\"number\"\n value={(value as number) ?? ''}\n onChange={(e) => onChange(e.target.value ? Number(e.target.value) : '')}\n min={question.settings?.min}\n max={question.settings?.max}\n placeholder={question.settings?.placeholder}\n className={inputClasses}\n {...ariaProps}\n />\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n case 'check':\n return (\n <div className={classNames?.fieldWrapper ?? defaultFieldWrapper}>\n <div className=\"flex items-start gap-2\">\n <input\n id={inputId}\n type=\"checkbox\"\n checked={!!value}\n onChange={(e) => onChange(e.target.checked)}\n className={classNames?.checkbox ?? defaultCheckbox}\n {...ariaProps}\n />\n <label htmlFor={inputId} className={checkboxLabelClasses}>\n {question.name}\n {question.required && <span className=\"text-red-500 ml-1\" aria-hidden=\"true\">*</span>}\n </label>\n </div>\n {renderHelpText()}\n {renderError()}\n </div>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * BookingForm - A dynamic form component for booking questions\n *\n * @example\n * ```tsx\n * const questions: Question[] = [\n * { id: 1, name: 'Personal Details', detail_type: 'heading' },\n * { id: 2, name: 'Full Name', detail_type: 'text_field', required: true },\n * { id: 3, name: 'Email', detail_type: 'text_field', required: true },\n * ];\n *\n * <BookingForm\n * questions={questions}\n * onSubmit={(values) => console.log(values)}\n * />\n * ```\n */\nexport function BookingForm({\n questions,\n onSubmit,\n submitLabel = 'Submit',\n className = '',\n labelClassName,\n classNames: classNamesProp,\n}: BookingFormProps) {\n // Merge deprecated labelClassName into classNames for backwards compatibility\n const classNames: BookingFormClassNames = {\n ...classNamesProp,\n label: classNamesProp?.label ?? labelClassName,\n };\n const [values, setValues] = useState<FormValues>({});\n const [errors, setErrors] = useState<FormErrors>({});\n const [touched, setTouched] = useState<Record<number, boolean>>({});\n\n\n // Check if a question should be visible based on conditional settings\n const isQuestionVisible = useCallback(\n (question: Question): boolean => {\n const { settings } = question;\n if (!settings?.conditional_answers) {\n return true;\n }\n\n // Check all conditions in conditional_answers\n // Each entry is { questionId: expectedAnswerValue }\n const conditionEntries = Object.entries(settings.conditional_answers as Record<string, unknown>);\n if (conditionEntries.length === 0) {\n return true;\n }\n\n // Question is visible if ANY condition is met\n return conditionEntries.some(([questionIdStr, expectedAnswer]) => {\n const questionId = Number(questionIdStr);\n const currentValue = (values as Record<string, unknown>)[String(questionId)];\n\n if (currentValue === undefined || currentValue === '' || currentValue === null) {\n return false;\n }\n\n // Selects typically store the option id (as string), but allow `{ id, name }` objects too\n const normalizedCurrent =\n typeof currentValue === 'object' && currentValue !== null && 'id' in (currentValue as any)\n ? (currentValue as any)\n : currentValue;\n\n // Build a set of candidate strings for comparison.\n // For select questions we support matching by BOTH option id and option name.\n const candidates = new Set<string>();\n\n const addCandidate = (v: unknown) => {\n if (v === undefined || v === null) return;\n const s = String(v).trim();\n if (s) candidates.add(s);\n };\n\n const sourceQuestion = questions.find((q) => q.id === questionId);\n\n if (typeof normalizedCurrent === 'object' && normalizedCurrent !== null) {\n addCandidate((normalizedCurrent as any).id);\n addCandidate((normalizedCurrent as any).name);\n } else {\n addCandidate(normalizedCurrent);\n }\n\n if (sourceQuestion?.detail_type === 'select' && sourceQuestion.options?.length) {\n // Try to resolve the selected option via id or name.\n const currentStr = [...candidates][0] ?? '';\n const optionById = sourceQuestion.options.find((o) => String(o.id) === currentStr);\n const optionByName = sourceQuestion.options.find((o) => String(o.name).trim() === currentStr);\n const opt = optionById ?? optionByName;\n if (opt) {\n addCandidate(opt.id);\n addCandidate(opt.name);\n }\n }\n\n const matchesScalar = (expected: unknown) => {\n const expectedStr = String(expected).trim();\n return candidates.has(expectedStr);\n };\n\n // Support multiple expected formats:\n // - scalar: \"Consultation\" / \"1\" / 1 / true\n // - array: [\"Consultation\", \"Follow-up\"]\n // - map/object: { \"Consultation\": true }\n if (Array.isArray(expectedAnswer)) {\n return expectedAnswer.some((v) => matchesScalar(v));\n }\n\n if (expectedAnswer && typeof expectedAnswer === 'object') {\n const expectedMap = expectedAnswer as Record<string, unknown>;\n for (const c of candidates) {\n if (Object.prototype.hasOwnProperty.call(expectedMap, c)) {\n const flag = expectedMap[c];\n return flag === undefined ? true : !!flag;\n }\n }\n return false;\n }\n\n return matchesScalar(expectedAnswer);\n });\n },\n [values, questions]\n );\n\n // Get visible questions\n const visibleQuestions = questions.filter(isQuestionVisible);\n\n\n // Validate a single field\n const validateField = useCallback((question: Question, value: FormValues[number]): string | null => {\n if (question.detail_type === 'heading') return null;\n\n if (question.required) {\n if (value === undefined || value === '' || value === null) {\n return `${question.name} is required`;\n }\n if (question.detail_type === 'check' && !value) {\n return `${question.name} must be checked`;\n }\n }\n\n if (question.detail_type === 'number' && value !== '' && value !== undefined) {\n const numValue = Number(value);\n if (question.settings?.min !== undefined && numValue < question.settings.min) {\n return `Minimum value is ${question.settings.min}`;\n }\n if (question.settings?.max !== undefined && numValue > question.settings.max) {\n return `Maximum value is ${question.settings.max}`;\n }\n }\n\n return null;\n }, []);\n\n // Validate all visible fields\n const validateAll = useCallback((): boolean => {\n const newErrors: FormErrors = {};\n let isValid = true;\n\n visibleQuestions.forEach((question) => {\n const error = validateField(question, values[question.id]);\n if (error) {\n newErrors[question.id] = error;\n isValid = false;\n }\n });\n\n setErrors(newErrors);\n return isValid;\n }, [visibleQuestions, values, validateField]);\n\n // Clear errors for hidden questions\n useEffect(() => {\n const visibleIds = new Set(visibleQuestions.map((q) => q.id));\n\n setErrors((prev) => {\n let changed = false;\n const next = { ...prev };\n\n Object.keys(next).forEach((idStr) => {\n const id = Number(idStr);\n if (!visibleIds.has(id)) {\n delete next[id];\n changed = true;\n }\n });\n\n return changed ? next : prev;\n });\n }, [visibleQuestions]);\n\n const handleChange = (questionId: number, value: string | number | boolean) => {\n setValues((prev) => ({ ...prev, [questionId]: value }));\n setTouched((prev) => ({ ...prev, [questionId]: true }));\n\n // Clear error on change if touched\n if (touched[questionId]) {\n const question = questions.find((q) => q.id === questionId);\n if (question) {\n const error = validateField(question, value);\n setErrors((prev) => {\n if (error) {\n return { ...prev, [questionId]: error };\n }\n const { [questionId]: _, ...rest } = prev;\n return rest;\n });\n }\n }\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n\n // Mark all fields as touched\n const allTouched: Record<number, boolean> = {};\n visibleQuestions.forEach((q) => {\n allTouched[q.id] = true;\n });\n setTouched(allTouched);\n\n if (validateAll()) {\n // Only include visible question values\n const visibleValues: FormValues = {};\n visibleQuestions.forEach((q) => {\n if (q.detail_type !== 'heading' && values[q.id] !== undefined) {\n visibleValues[q.id] = values[q.id];\n }\n });\n onSubmit(visibleValues);\n }\n };\n\n const defaultButton = 'w-full mt-4 px-4 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors';\n\n return (\n <form onSubmit={handleSubmit} className={className} noValidate>\n {visibleQuestions.map((question) => (\n <FormField\n key={question.id}\n question={question}\n value={values[question.id]}\n error={touched[question.id] ? errors[question.id] : undefined}\n onChange={(value) => handleChange(question.id, value)}\n classNames={classNames}\n />\n ))}\n <button\n type=\"submit\"\n className={classNames?.button ?? defaultButton}\n >\n {submitLabel}\n </button>\n </form>\n );\n}\n\nexport default BookingForm;\n"]}
|