@frak-labs/components 1.0.5 → 1.0.6-beta.dc7ed9db

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 (40) hide show
  1. package/cdn/Banner.L6HcKGUv.js +178 -0
  2. package/cdn/ButtonShare.O2wAgNZa.js +1 -0
  3. package/cdn/ButtonWallet.Cm8Qzz45.js +40 -0
  4. package/cdn/{GiftIcon.eRNTGQ_r.js → GiftIcon.DnSW4dJQ.js} +1 -1
  5. package/cdn/{OpenInAppButton.Dgb-qhM8.js → OpenInAppButton.CTBE114K.js} +1 -1
  6. package/cdn/PostPurchase.fx6VasmG.js +89 -0
  7. package/cdn/components.js +1 -1
  8. package/cdn/embeddedWallet.CDUxjdX2.js +1 -0
  9. package/cdn/{formatReward.B1ZyoceC.js → formatReward.Cmg_Q___.js} +1 -1
  10. package/cdn/loader.js +1 -1
  11. package/cdn/replay-V6FXES7X.CNozpSRg.js +1 -0
  12. package/cdn/{useGlobalComponents.TG9kIYSc.js → useGlobalComponents.pjgSwLk9.js} +1 -1
  13. package/cdn/{useLightDomStyles.tjNBKcOr.js → useLightDomStyles.2RQE6Iby.js} +1 -1
  14. package/cdn/usePlacement.CRfzWKVY.js +58 -0
  15. package/cdn/useReward.cZ7bx5ND.js +1 -0
  16. package/dist/{GiftIcon-BIp9FTJs.js → GiftIcon-WEWSJ8kV.js} +1 -1
  17. package/dist/banner.d.ts +1 -2
  18. package/dist/banner.js +3 -3
  19. package/dist/buttonShare.d.ts +1 -2
  20. package/dist/buttonShare.js +3 -3
  21. package/dist/buttonWallet.d.ts +1 -2
  22. package/dist/buttonWallet.js +17 -1
  23. package/dist/embeddedWallet-By3_p5Xc.js +18 -0
  24. package/dist/openInApp.d.ts +1 -3
  25. package/dist/openInApp.js +2 -2
  26. package/dist/postPurchase.d.ts +1 -2
  27. package/dist/postPurchase.js +4 -75
  28. package/dist/{useLightDomStyles-DVe5UDg6.js → useLightDomStyles-Xi52h2yV.js} +1 -1
  29. package/dist/usePlacement-SHVjZ5IW.js +404 -0
  30. package/package.json +3 -3
  31. package/cdn/Banner.CIxY6tCU.js +0 -178
  32. package/cdn/ButtonShare.elMtdxF3.js +0 -1
  33. package/cdn/ButtonWallet.BUUPX0gO.js +0 -40
  34. package/cdn/PostPurchase.Cy7-FrRh.js +0 -89
  35. package/cdn/replay-V6FXES7X.BoL9fAjx.js +0 -1
  36. package/cdn/sharingPage.BYsqcN9O.js +0 -1
  37. package/cdn/usePlacement.BgMXY5CX.js +0 -58
  38. package/cdn/useReward.B530suzR.js +0 -1
  39. package/dist/sharingPage-D6fQEXV9.js +0 -15
  40. package/dist/usePlacement-DzEuVg_u.js +0 -253
