@frak-labs/components 0.0.26-beta.07b0efba → 0.0.26-beta.1c24fe7c

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.
Files changed (42) hide show
  1. package/cdn/Banner.D-DB7VwI.js +1 -0
  2. package/cdn/ButtonShare.BM9Inhmf.js +1 -0
  3. package/cdn/ButtonWallet.B7UmJcbb.js +40 -0
  4. package/cdn/OpenInAppButton.B4mrsz6L.js +1 -0
  5. package/cdn/PostPurchase.CMa-59qR.js +1 -0
  6. package/cdn/components.js +1 -1
  7. package/cdn/formatReward.C8hlSKRj.js +1 -0
  8. package/cdn/jsxRuntime.module.5UNmmhNi.js +138 -0
  9. package/cdn/loader.css +0 -14
  10. package/cdn/loader.js +66 -1
  11. package/cdn/useLightDomStyles.D895e4W1.js +1 -0
  12. package/cdn/useReward.DEU0AF3P.js +1 -0
  13. package/cdn/useShareModal.CN9_P7Sl.js +1 -0
  14. package/dist/banner.d.ts +73 -0
  15. package/dist/banner.js +171 -0
  16. package/dist/buttonShare.d.ts +7 -5
  17. package/dist/buttonShare.js +59 -94
  18. package/dist/buttonWallet.d.ts +9 -1
  19. package/dist/buttonWallet.js +78 -33
  20. package/dist/formatReward-6JQldDEC.js +28 -0
  21. package/dist/openInApp.d.ts +2 -0
  22. package/dist/openInApp.js +22 -20
  23. package/dist/postPurchase.d.ts +98 -0
  24. package/dist/postPurchase.js +140 -0
  25. package/dist/useLightDomStyles-DukxuNnJ.js +44 -0
  26. package/dist/usePlacement-BbMuz8_A.js +340 -0
  27. package/dist/useReward-CI2yRrCj.js +67 -0
  28. package/dist/useShareModal-DHlayNqk.js +55 -0
  29. package/package.json +13 -7
  30. package/cdn/ButtonShare.DTdA_uHe.js +0 -1
  31. package/cdn/ButtonWallet.BmhX5cdV.js +0 -1
  32. package/cdn/OpenInAppButton.CKzDpG6O.js +0 -1
  33. package/cdn/Spinner.DvAzggQL.js +0 -1
  34. package/cdn/initFrakSdk._ycDRGU2.js +0 -66
  35. package/cdn/useClientReady.Dyagl92v.js +0 -1
  36. package/dist/Spinner-1CZC_zy6.js +0 -36
  37. package/dist/Spinner-CHZD3tMn.css +0 -1
  38. package/dist/buttonShare.css +0 -1
  39. package/dist/buttonWallet.css +0 -1
  40. package/dist/openInApp.css +0 -1
  41. package/dist/useClientReady-0vKBG0-p.js +0 -197
  42. package/dist/useReward-DAkT-7wT.js +0 -48
@@ -0,0 +1,28 @@
1
+ import { formatAmount, getCurrencyAmountKey, getSupportedCurrency } from "@frak-labs/core-sdk";
2
+
3
+ //#region src/utils/formatReward.ts
4
+ /**
5
+ * Format an {@link EstimatedReward} into a human-readable string.
6
+ *
7
+ * - `fixed` → e.g. `"5 €"`
8
+ * - `percentage` → if `basketAmount` is provided, computes the actual value
9
+ * (e.g. `"10 €"`), otherwise returns `"10 %"`
10
+ * - `tiered` → max tier value, e.g. `"50 €"`
11
+ */
12
+ function formatEstimatedReward(reward, currency, basketAmount) {
13
+ const supportedCurrency = getSupportedCurrency(currency);
14
+ const key = getCurrencyAmountKey(supportedCurrency);
15
+ switch (reward.payoutType) {
16
+ case "fixed": return formatAmount(Math.round(reward.amount[key]), supportedCurrency);
17
+ case "percentage":
18
+ if (basketAmount !== void 0) return formatAmount(Math.round(reward.percent * basketAmount / 100), supportedCurrency);
19
+ return `${reward.percent} %`;
20
+ case "tiered": {
21
+ const max = reward.tiers.reduce((acc, tier) => Math.max(acc, tier.amount[key]), 0);
22
+ return formatAmount(Math.round(max), supportedCurrency);
23
+ }
24
+ }
25
+ }
26
+
27
+ //#endregion
28
+ export { formatEstimatedReward as t };
@@ -6,6 +6,7 @@ import * as preact from "preact";
6
6
  * @inline
