@planetaexo/design-system 0.4.11 → 0.4.13

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 CHANGED
@@ -426,6 +426,19 @@ interface TravellerFormInviteLink {
426
426
  interface TravellerFormInviteEmailLabels {
427
427
  buttonLabel?: string;
428
428
  }
429
+ interface TripDetails {
430
+ bookingNumber: string;
431
+ adventure: string;
432
+ startingDate: string;
433
+ partner?: string | null;
434
+ }
435
+ interface TripDetailsLabels {
436
+ title?: string;
437
+ bookingNumber?: string;
438
+ adventure?: string;
439
+ startingDate?: string;
440
+ partner?: string;
441
+ }
429
442
  interface TravellerFormInviteEmailProps {
430
443
  /** Saudação (ex: "Olá Carlos!" ou "Hello!") */
431
444
  greeting: string;
@@ -445,8 +458,14 @@ interface TravellerFormInviteEmailProps {
445
458
  buttonLabel?: string;
446
459
  /** className extra no container (para uso no styleguide) */
447
460
  className?: string;
448
- }
449
- declare function TravellerFormInviteEmail({ greeting, bodyIntro, bodyInstruction, links, ignoreNote, teamSignature, logoUrl, buttonLabel, className, }: TravellerFormInviteEmailProps): react_jsx_runtime.JSX.Element;
461
+ /** Dados da viagem para exibição na seção Trip Details */
462
+ tripDetails?: TripDetails;
463
+ /** Labels da seção Trip Details (para i18n) */
464
+ tripDetailsLabels?: TripDetailsLabels;
465
+ /** Texto após os links (instrução de obrigatoriedade). */
466
+ bodyFooter?: string;
467
+ }
468
+ declare function TravellerFormInviteEmail({ greeting, bodyIntro, bodyInstruction, links, ignoreNote, teamSignature, logoUrl, buttonLabel, className, tripDetails, tripDetailsLabels, bodyFooter, }: TravellerFormInviteEmailProps): react_jsx_runtime.JSX.Element;
450
469
 
451
470
  interface BookingFormValues {
452
471
  adults: number;
package/dist/index.d.ts CHANGED
@@ -426,6 +426,19 @@ interface TravellerFormInviteLink {
426
426
  interface TravellerFormInviteEmailLabels {
427
427
  buttonLabel?: string;
428
428
  }
429
+ interface TripDetails {
430
+ bookingNumber: string;
431
+ adventure: string;
432
+ startingDate: string;
433
+ partner?: string | null;
434
+ }
435
+ interface TripDetailsLabels {
436
+ title?: string;
437
+ bookingNumber?: string;
438
+ adventure?: string;
439
+ startingDate?: string;
440
+ partner?: string;
441
+ }
429
442
  interface TravellerFormInviteEmailProps {
430
443
  /** Saudação (ex: "Olá Carlos!" ou "Hello!") */
431
444
  greeting: string;
@@ -445,8 +458,14 @@ interface TravellerFormInviteEmailProps {
445
458
  buttonLabel?: string;
446
459
  /** className extra no container (para uso no styleguide) */
447
460
  className?: string;
448
- }
449
- declare function TravellerFormInviteEmail({ greeting, bodyIntro, bodyInstruction, links, ignoreNote, teamSignature, logoUrl, buttonLabel, className, }: TravellerFormInviteEmailProps): react_jsx_runtime.JSX.Element;
461
+ /** Dados da viagem para exibição na seção Trip Details */
462
+ tripDetails?: TripDetails;
463
+ /** Labels da seção Trip Details (para i18n) */
464
+ tripDetailsLabels?: TripDetailsLabels;
465
+ /** Texto após os links (instrução de obrigatoriedade). */
466
+ bodyFooter?: string;
467
+ }
468
+ declare function TravellerFormInviteEmail({ greeting, bodyIntro, bodyInstruction, links, ignoreNote, teamSignature, logoUrl, buttonLabel, className, tripDetails, tripDetailsLabels, bodyFooter, }: TravellerFormInviteEmailProps): react_jsx_runtime.JSX.Element;
450
469
 
451
470
  interface BookingFormValues {
452
471
  adults: number;
package/dist/index.js CHANGED
@@ -1188,7 +1188,7 @@ function CountrySearchField({
1188
1188
  type: "button",
1189
1189
  onClick: () => handleSelect(c.code),
1190
1190
  className: cn(
1191
- "flex w-full items-center px-3 py-2 text-sm font-ui text-left transition-colors hover:bg-muted",
1191
+ "flex w-full items-center px-3 py-2 text-sm font-ui text-left text-foreground transition-colors hover:bg-muted",
1192
1192
  c.code === value && "bg-primary/10 text-primary font-semibold"
1193
1193
  ),
1194
1194
  children: c.name
@@ -3403,8 +3403,12 @@ function TravellerFormInviteEmail({
3403
3403
  teamSignature,
3404
3404
  logoUrl,
3405
3405
  buttonLabel = "Preencher formul\xE1rio",
3406
- className
3406
+ className,
3407
+ tripDetails,
3408
+ tripDetailsLabels,
3409
+ bodyFooter
3407
3410
  }) {
3411
+ var _a, _b, _c, _d, _e;
3408
3412
  return /* @__PURE__ */ jsxs(
3409
3413
  "div",
3410
3414
  {
@@ -3455,6 +3459,29 @@ function TravellerFormInviteEmail({
3455
3459
  }
3456
3460
  )
3457
3461
  ] }, i)) }),
3462
+ tripDetails && /* @__PURE__ */ jsxs("div", { style: {
3463
+ backgroundColor: "#f8fafc",
3464
+ border: "1px solid #e2e8f0",
3465
+ borderRadius: "8px",
3466
+ padding: "16px 20px",
3467
+ margin: "24px 0"
3468
+ }, children: [
3469
+ /* @__PURE__ */ jsx("p", { style: { fontWeight: "600", margin: "0 0 12px 0", fontSize: "14px" }, children: (_a = tripDetailsLabels == null ? void 0 : tripDetailsLabels.title) != null ? _a : "\u{1F4DD} Trip details:" }),
3470
+ [
3471
+ [(_b = tripDetailsLabels == null ? void 0 : tripDetailsLabels.bookingNumber) != null ? _b : "Booking Number", tripDetails.bookingNumber],
3472
+ [(_c = tripDetailsLabels == null ? void 0 : tripDetailsLabels.adventure) != null ? _c : "Adventure", tripDetails.adventure],
3473
+ [(_d = tripDetailsLabels == null ? void 0 : tripDetailsLabels.startingDate) != null ? _d : "Starting Date", tripDetails.startingDate],
3474
+ ...tripDetails.partner ? [[(_e = tripDetailsLabels == null ? void 0 : tripDetailsLabels.partner) != null ? _e : "Partner", tripDetails.partner]] : []
3475
+ ].map(([label, value], i) => /* @__PURE__ */ jsxs("p", { style: { margin: "4px 0", fontSize: "13px" }, children: [
3476
+ /* @__PURE__ */ jsxs("strong", { children: [
3477
+ label,
3478
+ ":"
3479
+ ] }),
3480
+ " ",
3481
+ value
3482
+ ] }, i))
3483
+ ] }),
3484
+ bodyFooter && /* @__PURE__ */ jsx("p", { style: { fontSize: "14px", lineHeight: "1.6", margin: "0 0 16px" }, children: bodyFooter }),
3458
3485
  /* @__PURE__ */ jsx(
3459
3486
  "p",
3460
3487
  {
@@ -4211,6 +4238,83 @@ function FloatingTextarea({
4211
4238
  )
4212
4239
  ] });
4213
4240
  }
4241
+ function SelectField({ field, value, onChange, error }) {
4242
+ var _a, _b, _c;
4243
+ const [open, setOpen] = React21.useState(false);
4244
+ const containerRef = React21.useRef(null);
4245
+ const options = (_a = field.options) != null ? _a : [];
4246
+ const selectedOpt = (_b = options.find((o) => o.value === value)) != null ? _b : null;
4247
+ React21.useEffect(() => {
4248
+ if (!open) return;
4249
+ const handleOutside = (e) => {
4250
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
4251
+ setOpen(false);
4252
+ }
4253
+ };
4254
+ document.addEventListener("mousedown", handleOutside);
4255
+ return () => document.removeEventListener("mousedown", handleOutside);
4256
+ }, [open]);
4257
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative w-full", children: [
4258
+ /* @__PURE__ */ jsxs(
4259
+ "button",
4260
+ {
4261
+ type: "button",
4262
+ onClick: () => setOpen((o) => !o),
4263
+ className: cn(
4264
+ "relative flex w-full items-center rounded-lg border border-border bg-background h-14 px-3 text-left transition-colors",
4265
+ open && "border-primary ring-1 ring-primary",
4266
+ error && !open && "border-destructive"
4267
+ ),
4268
+ children: [
4269
+ /* @__PURE__ */ jsxs(
4270
+ "span",
4271
+ {
4272
+ className: cn(
4273
+ "pointer-events-none absolute left-3 transition-all duration-150 font-ui",
4274
+ selectedOpt || open ? cn("top-2 text-xs", open ? "text-primary" : "text-muted-foreground") : "top-1/2 -translate-y-1/2 text-base text-muted-foreground"
4275
+ ),
4276
+ children: [
4277
+ field.label,
4278
+ field.required && /* @__PURE__ */ jsx("span", { className: "text-primary ml-0.5", children: "*" })
4279
+ ]
4280
+ }
4281
+ ),
4282
+ /* @__PURE__ */ jsx("span", { className: cn("flex-1 pt-3 text-base font-ui truncate", selectedOpt ? "text-foreground" : "invisible"), children: (_c = selectedOpt == null ? void 0 : selectedOpt.label) != null ? _c : "\u2014" }),
4283
+ /* @__PURE__ */ jsx(
4284
+ "svg",
4285
+ {
4286
+ className: cn("h-4 w-4 shrink-0 text-muted-foreground transition-transform", open && "rotate-180"),
4287
+ viewBox: "0 0 24 24",
4288
+ fill: "none",
4289
+ stroke: "currentColor",
4290
+ strokeWidth: "2",
4291
+ strokeLinecap: "round",
4292
+ strokeLinejoin: "round",
4293
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
4294
+ }
4295
+ )
4296
+ ]
4297
+ }
4298
+ ),
4299
+ open && /* @__PURE__ */ jsx("div", { className: "absolute top-[calc(100%+4px)] left-0 right-0 z-50 rounded-xl border border-border bg-background shadow-lg overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "max-h-52 overflow-y-auto py-1", children: options.map((opt) => /* @__PURE__ */ jsx(
4300
+ "button",
4301
+ {
4302
+ type: "button",
4303
+ onClick: () => {
4304
+ onChange(opt.value);
4305
+ setOpen(false);
4306
+ },
4307
+ className: cn(
4308
+ "flex w-full items-center px-3 py-2 text-sm font-ui text-left text-foreground transition-colors hover:bg-muted",
4309
+ opt.value === value && "bg-primary/10 text-primary font-semibold"
4310
+ ),
4311
+ children: opt.label
4312
+ },
4313
+ opt.value
4314
+ )) }) }),
4315
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive font-ui", children: error })
4316
+ ] });
4317
+ }
4214
4318
  function FieldRenderer({
4215
4319
  field,
4216
4320
  value,
@@ -4219,7 +4323,7 @@ function FieldRenderer({
4219
4323
  labels,
4220
4324
  error
4221
4325
  }) {
4222
- var _a, _b, _c;
4326
+ var _a, _b;
4223
4327
  const fieldId = `rf-${field.id}`;
4224
4328
  if (field.type === "name") {
4225
4329
  const v = asName(value);
@@ -4333,24 +4437,18 @@ function FieldRenderer({
4333
4437
  );
4334
4438
  }
4335
4439
  if (field.type === "select") {
4336
- const options = (_a = field.options) != null ? _a : [];
4337
- return /* @__PURE__ */ jsxs(
4338
- FloatingSelect,
4440
+ return /* @__PURE__ */ jsx(
4441
+ SelectField,
4339
4442
  {
4340
- label: field.label,
4341
- required: field.required,
4443
+ field,
4342
4444
  value: typeof value === "string" ? value : "",
4343
- onChange: (e) => onChange(e.target.value),
4344
- error,
4345
- children: [
4346
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, hidden: true }),
4347
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
4348
- ]
4445
+ onChange: (v) => onChange(v),
4446
+ error
4349
4447
  }
4350
4448
  );
4351
4449
  }
4352
4450
  if (field.type === "radio") {
4353
- const options = (_b = field.options) != null ? _b : [];
4451
+ const options = (_a = field.options) != null ? _a : [];
4354
4452
  return /* @__PURE__ */ jsxs("div", { children: [
4355
4453
  /* @__PURE__ */ jsxs("p", { className: "mb-3 text-sm font-ui font-medium text-foreground", children: [
4356
4454
  field.label,
@@ -4385,11 +4483,12 @@ function FieldRenderer({
4385
4483
  opt.value
4386
4484
  ))
4387
4485
  }
4388
- )
4486
+ ),
4487
+ error && /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-destructive font-ui", children: error })
4389
4488
  ] });
4390
4489
  }
4391
4490
  if (field.type === "checkbox") {
4392
- const options = (_c = field.options) != null ? _c : [];
4491
+ const options = (_b = field.options) != null ? _b : [];
4393
4492
  if (options.length === 0) {
4394
4493
  return /* @__PURE__ */ jsxs("label", { className: "flex min-h-9 cursor-pointer items-center gap-2.5 font-ui text-sm text-foreground", children: [
4395
4494
  /* @__PURE__ */ jsx(
@@ -4588,15 +4687,17 @@ function RegistrationForm({
4588
4687
  }
4589
4688
  }
4590
4689
  }
4690
+ const termsError = submitAttempted && termsEnabled && !termsAccepted;
4591
4691
  const firstErrorFieldId = Object.keys(fieldErrors)[0];
4692
+ const scrollTargetId = firstErrorFieldId ? `rf-${firstErrorFieldId}` : termsError ? "rf-terms" : null;
4592
4693
  React21.useEffect(() => {
4593
- if (!submitAttempted || !firstErrorFieldId) return;
4694
+ if (!submitAttempted || !scrollTargetId) return;
4594
4695
  const timer = setTimeout(() => {
4595
- const elem = document.getElementById(`rf-${firstErrorFieldId}`);
4696
+ const elem = document.getElementById(scrollTargetId);
4596
4697
  if (elem) elem.scrollIntoView({ behavior: "smooth", block: "center" });
4597
4698
  }, 50);
4598
4699
  return () => clearTimeout(timer);
4599
- }, [submitAttempted, firstErrorFieldId, validationErrors]);
4700
+ }, [submitAttempted, scrollTargetId]);
4600
4701
  return /* @__PURE__ */ jsxs(
4601
4702
  "form",
4602
4703
  {
@@ -4687,58 +4788,86 @@ function RegistrationForm({
4687
4788
  ((_a2 = field.helpText) == null ? void 0 : _a2.trim()) && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground font-ui leading-relaxed", children: field.helpText.trim() })
4688
4789
  ] }, field.id);
4689
4790
  }) }),
4690
- termsEnabled && terms && /* @__PURE__ */ jsxs(FormSection2, { title: L.termsSectionTitle, children: [
4791
+ termsEnabled && terms && /* @__PURE__ */ jsx("div", { id: "rf-terms", children: /* @__PURE__ */ jsxs(FormSection2, { title: L.termsSectionTitle, children: [
4691
4792
  /* @__PURE__ */ jsx("div", { className: "max-h-72 overflow-y-auto rounded-lg border border-border bg-muted/30 p-4", children: /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap text-sm leading-relaxed text-foreground font-ui", children: terms.markdown }) }),
4692
- acceptControl === "checkbox" ? /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-start gap-2.5 font-ui text-sm text-foreground", children: [
4693
- /* @__PURE__ */ jsx(
4694
- "input",
4793
+ acceptControl === "checkbox" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
4794
+ /* @__PURE__ */ jsxs(
4795
+ "label",
4695
4796
  {
4696
- type: "checkbox",
4697
- checked: termsAccepted,
4698
- required: true,
4699
- onChange: (e) => setField(TERMS_ACCEPT_KEY, e.target.checked),
4700
- className: "mt-0.5 h-4 w-4 shrink-0 accent-primary cursor-pointer"
4701
- }
4702
- ),
4703
- /* @__PURE__ */ jsx("span", { children: L.termsAccept })
4704
- ] }) : /* @__PURE__ */ jsxs(
4705
- "div",
4706
- {
4707
- role: "radiogroup",
4708
- "aria-label": L.termsSectionTitle,
4709
- className: "flex flex-wrap items-center gap-x-6 gap-y-3",
4710
- children: [
4711
- /* @__PURE__ */ jsxs("label", { className: "flex min-h-9 cursor-pointer items-center gap-2.5 font-ui text-sm text-foreground", children: [
4797
+ className: cn(
4798
+ "flex cursor-pointer items-start gap-2.5 font-ui text-sm",
4799
+ termsError ? "text-destructive" : "text-foreground"
4800
+ ),
4801
+ children: [
4712
4802
  /* @__PURE__ */ jsx(
4713
4803
  "input",
4714
4804
  {
4715
- type: "radio",
4716
- name: "registration-terms-accept",
4805
+ type: "checkbox",
4717
4806
  checked: termsAccepted,
4718
4807
  required: true,
4719
- onChange: () => setField(TERMS_ACCEPT_KEY, true),
4720
- className: "h-4 w-4 shrink-0 accent-primary cursor-pointer"
4808
+ onChange: (e) => setField(TERMS_ACCEPT_KEY, e.target.checked),
4809
+ className: "mt-0.5 h-4 w-4 shrink-0 accent-primary cursor-pointer"
4721
4810
  }
4722
4811
  ),
4723
- L.termsAccept
4724
- ] }),
4725
- /* @__PURE__ */ jsxs("label", { className: "flex min-h-9 cursor-pointer items-center gap-2.5 font-ui text-sm text-muted-foreground", children: [
4726
- /* @__PURE__ */ jsx(
4727
- "input",
4812
+ /* @__PURE__ */ jsxs("span", { children: [
4813
+ L.termsAccept,
4814
+ /* @__PURE__ */ jsx("span", { className: "text-primary ml-0.5", children: "*" })
4815
+ ] })
4816
+ ]
4817
+ }
4818
+ ),
4819
+ termsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive font-ui", children: L.requiredFieldError })
4820
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
4821
+ /* @__PURE__ */ jsxs(
4822
+ "div",
4823
+ {
4824
+ role: "radiogroup",
4825
+ "aria-label": L.termsSectionTitle,
4826
+ className: "flex flex-wrap items-center gap-x-6 gap-y-3",
4827
+ children: [
4828
+ /* @__PURE__ */ jsxs(
4829
+ "label",
4728
4830
  {
4729
- type: "radio",
4730
- name: "registration-terms-accept",
4731
- checked: current[TERMS_ACCEPT_KEY] === false,
4732
- onChange: () => setField(TERMS_ACCEPT_KEY, false),
4733
- className: "h-4 w-4 shrink-0 accent-primary cursor-pointer"
4831
+ className: cn(
4832
+ "flex min-h-9 cursor-pointer items-center gap-2.5 font-ui text-sm",
4833
+ termsError ? "text-destructive" : "text-foreground"
4834
+ ),
4835
+ children: [
4836
+ /* @__PURE__ */ jsx(
4837
+ "input",
4838
+ {
4839
+ type: "radio",
4840
+ name: "registration-terms-accept",
4841
+ checked: termsAccepted,
4842
+ required: true,
4843
+ onChange: () => setField(TERMS_ACCEPT_KEY, true),
4844
+ className: "h-4 w-4 shrink-0 accent-primary cursor-pointer"
4845
+ }
4846
+ ),
4847
+ L.termsAccept,
4848
+ /* @__PURE__ */ jsx("span", { className: "text-primary ml-0.5", children: "*" })
4849
+ ]
4734
4850
  }
4735
4851
  ),
4736
- L.termsDecline
4737
- ] })
4738
- ]
4739
- }
4740
- )
4741
- ] }),
4852
+ /* @__PURE__ */ jsxs("label", { className: "flex min-h-9 cursor-pointer items-center gap-2.5 font-ui text-sm text-muted-foreground", children: [
4853
+ /* @__PURE__ */ jsx(
4854
+ "input",
4855
+ {
4856
+ type: "radio",
4857
+ name: "registration-terms-accept",
4858
+ checked: current[TERMS_ACCEPT_KEY] === false,
4859
+ onChange: () => setField(TERMS_ACCEPT_KEY, false),
4860
+ className: "h-4 w-4 shrink-0 accent-primary cursor-pointer"
4861
+ }
4862
+ ),
4863
+ L.termsDecline
4864
+ ] })
4865
+ ]
4866
+ }
4867
+ ),
4868
+ termsError && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive font-ui", children: L.requiredFieldError })
4869
+ ] })
4870
+ ] }) }),
4742
4871
  error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive font-ui", children: error }),
4743
4872
  /* @__PURE__ */ jsx("div", { className: "flex justify-center pt-2", children: /* @__PURE__ */ jsx(
4744
4873
  "button",