@@ -0,0 +1,404 @@
1
+ import register from "preact-custom-element";
2
+ import * as coreSdkIndex from "@frak-labs/core-sdk";
3
+ import { decompressJsonFromB64, sdkConfigStore, setupClient, trackEvent, withCache } from "@frak-labs/core-sdk";
4
+ import * as coreSdkActions from "@frak-labs/core-sdk/actions";
5
+ import { displaySharingPage } from "@frak-labs/core-sdk/actions";
6
+ import { useEffect, useMemo, useState } from "preact/hooks";
7
+ //#region src/actions/sharingPage.ts
8
+ async function openSharingPage(targetInteraction, placement, options) {
9
+ if (!window.FrakSetup?.client) {
10
+ console.error("Frak client not found");
11
+ return;
12
+ }
13
+ await displaySharingPage(window.FrakSetup.client, {
14
+ ...options?.link && { link: options.link },
15
+ ...options?.products?.length && { products: options.products },
16
+ ...targetInteraction && { metadata: { targetInteraction } }
17
+ }, placement);
18
+ }
19
+ //#endregion
20
+ //#region src/utils/dom/detectListenerPreloads.ts
21
+ /**
22
+ * Tags that count as "Frak components" for the purpose of preload detection.
23
+ *
24
+ * Kept in sync with the registry in `bootstrap/loader.ts#COMPONENTS_MAP` —
25
+ * any new public custom element should be added here too so the iframe
26
+ * preload hash reflects the user's actual page surface.
27
+ */
28
+ const FRAK_COMPONENT_SELECTOR = [
29
+ "frak-button-share",
30
+ "frak-button-wallet",
31
+ "frak-open-in-app",
32
+ "frak-post-purchase",
33
+ "frak-banner"
34
+ ].join(",");
35
+ /**
36
+ * Dynamically compute the iframe preload list based on which Frak components
37
+ * are present in the current document.
38
+ *
39
+ * Behaviour:
40
+ * - No `frak-*` element on the page → `[]` (caller should skip the
41
+ * `#preload=...` hash entirely so the listener doesn't warm chunks no one
42
+ * will use).
43
+ * - At least one `frak-*` element → `["sharing"]`. Every public component
44
+ * eventually opens the sharing flow (directly via `<frak-button-share>` or
45
+ * indirectly via wallet/post-purchase/banner CTAs), so a single hint
46
+ * covers the whole surface without bloating the iframe URL.
47
+ *
48
+ * Called once during {@link initFrakSdk}, before {@link setupClient} creates
49
+ * the iframe. Dynamically-mounted components (added after init) still work —
50
+ * the listener loads handlers on demand — they just skip the warm-up.
51
+ */
52
+ function detectListenerPreloads() {
53
+ if (typeof document === "undefined") return [];
54
+ return document.querySelector(FRAK_COMPONENT_SELECTOR) !== null ? ["sharing"] : [];
55
+ }
56
+ //#endregion
57
+ //#region src/utils/sharingPageProducts.ts
58
+ /**
59
+ * Whether `value` is a syntactically valid URL with an `http(s):` scheme.
60
+ *
61
+ * Used to gate `imageUrl` / `link` fields coming from untrusted inputs (the
62
+ * public `products` prop on `<frak-post-purchase>`, decoded query params for
63
+ * Klaviyo / email share links, etc.) — the listener-side sharing-page builder
64
+ * calls `new URL(...)` on the incoming product link, and a `javascript:` URL
65
+ * would be a XSS sink in any consumer that binds the value to an `href`.
66
+ */
67
+ function isHttpUrl(value) {
68
+ try {
69
+ const parsed = new URL(value);
70
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+ /**
76
+ * Coerce a raw `products` value into a candidate array suitable for
77
+ * per-item normalisation, or null when it cannot be reduced to one.
78
+ *
79
+ * Accepts:
80
+ * - Real arrays (JS-property surface, decompressed query payloads).
81
+ * - JSON-stringified arrays (HTML-attribute surface — WP / Magento
82
+ * server-render delivers attribute values as raw strings).
83
+ *
84
+ * Anything else (non-array non-string, JSON parse failure, JSON that
85
+ * decodes to a non-array) is treated as "no products" so the share still
86
+ * works without the product card section.
87
+ */
88
+ function coerceProductCandidates(products) {
89
+ if (!products) return null;
90
+ if (Array.isArray(products)) return products;
91
+ if (typeof products !== "string") return null;
92
+ try {
93
+ const parsed = JSON.parse(products);
94
+ return Array.isArray(parsed) ? parsed : null;
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+ /**
100
+ * Normalise one untrusted candidate into a {@link SharingPageProduct}, or
101
+ * return null when the candidate has no usable title.
102
+ *
103
+ * The `products` payload is a public API boundary — merchants can set it
104
+ * server-side via WP / Magento, imperatively from arbitrary JS, or via
105
+ * email-template query params built by Klaviyo. Each entry is validated
106
+ * structurally so a malformed `link` reaching `new URL(...)` downstream
107
+ * would not crash the sharing-page builder, and so a `javascript:` URL
108
+ * cannot slip through as `imageUrl` / `link`.
109
+ */
110
+ function normalizeProductCandidate(candidate) {
111
+ if (!candidate || typeof candidate !== "object") return null;
112
+ const item = candidate;
113
+ const title = typeof item.title === "string" ? item.title.trim() : "";
114
+ if (title === "") return null;
115
+ const entry = { title };
116
+ if (typeof item.imageUrl === "string" && isHttpUrl(item.imageUrl)) entry.imageUrl = item.imageUrl;
117
+ if (typeof item.link === "string" && isHttpUrl(item.link)) entry.link = item.link;
118
+ if (typeof item.utmContent === "string" && item.utmContent !== "") entry.utmContent = item.utmContent;
119
+ return entry;
120
+ }
121
+ /**
122
+ * Pipe `coerceProductCandidates` + `normalizeProductCandidate` over an
123
+ * untrusted value and return a non-empty {@link SharingPageProduct}[] or
124
+ * `undefined` when nothing usable came out.
125
+ *
126
+ * The undefined sentinel is what `openSharingPage` / `displaySharingPage`
127
+ * expect when the caller has no products to show — the sharing page just
128
+ * skips the product card section.
129
+ */
130
+ function sanitizeProductList(input) {
131
+ const candidates = coerceProductCandidates(input);
132
+ if (!candidates) return void 0;
133
+ const sanitized = [];
134
+ for (const candidate of candidates) {
135
+ const entry = normalizeProductCandidate(candidate);
136
+ if (entry) sanitized.push(entry);
137
+ }
138
+ return sanitized.length > 0 ? sanitized : void 0;
139
+ }
140
+ /**
141
+ * Decode a `products` URL query param produced by
142
+ * `compressJsonToB64(productsArray)` — the encoding Klaviyo (and any
143
+ * other email tool) uses when embedding the product list of an order
144
+ * confirmation into a Frak share CTA.
145
+ *
146
+ * The result is run through `sanitizeProductList` so every link / image
147
+ * URL is structurally validated before reaching `new URL(...)` downstream.
148
+ * Malformed / tampered payloads degrade gracefully to `undefined` — the
149
+ * share still works, just without the product card section.
150
+ */
151
+ function decodeProductsParam(value) {
152
+ if (!value) return void 0;
153
+ let decoded;
154
+ try {
155
+ decoded = decompressJsonFromB64(value);
156
+ } catch {
157
+ return;
158
+ }
159
+ if (decoded === null) return void 0;
160
+ return sanitizeProductList(decoded);
161
+ }
162
+ //#endregion
163
+ //#region src/bootstrap/clientReady.ts
164
+ const CUSTOM_EVENT_NAME = "frak:client";
165
+ /**
166
+ * Dispatch a custom event when the Frak client is ready
167
+ */
168
+ function dispatchClientReadyEvent() {
169
+ const event = new CustomEvent(CUSTOM_EVENT_NAME);
170
+ window.dispatchEvent(event);
171
+ }
172
+ /**
173
+ * Add or remove an event listener for when the Frak client is ready
174
+ * @param action
175
+ * @param callback
176
+ */
177
+ function onClientReady(action, callback) {
178
+ if (window.FrakSetup?.client && action === "add") {
179
+ callback();
180
+ return;
181
+ }
182
+ (action === "add" ? window.addEventListener : window.removeEventListener)(CUSTOM_EVENT_NAME, callback, false);
183
+ }
184
+ //#endregion
185
+ //#region src/bootstrap/initFrakSdk.ts
186
+ /**
187
+ * Initializes the Frak SDK client and sets up necessary configurations.
188
+ * Uses withCache for inflight dedup — concurrent callers share the same promise.
189
+ * Failures are not cached, allowing retry on next call.
190
+ *
191
+ * @returns {Promise<void>}
192
+ */
193
+ function initFrakSdk() {
194
+ window.FrakSetup.core = {
195
+ ...coreSdkIndex,
196
+ ...coreSdkActions
197
+ };
198
+ if (window.FrakSetup?.client) return Promise.resolve();
199
+ return withCache(() => doInit(), {
200
+ cacheKey: "frak-sdk-init",
201
+ cacheTime: Number.POSITIVE_INFINITY
202
+ }).catch((err) => {
203
+ trackEvent(window.FrakSetup?.client, "sdk_init_failed", {
204
+ reason: err instanceof Error ? err.message : typeof err === "string" ? err : "unknown",
205
+ config_missing: !window.FrakSetup?.config
206
+ });
207
+ });
208
+ }
209
+ /**
210
+ * Performs the actual SDK initialization.
211
+ * Throws on failure so withCache doesn't cache failed attempts.
212
+ */
213
+ async function doInit() {
214
+ if (!window.FrakSetup?.config) throw new Error("[Frak SDK] Configuration not found. Please ensure window.FrakSetup.config is set.");
215
+ console.log("[Frak SDK] Starting initialization");
216
+ const client = await setupClient({ config: withDynamicPreload(window.FrakSetup.config) });
217
+ if (!client) throw new Error("[Frak SDK] Failed to create client");
218
+ window.FrakSetup.client = client;
219
+ console.log("[Frak SDK] Client initialized successfully");
220
+ dispatchClientReadyEvent();
221
+ coreSdkActions.setupReferral(client);
222
+ handleActionQueryParam();
223
+ }
224
+ /**
225
+ * Inject a dynamically-computed `preload` list when the caller hasn't set
226
+ * one explicitly.
227
+ *
228
+ * Rationale: the listener iframe warms Ring 1/Ring 2 chunks based on the
229
+ * `#preload=...` hash. The components CDN entry can detect which Frak
230
+ * components are actually on the page and avoid the warm-up cost when none
231
+ * are mounted. An explicit `config.preload` (including `[]`) is respected
232
+ * as an escape hatch.
233
+ */
234
+ function withDynamicPreload(config) {
235
+ if (config.preload !== void 0) return config;
236
+ return {
237
+ ...config,
238
+ preload: detectListenerPreloads()
239
+ };
240
+ }
241
+ /**
242
+ * Check the query param contain params for an auto opening of the frak modal
243
+ */
244
+ function handleActionQueryParam() {
245
+ const url = new URL(window.location.href);
246
+ if (url.searchParams.get("frakAction") !== "share") return;
247
+ console.log("[Frak SDK] Auto open share via query param");
248
+ const link = url.searchParams.get("link") ?? void 0;
249
+ const placement = url.searchParams.get("placement") ?? void 0;
250
+ const products = decodeProductsParam(url.searchParams.get("products"));
251
+ url.searchParams.delete("frakAction");
252
+ url.searchParams.delete("link");
253
+ url.searchParams.delete("placement");
254
+ url.searchParams.delete("products");
255
+ window.history.replaceState({}, "", url.toString());
256
+ openSharingPage(void 0, placement, {
257
+ link,
258
+ products
259
+ });
260
+ }
261
+ //#endregion
262
+ //#region src/utils/browser/onDocumentReady.ts
263
+ /**
264
+ * When the document is ready, run the callback
265
+ * @param callback
266
+ */
267
+ function onDocumentReady(callback) {
268
+ if (document.readyState === "complete" || document.readyState === "interactive") setTimeout(callback, 1);
269
+ else document.addEventListener("DOMContentLoaded", callback);
270
+ }
271
+ //#endregion
272
+ //#region src/webcomponent/registerWebComponent.ts
273
+ /**
274
+ * Registers a Preact component as a custom web component
275
+ *
276
+ * @param component - The Preact component to register
277
+ * @param tagName - The custom element tag name (e.g., "frak-button-wallet")
278
+ * @param observedAttributes - Array of attribute names to observe for changes
279
+ * @param options - Registration options (e.g., { shadow: true })
280
+ */
281
+ function registerWebComponent(component, tagName, observedAttributes = [], options = { shadow: true }) {
282
+ if (typeof window !== "undefined") {
283
+ onDocumentReady(initFrakSdk);
284
+ if (!customElements.get(tagName)) register(component, tagName, observedAttributes, options);
285
+ }
286
+ }
287
+ //#endregion
288
+ //#region src/hooks/useClientReady.ts
289
+ function useClientReady() {
290
+ const [shouldRender, setShouldRender] = useState(() => {
291
+ if (!(window.FrakSetup?.config?.waitForBackendConfig !== false)) return true;
292
+ return sdkConfigStore.isResolved;
293
+ });
294
+ const [isHidden, setIsHidden] = useState(() => sdkConfigStore.getConfig().hidden ?? false);
295
+ const [isClientReady, setIsClientReady] = useState(() => !!window.FrakSetup?.client);
296
+ useEffect(() => {
297
+ const currentConfig = sdkConfigStore.getConfig();
298
+ if (currentConfig.isResolved) {
299
+ setShouldRender(true);
300
+ setIsHidden(currentConfig.hidden ?? false);
301
+ }
302
+ if (window.FrakSetup?.client) setIsClientReady(true);
303
+ const onConfig = (e) => {
304
+ const config = e.detail;
305
+ if (config.isResolved) setShouldRender(true);
306
+ setIsHidden(config.hidden ?? false);
307
+ };
308
+ window.addEventListener("frak:config", onConfig);
309
+ const handleReady = () => setIsClientReady(true);
310
+ onClientReady("add", handleReady);
311
+ return () => {
312
+ window.removeEventListener("frak:config", onConfig);
313
+ onClientReady("remove", handleReady);
314
+ };
315
+ }, []);
316
+ return {
317
+ shouldRender,
318
+ isHidden,
319
+ isClientReady
320
+ };
321
+ }
322
+ //#endregion
323
+ //#region src/styles/sharedCss.ts
324
+ const sharedCss = `
325
+ :host {
326
+ display: contents;
327
+ }
328
+
329
+ :host([hidden]) {
330
+ display: none;
331
+ }
332
+
333
+ .button:disabled {
334
+ opacity: 0.7;
335
+ cursor: default;
336
+ }
337
+
338
+ .button__fadeIn {
339
+ animation: frak-fadeIn 300ms ease-in;
340
+ }
341
+
342
+ @keyframes frak-fadeIn {
343
+ from {
344
+ opacity: 0;
345
+ }
346
+
347
+ to {
348
+ opacity: 1;
349
+ }
350
+ }
351
+ `;
352
+ function buildStyleContent(componentCss, placementCss) {
353
+ return placementCss ? `${sharedCss}\n${componentCss}\n${placementCss}` : `${sharedCss}\n${componentCss}`;
354
+ }
355
+ const lightDomBaseCss = `
356
+ :where(frak-button-share, frak-open-in-app) {
357
+ display: contents;
358
+ }
359
+
360
+ :where(frak-button-share .button, frak-open-in-app .button) {
361
+ display: flex;
362
+ align-items: center;
363
+ justify-content: center;
364
+ gap: 10px;
365
+ }
366
+
367
+ :where(frak-button-share .button:disabled, frak-open-in-app .button:disabled) {
368
+ opacity: 0.7;
369
+ cursor: default;
370
+ }
371
+
372
+ :where(frak-button-share .button__fadeIn, frak-open-in-app .button__fadeIn) {
373
+ animation: frak-fadeIn 300ms ease-in;
374
+ }
375
+
376
+ @keyframes frak-fadeIn {
377
+ from {
378
+ opacity: 0;
379
+ }
380
+
381
+ to {
382
+ opacity: 1;
383
+ }
384
+ }
385
+ `;
386
+ //#endregion
387
+ //#region src/hooks/usePlacement.ts
388
+ function getPlacement(id) {
389
+ return sdkConfigStore.getConfig().placements?.[id];
390
+ }
391
+ function usePlacement(placementId) {
392
+ const [configVersion, setConfigVersion] = useState(0);
393
+ useEffect(() => {
394
+ const onConfig = (_e) => {
395
+ setConfigVersion((v) => v + 1);
396
+ };
397
+ window.addEventListener("frak:config", onConfig);
398
+ setConfigVersion((v) => v + 1);
399
+ return () => window.removeEventListener("frak:config", onConfig);
400
+ }, []);
401
+ return useMemo(() => placementId ? getPlacement(placementId) : void 0, [placementId, configVersion]);
402
+ }
403
+ //#endregion
404
+ export { registerWebComponent as a, useClientReady as i, buildStyleContent as n, sanitizeProductList as o, lightDomBaseCss as r, openSharingPage as s, usePlacement as t };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "url": "https://twitter.com/QNivelais"
12
12
  }
13
13
  ],
14
- "version": "1.0.5",
14
+ "version": "1.0.6-beta.dc7ed9db",
15
15
  "description": "Frak Wallet components, helping any person to interact with the Frak wallet.",
16
16
  "repository": {
17
17
  "url": "https://github.com/frak-id/wallet",
@@ -86,8 +86,8 @@
86
86
  "publish": "echo 'Publishing components...'"
87
87
  },
88
88
  "dependencies": {
89
- "@frak-labs/core-sdk": "1.1.0",
90
- "@frak-labs/frame-connector": "0.2.0",
89
+ "@frak-labs/core-sdk": "1.1.1-beta.dc7ed9db",
90
+ "@frak-labs/frame-connector": "0.2.0-beta.dc7ed9db",
91
91
  "preact": "^10.29.0",
92
92
  "preact-custom-element": "^4.6.0",
93
93
  "@frak-labs/design-system": "0.0.0"
@@ -1,178 +0,0 @@
1
- import{T as e,b as t,c as n,d as r,u as i,v as a}from"./loader.js";import{a as o,c as s,d as c,i as l,l as u,o as d,s as f,t as ee,u as p}from"./usePlacement.BgMXY5CX.js";import{a as m,i as h,n as g,r as te,t as _}from"./GiftIcon.eRNTGQ_r.js";import{t as v}from"./useGlobalComponents.TG9kIYSc.js";import{t as ne}from"./useLightDomStyles.tjNBKcOr.js";import{t as re}from"./useReward.B530suzR.js";var y=`inAppBanner_body__1ibpiy75`,b=`inAppBanner_closeButton__1ibpiy78`,x=`inAppBanner_container__1ibpiy71`,S=`inAppBanner_cta__1ibpiy77`,C=`inAppBanner_description__1ibpiy76`,w=`inAppBanner_header__1ibpiy72`,T=`inAppBanner_iconWrapper__1ibpiy73`,E=`inAppBanner_title__1ibpiy74`;function D({title:e,description:t,cta:n,dismissLabel:r,onAction:i,onDismiss:a,className:o,classNames:s}){return p(`div`,{className:`${x}${o?` ${o}`:``}`,role:`alert`,children:[p(`div`,{className:w,children:[p(`span`,{className:`${T}${s?.icon?` ${s.icon}`:``}`,children:p(g,{width:20,height:20})}),p(`p`,{className:`${E}${s?.title?` ${s.title}`:``}`,children:e})]}),p(`div`,{className:y,children:[p(`p`,{className:`${C}${s?.description?` ${s.description}`:``}`,children:t}),p(`button`,{type:`button`,className:`${S}${s?.cta?` ${s.cta}`:``}`,onClick:i,children:[n,p(h,{width:14,height:14})]})]}),p(`button`,{type:`button`,className:`${b}${s?.close?` ${s.close}`:``}`,onClick:a,"aria-label":r,children:p(m,{width:16,height:16})})]})}var O=`Banner_frakLogo__1gnumzia`,k=`Banner_iconSvg__1gnumzi2`,A=`Banner_referral__1gnumzi3 reset_base__1831jhd0 Banner_rootBase__1gnumzi1`,j=`Banner_referralBody__1gnumzi6`,M=`Banner_referralCta__1gnumzi9 sharedBaseCss_buttonReset__7cswil0`,ie=`Banner_referralDescription__1gnumzi8 reset_base__1831jhd0`,ae=`Banner_referralIconWrapper__1gnumzi4`,N=`Banner_referralImage__1gnumzi5`,P=`Banner_referralTitle__1gnumzi7 reset_base__1831jhd0`;function F({placement:c,classname:m=``,interaction:h,referralTitle:g,referralDescription:y,referralCta:b,inappTitle:x,inappDescription:S,inappCta:C,imageUrl:w,preview:T,previewMode:E,allowInappRedirect:F}){let I=!!T,L=E===`inapp`?`inapp`:`referral`,R=F===!0||F===`true`,z=ee(c),{shouldRender:B,isHidden:V,isClientReady:H}=l();ne(`frak-banner`,c,z?.components?.banner?.css,`@keyframes inAppBanner_fadeIn__1ibpiy70 {
2
- from {
3
- opacity: 0;
4
- transform: translateY(-4px);
5
- }
6
- to {
7
- opacity: 1;
8
- transform: translateY(0);
9
- }
10
- }
11
- .inAppBanner_container__1ibpiy71 {
12
- position: fixed;
13
- top: max(8px, env(safe-area-inset-top));
14
- left: 16px;
15
- right: 16px;
16
- z-index: 1000;
17
- display: flex;
18
- flex-direction: column;
19
- gap: 4px;
20
- padding: 12px 16px;
21
- padding-right: 32px;
22
- border-radius: 12px;
23
- background-color: #000000CC;
24
- backdrop-filter: blur(12px);
25
- -webkit-backdrop-filter: blur(12px);
26
- color: #ffffff;
27
- animation: inAppBanner_fadeIn__1ibpiy70 300ms ease-out;
28
- }
29
- .inAppBanner_header__1ibpiy72 {
30
- display: flex;
31
- align-items: center;
32
- gap: 8px;
33
- }
34
- .inAppBanner_iconWrapper__1ibpiy73 {
35
- flex-shrink: 0;
36
- width: 20px;
37
- height: 20px;
38
- display: flex;
39
- align-items: center;
40
- justify-content: center;
41
- color: #ffffff;
42
- }
43
- .inAppBanner_title__1ibpiy74 {
44
- margin: 0;
45
- padding: 0;
46
- font-size: 16px;
47
- font-weight: 500;
48
- line-height: 26px;
49
- color: var(--text-onAction__pbq4ak6);
50
- }
51
- .inAppBanner_body__1ibpiy75 {
52
- display: flex;
53
- flex-wrap: wrap;
54
- align-items: baseline;
55
- gap: 0 4px;
56
- }
57
- .inAppBanner_description__1ibpiy76 {
58
- margin: 0;
59
- padding: 0;
60
- font-size: 14px;
61
- color: var(--text-onAction__pbq4ak6);
62
- line-height: 22px;
63
- opacity: 0.96;
64
- }
65
- .inAppBanner_cta__1ibpiy77 {
66
- all: unset;
67
- display: inline-flex;
68
- align-items: center;
69
- gap: 4px;
70
- color: #2BB2FF;
71
- font-size: 14px;
72
- font-weight: 600;
73
- text-decoration: underline;
74
- text-underline-offset: 2px;
75
- cursor: pointer;
76
- }
77
- .inAppBanner_cta__1ibpiy77:focus-visible {
78
- outline: 2px solid #2BB2FF;
79
- outline-offset: 2px;
80
- border-radius: 4px;
81
- }
82
- .inAppBanner_closeButton__1ibpiy78 {
83
- all: unset;
84
- position: absolute;
85
- top: 8px;
86
- right: 8px;
87
- width: 28px;
88
- height: 28px;
89
- display: flex;
90
- align-items: center;
91
- justify-content: center;
92
- border-radius: 9999px;
93
- color: rgba(255, 255, 255, 0.6);
94
- cursor: pointer;
95
- }
96
- .inAppBanner_closeButton__1ibpiy78:focus-visible {
97
- outline: 2px solid #ffffff;
98
- outline-offset: 2px;
99
- }@keyframes Banner_fadeIn__1gnumzi0 {
100
- from {
101
- opacity: 0;
102
- transform: translateY(-4px);
103
- }
104
- to {
105
- opacity: 1;
106
- transform: translateY(0);
107
- }
108
- }
109
- .Banner_rootBase__1gnumzi1 {
110
- position: relative;
111
- display: flex;
112
- animation: Banner_fadeIn__1gnumzi0 300ms ease-out;
113
- }
114
- .Banner_iconSvg__1gnumzi2 {
115
- width: 100%;
116
- height: 100%;
117
- }
118
- .Banner_referral__1gnumzi3 {
119
- flex-direction: row;
120
- align-items: center;
121
- gap: 16px;
122
- padding: 16px;
123
- background-color: #ffffff;
124
- color: var(--text-primary__pbq4ak0);
125
- border: 1px solid var(--border-default__pbq4akv);
126
- border-radius: 12px;
127
- }
128
- .Banner_referralIconWrapper__1gnumzi4 {
129
- flex-shrink: 0;
130
- align-self: flex-start;
131
- display: flex;
132
- align-items: center;
133
- justify-content: center;
134
- width: 40px;
135
- height: 40px;
136
- overflow: hidden;
137
- }
138
- .Banner_referralImage__1gnumzi5 {
139
- max-width: 100%;
140
- max-height: 100%;
141
- width: auto;
142
- height: auto;
143
- object-fit: contain;
144
- display: block;
145
- }
146
- .Banner_referralBody__1gnumzi6 {
147
- flex: 1;
148
- min-width: 0;
149
- }
150
- .Banner_referralTitle__1gnumzi7 {
151
- font-size: 16px;
152
- font-weight: 600;
153
- color: var(--text-primary__pbq4ak0);
154
- line-height: 22px;
155
- }
156
- .Banner_referralDescription__1gnumzi8 {
157
- margin-bottom: 8px;
158
- font-size: 14px;
159
- color: #979797;
160
- line-height: 22px;
161
- }
162
- .Banner_referralCta__1gnumzi9 {
163
- display: inline-block;
164
- padding: 8px 16px;
165
- border: 1px solid #000000;
166
- border-radius: 9999px;
167
- color: var(--text-primary__pbq4ak0);
168
- font-size: 10px;
169
- font-weight: 700;
170
- line-height: 12px;
171
- text-transform: uppercase;
172
- }
173
- .Banner_frakLogo__1gnumzia {
174
- position: absolute;
175
- right: 16px;
176
- bottom: 12px;
177
- pointer-events: none;
178
- }`,e);let[U,W]=f(!1),[G,K]=f(()=>I?L:R&&a?`inapp`:null),q=o(null);u(()=>{I&&K(L)},[I,L]);let{reward:J}=re(G===`referral`&&H,h),[Y,X]=f(null);u(()=>{let e=window.FrakSetup?.client;G!==`inapp`||I||!H||!e||n(e).then(e=>X(e)).catch(()=>{})},[G,I,H]),u(()=>{I||!G||U||q.current!==G&&H&&(t(window.FrakSetup?.client,`banner_impression`,{placement:c,variant:G,has_reward:G===`referral`?!!J:void 0}),q.current=G)},[G,U,H,I,c]),u(()=>{if(I||G===`inapp`)return;let e=()=>K(`referral`);return window.addEventListener(i,e),()=>window.removeEventListener(i,e)},[I,G]);let Z=s(async()=>{if(I)return;if(t(window.FrakSetup?.client,`banner_resolved`,{placement:c,variant:G??`referral`,outcome:`clicked`}),G===`referral`){W(!0);return}let e=Y;if(!e&&window.FrakSetup?.client)try{e=await n(window.FrakSetup?.client)}catch{}let i=window.location.href;if(e){let t=new URL(i);t.searchParams.set(`fmt`,e),i=t.toString()}r(i)},[I,G,Y,c]),oe=s(()=>{I||(t(window.FrakSetup?.client,`banner_resolved`,{placement:c,variant:G??`referral`,outcome:`dismissed`}),W(!0))},[I,G,c]),se=v(),Q=z?.components?.banner??se?.banner,$=d(()=>{if(G===`referral`){let e=J?`Earn ${J} on purchases on this site`:`You've been referred!`;return{title:g??Q?.referralTitle??e,description:y??Q?.referralDescription??`Earn rewards after your purchase via the Frak partner app.`,cta:b??Q?.referralCta??`Got it`}}return{title:x??Q?.inappTitle??`Open in your browser`,description:S??Q?.inappDescription??`For a better experience and to earn your rewards, open this page in your default browser.`,cta:C??Q?.inappCta??`Open browser`}},[G,J,Q,g,y,b,x,S,C]);if(!G||!I&&(!B||V||U))return null;let ce=[A,`frak-banner`,`frak-banner--${G}`,m].filter(Boolean).join(` `);return G===`inapp`?p(D,{title:$.title,description:$.description,cta:$.cta,dismissLabel:`Dismiss`,onAction:Z,onDismiss:oe,className:[`frak-banner`,`frak-banner--inapp`,m].filter(Boolean).join(` `),classNames:{icon:`frak-banner__icon`,title:`frak-banner__title`,description:`frak-banner__description`,cta:`frak-banner__cta`,close:`frak-banner__close`}}):p(`div`,{class:ce,role:`alert`,children:[p(`div`,{class:`${ae} frak-banner__icon`,children:w?p(`img`,{src:w,alt:``,class:N}):p(_,{class:k})}),p(`div`,{class:`${j} frak-banner__text`,children:[p(`p`,{class:`${P} frak-banner__title`,children:$.title}),p(`p`,{class:`${ie} frak-banner__description`,children:$.description}),p(`button`,{type:`button`,class:`${M} frak-banner__cta`,onClick:Z,children:$.cta})]}),p(te,{class:`${O} frak-banner__logo`,width:42,height:24})]})}c(F,`frak-banner`,[`placement`,`classname`,`interaction`,`referralTitle`,`referralDescription`,`referralCta`,`inappTitle`,`inappDescription`,`inappCta`,`preview`,`previewMode`,`imageUrl`,`allowInappRedirect`],{shadow:!1});export{F as Banner};
@@ -1 +0,0 @@
1
- import{b as e,i as t}from"./loader.js";import{c as n,d as r,i,o as a,t as o,u as s}from"./usePlacement.BgMXY5CX.js";import{t as c}from"./useGlobalComponents.TG9kIYSc.js";import{t as l}from"./useLightDomStyles.tjNBKcOr.js";import{t as u}from"./formatReward.B1ZyoceC.js";import{t as d}from"./useReward.B530suzR.js";import{t as f}from"./sharingPage.BYsqcN9O.js";function p({placement:r,text:p=`Share and earn!`,classname:m=``,useReward:h,noRewardText:g,targetInteraction:_,clickAction:v,preview:y}){let b=!!y,x=o(r),S=c(),C=x?.components?.buttonShare??S?.buttonShare;l(`frak-button-share`,r,C?.css);let w=a(()=>x?.targetInteraction===void 0?_:x.targetInteraction,[x?.targetInteraction,_]),T=C?.text??p,E=C?.noRewardText??g,D=a(()=>C?.useReward??h===!0,[C?.useReward,h]),O=a(()=>C?.clickAction??v??`sharing-page`,[C?.clickAction,v]),{shouldRender:k,isHidden:A,isClientReady:j}=i(),{reward:M}=d(D&&j,w),N=a(()=>D?M?T.includes(`{REWARD}`)?u(T,M):`${T} ${M}`:E??u(T,void 0):T,[D,T,E,M]),P=n(()=>{if(!b){if(e(window.FrakSetup.client,`share_button_clicked`,{placement:r,target_interaction:w,has_reward:!!M,click_action:O}),O===`embedded-wallet`){t(w,r);return}f(w,r)}},[b,O,w,r,M]);if(!b&&(!k||A))return null;let F=[`button`,`button__fadeIn`,m].filter(Boolean).join(` `);return s(`button`,{type:`button`,disabled:!b&&!j,class:F,onClick:P,children:N})}r(p,`frak-button-share`,[`text`,`placement`,`classname`,`clickAction`,`useReward`,`noRewardText`,`targetInteraction`,`preview`],{shadow:!1});export{p as ButtonShare};
@@ -1,40 +0,0 @@
1
- import{r as e}from"./loader.js";import{d as t,i as n,l as r,m as i,n as a,o,s,t as c,u as l}from"./usePlacement.BgMXY5CX.js";import{t as u}from"./useReward.B530suzR.js";function d(e){return l(`svg`,{fill:`none`,height:`1em`,viewBox:`0 0 28 28`,width:`1em`,xmlns:`http://www.w3.org/2000/svg`,...e,children:[l(`title`,{children:`Gift icon`}),l(`path`,{d:`m23.1427 13.9999v11.4285h-18.2857v-11.4285m9.1429 11.4285v-17.14282m0 0h-5.1429c-.75776 0-1.48448-.30102-2.0203-.83684s-.83684-1.26255-.83684-2.02031.30102-1.48448.83684-2.0203 1.26254-.83684 2.0203-.83684c4 0 5.1429 5.71429 5.1429 5.71429zm0 0h5.1428c.7578 0 1.4845-.30102 2.0203-.83684s.8369-1.26255.8369-2.02031-.3011-1.48448-.8369-2.0203-1.2625-.83684-2.0203-.83684c-4 0-5.1428 5.71429-5.1428 5.71429zm-11.42861 0h22.85711v5.71432h-22.85711z`,stroke:`#fff`,"stroke-linecap":`round`,"stroke-linejoin":`round`})]})}function f({placement:t,classname:f=``,useReward:p,targetInteraction:m}){let h=c(t),g=o(()=>h?.targetInteraction===void 0?m:h.targetInteraction,[h?.targetInteraction,m]),_=o(()=>p===!0,[p]),{shouldRender:v,isHidden:y,isClientReady:b}=n(),{reward:x}=u(_&&b,g),[S,C]=s(`right`);if(r(()=>{let e=h?.components?.buttonWallet?.position,t=window.FrakSetup?.modalWalletConfig?.metadata?.position;C(e??t??`right`)},[h?.components?.buttonWallet?.position]),!v||y)return null;let w=[`button`,`button__fadeIn`,S===`left`?`button__left`:`button__right`,f].filter(Boolean).join(` `);return l(i,{children:[l(`style`,{children:a(`
2
- .button {
3
- all: unset;
4
- position: fixed;
5
- bottom: 20px;
6
- z-index: 2000000;
7
- display: flex;
8
- justify-content: center;
9
- align-items: center;
10
- background-color: #3e557e;
11
- width: 45px;
12
- height: 45px;
13
- border-radius: 50%;
14
- cursor: pointer;
15
- text-align: center;
16
- font-size: 24px;
17
- }
18
-
19
- .button__left {
20
- left: 20px;
21
- }
22
-
23
- .button__right {
24
- right: 20px;
25
- }
26
-
27
- .reward {
28
- position: absolute;
29
- top: -4px;
30
- right: 27px;
31
- padding: 2px 3px;
32
- border-radius: 5px;
33
- background: #ff3f3f;
34
- font-size: 9px;
35
- color: #fff;
36
- font-weight: 600;
37
- white-space: nowrap;
38
- line-height: 9px;
39
- }
40
- `,h?.components?.buttonWallet?.css)}),l(`button`,{type:`button`,"aria-label":`Open wallet`,part:`button`,disabled:!b,class:w,onClick:()=>{e(g,t)},children:[l(d,{}),x&&l(`span`,{class:`reward`,children:x})]})]})}t(f,`frak-button-wallet`,[`placement`,`classname`,`useReward`,`targetInteraction`],{shadow:!0});export{f as ButtonWallet};