@bookinglab/booking-ui-react 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +233 -188
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -111
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,132 @@
|
|
|
1
|
-
import { forwardRef, useId, useState, useCallback, useImperativeHandle, useEffect, useRef } from 'react';
|
|
1
|
+
import React, { forwardRef, useId, useState, useCallback, useImperativeHandle, useEffect, useRef } from 'react';
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
6
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
7
|
var cx = (...classes) => classes.filter(Boolean).join(" ");
|
|
8
|
+
function DateInputGroup({
|
|
9
|
+
question,
|
|
10
|
+
value,
|
|
11
|
+
onChange,
|
|
12
|
+
inputId,
|
|
13
|
+
hasError,
|
|
14
|
+
errorId,
|
|
15
|
+
fieldsetCls,
|
|
16
|
+
legendCls,
|
|
17
|
+
groupCls,
|
|
18
|
+
itemCls,
|
|
19
|
+
sublabelCls,
|
|
20
|
+
dateInputCls,
|
|
21
|
+
helpTextAbove,
|
|
22
|
+
helpTextBelow,
|
|
23
|
+
renderError
|
|
24
|
+
}) {
|
|
25
|
+
const parseValue = (raw) => {
|
|
26
|
+
const m = raw.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
27
|
+
if (!m) return { day: "", month: "", year: "" };
|
|
28
|
+
return { year: m[1], month: String(Number(m[2])), day: String(Number(m[3])) };
|
|
29
|
+
};
|
|
30
|
+
const [parts, setParts] = useState(() => parseValue(value));
|
|
31
|
+
const lastEmittedRef = React.useRef(value);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (value !== lastEmittedRef.current) {
|
|
34
|
+
setParts(parseValue(value));
|
|
35
|
+
lastEmittedRef.current = value;
|
|
36
|
+
}
|
|
37
|
+
}, [value]);
|
|
38
|
+
const buildValue = (d, m, y) => {
|
|
39
|
+
if (!/^\d{1,2}$/.test(d) || !/^\d{1,2}$/.test(m) || !/^\d{4}$/.test(y)) return "";
|
|
40
|
+
const dn = Number(d), mn = Number(m), yn = Number(y);
|
|
41
|
+
const date = new Date(yn, mn - 1, dn);
|
|
42
|
+
if (date.getFullYear() !== yn || date.getMonth() !== mn - 1 || date.getDate() !== dn) return "";
|
|
43
|
+
return `${String(yn).padStart(4, "0")}-${String(mn).padStart(2, "0")}-${String(dn).padStart(2, "0")}`;
|
|
44
|
+
};
|
|
45
|
+
const handleSubChange = (part, v) => {
|
|
46
|
+
const sanitized = v.replace(/[^\d]/g, "");
|
|
47
|
+
const max = part === "year" ? 4 : 2;
|
|
48
|
+
const next = { ...parts, [part]: sanitized.slice(0, max) };
|
|
49
|
+
setParts(next);
|
|
50
|
+
const combined = buildValue(next.day, next.month, next.year);
|
|
51
|
+
lastEmittedRef.current = combined;
|
|
52
|
+
onChange(combined);
|
|
53
|
+
};
|
|
54
|
+
const dayId = `${inputId}-day`;
|
|
55
|
+
const monthId = `${inputId}-month`;
|
|
56
|
+
const yearId = `${inputId}-year`;
|
|
57
|
+
return /* @__PURE__ */ jsxs("fieldset", { className: fieldsetCls, "aria-describedby": hasError ? errorId : void 0, children: [
|
|
58
|
+
/* @__PURE__ */ jsxs("legend", { className: legendCls, children: [
|
|
59
|
+
question.name,
|
|
60
|
+
question.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
61
|
+
] }),
|
|
62
|
+
helpTextAbove,
|
|
63
|
+
/* @__PURE__ */ jsxs("div", { className: groupCls, role: "group", children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("div", { className: itemCls, children: [
|
|
65
|
+
/* @__PURE__ */ jsx("label", { htmlFor: dayId, className: sublabelCls, children: "Day" }),
|
|
66
|
+
/* @__PURE__ */ jsx(
|
|
67
|
+
"input",
|
|
68
|
+
{
|
|
69
|
+
id: dayId,
|
|
70
|
+
type: "text",
|
|
71
|
+
inputMode: "numeric",
|
|
72
|
+
pattern: "[0-9]*",
|
|
73
|
+
maxLength: 2,
|
|
74
|
+
value: parts.day,
|
|
75
|
+
onChange: (e) => handleSubChange("day", e.target.value),
|
|
76
|
+
className: cx(dateInputCls, "w-16"),
|
|
77
|
+
"aria-invalid": hasError ? true : void 0,
|
|
78
|
+
"aria-required": question.required || void 0
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ jsxs("div", { className: itemCls, children: [
|
|
83
|
+
/* @__PURE__ */ jsx("label", { htmlFor: monthId, className: sublabelCls, children: "Month" }),
|
|
84
|
+
/* @__PURE__ */ jsx(
|
|
85
|
+
"input",
|
|
86
|
+
{
|
|
87
|
+
id: monthId,
|
|
88
|
+
type: "text",
|
|
89
|
+
inputMode: "numeric",
|
|
90
|
+
pattern: "[0-9]*",
|
|
91
|
+
maxLength: 2,
|
|
92
|
+
value: parts.month,
|
|
93
|
+
onChange: (e) => handleSubChange("month", e.target.value),
|
|
94
|
+
className: cx(dateInputCls, "w-16"),
|
|
95
|
+
"aria-invalid": hasError ? true : void 0,
|
|
96
|
+
"aria-required": question.required || void 0
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
] }),
|
|
100
|
+
/* @__PURE__ */ jsxs("div", { className: itemCls, children: [
|
|
101
|
+
/* @__PURE__ */ jsx("label", { htmlFor: yearId, className: sublabelCls, children: "Year" }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
"input",
|
|
104
|
+
{
|
|
105
|
+
id: yearId,
|
|
106
|
+
type: "text",
|
|
107
|
+
inputMode: "numeric",
|
|
108
|
+
pattern: "[0-9]*",
|
|
109
|
+
maxLength: 4,
|
|
110
|
+
value: parts.year,
|
|
111
|
+
onChange: (e) => handleSubChange("year", e.target.value),
|
|
112
|
+
className: cx(dateInputCls, "w-24"),
|
|
113
|
+
"aria-invalid": hasError ? true : void 0,
|
|
114
|
+
"aria-required": question.required || void 0
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] })
|
|
118
|
+
] }),
|
|
119
|
+
helpTextBelow,
|
|
120
|
+
renderError()
|
|
121
|
+
] });
|
|
122
|
+
}
|
|
8
123
|
function FormField({
|
|
9
124
|
question,
|
|
10
125
|
value,
|
|
11
126
|
error,
|
|
12
127
|
onChange,
|
|
13
|
-
classNames
|
|
128
|
+
classNames,
|
|
129
|
+
displayHelpTextBelowLabel = false
|
|
14
130
|
}) {
|
|
15
131
|
const inputId = `question-${question.id}`;
|
|
16
132
|
const errorId = `${inputId}-error`;
|
|
@@ -43,6 +159,8 @@ function FormField({
|
|
|
43
159
|
}
|
|
44
160
|
);
|
|
45
161
|
};
|
|
162
|
+
const helpTextAbove = displayHelpTextBelowLabel ? renderHelpText() : null;
|
|
163
|
+
const helpTextBelow = displayHelpTextBelowLabel ? null : renderHelpText();
|
|
46
164
|
const renderError = () => {
|
|
47
165
|
if (!error) return null;
|
|
48
166
|
return /* @__PURE__ */ jsx("p", { id: errorId, className: classNames?.errorText ?? defaultErrorText, role: "alert", children: error });
|
|
@@ -61,6 +179,7 @@ function FormField({
|
|
|
61
179
|
case "text_field":
|
|
62
180
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
63
181
|
renderLabel(),
|
|
182
|
+
helpTextAbove,
|
|
64
183
|
/* @__PURE__ */ jsx(
|
|
65
184
|
"input",
|
|
66
185
|
{
|
|
@@ -73,12 +192,13 @@ function FormField({
|
|
|
73
192
|
...ariaProps
|
|
74
193
|
}
|
|
75
194
|
),
|
|
76
|
-
|
|
195
|
+
helpTextBelow,
|
|
77
196
|
renderError()
|
|
78
197
|
] });
|
|
79
198
|
case "text_area":
|
|
80
199
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
81
200
|
renderLabel(),
|
|
201
|
+
helpTextAbove,
|
|
82
202
|
/* @__PURE__ */ jsx(
|
|
83
203
|
"textarea",
|
|
84
204
|
{
|
|
@@ -91,12 +211,13 @@ function FormField({
|
|
|
91
211
|
...ariaProps
|
|
92
212
|
}
|
|
93
213
|
),
|
|
94
|
-
|
|
214
|
+
helpTextBelow,
|
|
95
215
|
renderError()
|
|
96
216
|
] });
|
|
97
217
|
case "select":
|
|
98
218
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
99
219
|
renderLabel(),
|
|
220
|
+
helpTextAbove,
|
|
100
221
|
/* @__PURE__ */ jsxs(
|
|
101
222
|
"select",
|
|
102
223
|
{
|
|
@@ -111,12 +232,13 @@ function FormField({
|
|
|
111
232
|
]
|
|
112
233
|
}
|
|
113
234
|
),
|
|
114
|
-
|
|
235
|
+
helpTextBelow,
|
|
115
236
|
renderError()
|
|
116
237
|
] });
|
|
117
238
|
case "radio":
|
|
118
239
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
119
240
|
renderLabel(),
|
|
241
|
+
helpTextAbove,
|
|
120
242
|
/* @__PURE__ */ jsx(
|
|
121
243
|
"div",
|
|
122
244
|
{
|
|
@@ -144,34 +266,10 @@ function FormField({
|
|
|
144
266
|
})
|
|
145
267
|
}
|
|
146
268
|
),
|
|
147
|
-
|
|
269
|
+
helpTextBelow,
|
|
148
270
|
renderError()
|
|
149
271
|
] });
|
|
150
272
|
case "date": {
|
|
151
|
-
const raw = typeof value === "string" ? value : "";
|
|
152
|
-
const match2 = raw.match(/^(\d{0,4})-?(\d{0,2})-?(\d{0,2})$/);
|
|
153
|
-
const yearPart = match2?.[1] ?? "";
|
|
154
|
-
const monthPart = match2?.[2] ? String(Number(match2[2])) : "";
|
|
155
|
-
const dayPart = match2?.[3] ? String(Number(match2[3])) : "";
|
|
156
|
-
const dayId = `${inputId}-day`;
|
|
157
|
-
const monthId = `${inputId}-month`;
|
|
158
|
-
const yearId = `${inputId}-year`;
|
|
159
|
-
const buildValue = (d, m, y) => {
|
|
160
|
-
const dt = d.trim();
|
|
161
|
-
const mt = m.trim();
|
|
162
|
-
const yt = y.trim();
|
|
163
|
-
if (!dt && !mt && !yt) return "";
|
|
164
|
-
if (/^\d{1,2}$/.test(dt) && /^\d{1,2}$/.test(mt) && /^\d{4}$/.test(yt)) {
|
|
165
|
-
const dn = Number(dt);
|
|
166
|
-
const mn = Number(mt);
|
|
167
|
-
const yn = Number(yt);
|
|
168
|
-
const date = new Date(yn, mn - 1, dn);
|
|
169
|
-
if (date.getFullYear() === yn && date.getMonth() === mn - 1 && date.getDate() === dn) {
|
|
170
|
-
return `${String(yn).padStart(4, "0")}-${String(mn).padStart(2, "0")}-${String(dn).padStart(2, "0")}`;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return "";
|
|
174
|
-
};
|
|
175
273
|
const dateInputBase = classNames?.dateInput ?? classNames?.input ?? defaultInput;
|
|
176
274
|
const dateInputCls = hasError ? cx(dateInputBase, classNames?.inputError ?? defaultInputError) : dateInputBase;
|
|
177
275
|
const sublabelCls = classNames?.dateInputLabel ?? "block text-xs font-medium mb-1 text-gray-700";
|
|
@@ -179,91 +277,31 @@ function FormField({
|
|
|
179
277
|
const groupCls = classNames?.dateInputGroup ?? "flex flex-row gap-3 items-end";
|
|
180
278
|
const legendCls = classNames?.dateLegend ?? labelClasses;
|
|
181
279
|
const fieldsetCls = classNames?.dateFieldset ?? (classNames?.fieldWrapper ?? defaultFieldWrapper);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const next = {
|
|
185
|
-
day: part === "day" ? sanitized : dayPart,
|
|
186
|
-
month: part === "month" ? sanitized : monthPart,
|
|
187
|
-
year: part === "year" ? sanitized : yearPart
|
|
188
|
-
};
|
|
189
|
-
const combined = buildValue(next.day, next.month, next.year);
|
|
190
|
-
onChange(combined);
|
|
191
|
-
};
|
|
192
|
-
return /* @__PURE__ */ jsxs(
|
|
193
|
-
"fieldset",
|
|
280
|
+
return /* @__PURE__ */ jsx(
|
|
281
|
+
DateInputGroup,
|
|
194
282
|
{
|
|
195
|
-
|
|
196
|
-
"
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
inputMode: "numeric",
|
|
211
|
-
pattern: "[0-9]*",
|
|
212
|
-
maxLength: 2,
|
|
213
|
-
value: dayPart,
|
|
214
|
-
onChange: (e) => handleSubChange("day", e.target.value),
|
|
215
|
-
className: cx(dateInputCls, "w-16"),
|
|
216
|
-
"aria-invalid": hasError ? true : void 0,
|
|
217
|
-
"aria-required": question.required || void 0
|
|
218
|
-
}
|
|
219
|
-
)
|
|
220
|
-
] }),
|
|
221
|
-
/* @__PURE__ */ jsxs("div", { className: itemCls, children: [
|
|
222
|
-
/* @__PURE__ */ jsx("label", { htmlFor: monthId, className: sublabelCls, children: "Month" }),
|
|
223
|
-
/* @__PURE__ */ jsx(
|
|
224
|
-
"input",
|
|
225
|
-
{
|
|
226
|
-
id: monthId,
|
|
227
|
-
type: "text",
|
|
228
|
-
inputMode: "numeric",
|
|
229
|
-
pattern: "[0-9]*",
|
|
230
|
-
maxLength: 2,
|
|
231
|
-
value: monthPart,
|
|
232
|
-
onChange: (e) => handleSubChange("month", e.target.value),
|
|
233
|
-
className: cx(dateInputCls, "w-16"),
|
|
234
|
-
"aria-invalid": hasError ? true : void 0,
|
|
235
|
-
"aria-required": question.required || void 0
|
|
236
|
-
}
|
|
237
|
-
)
|
|
238
|
-
] }),
|
|
239
|
-
/* @__PURE__ */ jsxs("div", { className: itemCls, children: [
|
|
240
|
-
/* @__PURE__ */ jsx("label", { htmlFor: yearId, className: sublabelCls, children: "Year" }),
|
|
241
|
-
/* @__PURE__ */ jsx(
|
|
242
|
-
"input",
|
|
243
|
-
{
|
|
244
|
-
id: yearId,
|
|
245
|
-
type: "text",
|
|
246
|
-
inputMode: "numeric",
|
|
247
|
-
pattern: "[0-9]*",
|
|
248
|
-
maxLength: 4,
|
|
249
|
-
value: yearPart,
|
|
250
|
-
onChange: (e) => handleSubChange("year", e.target.value),
|
|
251
|
-
className: cx(dateInputCls, "w-24"),
|
|
252
|
-
"aria-invalid": hasError ? true : void 0,
|
|
253
|
-
"aria-required": question.required || void 0
|
|
254
|
-
}
|
|
255
|
-
)
|
|
256
|
-
] })
|
|
257
|
-
] }),
|
|
258
|
-
renderHelpText(),
|
|
259
|
-
renderError()
|
|
260
|
-
]
|
|
283
|
+
question,
|
|
284
|
+
value: typeof value === "string" ? value : "",
|
|
285
|
+
onChange,
|
|
286
|
+
inputId,
|
|
287
|
+
hasError,
|
|
288
|
+
errorId,
|
|
289
|
+
fieldsetCls,
|
|
290
|
+
legendCls,
|
|
291
|
+
groupCls,
|
|
292
|
+
itemCls,
|
|
293
|
+
sublabelCls,
|
|
294
|
+
dateInputCls,
|
|
295
|
+
helpTextAbove,
|
|
296
|
+
helpTextBelow,
|
|
297
|
+
renderError
|
|
261
298
|
}
|
|
262
299
|
);
|
|
263
300
|
}
|
|
264
301
|
case "number":
|
|
265
302
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
266
303
|
renderLabel(),
|
|
304
|
+
helpTextAbove,
|
|
267
305
|
/* @__PURE__ */ jsx(
|
|
268
306
|
"input",
|
|
269
307
|
{
|
|
@@ -278,11 +316,12 @@ function FormField({
|
|
|
278
316
|
...ariaProps
|
|
279
317
|
}
|
|
280
318
|
),
|
|
281
|
-
|
|
319
|
+
helpTextBelow,
|
|
282
320
|
renderError()
|
|
283
321
|
] });
|
|
284
322
|
case "check":
|
|
285
323
|
return /* @__PURE__ */ jsxs("div", { className: classNames?.fieldWrapper ?? defaultFieldWrapper, children: [
|
|
324
|
+
helpTextAbove,
|
|
286
325
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
287
326
|
/* @__PURE__ */ jsx(
|
|
288
327
|
"input",
|
|
@@ -300,7 +339,7 @@ function FormField({
|
|
|
300
339
|
question.required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", "aria-hidden": "true", children: "*" })
|
|
301
340
|
] })
|
|
302
341
|
] }),
|
|
303
|
-
|
|
342
|
+
helpTextBelow,
|
|
304
343
|
renderError()
|
|
305
344
|
] });
|
|
306
345
|
default:
|
|
@@ -312,6 +351,7 @@ function BookingForm({
|
|
|
312
351
|
onSubmit,
|
|
313
352
|
submitLabel = "Submit",
|
|
314
353
|
isAdmin = false,
|
|
354
|
+
displayHelpTextBelowLabel = false,
|
|
315
355
|
className = "",
|
|
316
356
|
labelClassName,
|
|
317
357
|
classNames: classNamesProp
|
|
@@ -508,7 +548,8 @@ function BookingForm({
|
|
|
508
548
|
value: values[question.id],
|
|
509
549
|
error: touched[question.id] ? errors[question.id] : void 0,
|
|
510
550
|
onChange: (value) => handleChange(question.id, value),
|
|
511
|
-
classNames
|
|
551
|
+
classNames,
|
|
552
|
+
displayHelpTextBelowLabel
|
|
512
553
|
},
|
|
513
554
|
question.id
|
|
514
555
|
)),
|