@planetaexo/design-system 0.4.6 → 0.4.7

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.cjs CHANGED
@@ -341,14 +341,16 @@ var FloatingInput = React21__namespace.forwardRef(
341
341
  FloatingInput.displayName = "FloatingInput";
342
342
  var FloatingSelect = React21__namespace.forwardRef(
343
343
  (_a, ref) => {
344
- var _b = _a, { label, error, id, className, required, children } = _b, props = __objRest(_b, ["label", "error", "id", "className", "required", "children"]);
344
+ var _b = _a, { label, error, id, className, required, children, value } = _b, props = __objRest(_b, ["label", "error", "id", "className", "required", "children", "value"]);
345
345
  const inputId = id != null ? id : React21__namespace.useId();
346
+ const hasValue = typeof value === "string" ? value !== "" : value !== void 0 && value !== null;
346
347
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", className), children: [
347
348
  /* @__PURE__ */ jsxRuntime.jsx(
348
349
  "select",
349
350
  __spreadProps(__spreadValues({
350
351
  id: inputId,
351
352
  ref,
353
+ value,
352
354
  className: cn(
353
355
  "peer block w-full h-14 appearance-none rounded-lg border border-border bg-background",
354
356
  "px-3 pt-6 pb-2 text-base text-foreground font-ui",
@@ -382,7 +384,7 @@ var FloatingSelect = React21__namespace.forwardRef(
382
384
  "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2",
383
385
  "text-base text-muted-foreground font-ui transition-all duration-150",
384
386
  "peer-focus:top-2 peer-focus:translate-y-0 peer-focus:text-xs peer-focus:text-primary",
385
- "peer-not-placeholder-shown:top-2 peer-not-placeholder-shown:translate-y-0 peer-not-placeholder-shown:text-xs"
387
+ hasValue && "top-2 translate-y-0 text-xs"
386
388
  ),
387
389
  children: [
388
390
  label,
@@ -910,6 +912,7 @@ function BirthDateField({
910
912
  required,
911
913
  value,
912
914
  onChange,
915
+ error,
913
916
  className
914
917
  }) {
915
918
  const [open, setOpen] = React21__namespace.useState(false);
@@ -952,7 +955,7 @@ function BirthDateField({
952
955
  {
953
956
  className: cn(
954
957
  "flex items-center rounded-lg border border-border bg-background h-14 transition-colors",
955
- open ? "border-primary ring-1 ring-primary" : "focus-within:border-primary focus-within:ring-1 focus-within:ring-primary"
958
+ error ? "border-destructive focus-within:border-destructive focus-within:ring-destructive" : open ? "border-primary ring-1 ring-primary" : "focus-within:border-primary focus-within:ring-1 focus-within:ring-primary"
956
959
  ),
957
960
  children: [
958
961
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 h-full", children: [
@@ -3984,7 +3987,7 @@ function BookingForm({
3984
3987
  );
3985
3988
  }
3986
3989
  var DEFAULT_LABELS3 = {
3987
- formSubtitle: "Free enquiry \u2013 no commitment",
3990
+ formSubtitle: "To confirm your participation, please complete this short form. It's required for all travellers and helps us coordinate logistics with our local partners and tailor the experience to you.",
3988
3991
  detailsSectionTitle: "Your details",
3989
3992
  tripInfoSectionTitle: "Trip info",
3990
3993
  termsSectionTitle: "Terms & conditions",
@@ -4004,7 +4007,8 @@ var DEFAULT_LABELS3 = {
4004
4007
  emergencyContactPhoneLabel: "Contact phone",
4005
4008
  nationalityLabel: "Nationality",
4006
4009
  selectPlaceholder: "Select an option\u2026",
4007
- optionalLabel: "(optional)"
4010
+ optionalLabel: "(optional)",
4011
+ requiredFieldError: "Required"
4008
4012
  };
4009
4013
  var TERMS_ACCEPT_KEY = "__registrationTermsAccepted";
4010
4014
  function emptyName() {
@@ -4107,7 +4111,34 @@ function FormSection2({
4107
4111
  children
4108
4112
  ] });
4109
4113
  }
4110
- function PhoneInput({ id, label, required, value, onChange }) {
4114
+ function isFieldEmpty(field, value) {
4115
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
4116
+ if (value === void 0 || value === null) return true;
4117
+ switch (field.type) {
4118
+ case "phone": {
4119
+ const p = value;
4120
+ return !((_a = p == null ? void 0 : p.nationalNumber) == null ? void 0 : _a.trim());
4121
+ }
4122
+ case "name": {
4123
+ const n = value;
4124
+ return !((_b = n == null ? void 0 : n.firstName) == null ? void 0 : _b.trim()) && !((_c = n == null ? void 0 : n.lastName) == null ? void 0 : _c.trim());
4125
+ }
4126
+ case "emergencyContact": {
4127
+ const ec = value;
4128
+ return !((_e = (_d = ec == null ? void 0 : ec.contactName) == null ? void 0 : _d.firstName) == null ? void 0 : _e.trim()) && !((_g = (_f = ec == null ? void 0 : ec.contactName) == null ? void 0 : _f.lastName) == null ? void 0 : _g.trim()) && !((_i = (_h = ec == null ? void 0 : ec.phone) == null ? void 0 : _h.nationalNumber) == null ? void 0 : _i.trim());
4129
+ }
4130
+ case "checkbox":
4131
+ if (Array.isArray(value)) return value.length === 0;
4132
+ return value === false;
4133
+ case "date":
4134
+ case "birthDate":
4135
+ if (value instanceof Date) return false;
4136
+ return typeof value !== "string" || !value.trim();
4137
+ default:
4138
+ return typeof value === "string" ? !value.trim() : false;
4139
+ }
4140
+ }
4141
+ function PhoneInput({ id, label, required, value, onChange, error }) {
4111
4142
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full min-w-0", children: [
4112
4143
  /* @__PURE__ */ jsxRuntime.jsx(
4113
4144
  PhoneCountrySelect,
@@ -4131,7 +4162,8 @@ function PhoneInput({ id, label, required, value, onChange }) {
4131
4162
  "peer block h-14 w-full rounded-r-lg border border-border bg-background",
4132
4163
  "px-3 pt-5 pb-2 text-base text-foreground font-ui",
4133
4164
  "transition-colors placeholder-transparent",
4134
- "focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary"
4165
+ "focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary",
4166
+ error && "border-destructive focus:border-destructive focus:ring-destructive"
4135
4167
  )
4136
4168
  }
4137
4169
  ),
@@ -4160,7 +4192,8 @@ function FloatingTextarea({
4160
4192
  required,
4161
4193
  optionalHint,
4162
4194
  value,
4163
- onChange
4195
+ onChange,
4196
+ error
4164
4197
  }) {
4165
4198
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
4166
4199
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4176,7 +4209,8 @@ function FloatingTextarea({
4176
4209
  "peer block w-full resize-none rounded-lg border border-border bg-background",
4177
4210
  "px-3 pt-6 pb-3 text-base text-foreground font-ui",
4178
4211
  "transition-colors placeholder-transparent",
4179
- "focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary"
4212
+ "focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary",
4213
+ error && "border-destructive focus:border-destructive focus:ring-destructive"
4180
4214
  )
4181
4215
  }
4182
4216
  ),
@@ -4203,7 +4237,8 @@ function FieldRenderer({
4203
4237
  value,
4204
4238
  onChange,
4205
4239
  defaultPhoneCountry,
4206
- labels
4240
+ labels,
4241
+ error
4207
4242
  }) {
4208
4243
  var _a, _b, _c;
4209
4244
  const fieldId = `rf-${field.id}`;
@@ -4221,7 +4256,8 @@ function FieldRenderer({
4221
4256
  label: labels.firstNameLabel,
4222
4257
  required: field.required,
4223
4258
  value: v.firstName,
4224
- onChange: (e) => onChange(__spreadProps(__spreadValues({}, v), { firstName: e.target.value }))
4259
+ onChange: (e) => onChange(__spreadProps(__spreadValues({}, v), { firstName: e.target.value })),
4260
+ error: error && !v.firstName.trim() ? error : void 0
4225
4261
  }
4226
4262
  ),
4227
4263
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4230,7 +4266,8 @@ function FieldRenderer({
4230
4266
  label: labels.lastNameLabel,
4231
4267
  required: field.required,
4232
4268
  value: v.lastName,
4233
- onChange: (e) => onChange(__spreadProps(__spreadValues({}, v), { lastName: e.target.value }))
4269
+ onChange: (e) => onChange(__spreadProps(__spreadValues({}, v), { lastName: e.target.value })),
4270
+ error: error && !v.lastName.trim() ? error : void 0
4234
4271
  }
4235
4272
  )
4236
4273
  ] })
@@ -4245,7 +4282,8 @@ function FieldRenderer({
4245
4282
  label: field.label,
4246
4283
  required: field.required,
4247
4284
  value: v,
4248
- onChange
4285
+ onChange,
4286
+ error
4249
4287
  }
4250
4288
  );
4251
4289
  }
@@ -4268,7 +4306,8 @@ function FieldRenderer({
4268
4306
  contactName: __spreadProps(__spreadValues({}, v.contactName), {
4269
4307
  firstName: e.target.value
4270
4308
  })
4271
- }))
4309
+ })),
4310
+ error: error && !v.contactName.firstName.trim() ? error : void 0
4272
4311
  }
4273
4312
  ),
4274
4313
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4281,7 +4320,8 @@ function FieldRenderer({
4281
4320
  contactName: __spreadProps(__spreadValues({}, v.contactName), {
4282
4321
  lastName: e.target.value
4283
4322
  })
4284
- }))
4323
+ })),
4324
+ error: error && !v.contactName.lastName.trim() ? error : void 0
4285
4325
  }
4286
4326
  )