7
7
  */
8
8
  type OpenInAppButtonProps = {
9
+ placement?: string;
9
10
  /**
10
11
  * Text to display on the button
11
12
  * @defaultValue `"Open in App"`
@@ -45,6 +46,7 @@ type OpenInAppButtonProps = {
45
46
  * ```
46
47
  */
47
48
  declare function OpenInAppButton({
49
+ placement: placementId,
48
50
  text,
49
51
  classname
50
52
  }: OpenInAppButtonProps): preact.JSX.Element | null;
package/dist/openInApp.js CHANGED
@@ -1,9 +1,8 @@
1
- import { n as registerWebComponent, t as useClientReady } from "./useClientReady-0vKBG0-p.js";
2
- import { t as Spinner } from "./Spinner-1CZC_zy6.js";
1
+ import { a as useClientReady, o as registerWebComponent, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
2
+ import { t as useLightDomStyles } from "./useLightDomStyles-DukxuNnJ.js";
3
3
  import { DEEP_LINK_SCHEME, trackEvent, triggerDeepLinkWithFallback } from "@frak-labs/core-sdk";
4
- import { cx } from "class-variance-authority";
5
4
  import { useMemo } from "preact/hooks";
6
- import { jsx, jsxs } from "preact/jsx-runtime";
5
+ import { jsx } from "preact/jsx-runtime";
7
6
 
8
7
  //#region src/utils/isMobile.ts
9
8
  /**
@@ -48,11 +47,6 @@ function openFrakWalletApp(path = DEFAULT_PATH) {
48
47
  } });
49
48
  }
50
49
 
51
- //#endregion
52
- //#region src/components/OpenInAppButton/OpenInAppButton.module.css?css_module
53
- const classes = { "button": "XYfqGq_button" };
54
- const _button0 = classes["button"];
55
-
56
50
  //#endregion
57
51
  //#region src/components/OpenInAppButton/OpenInAppButton.tsx
58
52
  /**
@@ -81,30 +75,38 @@ const _button0 = classes["button"];
81
75
  * <frak-open-in-app classname="button button-primary"></frak-open-in-app>
82
76
  * ```
83
77
  */
84
- function OpenInAppButton({ text = "Open in App", classname = "" }) {
85
- const { isClientReady } = useClientReady();
78
+ function OpenInAppButton({ placement: placementId, text = "Open in App", classname = "" }) {
79
+ const placement = usePlacement(placementId);
80
+ const { shouldRender, isHidden, isClientReady } = useClientReady();
86
81
  const { isMobile } = useIsMobile();
87
- if (!isMobile) return null;
82
+ useLightDomStyles("frak-open-in-app", placementId, placement?.components?.openInApp?.css);
83
+ const resolvedText = placement?.components?.openInApp?.text ?? text;
84
+ if (!isMobile || !shouldRender || isHidden) return null;
88
85
  const handleClick = () => {
89
86
  openFrakWalletApp();
90
87
  };
91
- return /* @__PURE__ */ jsxs("button", {
88
+ const buttonClass = [
89
+ "button",
90
+ "button__fadeIn",
91
+ classname
92
+ ].filter(Boolean).join(" ");
93
+ return /* @__PURE__ */ jsx("button", {
92
94
  type: "button",
93
95
  "aria-label": "Open in Frak Wallet app",
94
- className: cx(classes.button, classname, "override"),
95
96
  disabled: !isClientReady,
97
+ class: buttonClass,
96
98
  onClick: handleClick,
97
- children: [
98
- !isClientReady && /* @__PURE__ */ jsx(Spinner, {}),
99
- " ",
100
- text
101
- ]
99
+ children: resolvedText
102
100
  });
103
101
  }
104
102
 
105
103
  //#endregion
106
104
  //#region src/components/OpenInAppButton/index.ts
107
- registerWebComponent(OpenInAppButton, "frak-open-in-app", ["text"], { shadow: false });
105
+ registerWebComponent(OpenInAppButton, "frak-open-in-app", [
106
+ "text",
107
+ "placement",
108
+ "classname"
109
+ ], { shadow: false });
108
110
 
109
111
  //#endregion
110
112
  export { OpenInAppButton };
@@ -0,0 +1,98 @@
1
+ import * as preact from "preact";
2
+
3
+ //#region src/components/PostPurchase/types.d.ts
4
+ /**
5
+ * Props for the {@link PostPurchase} component.
6
+ * @inline
7
+ */
8
+ type PostPurchaseProps = {
9
+ /**
10
+ * Merchant customer ID for purchase tracking fallback.
11
+ * All three tracking props (`customerId`, `orderId`, `token`) must be
12
+ * present for tracking to fire.
13
+ */
14
+ customerId?: string;
15
+ /**
16
+ * Merchant order ID for purchase tracking fallback.
17
+ */
18
+ orderId?: string;
19
+ /**
20
+ * Checkout token for purchase tracking fallback.
21
+ */
22
+ token?: string;
23
+ /**
24
+ * Base URL to share. Falls back to the merchant domain returned by
25
+ * the backend when omitted.
26
+ */
27
+ sharingUrl?: string;
28
+ /**
29
+ * Override the merchant ID resolved from the SDK config.
30
+ */
31
+ merchantId?: string;
32
+ /**
33
+ * Placement ID for backend-driven CSS customization.
34
+ */
35
+ placement?: string;
36
+ /**
37
+ * CSS class names passed through to the root element (Light DOM).
38
+ */
39
+ classname?: string;
40
+ /**
41
+ * Force a display variant instead of relying on the backend evaluation.
42
+ */
43
+ variant?: "referrer" | "referee";
44
+ };
45
+ //#endregion
46
+ //#region src/components/PostPurchase/PostPurchase.d.ts
47
+ /**
48
+ * Post-purchase card component.
49
+ *
50
+ * Renders an inline card on the merchant's thank-you / order-status page
51
+ * that either congratulates a referee or invites a referrer to share.
52
+ *
53
+ * Fetches referral status and merchant information via two independent
54
+ * RPC calls, then computes the display variant locally.
55
+ *
56
+ * @group components
57
+ *
58
+ * @example
59
+ * Minimal — just show the card:
60
+ * ```html
61
+ * <frak-post-purchase></frak-post-purchase>
62
+ * ```
63
+ *
64
+ * @example
65
+ * With purchase tracking fallback and custom sharing URL:
66
+ * ```html
67
+ * <frak-post-purchase
68
+ * customer-id="cust_123"
69
+ * order-id="ord_456"
70
+ * token="checkout_abc"
71
+ * sharing-url="https://merchant.com/product/shoes"
72
+ * ></frak-post-purchase>
73
+ * ```
74
+ */
75
+ declare function PostPurchase({
76
+ customerId,
77
+ orderId,
78
+ token,
79
+ sharingUrl,
80
+ merchantId,
81
+ placement: placementId,
82
+ classname,
83
+ variant: forcedVariant
84
+ }: PostPurchaseProps): preact.JSX.Element | null;
85
+ //#endregion
86
+ //#region src/components/PostPurchase/index.d.ts
87
+ /**
88
+ * Custom element interface for `<frak-post-purchase>`.
89
+ * Combines standard {@link HTMLElement} with {@link PostPurchaseProps}.
90
+ */
91
+ interface PostPurchaseElement extends HTMLElement, PostPurchaseProps {}
92
+ declare global {
93
+ interface HTMLElementTagNameMap {
94
+ "frak-post-purchase": PostPurchaseElement;
95
+ }
96
+ }
97
+ //#endregion
98
+ export { PostPurchase, PostPurchaseElement };
@@ -0,0 +1,140 @@
1
+ import { a as useClientReady, o as registerWebComponent, t as usePlacement } from "./usePlacement-BbMuz8_A.js";
2
+ import { t as useLightDomStyles } from "./useLightDomStyles-DukxuNnJ.js";
3
+ import { t as formatEstimatedReward } from "./formatReward-6JQldDEC.js";
4
+ import { t as useShareModal } from "./useShareModal-DHlayNqk.js";
5
+ import { getMerchantInformation, getUserReferralStatus, trackPurchaseStatus } from "@frak-labs/core-sdk/actions";
6
+ import { useEffect, useMemo, useState } from "preact/hooks";
7
+ import { jsx, jsxs } from "preact/jsx-runtime";
8
+ import { FrakRpcError, RpcErrorCodes } from "@frak-labs/frame-connector";
9
+
10
+ //#region src/components/PostPurchase/PostPurchase.tsx
11
+ /**
12
+ * Given referral status and merchant info, compute the display variant
13
+ * and pick the appropriate purchase reward.
14
+ */
15
+ function resolvePostPurchaseContext(referralStatus, merchantInfo) {
16
+ const purchaseReward = merchantInfo.rewards.find((r) => r.interactionTypeKey === "purchase" && (r.referrer || r.referee));
17
+ if (!purchaseReward) return null;
18
+ const variant = referralStatus?.isReferred && purchaseReward.referee ? "referee" : "referrer";
19
+ return {
20
+ variant,
21
+ reward: variant === "referee" ? purchaseReward.referee : purchaseReward.referrer,
22
+ merchantDomain: merchantInfo.onChainMetadata.domain
23
+ };
24
+ }
25
+ /**
26
+ * Post-purchase card component.
27
+ *
28
+ * Renders an inline card on the merchant's thank-you / order-status page
29
+ * that either congratulates a referee or invites a referrer to share.
30
+ *
31
+ * Fetches referral status and merchant information via two independent
32
+ * RPC calls, then computes the display variant locally.
33
+ *
34
+ * @group components
35
+ *
36
+ * @example
37
+ * Minimal — just show the card:
38
+ * ```html
39
+ * <frak-post-purchase></frak-post-purchase>
40
+ * ```
41
+ *
42
+ * @example
43
+ * With purchase tracking fallback and custom sharing URL:
44
+ * ```html
45
+ * <frak-post-purchase
46
+ * customer-id="cust_123"
47
+ * order-id="ord_456"
48
+ * token="checkout_abc"
49
+ * sharing-url="https://merchant.com/product/shoes"
50
+ * ></frak-post-purchase>
51
+ * ```
52
+ */
53
+ function PostPurchase({ customerId, orderId, token, sharingUrl, merchantId, placement: placementId, classname = "", variant: forcedVariant }) {
54
+ const { shouldRender, isHidden, isClientReady } = useClientReady();
55
+ const placement = usePlacement(placementId);
56
+ useLightDomStyles("frak-post-purchase", placementId, placement?.components?.postPurchase?.css);
57
+ const [context, setContext] = useState(null);
58
+ const [hasFetched, setHasFetched] = useState(false);
59
+ useEffect(() => {
60
+ if (!isClientReady || !customerId || !orderId || !token) return;
61
+ trackPurchaseStatus({
62
+ customerId,
63
+ orderId,
64
+ token,
65
+ merchantId
66
+ }).catch(() => {});
67
+ }, [
68
+ isClientReady,
69
+ customerId,
70
+ orderId,
71
+ token,
72
+ merchantId
73
+ ]);
74
+ useEffect(() => {
75
+ if (!isClientReady || hasFetched) return;
76
+ const client = window.FrakSetup?.client;
77
+ if (!client) return;
78
+ setHasFetched(true);
79
+ Promise.all([getUserReferralStatus(client), getMerchantInformation(client)]).then(([referralStatus, merchantInfo]) => {
80
+ setContext(resolvePostPurchaseContext(referralStatus, merchantInfo));
81
+ }).catch((e) => {
82
+ if (e instanceof FrakRpcError && e.code === RpcErrorCodes.configError) return;
83
+ console.warn("[Frak] Post-purchase context error", e);
84
+ });
85
+ }, [isClientReady, hasFetched]);
86
+ const resolvedVariant = forcedVariant ?? context?.variant;
87
+ const resolvedSharingUrl = sharingUrl ?? context?.merchantDomain;
88
+ const rewardText = useMemo(() => {
89
+ if (!context?.reward) return void 0;
90
+ const currency = window.FrakSetup?.client?.config?.metadata?.currency;
91
+ return formatEstimatedReward(context.reward, currency);
92
+ }, [context?.reward]);
93
+ const postPurchaseConfig = placement?.components?.postPurchase;
94
+ const texts = useMemo(() => {
95
+ const applyReward = (text) => rewardText ? text.replace("{REWARD}", rewardText) : text;
96
+ return {
97
+ message: resolvedVariant === "referee" ? rewardText ? applyReward(postPurchaseConfig?.refereeText ?? "You just earned {REWARD}! Share with friends to earn even more.") : postPurchaseConfig?.refereeNoRewardText ?? "You just earned a reward! Share with friends to earn even more." : rewardText ? applyReward(postPurchaseConfig?.referrerText ?? "Earn {REWARD} by sharing this with your friends!") : postPurchaseConfig?.referrerNoRewardText ?? "Share this with your friends and earn rewards!",
98
+ cta: rewardText ? applyReward(postPurchaseConfig?.ctaText ?? "Share & earn {REWARD}") : postPurchaseConfig?.ctaNoRewardText ?? "Share & earn"
99
+ };
100
+ }, [
101
+ resolvedVariant,
102
+ rewardText,
103
+ postPurchaseConfig
104
+ ]);
105
+ const { handleShare } = useShareModal(void 0, placementId, resolvedSharingUrl);
106
+ if (!shouldRender || isHidden) return null;
107
+ if (!context || !resolvedVariant) return null;
108
+ return /* @__PURE__ */ jsxs("div", {
109
+ class: ["post-purchase", classname].filter(Boolean).join(" "),
110
+ children: [/* @__PURE__ */ jsx("div", {
111
+ class: "post-purchase__content",
112
+ children: /* @__PURE__ */ jsx("p", {
113
+ class: "post-purchase__message",
114
+ children: texts.message
115
+ })
116
+ }), /* @__PURE__ */ jsx("button", {
117
+ type: "button",
118
+ class: "post-purchase__cta button",
119
+ disabled: !isClientReady,
120
+ onClick: handleShare,
121
+ children: texts.cta
122
+ })]
123
+ });
124
+ }
125
+
126
+ //#endregion
127
+ //#region src/components/PostPurchase/index.ts
128
+ registerWebComponent(PostPurchase, "frak-post-purchase", [
129
+ "customerId",
130
+ "orderId",
131
+ "token",
132
+ "sharingUrl",
133
+ "merchantId",
134
+ "placement",
135
+ "classname",
136
+ "variant"
137
+ ], { shadow: false });
138
+
139
+ //#endregion
140
+ export { PostPurchase };
@@ -0,0 +1,44 @@
1
+ import { i as lightDomBaseCss } from "./usePlacement-BbMuz8_A.js";
2
+ import { useEffect } from "preact/hooks";
3
+
4
+ //#region src/utils/styleManager.ts
5
+ function ensureStyle(id, css) {
6
+ const existing = document.getElementById(id);
7
+ if (existing) {
8
+ if (existing.textContent !== css) existing.textContent = css;
9
+ return;
10
+ }
11
+ const style = document.createElement("style");
12
+ style.id = id;
13
+ style.textContent = css;
14
+ document.head.appendChild(style);
15
+ }
16
+ function injectBase(tag, css) {
17
+ ensureStyle(`frak-base-${tag}`, css);
18
+ }
19
+ function injectPlacement(tag, placementId, scopedCss) {
20
+ ensureStyle(`frak-placement-${tag}-${placementId}`, scopedCss);
21
+ }
22
+ const styleManager = {
23
+ injectBase,
24
+ injectPlacement
25
+ };
26
+
27
+ //#endregion
28
+ //#region src/hooks/useLightDomStyles.ts
29
+ function useLightDomStyles(tag, placementId, placementCss, baseCss) {
30
+ useEffect(() => {
31
+ styleManager.injectBase(tag, baseCss ?? lightDomBaseCss);
32
+ }, [tag]);
33
+ useEffect(() => {
34
+ if (!placementId || !placementCss) return;
35
+ styleManager.injectPlacement(tag, placementId, placementCss);
36
+ }, [
37
+ tag,
38
+ placementId,
39
+ placementCss
40
+ ]);
41
+ }
42
+
43
+ //#endregion
44
+ export { useLightDomStyles as t };