@hook-sdk/template 0.26.0 → 0.27.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.js CHANGED
@@ -632,8 +632,8 @@ function usePaywallState() {
632
632
  opening: submitting,
633
633
  availableMethods: methods,
634
634
  monthlyEquivalent,
635
- dismissPix: () => {
636
- },
635
+ // G154 fix (template 0.24.0 + SDK 0.26.0): wired to SDK action.
636
+ dismissPix: subscription.clearPixPending,
637
637
  refreshPlan: () => {
638
638
  }
639
639
  };
@@ -3871,22 +3871,39 @@ function useFeature(name) {
3871
3871
  import { useEffect as useEffect17, useMemo as useMemo12 } from "react";
3872
3872
  import { useHook as useHook21 } from "@hook-sdk/sdk";
3873
3873
 
3874
- // src/components/paywall/PaywallMethodTabs.tsx
3874
+ // src/components/paywall/PaywallProvider.tsx
3875
+ import { createContext as createContext4 } from "react";
3875
3876
  import { jsx as jsx32 } from "react/jsx-runtime";
3877
+ var PaywallContext = createContext4(null);
3878
+ function PaywallProvider({ children }) {
3879
+ const state = usePaywallState();
3880
+ return /* @__PURE__ */ jsx32(PaywallContext.Provider, { value: state, children });
3881
+ }
3882
+
3883
+ // src/components/paywall/usePaywallContext.ts
3884
+ import { useContext as useContext5 } from "react";
3885
+ function usePaywallContext() {
3886
+ const ctx = useContext5(PaywallContext);
3887
+ if (!ctx) {
3888
+ throw new Error("usePaywallContext must be used within <PaywallProvider>");
3889
+ }
3890
+ return ctx;
3891
+ }
3892
+
3893
+ // src/components/paywall/PaywallMethodTabs.tsx
3894
+ import { jsx as jsx33 } from "react/jsx-runtime";
3876
3895
  function PaywallMethodTabs({
3877
- methods,
3878
- selected,
3879
- onSelect,
3880
3896
  labels,
3881
3897
  className,
3882
3898
  tabClassName,
3883
3899
  tabActiveClassName
3884
3900
  }) {
3901
+ const { methods, selectedMethod, selectMethod } = usePaywallContext();
3885
3902
  if (methods.length < 2) return null;
3886
- return /* @__PURE__ */ jsx32("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3887
- const active = m === selected;
3903
+ return /* @__PURE__ */ jsx33("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3904
+ const active = m === selectedMethod;
3888
3905
  const label = labels[m] ?? m;
3889
- return /* @__PURE__ */ jsx32(
3906
+ return /* @__PURE__ */ jsx33(
3890
3907
  "button",
3891
3908
  {
3892
3909
  type: "button",
@@ -3894,7 +3911,7 @@ function PaywallMethodTabs({
3894
3911
  "aria-selected": active,
3895
3912
  "aria-controls": `paywall-tab-${m}`,
3896
3913
  tabIndex: active ? 0 : -1,
3897
- onClick: () => onSelect(m),
3914
+ onClick: () => selectMethod(m),
3898
3915
  className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
3899
3916
  children: label
3900
3917
  },
@@ -3904,36 +3921,51 @@ function PaywallMethodTabs({
3904
3921
  }
3905
3922
 
3906
3923
  // src/components/paywall/PaywallMethodContent.tsx
3907
- import { jsx as jsx33 } from "react/jsx-runtime";
3908
- function PaywallMethodContent({
3909
- method,
3910
- copy,
3911
- hasConsumedTrial = false,
3912
- className,
3913
- rowClassName
3914
- }) {
3915
- const useCardConsumed = method === "card" && hasConsumedTrial && copy.cardConsumedTrial;
3916
- const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : method === "pix-auto" || method === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
3917
- return /* @__PURE__ */ jsx33("div", { role: "tabpanel", id: `paywall-tab-${method}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx33("div", { className: rowClassName, children: row }, i)) });
3924
+ import { jsx as jsx34 } from "react/jsx-runtime";
3925
+ function PaywallMethodContent({ copy, className, rowClassName }) {
3926
+ const { selectedMethod, hasConsumedTrial } = usePaywallContext();
3927
+ const useCardConsumed = selectedMethod === "card" && hasConsumedTrial && copy.cardConsumedTrial;
3928
+ const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : selectedMethod === "pix-auto" || selectedMethod === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
3929
+ return /* @__PURE__ */ jsx34("div", { role: "tabpanel", id: `paywall-tab-${selectedMethod}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx34("div", { className: rowClassName, children: row }, i)) });
3918
3930
  }
3919
3931
 
3920
3932
  // src/components/paywall/PaywallCyclePicker.tsx
3921
- import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
3933
+ import { jsx as jsx35, jsxs as jsxs21 } from "react/jsx-runtime";
3934
+ var VARIANT_CLASSES = {
3935
+ default: { card: "", cardSelected: "" },
3936
+ "premium-gold": {
3937
+ card: "",
3938
+ cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
3939
+ },
3940
+ "pink-pill": {
3941
+ card: "rounded-2xl",
3942
+ cardSelected: "border-2 border-pink-500"
3943
+ }
3944
+ };
3922
3945
  function PaywallCyclePicker({
3923
- cycles,
3924
- selected,
3925
- onSelect,
3926
- priceCentsByCycle,
3927
- anchorCentsByCycle,
3928
- monthlyEquivByCycle,
3929
3946
  labels,
3930
3947
  className,
3931
3948
  cardClassName,
3932
3949
  cardSelectedClassName,
3933
- anchorClassName
3950
+ anchorClassName,
3951
+ variant = "default",
3952
+ render
3934
3953
  }) {
3954
+ const ctx = usePaywallContext();
3955
+ const { cycle: selected, setCycle, plan, anchorPriceCents } = ctx;
3956
+ const cycles = ["MONTHLY", "YEARLY"];
3957
+ if (render) {
3958
+ return /* @__PURE__ */ jsx35("div", { className, children: render({ cycles, selected, setCycle, plan, anchorPriceCents }) });
3959
+ }
3935
3960
  if (cycles.length < 2) return null;
3936
- return /* @__PURE__ */ jsx34(
3961
+ const v = VARIANT_CLASSES[variant];
3962
+ const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
3963
+ const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
3964
+ const monthlyCents = plan?.monthlyCents ?? 0;
3965
+ const yearlyCents = plan?.yearlyCents ?? 0;
3966
+ const anchorMonthly = plan?.anchorMonthlyCents ?? null;
3967
+ const anchorYearly = plan?.anchorYearlyCents ?? null;
3968
+ return /* @__PURE__ */ jsx35(
3937
3969
  "div",
3938
3970
  {
3939
3971
  role: "radiogroup",
@@ -3943,21 +3975,25 @@ function PaywallCyclePicker({
3943
3975
  const active = c === selected;
3944
3976
  const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
3945
3977
  const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
3946
- const mainCents = c === "YEARLY" ? monthlyEquivByCycle[c] : priceCentsByCycle[c];
3947
- const anchorCents = anchorCentsByCycle[c];
3978
+ const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
3979
+ const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
3948
3980
  return /* @__PURE__ */ jsxs21(
3949
3981
  "button",
3950
3982
  {
3951
3983
  type: "button",
3952
3984
  role: "radio",
3953
3985
  "aria-checked": active,
3954
- onClick: () => onSelect(c),
3955
- className: ["flex flex-col items-center gap-0.5", cardClassName, active ? cardSelectedClassName : ""].filter(Boolean).join(" "),
3986
+ onClick: () => setCycle(c),
3987
+ className: [
3988
+ "flex flex-col items-center gap-0.5",
3989
+ composedCardClassName,
3990
+ active ? composedCardSelectedClassName : ""
3991
+ ].filter(Boolean).join(" "),
3956
3992
  children: [
3957
- /* @__PURE__ */ jsx34("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
3958
- /* @__PURE__ */ jsx34("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
3959
- /* @__PURE__ */ jsx34("span", { className: "text-xs opacity-60 leading-tight", children: label }),
3960
- anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx34("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx34("s", { children: formatBRL(anchorCents) }) }) : null
3993
+ /* @__PURE__ */ jsx35("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
3994
+ /* @__PURE__ */ jsx35("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
3995
+ /* @__PURE__ */ jsx35("span", { className: "text-xs opacity-60 leading-tight", children: label }),
3996
+ anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx35("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx35("s", { children: formatBRL(anchorCents) }) }) : null
3961
3997
  ]
3962
3998
  },
3963
3999
  c
@@ -3967,62 +4003,51 @@ function PaywallCyclePicker({
3967
4003
  );
3968
4004
  }
3969
4005
 
3970
- // src/components/paywall/PaywallCta.tsx
3971
- import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
3972
- function PaywallCta({
3973
- ctaLabel,
3974
- loadingLabel,
3975
- switchHint,
3976
- trustLine,
3977
- onClick,
3978
- disabled = false,
3979
- loading = false,
3980
- className,
3981
- buttonClassName,
3982
- switchHintClassName,
3983
- trustClassName
3984
- }) {
3985
- const label = loading && loadingLabel ? loadingLabel : ctaLabel;
3986
- return /* @__PURE__ */ jsxs22("div", { className, children: [
3987
- /* @__PURE__ */ jsx35(
3988
- "button",
3989
- {
3990
- type: "button",
3991
- onClick,
3992
- disabled: disabled || loading,
3993
- className: buttonClassName,
3994
- children: label
3995
- }
3996
- ),
3997
- switchHint ? /* @__PURE__ */ jsx35("p", { className: switchHintClassName, children: switchHint }) : null,
3998
- /* @__PURE__ */ jsx35("p", { className: trustClassName, children: trustLine })
3999
- ] });
4000
- }
4001
-
4002
4006
  // src/components/paywall/Paywall.tsx
4003
- import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
4007
+ import { jsx as jsx36, jsxs as jsxs22 } from "react/jsx-runtime";
4004
4008
  var NBSP = "\xA0";
4005
4009
  function Paywall({
4006
4010
  copy,
4007
4011
  themeClasses = {},
4008
4012
  slots = {},
4009
4013
  onBeforeCheckout
4014
+ }) {
4015
+ return /* @__PURE__ */ jsx36(PaywallProvider, { children: /* @__PURE__ */ jsx36(
4016
+ PaywallInner,
4017
+ {
4018
+ copy,
4019
+ themeClasses,
4020
+ slots,
4021
+ onBeforeCheckout
4022
+ }
4023
+ ) });
4024
+ }
4025
+ function PaywallInner({
4026
+ copy,
4027
+ themeClasses = {},
4028
+ slots = {},
4029
+ onBeforeCheckout
4010
4030
  }) {
4011
4031
  const { track: track2 } = useHook21();
4012
- const s = usePaywallState();
4032
+ const s = usePaywallContext();
4013
4033
  const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
4014
- const monthlyEquivLabel = formatBRL(s.currentMonthlyEquivCents).replace(new RegExp(NBSP, "g"), " ");
4015
4034
  const trialDaysCardLabel = String(s.trialDaysCard);
4016
4035
  const ctaLabel = useMemo12(() => {
4017
4036
  if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
4018
4037
  if (s.selectedMethod === "card") {
4019
4038
  if (s.hasConsumedTrial && copy.cardConsumedTrial) {
4020
- return interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
4039
+ return interp(copy.cardConsumedTrial.ctaTemplate, {
4040
+ price: priceLabel,
4041
+ days: trialDaysCardLabel
4042
+ });
4021
4043
  }
4022
4044
  if (s.trialDaysCard > 0) {
4023
4045
  return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
4024
4046
  }
4025
- return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel }) : `Assinar por ${priceLabel}`;
4047
+ return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, {
4048
+ price: priceLabel,
4049
+ days: trialDaysCardLabel
4050
+ }) : `Assinar por ${priceLabel}`;
4026
4051
  }
4027
4052
  return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
4028
4053
  }, [
@@ -4060,10 +4085,10 @@ function Paywall({
4060
4085
  await s.submit();
4061
4086
  };
4062
4087
  const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
4063
- return /* @__PURE__ */ jsxs23("div", { className: themeClasses.container, children: [
4088
+ return /* @__PURE__ */ jsxs22("div", { className: themeClasses.container, children: [
4064
4089
  slots.heroSlot,
4065
4090
  /* @__PURE__ */ jsx36("h1", { className: themeClasses.headline, children: copy.headline }),
4066
- /* @__PURE__ */ jsx36("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs23("li", { className: themeClasses.feature, children: [
4091
+ /* @__PURE__ */ jsx36("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs22("li", { className: themeClasses.feature, children: [
4067
4092
  "\u2713 ",
4068
4093
  /* @__PURE__ */ jsx36("span", { children: f })
4069
4094
  ] }, f)) }),
@@ -4071,21 +4096,6 @@ function Paywall({
4071
4096
  slots.cyclePickerSlot ?? /* @__PURE__ */ jsx36(
4072
4097
  PaywallCyclePicker,
4073
4098
  {
4074
- cycles: ["MONTHLY", "YEARLY"],
4075
- selected: s.cycle,
4076
- onSelect: s.selectCycle,
4077
- priceCentsByCycle: {
4078
- MONTHLY: priceCentsForCycle(s, "MONTHLY"),
4079
- YEARLY: priceCentsForCycle(s, "YEARLY")
4080
- },
4081
- anchorCentsByCycle: {
4082
- MONTHLY: anchorForCycle(s, "MONTHLY"),
4083
- YEARLY: anchorForCycle(s, "YEARLY")
4084
- },
4085
- monthlyEquivByCycle: {
4086
- MONTHLY: priceCentsForCycle(s, "MONTHLY"),
4087
- YEARLY: Math.round(priceCentsForCycle(s, "YEARLY") / 12)
4088
- },
4089
4099
  labels: copy.cycle,
4090
4100
  cardClassName: themeClasses.cycleCard,
4091
4101
  cardSelectedClassName: themeClasses.cycleCardSelected,
@@ -4095,9 +4105,6 @@ function Paywall({
4095
4105
  /* @__PURE__ */ jsx36(
4096
4106
  PaywallMethodTabs,
4097
4107
  {
4098
- methods: s.methods,
4099
- selected: s.selectedMethod,
4100
- onSelect: s.selectMethod,
4101
4108
  labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
4102
4109
  className: themeClasses.tabs,
4103
4110
  tabClassName: themeClasses.tab,
@@ -4107,7 +4114,6 @@ function Paywall({
4107
4114
  /* @__PURE__ */ jsx36(
4108
4115
  PaywallMethodContent,
4109
4116
  {
4110
- method: s.selectedMethod,
4111
4117
  copy: {
4112
4118
  pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
4113
4119
  card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
@@ -4118,27 +4124,27 @@ function Paywall({
4118
4124
  ctaTemplate: copy.cardConsumedTrial.ctaTemplate
4119
4125
  } : void 0
4120
4126
  },
4121
- hasConsumedTrial: s.hasConsumedTrial,
4122
4127
  className: themeClasses.tabContent,
4123
4128
  rowClassName: themeClasses.tabContentRow
4124
4129
  }
4125
4130
  ),
4126
4131
  slots.beforeCtaSlot,
4127
- /* @__PURE__ */ jsx36(
4128
- PaywallCta,
4129
- {
4130
- ctaLabel,
4131
- loadingLabel: "Abrindo checkout\u2026",
4132
- switchHint,
4133
- trustLine: copy.trustLine,
4134
- onClick: handleCta,
4135
- disabled: s.submitting,
4136
- loading: s.submitting,
4137
- buttonClassName: ctaTheme,
4138
- switchHintClassName: themeClasses.switchHint,
4139
- trustClassName: themeClasses.trustLine
4140
- }
4141
- )
4132
+ /* @__PURE__ */ jsxs22("div", { children: [
4133
+ /* @__PURE__ */ jsx36(
4134
+ "button",
4135
+ {
4136
+ type: "button",
4137
+ onClick: () => {
4138
+ void handleCta();
4139
+ },
4140
+ disabled: s.submitting,
4141
+ className: ctaTheme,
4142
+ children: s.submitting ? "Abrindo checkout\u2026" : ctaLabel
4143
+ }
4144
+ ),
4145
+ switchHint ? /* @__PURE__ */ jsx36("p", { className: themeClasses.switchHint, children: switchHint }) : null,
4146
+ /* @__PURE__ */ jsx36("p", { className: themeClasses.trustLine, children: copy.trustLine })
4147
+ ] })
4142
4148
  ] });
4143
4149
  }
4144
4150
  function interp(tpl, vars) {
@@ -4152,13 +4158,438 @@ function interpolateCopy(m, price, days) {
4152
4158
  switchHint: m.switchHint
4153
4159
  };
4154
4160
  }
4155
- function priceCentsForCycle(s, c) {
4156
- return s.plan ? c === "YEARLY" ? s.plan.yearlyCents ?? 0 : s.plan.monthlyCents : 0;
4161
+
4162
+ // src/components/paywall/PaywallCta.tsx
4163
+ import { jsx as jsx37, jsxs as jsxs23 } from "react/jsx-runtime";
4164
+ function PaywallCta({
4165
+ ctaLabel,
4166
+ loadingLabel,
4167
+ switchHint,
4168
+ trustLine,
4169
+ className,
4170
+ buttonClassName,
4171
+ switchHintClassName,
4172
+ trustClassName
4173
+ }) {
4174
+ const { submit, submitting } = usePaywallContext();
4175
+ const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
4176
+ return /* @__PURE__ */ jsxs23("div", { className, children: [
4177
+ /* @__PURE__ */ jsx37(
4178
+ "button",
4179
+ {
4180
+ type: "button",
4181
+ onClick: () => {
4182
+ void submit();
4183
+ },
4184
+ disabled: submitting,
4185
+ className: buttonClassName,
4186
+ children: label
4187
+ }
4188
+ ),
4189
+ switchHint ? /* @__PURE__ */ jsx37("p", { className: switchHintClassName, children: switchHint }) : null,
4190
+ /* @__PURE__ */ jsx37("p", { className: trustClassName, children: trustLine })
4191
+ ] });
4192
+ }
4193
+
4194
+ // src/components/paywall/blocks/PaywallEyebrow.tsx
4195
+ import { jsx as jsx38 } from "react/jsx-runtime";
4196
+ var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
4197
+ function PaywallEyebrow({ text, className }) {
4198
+ return /* @__PURE__ */ jsx38("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
4199
+ }
4200
+
4201
+ // src/components/paywall/blocks/PaywallHero.tsx
4202
+ import { jsx as jsx39, jsxs as jsxs24 } from "react/jsx-runtime";
4203
+ var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
4204
+ function PaywallHero({
4205
+ src,
4206
+ alt = "",
4207
+ headline,
4208
+ aspectRatio = "16/9",
4209
+ gradientClassName,
4210
+ className,
4211
+ headlineClassName,
4212
+ imgClassName,
4213
+ render
4214
+ }) {
4215
+ if (render) {
4216
+ return /* @__PURE__ */ jsx39("div", { className, children: render({ src, headline }) });
4217
+ }
4218
+ return /* @__PURE__ */ jsxs24(
4219
+ "div",
4220
+ {
4221
+ className: ["relative overflow-hidden", className].filter(Boolean).join(" "),
4222
+ style: { aspectRatio },
4223
+ children: [
4224
+ /* @__PURE__ */ jsx39(
4225
+ "img",
4226
+ {
4227
+ src,
4228
+ alt,
4229
+ className: ["absolute inset-0 w-full h-full object-cover", imgClassName].filter(Boolean).join(" ")
4230
+ }
4231
+ ),
4232
+ /* @__PURE__ */ jsx39("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
4233
+ headline ? /* @__PURE__ */ jsx39(
4234
+ "h1",
4235
+ {
4236
+ className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
4237
+ children: headline
4238
+ }
4239
+ ) : null
4240
+ ]
4241
+ }
4242
+ );
4243
+ }
4244
+
4245
+ // src/components/paywall/blocks/PaywallHeadline.tsx
4246
+ import { jsx as jsx40 } from "react/jsx-runtime";
4247
+ var DEFAULT_HEADLINE_CLASSES = "text-2xl font-bold leading-tight";
4248
+ function PaywallHeadline({ text, className, as = "h1" }) {
4249
+ const Tag = as;
4250
+ return /* @__PURE__ */ jsx40(Tag, { className: [DEFAULT_HEADLINE_CLASSES, className].filter(Boolean).join(" "), children: text });
4157
4251
  }
4158
- function anchorForCycle(s, c) {
4159
- if (!s.plan) return null;
4160
- if (c === "YEARLY") return s.plan.anchorYearlyCents ?? null;
4161
- return s.plan.anchorMonthlyCents ?? null;
4252
+
4253
+ // src/components/paywall/blocks/PaywallPriceHeadline.tsx
4254
+ import { jsx as jsx41 } from "react/jsx-runtime";
4255
+ var DEFAULT_CLASS = "text-2xl font-bold leading-tight";
4256
+ var CYCLE_LABEL = {
4257
+ MONTHLY: "mensal",
4258
+ YEARLY: "anual"
4259
+ };
4260
+ function PaywallPriceHeadline({
4261
+ template,
4262
+ className,
4263
+ as = "h1",
4264
+ render
4265
+ }) {
4266
+ const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
4267
+ const yearlyCents = plan?.yearlyCents ?? null;
4268
+ const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
4269
+ const monthlyEquiv = currentMonthlyEquivCents ?? 0;
4270
+ const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
4271
+ const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
4272
+ if (render) {
4273
+ const RootTag2 = as;
4274
+ return /* @__PURE__ */ jsx41(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
4275
+ }
4276
+ const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
4277
+ const RootTag = as;
4278
+ return /* @__PURE__ */ jsx41(RootTag, { className: rootClasses, children: text });
4279
+ }
4280
+
4281
+ // src/components/paywall/blocks/PaywallCountdown.tsx
4282
+ import { useEffect as useEffect18, useRef as useRef8, useState as useState16 } from "react";
4283
+ import { jsx as jsx42 } from "react/jsx-runtime";
4284
+ var DEFAULT_COUNTDOWN_CLASSES = "font-mono tabular-nums";
4285
+ function resolveDeadlineMs(deadline) {
4286
+ if (deadline instanceof Date) return deadline.getTime();
4287
+ if (typeof deadline === "string") return new Date(deadline).getTime();
4288
+ const { sessionStorageKey, durationMs } = deadline;
4289
+ if (typeof window === "undefined" || typeof window.sessionStorage === "undefined") {
4290
+ return Date.now() + durationMs;
4291
+ }
4292
+ const stored = window.sessionStorage.getItem(sessionStorageKey);
4293
+ const parsed = stored ? Number.parseInt(stored, 10) : NaN;
4294
+ const now = Date.now();
4295
+ if (!Number.isFinite(parsed) || parsed < now) {
4296
+ const target = now + durationMs;
4297
+ window.sessionStorage.setItem(sessionStorageKey, String(target));
4298
+ return target;
4299
+ }
4300
+ return parsed;
4301
+ }
4302
+ function computeRemaining(deadlineMs) {
4303
+ const diff = Math.max(0, deadlineMs - Date.now());
4304
+ const totalSeconds = Math.floor(diff / 1e3);
4305
+ const h = Math.floor(totalSeconds / 3600);
4306
+ const m = Math.floor(totalSeconds % 3600 / 60);
4307
+ const s = totalSeconds % 60;
4308
+ return { h, m, s, expired: diff === 0 };
4309
+ }
4310
+ function pad(n) {
4311
+ return String(n).padStart(2, "0");
4312
+ }
4313
+ function PaywallCountdown({
4314
+ deadline,
4315
+ format = "h:m:s",
4316
+ onExpire,
4317
+ className,
4318
+ render
4319
+ }) {
4320
+ const deadlineMsRef = useRef8(null);
4321
+ if (deadlineMsRef.current === null) {
4322
+ deadlineMsRef.current = resolveDeadlineMs(deadline);
4323
+ }
4324
+ const [state, setState] = useState16(() => computeRemaining(deadlineMsRef.current));
4325
+ const expiredCalledRef = useRef8(false);
4326
+ useEffect18(() => {
4327
+ if (state.expired) {
4328
+ if (!expiredCalledRef.current) {
4329
+ expiredCalledRef.current = true;
4330
+ onExpire?.();
4331
+ }
4332
+ return;
4333
+ }
4334
+ const tick = () => {
4335
+ const next = computeRemaining(deadlineMsRef.current);
4336
+ setState(next);
4337
+ if (next.expired && !expiredCalledRef.current) {
4338
+ expiredCalledRef.current = true;
4339
+ onExpire?.();
4340
+ }
4341
+ };
4342
+ const id = setInterval(tick, 1e3);
4343
+ return () => clearInterval(id);
4344
+ }, [state.expired]);
4345
+ if (render) {
4346
+ return /* @__PURE__ */ jsx42("div", { className, children: render(state) });
4347
+ }
4348
+ const formatted = format === "h:m:s" ? `${pad(state.h)}:${pad(state.m)}:${pad(state.s)}` : `${pad(state.h * 60 + state.m)}:${pad(state.s)}`;
4349
+ return /* @__PURE__ */ jsx42("div", { className: [DEFAULT_COUNTDOWN_CLASSES, className].filter(Boolean).join(" "), children: formatted });
4350
+ }
4351
+
4352
+ // src/components/paywall/blocks/PaywallFeatures.tsx
4353
+ import { jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
4354
+ function PaywallFeatures({
4355
+ items,
4356
+ IconComponent,
4357
+ className,
4358
+ itemClassName,
4359
+ iconClassName,
4360
+ render,
4361
+ renderItem
4362
+ }) {
4363
+ if (render) {
4364
+ return /* @__PURE__ */ jsx43("div", { className, children: render({ items }) });
4365
+ }
4366
+ if (renderItem) {
4367
+ return /* @__PURE__ */ jsx43("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsx43("li", { children: renderItem(item, idx) }, idx)) });
4368
+ }
4369
+ return /* @__PURE__ */ jsx43("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsxs25("li", { className: itemClassName, children: [
4370
+ IconComponent ? /* @__PURE__ */ jsx43(IconComponent, { className: iconClassName }) : /* @__PURE__ */ jsx43("span", { className: iconClassName, "aria-hidden": "true", children: "\u2713" }),
4371
+ /* @__PURE__ */ jsx43("span", { children: item })
4372
+ ] }, idx)) });
4373
+ }
4374
+
4375
+ // src/components/paywall/blocks/PaywallFeaturesCard.tsx
4376
+ import { jsx as jsx44, jsxs as jsxs26 } from "react/jsx-runtime";
4377
+ var DEFAULT_CARD_CLASSES = "rounded-xl border p-4";
4378
+ function PaywallFeaturesCard({
4379
+ title,
4380
+ items,
4381
+ className,
4382
+ cardClassName,
4383
+ titleClassName,
4384
+ itemClassName,
4385
+ renderItem
4386
+ }) {
4387
+ return /* @__PURE__ */ jsx44("div", { className, children: /* @__PURE__ */ jsxs26("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
4388
+ title ? /* @__PURE__ */ jsx44("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
4389
+ /* @__PURE__ */ jsx44("ul", { children: items.map(
4390
+ (item, idx) => renderItem ? /* @__PURE__ */ jsx44("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs26("li", { className: itemClassName, children: [
4391
+ /* @__PURE__ */ jsx44("span", { "aria-hidden": "true", children: "\u2022" }),
4392
+ " ",
4393
+ /* @__PURE__ */ jsx44("span", { children: item })
4394
+ ] }, idx)
4395
+ ) })
4396
+ ] }) });
4397
+ }
4398
+
4399
+ // src/components/paywall/blocks/PaywallTrophyBadge.tsx
4400
+ import { jsx as jsx45, jsxs as jsxs27 } from "react/jsx-runtime";
4401
+ var DEFAULT_CHIP_CLASSES = "inline-flex items-center gap-1 px-3 py-1 rounded-full bg-yellow-100 text-yellow-900 text-sm font-medium";
4402
+ var FLOATING_CLASSES = "absolute top-2 right-2 z-10 shadow-md";
4403
+ function PaywallTrophyBadge({
4404
+ text,
4405
+ className,
4406
+ iconClassName,
4407
+ floating = false,
4408
+ render
4409
+ }) {
4410
+ if (render) {
4411
+ return /* @__PURE__ */ jsx45("div", { className, children: render({ text }) });
4412
+ }
4413
+ const rootClasses = [
4414
+ DEFAULT_CHIP_CLASSES,
4415
+ floating ? FLOATING_CLASSES : "",
4416
+ className
4417
+ ].filter(Boolean).join(" ");
4418
+ return /* @__PURE__ */ jsxs27("div", { className: rootClasses, children: [
4419
+ /* @__PURE__ */ jsx45("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
4420
+ /* @__PURE__ */ jsx45("span", { children: text })
4421
+ ] });
4422
+ }
4423
+
4424
+ // src/components/paywall/blocks/PaywallAnchorPrice.tsx
4425
+ import { jsx as jsx46 } from "react/jsx-runtime";
4426
+ var DEFAULT_CLASS2 = "text-sm opacity-60 line-through";
4427
+ function PaywallAnchorPrice({
4428
+ className,
4429
+ render
4430
+ }) {
4431
+ const { anchorPriceCents, cycle } = usePaywallContext();
4432
+ if (anchorPriceCents === null || anchorPriceCents === void 0 || anchorPriceCents <= 0) {
4433
+ return null;
4434
+ }
4435
+ void cycle;
4436
+ const formatted = formatBRL(anchorPriceCents);
4437
+ const rootClasses = [DEFAULT_CLASS2, className].filter(Boolean).join(" ");
4438
+ if (render) {
4439
+ return /* @__PURE__ */ jsx46("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
4440
+ }
4441
+ return /* @__PURE__ */ jsx46("span", { className: rootClasses, children: formatted });
4442
+ }
4443
+
4444
+ // src/components/paywall/blocks/PaywallTestimonials.tsx
4445
+ import { jsx as jsx47, jsxs as jsxs28 } from "react/jsx-runtime";
4446
+ var DEFAULT_ROOT = "flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2";
4447
+ var DEFAULT_CARD = "snap-start shrink-0 w-72 rounded-2xl border p-4 flex flex-col gap-2";
4448
+ var DEFAULT_AVATAR = "w-10 h-10 rounded-full object-cover";
4449
+ var DEFAULT_QUOTE = "text-sm leading-snug";
4450
+ var DEFAULT_NAME = "text-xs font-semibold opacity-80";
4451
+ var DEFAULT_STARS = "text-yellow-500 text-sm";
4452
+ function clampStars(n) {
4453
+ return Math.max(0, Math.min(5, Math.round(n)));
4454
+ }
4455
+ function PaywallTestimonials({
4456
+ items,
4457
+ className,
4458
+ cardClassName,
4459
+ avatarClassName,
4460
+ quoteClassName,
4461
+ nameClassName,
4462
+ starsClassName,
4463
+ renderItem
4464
+ }) {
4465
+ const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
4466
+ const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
4467
+ const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
4468
+ const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
4469
+ const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
4470
+ const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
4471
+ return /* @__PURE__ */ jsx47("div", { className: rootClasses, children: items.map((item, idx) => {
4472
+ if (renderItem) return renderItem(item, idx);
4473
+ const filled = clampStars(item.stars);
4474
+ const empty = 5 - filled;
4475
+ return /* @__PURE__ */ jsxs28("div", { className: cardClasses, children: [
4476
+ /* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-2", children: [
4477
+ item.avatar ? /* @__PURE__ */ jsx47(
4478
+ "img",
4479
+ {
4480
+ src: item.avatar,
4481
+ alt: "",
4482
+ loading: "lazy",
4483
+ className: avatarClasses,
4484
+ "aria-hidden": "true"
4485
+ }
4486
+ ) : null,
4487
+ /* @__PURE__ */ jsx47("div", { className: nameClasses, children: item.name })
4488
+ ] }),
4489
+ /* @__PURE__ */ jsxs28("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
4490
+ "\u2605".repeat(filled),
4491
+ "\u2606".repeat(empty)
4492
+ ] }),
4493
+ /* @__PURE__ */ jsx47("p", { className: quoteClasses, children: item.quote })
4494
+ ] }, idx);
4495
+ }) });
4496
+ }
4497
+
4498
+ // src/components/paywall/blocks/PaywallStatsRow.tsx
4499
+ import { jsx as jsx48, jsxs as jsxs29 } from "react/jsx-runtime";
4500
+ var DEFAULT_ROOT2 = "grid grid-cols-3 gap-4";
4501
+ var DEFAULT_CELL = "flex flex-col items-center text-center";
4502
+ var DEFAULT_VALUE = "text-2xl font-bold";
4503
+ var DEFAULT_LABEL = "text-xs opacity-70";
4504
+ function PaywallStatsRow({
4505
+ stats,
4506
+ className,
4507
+ cellClassName,
4508
+ valueClassName,
4509
+ labelClassName,
4510
+ renderCell
4511
+ }) {
4512
+ const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
4513
+ const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
4514
+ const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
4515
+ const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
4516
+ return /* @__PURE__ */ jsx48("div", { className: rootClasses, children: stats.map((stat, idx) => {
4517
+ if (renderCell) return renderCell(stat, idx);
4518
+ return /* @__PURE__ */ jsxs29("div", { className: cellClasses, children: [
4519
+ stat.icon ? /* @__PURE__ */ jsx48("div", { "aria-hidden": "true", children: stat.icon }) : null,
4520
+ /* @__PURE__ */ jsx48("div", { className: valueClasses, children: stat.value }),
4521
+ /* @__PURE__ */ jsx48("div", { className: labelClasses, children: stat.label })
4522
+ ] }, idx);
4523
+ }) });
4524
+ }
4525
+
4526
+ // src/components/paywall/blocks/PaywallFinePrint.tsx
4527
+ import { jsx as jsx49 } from "react/jsx-runtime";
4528
+ var DEFAULT_CLASS3 = "text-xs opacity-60 leading-snug";
4529
+ var CYCLE_LABEL2 = {
4530
+ MONTHLY: "mensal",
4531
+ YEARLY: "anual"
4532
+ };
4533
+ function PaywallFinePrint({
4534
+ template,
4535
+ className,
4536
+ render
4537
+ }) {
4538
+ const {
4539
+ currentPriceCents,
4540
+ cycle,
4541
+ trialDaysCard,
4542
+ trialDaysPix,
4543
+ selectedMethod
4544
+ } = usePaywallContext();
4545
+ const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
4546
+ const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
4547
+ const priceFormatted = formatBRL(currentPriceCents ?? 0);
4548
+ const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
4549
+ if (render) {
4550
+ return /* @__PURE__ */ jsx49("p", { className: className || void 0, children: render({
4551
+ currentPriceCents: currentPriceCents ?? 0,
4552
+ cycle,
4553
+ trialDays: trialDays ?? 0,
4554
+ selectedMethod
4555
+ }) });
4556
+ }
4557
+ const text = template.replaceAll("{price}", priceFormatted).replaceAll("{trialDays}", String(trialDays ?? 0)).replaceAll("{cycle}", cycleLabel);
4558
+ return /* @__PURE__ */ jsx49("p", { className: rootClasses, children: text });
4559
+ }
4560
+
4561
+ // src/components/paywall/blocks/PaywallTrustLine.tsx
4562
+ import { jsx as jsx50, jsxs as jsxs30 } from "react/jsx-runtime";
4563
+ var DEFAULT_ROOT3 = "flex items-center gap-3";
4564
+ var DEFAULT_ITEM = "flex items-center gap-1.5 text-xs";
4565
+ function PaywallTrustLine({
4566
+ items,
4567
+ className,
4568
+ itemClassName,
4569
+ renderItem
4570
+ }) {
4571
+ const rootClasses = [DEFAULT_ROOT3, className].filter(Boolean).join(" ");
4572
+ const itemClasses = [DEFAULT_ITEM, itemClassName].filter(Boolean).join(" ");
4573
+ return /* @__PURE__ */ jsx50("div", { className: rootClasses, children: items.map((item, idx) => {
4574
+ if (renderItem) return renderItem(item, idx);
4575
+ return /* @__PURE__ */ jsxs30("span", { className: itemClasses, children: [
4576
+ /* @__PURE__ */ jsx50("span", { "aria-hidden": "true", children: item.icon }),
4577
+ /* @__PURE__ */ jsx50("span", { children: item.text })
4578
+ ] }, idx);
4579
+ }) });
4580
+ }
4581
+
4582
+ // src/components/paywall/blocks/PaywallStickyFooter.tsx
4583
+ import { jsx as jsx51 } from "react/jsx-runtime";
4584
+ var DEFAULT_CLASSES = "sticky bottom-0 left-0 right-0 bg-background";
4585
+ var SAFE_AREA_CLASS = "pb-[env(safe-area-inset-bottom)]";
4586
+ function PaywallStickyFooter({
4587
+ children,
4588
+ className,
4589
+ safeAreaInsets = true
4590
+ }) {
4591
+ const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
4592
+ return /* @__PURE__ */ jsx51("div", { className: classes, children });
4162
4593
  }
4163
4594
  export {
4164
4595
  AppConfigProvider,
@@ -4177,10 +4608,26 @@ export {
4177
4608
  OnboardingFlow,
4178
4609
  PaymentReturnHandler,
4179
4610
  Paywall,
4611
+ PaywallAnchorPrice,
4612
+ PaywallContext,
4613
+ PaywallCountdown,
4180
4614
  PaywallCta,
4181
4615
  PaywallCyclePicker,
4616
+ PaywallEyebrow,
4617
+ PaywallFeatures,
4618
+ PaywallFeaturesCard,
4619
+ PaywallFinePrint,
4620
+ PaywallHeadline,
4621
+ PaywallHero,
4182
4622
  PaywallMethodContent,
4183
4623
  PaywallMethodTabs,
4624
+ PaywallPriceHeadline,
4625
+ PaywallProvider,
4626
+ PaywallStatsRow,
4627
+ PaywallStickyFooter,
4628
+ PaywallTestimonials,
4629
+ PaywallTrophyBadge,
4630
+ PaywallTrustLine,
4184
4631
  PersistenceRegistry,
4185
4632
  PixWaitingPageDefault,
4186
4633
  PreAuthShell,
@@ -4211,6 +4658,7 @@ export {
4211
4658
  useInstallPrompt,
4212
4659
  useLoginForm,
4213
4660
  useOnboardingStep,
4661
+ usePaywallContext,
4214
4662
  usePaywallState,
4215
4663
  usePlan,
4216
4664
  usePush,