4287
4327
  ] }),
@@ -4292,7 +4332,8 @@ function FieldRenderer({
4292
4332
  label: labels.emergencyContactPhoneLabel,
4293
4333
  required: field.required,
4294
4334
  value: v.phone,
4295
- onChange: (phone) => onChange(__spreadProps(__spreadValues({}, v), { phone }))
4335
+ onChange: (phone) => onChange(__spreadProps(__spreadValues({}, v), { phone })),
4336
+ error: error && !v.phone.nationalNumber.trim() ? error : void 0
4296
4337
  }
4297
4338
  )
4298
4339
  ] })
@@ -4307,7 +4348,8 @@ function FieldRenderer({
4307
4348
  required: field.required,
4308
4349
  optionalHint: !field.required ? labels.optionalLabel : void 0,
4309
4350
  value: typeof value === "string" ? value : "",
4310
- onChange
4351
+ onChange,
4352
+ error
4311
4353
  }
4312
4354
  );
4313
4355
  }
@@ -4320,6 +4362,7 @@ function FieldRenderer({
4320
4362
  required: field.required,
4321
4363
  value: typeof value === "string" ? value : "",
4322
4364
  onChange: (e) => onChange(e.target.value),
4365
+ error,
4323
4366
  children: [
4324
4367
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, hidden: true }),
4325
4368
  options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
@@ -4425,25 +4468,15 @@ function FieldRenderer({
4425
4468
  }
4426
4469
  );
4427
4470
  }
4428
- if (field.type === "date") {
4429
- return /* @__PURE__ */ jsxRuntime.jsx(
4430
- DatePickerField,
4431
- {
4432
- label: field.label,
4433
- required: field.required,
4434
- value: asDate(value),
4435
- onChange: (d) => onChange(d)
4436
- }
4437
- );
4438
- }
4439
- if (field.type === "birthDate") {
4471
+ if (field.type === "date" || field.type === "birthDate") {
4440
4472
  return /* @__PURE__ */ jsxRuntime.jsx(
4441
4473
  BirthDateField,
4442
4474
  {
4443
4475
  label: field.label,
4444
4476
  required: field.required,
4445
4477
  value: asDate(value),
4446
- onChange: (d) => onChange(d)
4478
+ onChange: (d) => onChange(d),
4479
+ error
4447
4480
  }
4448
4481
  );
4449
4482
  }
@@ -4455,7 +4488,8 @@ function FieldRenderer({
4455
4488
  required: field.required,
4456
4489
  type: "number",
4457
4490
  value: value == null ? "" : String(value),
4458
- onChange: (e) => onChange(e.target.value)
4491
+ onChange: (e) => onChange(e.target.value),
4492
+ error
4459
4493
  }
4460
4494
  );
4461
4495
  }
@@ -4467,7 +4501,8 @@ function FieldRenderer({
4467
4501
  required: field.required,
4468
4502
  type: htmlType,
4469
4503
  value: typeof value === "string" ? value : "",
4470
- onChange: (e) => onChange(e.target.value)
4504
+ onChange: (e) => onChange(e.target.value),
4505
+ error
4471
4506
  }
4472
4507
  );
4473
4508
  }
@@ -4516,6 +4551,7 @@ function RegistrationForm({
4516
4551
  includeTerms
4517
4552
  )
4518
4553
  );
4554
+ const [submitAttempted, setSubmitAttempted] = React21__namespace.useState(false);
4519
4555
  React21__namespace.useEffect(() => {
4520
4556
  if (isControlled) return;
4521
4557
  setInternal((prev) => {
@@ -4537,15 +4573,28 @@ function RegistrationForm({
4537
4573
  if (!isControlled) setInternal(next);
4538
4574
  onChange == null ? void 0 : onChange(next);
4539
4575
  };
4576
+ const termsAccepted = current[TERMS_ACCEPT_KEY] === true;
4577
+ const termsEnabled = includeTerms && !!terms;
4578
+ const acceptControl = (_a = terms == null ? void 0 : terms.acceptControl) != null ? _a : "checkbox";
4540
4579
  const handleSubmit = (e) => {
4541
4580
  e.preventDefault();
4581
+ setSubmitAttempted(true);
4582
+ const hasRequiredEmpty = sortedFields.some(
4583
+ (f) => f.required && isFieldEmpty(f, current[f.id])
4584
+ );
4585
+ if (hasRequiredEmpty || termsEnabled && !termsAccepted) return;
4542
4586
  onSubmit == null ? void 0 : onSubmit(current);
4543
4587
  };
4544
- const termsAccepted = current[TERMS_ACCEPT_KEY] === true;
4545
- const termsEnabled = includeTerms && !!terms;
4546
- const acceptControl = (_a = terms == null ? void 0 : terms.acceptControl) != null ? _a : "checkbox";
4547
4588
  const dateRange = formatDateRange(adventure, dateFormatter);
4548
4589
  const hasTripInfo = !!adventure || !!booking || !!traveller;
4590
+ const fieldErrors = {};
4591
+ if (submitAttempted) {
4592
+ for (const field of sortedFields) {
4593
+ if (field.required && isFieldEmpty(field, current[field.id])) {
4594
+ fieldErrors[field.id] = L.requiredFieldError;
4595
+ }
4596
+ }
4597
+ }
4549
4598
  return /* @__PURE__ */ jsxRuntime.jsxs(
4550
4599
  "form",
4551
4600
  {
@@ -4629,7 +4678,8 @@ function RegistrationForm({
4629
4678
  value: current[field.id],
4630
4679
  onChange: (v) => setField(field.id, v),
4631
4680
  defaultPhoneCountry,
4632
- labels: L
4681
+ labels: L,
4682
+ error: fieldErrors[field.id]
4633
4683
  }
4634
4684
  ),
4635
4685
  ((_a2 = field.helpText) == null ? void 0 : _a2.trim()) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground font-ui leading-relaxed", children: field.helpText.trim() })