fragment-headless-sdk 2.3.1 → 2.3.2
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/components/Announcement/AnnouncementButton.d.ts +1 -2
- package/dist/components/Announcement/AnnouncementButton.js +3 -5
- package/dist/components/Announcement/index.js +7 -9
- package/dist/components/Hero/DesktopHero.d.ts +1 -2
- package/dist/components/Hero/DesktopHero.js +3 -10
- package/dist/components/Hero/MobileHero.d.ts +1 -2
- package/dist/components/Hero/MobileHero.js +3 -10
- package/dist/components/Hero/index.js +7 -9
- package/dist/types/announcement.d.ts +0 -2
- package/dist/types/hero.d.ts +0 -2
- package/dist/utils/cache.js +5 -3
- package/dist/utils/metrics.d.ts +2 -4
- package/dist/utils/metrics.js +3 -51
- package/docs/CHANGELOG.md +13 -0
- package/package.json +27 -4
- package/readme.md +6 -4
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { IAnnouncementContent } from "../../types";
|
|
3
|
-
export default function AnnouncementButton({ content, buttonHref,
|
|
3
|
+
export default function AnnouncementButton({ content, buttonHref, }: {
|
|
4
4
|
content: IAnnouncementContent;
|
|
5
5
|
buttonHref?: string;
|
|
6
|
-
clickHref?: string;
|
|
7
6
|
}): React.JSX.Element | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ButtonType, SectionType } from "../../constants";
|
|
3
3
|
import { fireClickMetric, mergeSlotAttributes, mergeSlotClasses, mergeSlotStyles, resolveAnnouncementColors, } from "../../utils";
|
|
4
|
-
export default function AnnouncementButton({ content, buttonHref,
|
|
4
|
+
export default function AnnouncementButton({ content, buttonHref, }) {
|
|
5
5
|
// Don’t render if no button or explicitly None
|
|
6
6
|
if (!content?.buttonText || content.buttonType === ButtonType.None)
|
|
7
7
|
return null;
|
|
@@ -29,9 +29,7 @@ export default function AnnouncementButton({ content, buttonHref, clickHref, })
|
|
|
29
29
|
delete attributes["aria-label"];
|
|
30
30
|
}
|
|
31
31
|
const handleClick = React.useCallback(() => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
fireClickMetric(clickHref, content.measurementId, content.sectionType || SectionType.Announcement, content.sectionId);
|
|
35
|
-
}, [clickHref, content.measurementId, content.sectionType, content.sectionId]);
|
|
32
|
+
fireClickMetric(content.measurementId, content.sectionType || SectionType.Announcement, content.sectionId);
|
|
33
|
+
}, [content.measurementId, content.sectionType, content.sectionId]);
|
|
36
34
|
return (React.createElement("a", { href: buttonHref, className: className, style: style, onClick: handleClick, ...(attributes ?? {}), "aria-label": ariaLabel }, content.buttonText));
|
|
37
35
|
}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { XMarkIcon } from "@heroicons/react/24/outline";
|
|
2
3
|
import React, { useEffect, useRef } from "react";
|
|
3
4
|
import { AnnouncementType, ButtonType, SectionType } from "../../constants";
|
|
4
|
-
import {
|
|
5
|
+
import { fireImpressionWhenVisible, getThemeClasses, mergeSlotAttributes, mergeSlotClasses, mergeSlotStyles, resolveAnnouncementColors, } from "../../utils";
|
|
5
6
|
import AnnouncementButton from "./AnnouncementButton";
|
|
6
7
|
import { AnnouncementStyles } from "./AnnouncementStyles";
|
|
7
8
|
import CountdownTimer from "./CountdownTimer";
|
|
8
9
|
export default function Announcement({ content, type, handleClose, }) {
|
|
9
10
|
const ref = useRef(null);
|
|
10
11
|
useEffect(() => {
|
|
11
|
-
if (ref.current
|
|
12
|
-
fireImpressionWhenVisible(ref.current, content.
|
|
12
|
+
if (ref.current) {
|
|
13
|
+
fireImpressionWhenVisible(ref.current, content.measurementId, content.sectionType || SectionType.Announcement, content.sectionId);
|
|
13
14
|
}
|
|
14
|
-
}, [content?.
|
|
15
|
+
}, [content?.measurementId, content?.sectionType, content?.sectionId]);
|
|
15
16
|
const buttonHref = content?.buttonLink || undefined;
|
|
16
|
-
const clickTrackingHref = content?.buttonLink && content?.clickUrlBase
|
|
17
|
-
? buildClickUrl(content.clickUrlBase, content.buttonLink)
|
|
18
|
-
: undefined;
|
|
19
17
|
if (!content)
|
|
20
18
|
return null;
|
|
21
19
|
const styling = content.styling;
|
|
@@ -60,13 +58,13 @@ export default function Announcement({ content, type, handleClose, }) {
|
|
|
60
58
|
React.createElement("div", { className: marqueeContentClass, style: marqueeContentStyle, ...(marqueeContentAttributes ?? {}), dangerouslySetInnerHTML: {
|
|
61
59
|
__html: content.announcementHtml || "",
|
|
62
60
|
} }))),
|
|
63
|
-
content.buttonText && content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: buttonHref
|
|
61
|
+
content.buttonText && content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: buttonHref })))) : (React.createElement("div", { className: contentRowClass, style: contentRowStyle, ...(contentRowAttributes ?? {}) },
|
|
64
62
|
React.createElement("div", { className: announcementTextClass, style: announcementTextStyle, ...(announcementTextAttributes ?? {}) },
|
|
65
63
|
React.createElement("div", { dangerouslySetInnerHTML: {
|
|
66
64
|
__html: content.announcementHtml || "",
|
|
67
65
|
} })),
|
|
68
66
|
type === AnnouncementType.Countdown && (React.createElement(CountdownTimer, { content: content })),
|
|
69
|
-
content.buttonText && content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: buttonHref
|
|
67
|
+
content.buttonText && content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: buttonHref })))),
|
|
70
68
|
React.createElement("div", { onClick: handleClose, className: closeButtonClass, style: closeButtonStyle, ...(closeButtonAttributes ?? {}) },
|
|
71
69
|
React.createElement(XMarkIcon, { className: "w-5 h-5" })))));
|
|
72
70
|
}
|
|
@@ -3,7 +3,6 @@ import { IHeroContent } from "../../types";
|
|
|
3
3
|
import { resolveHeroColors, resolveHeroTypography } from "../../utils/hero-resolvers";
|
|
4
4
|
interface HeroViewProps {
|
|
5
5
|
buttonHref?: string;
|
|
6
|
-
clickHref?: string;
|
|
7
6
|
content: IHeroContent;
|
|
8
7
|
colors: ReturnType<typeof resolveHeroColors>;
|
|
9
8
|
contentWidthClass: string;
|
|
@@ -11,5 +10,5 @@ interface HeroViewProps {
|
|
|
11
10
|
position: "left" | "center" | "right";
|
|
12
11
|
height: string;
|
|
13
12
|
}
|
|
14
|
-
export default function DesktopHero({ buttonHref,
|
|
13
|
+
export default function DesktopHero({ buttonHref, content, colors, contentWidthClass, typography, position, height, }: HeroViewProps): React.JSX.Element;
|
|
15
14
|
export {};
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { SectionType } from "../../constants";
|
|
3
3
|
import { fireClickMetric } from "../../utils";
|
|
4
4
|
import { DEFAULT_CONTENT_WIDTH_CLASS, joinClassNames, renderText, } from "../../utils/hero-resolvers";
|
|
5
|
-
export default function DesktopHero({ buttonHref,
|
|
5
|
+
export default function DesktopHero({ buttonHref, content, colors, contentWidthClass, typography, position, height, }) {
|
|
6
6
|
const getPositionClasses = () => {
|
|
7
7
|
switch (position) {
|
|
8
8
|
case "center":
|
|
@@ -15,15 +15,8 @@ export default function DesktopHero({ buttonHref, clickHref, content, colors, co
|
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
17
|
const handleClick = React.useCallback(() => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
fireClickMetric(clickHref, content.measurementId, content.sectionType || SectionType.HeroBanner, content.sectionId);
|
|
21
|
-
}, [
|
|
22
|
-
clickHref,
|
|
23
|
-
content.measurementId,
|
|
24
|
-
content.sectionType,
|
|
25
|
-
content.sectionId,
|
|
26
|
-
]);
|
|
18
|
+
fireClickMetric(content.measurementId, content.sectionType || SectionType.HeroBanner, content.sectionId);
|
|
19
|
+
}, [content.measurementId, content.sectionType, content.sectionId]);
|
|
27
20
|
return (React.createElement("div", { className: `relative ${height} gap-4 w-full`, style: { backgroundColor: colors.background } },
|
|
28
21
|
content?.videoUrl ? (React.createElement("video", { src: content.videoUrl, autoPlay: true, muted: true, loop: true, playsInline: true, className: "absolute inset-0 z-0 object-cover w-full h-full" })) : (
|
|
29
22
|
/* Image Background */
|
|
@@ -3,10 +3,9 @@ import { IHeroContent } from "../../types";
|
|
|
3
3
|
import { resolveHeroColors, resolveHeroTypography } from "../../utils/hero-resolvers";
|
|
4
4
|
interface MobileHeroProps {
|
|
5
5
|
buttonHref?: string;
|
|
6
|
-
clickHref?: string;
|
|
7
6
|
content: IHeroContent;
|
|
8
7
|
colors: ReturnType<typeof resolveHeroColors>;
|
|
9
8
|
typography: ReturnType<typeof resolveHeroTypography>;
|
|
10
9
|
}
|
|
11
|
-
export default function MobileHero({ buttonHref,
|
|
10
|
+
export default function MobileHero({ buttonHref, content, colors, typography, }: MobileHeroProps): React.JSX.Element;
|
|
12
11
|
export {};
|
|
@@ -2,17 +2,10 @@ import React from "react";
|
|
|
2
2
|
import { SectionType } from "../../constants";
|
|
3
3
|
import { fireClickMetric } from "../../utils";
|
|
4
4
|
import { renderText, } from "../../utils/hero-resolvers";
|
|
5
|
-
export default function MobileHero({ buttonHref,
|
|
5
|
+
export default function MobileHero({ buttonHref, content, colors, typography, }) {
|
|
6
6
|
const handleClick = React.useCallback(() => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
fireClickMetric(clickHref, content.measurementId, content.sectionType || SectionType.HeroBanner, content.sectionId);
|
|
10
|
-
}, [
|
|
11
|
-
clickHref,
|
|
12
|
-
content.measurementId,
|
|
13
|
-
content.sectionType,
|
|
14
|
-
content.sectionId,
|
|
15
|
-
]);
|
|
7
|
+
fireClickMetric(content.measurementId, content.sectionType || SectionType.HeroBanner, content.sectionId);
|
|
8
|
+
}, [content.measurementId, content.sectionType, content.sectionId]);
|
|
16
9
|
return (React.createElement("div", { className: "relative z-10 mx-auto gap-4 flex max-w-screen-md flex-col items-center justify-center py-6 text-center", style: { backgroundColor: colors.background } },
|
|
17
10
|
renderText({
|
|
18
11
|
fontSize: typography.title.fontSize,
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import React, { useEffect, useRef } from "react";
|
|
2
3
|
import { SectionType } from "../../constants";
|
|
3
|
-
import {
|
|
4
|
+
import { fireImpressionWhenVisible, } from "../../utils";
|
|
4
5
|
import { resolveContentWidthClass, resolveHeight, resolveHeroColors, resolveHeroTypography, resolvePosition, } from "../../utils/hero-resolvers";
|
|
5
6
|
import DesktopHero from "./DesktopHero";
|
|
6
7
|
import MobileHero from "./MobileHero";
|
|
7
8
|
export default function Hero({ content }) {
|
|
8
9
|
const ref = useRef(null);
|
|
9
10
|
useEffect(() => {
|
|
10
|
-
if (ref.current
|
|
11
|
-
fireImpressionWhenVisible(ref.current, content.
|
|
11
|
+
if (ref.current) {
|
|
12
|
+
fireImpressionWhenVisible(ref.current, content.measurementId, content.sectionType || SectionType.HeroBanner, content.sectionId);
|
|
12
13
|
}
|
|
13
|
-
}, [content?.
|
|
14
|
+
}, [content?.measurementId, content?.sectionType, content?.sectionId]);
|
|
14
15
|
const buttonHref = content?.buttonLink || undefined;
|
|
15
|
-
const clickTrackingHref = content?.buttonLink && content?.clickUrlBase
|
|
16
|
-
? buildClickUrl(content.clickUrlBase, content.buttonLink)
|
|
17
|
-
: undefined;
|
|
18
16
|
if (!content)
|
|
19
17
|
return null;
|
|
20
18
|
const colors = resolveHeroColors(content);
|
|
@@ -24,7 +22,7 @@ export default function Hero({ content }) {
|
|
|
24
22
|
const height = resolveHeight(content);
|
|
25
23
|
return (React.createElement("div", { className: "bg-black", ref: ref, style: { backgroundColor: colors.background } },
|
|
26
24
|
React.createElement("div", { className: "hidden lg:block" },
|
|
27
|
-
React.createElement(DesktopHero, { content: content, buttonHref: buttonHref,
|
|
25
|
+
React.createElement(DesktopHero, { content: content, buttonHref: buttonHref, colors: colors, contentWidthClass: contentWidthClass, typography: typography, position: position, height: height })),
|
|
28
26
|
React.createElement("div", { className: "block lg:hidden" },
|
|
29
|
-
React.createElement(MobileHero, { content: content, buttonHref: buttonHref,
|
|
27
|
+
React.createElement(MobileHero, { content: content, buttonHref: buttonHref, colors: colors, typography: typography }))));
|
|
30
28
|
}
|
package/dist/types/hero.d.ts
CHANGED
package/dist/utils/cache.js
CHANGED
|
@@ -15,12 +15,14 @@ export async function revalidateFragmentCache(tags) {
|
|
|
15
15
|
try {
|
|
16
16
|
// Dynamic import to avoid issues in non-Next.js environments
|
|
17
17
|
// @ts-ignore - next/cache may not be available in all environments
|
|
18
|
-
const
|
|
18
|
+
const mod = await import("next/cache");
|
|
19
|
+
// Cast: Next.js types vary by version (1 arg vs 2 arg with profile). Pass tag + "max" for Next 16+.
|
|
20
|
+
const revalidateTag = mod.revalidateTag;
|
|
19
21
|
if (tags && tags.length > 0) {
|
|
20
22
|
// Revalidate specific tags
|
|
21
23
|
tags.forEach((tag) => {
|
|
22
24
|
try {
|
|
23
|
-
revalidateTag(tag);
|
|
25
|
+
revalidateTag(tag, "max");
|
|
24
26
|
}
|
|
25
27
|
catch (err) {
|
|
26
28
|
console.warn(`Failed to revalidate tag ${tag}:`, err);
|
|
@@ -31,7 +33,7 @@ export async function revalidateFragmentCache(tags) {
|
|
|
31
33
|
// Revalidate all Fragment resource types
|
|
32
34
|
Object.values(ResourceType).forEach((type) => {
|
|
33
35
|
try {
|
|
34
|
-
revalidateTag(`fragment-${type}
|
|
36
|
+
revalidateTag(`fragment-${type}`, "max");
|
|
35
37
|
}
|
|
36
38
|
catch (err) {
|
|
37
39
|
console.warn(`Failed to revalidate fragment-${type}:`, err);
|
package/dist/utils/metrics.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { SectionType } from "../constants";
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function buildClickUrl(clickUrlBase: string, targetHref: string): string;
|
|
4
|
-
export declare function fireClickMetric(clickUrl: string, measurementId?: string, sectionType?: SectionType, sectionId?: string): void;
|
|
2
|
+
export declare function fireClickMetric(measurementId?: string, sectionType?: SectionType, sectionId?: string): void;
|
|
5
3
|
declare global {
|
|
6
4
|
interface Window {
|
|
7
5
|
gtag?: (command: string, targetId: string | Date, config?: Record<string, unknown>) => void;
|
|
@@ -12,4 +10,4 @@ declare global {
|
|
|
12
10
|
* Fire a scroll past metric when user scrolls past a section
|
|
13
11
|
*/
|
|
14
12
|
export declare function fireScrollPastMetric(measurementId: string | undefined, sectionType: SectionType, sectionId: string): void;
|
|
15
|
-
export declare function fireImpressionWhenVisible(el: HTMLElement,
|
|
13
|
+
export declare function fireImpressionWhenVisible(el: HTMLElement, measurementId?: string, sectionType?: SectionType, sectionId?: string): void;
|
package/dist/utils/metrics.js
CHANGED
|
@@ -1,59 +1,15 @@
|
|
|
1
1
|
import { SectionType } from "../constants";
|
|
2
2
|
import { setAttribution } from "./attribution";
|
|
3
|
-
|
|
4
|
-
export function toBase64Url(input) {
|
|
5
|
-
// Handles emojis & non-ASCII reliably:
|
|
6
|
-
const b64 = btoa(encodeURIComponent(input).replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode(parseInt(p1, 16))));
|
|
7
|
-
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
8
|
-
}
|
|
9
|
-
// --- Robust query appender ---
|
|
10
|
-
function appendQuery(url, key, value) {
|
|
11
|
-
const sep = url.includes("?") ? "&" : "?";
|
|
12
|
-
return `${url}${sep}${key}=${value}`;
|
|
13
|
-
}
|
|
14
|
-
// Build the tracking URL that encodes the final destination for metrics
|
|
15
|
-
export function buildClickUrl(clickUrlBase, targetHref) {
|
|
16
|
-
const u = encodeURIComponent(toBase64Url(targetHref));
|
|
17
|
-
return appendQuery(clickUrlBase, "u", u);
|
|
18
|
-
}
|
|
19
|
-
// Fire the click tracking URL without relying on a redirect
|
|
20
|
-
// Default to GET so legacy tracking endpoints continue to accept the request
|
|
21
|
-
export function fireClickMetric(clickUrl, measurementId, sectionType, sectionId) {
|
|
3
|
+
export function fireClickMetric(measurementId, sectionType, sectionId) {
|
|
22
4
|
if (typeof window === "undefined")
|
|
23
5
|
return;
|
|
24
|
-
if (!clickUrl)
|
|
25
|
-
return;
|
|
26
6
|
// Store attribution for potential purchase/add-to-cart
|
|
27
7
|
if (sectionType && sectionId) {
|
|
28
8
|
setAttribution(sectionId, sectionType);
|
|
29
9
|
}
|
|
30
|
-
// Send to GA4 first (if available)
|
|
31
10
|
if (measurementId && sectionType && sectionId) {
|
|
32
11
|
sendGA4Event("click", measurementId, sectionType, sectionId);
|
|
33
12
|
}
|
|
34
|
-
try {
|
|
35
|
-
if (typeof fetch === "function") {
|
|
36
|
-
fetch(clickUrl, {
|
|
37
|
-
method: "GET",
|
|
38
|
-
mode: "no-cors",
|
|
39
|
-
keepalive: true,
|
|
40
|
-
}).catch(() => {
|
|
41
|
-
/* no-op */
|
|
42
|
-
});
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// swallow and fall back to <img>
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
const img = new Image();
|
|
51
|
-
img.referrerPolicy = "strict-origin-when-cross-origin";
|
|
52
|
-
img.src = clickUrl;
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
// nothing else we can do
|
|
56
|
-
}
|
|
57
13
|
}
|
|
58
14
|
/**
|
|
59
15
|
* Generates a section-specific event name for GA4 tracking.
|
|
@@ -102,25 +58,21 @@ export function fireScrollPastMetric(measurementId, sectionType, sectionId) {
|
|
|
102
58
|
}
|
|
103
59
|
// --- View tracking (once per element) ---
|
|
104
60
|
const seenEls = typeof WeakSet !== "undefined" ? new WeakSet() : null;
|
|
105
|
-
export function fireImpressionWhenVisible(el,
|
|
61
|
+
export function fireImpressionWhenVisible(el, measurementId, sectionType, sectionId) {
|
|
106
62
|
if (typeof window === "undefined")
|
|
107
63
|
return; // SSR guard
|
|
108
|
-
if (!el
|
|
64
|
+
if (!el)
|
|
109
65
|
return;
|
|
110
66
|
if (seenEls && seenEls.has(el))
|
|
111
67
|
return; // de-dupe by element
|
|
112
68
|
let fired = false;
|
|
113
69
|
let hasScrolledPast = false;
|
|
114
|
-
const img = new Image();
|
|
115
70
|
const fire = () => {
|
|
116
71
|
if (fired)
|
|
117
72
|
return;
|
|
118
73
|
fired = true;
|
|
119
74
|
if (seenEls)
|
|
120
75
|
seenEls.add(el);
|
|
121
|
-
img.referrerPolicy = "strict-origin-when-cross-origin";
|
|
122
|
-
img.src = pixelUrl; // GET to your /api/v1/metrics/i?e=...&sig=...
|
|
123
|
-
// Also send to GA4 if available
|
|
124
76
|
if (measurementId && sectionType && sectionId) {
|
|
125
77
|
sendGA4Event("view", measurementId, sectionType, sectionId);
|
|
126
78
|
}
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
### [Unreleased]
|
|
9
|
+
|
|
10
|
+
#### Removed
|
|
11
|
+
|
|
12
|
+
### [2.3.2] - 2026-02-27
|
|
13
|
+
|
|
14
|
+
#### Removed
|
|
15
|
+
|
|
16
|
+
- **Legacy server-side tracking** – The previous pixel/click tracking system has been fully replaced by Google Analytics 4 (GA4):
|
|
17
|
+
- `impressionUrl` and `clickUrlBase` on content objects are no longer used; the API now provides `measurementId`, `sectionId`, and `sectionType` for GA4 events.
|
|
18
|
+
- The `makeSignedMetricUrls` helper and server-side `/api/v1/t` endpoints (impression/click) have been removed from the Fragment app; the SDK sends view/click/scroll_past via `gtag` only.
|
|
19
|
+
- See [2.2.0] for GA4 integration details. Historical entries [1.0.5] and earlier described the old pixel-based flow.
|
|
20
|
+
|
|
8
21
|
### [2.3.1] - 2026-01-25
|
|
9
22
|
|
|
10
23
|
### 🔧 Technical Improvements
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fragment-headless-sdk",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
|
+
"description": "Headless SDK for Fragment Shopify storefronts",
|
|
4
5
|
"license": "MIT",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
@@ -15,12 +16,34 @@
|
|
|
15
16
|
"dist",
|
|
16
17
|
"docs/CHANGELOG.md"
|
|
17
18
|
],
|
|
19
|
+
"sideEffects": false,
|
|
18
20
|
"scripts": {
|
|
19
|
-
"build": "tsc"
|
|
21
|
+
"build": "tsc && mkdir -p dist/styles && cp src/styles/*.css dist/styles/",
|
|
22
|
+
"dev": "tsc --watch",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"clean": "rm -rf dist .turbo"
|
|
20
25
|
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"registry": "https://registry.npmjs.org/"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/sevenbrand/fragment-monorepo",
|
|
33
|
+
"directory": "packages/sdk"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"shopify",
|
|
37
|
+
"headless",
|
|
38
|
+
"fragment",
|
|
39
|
+
"sdk",
|
|
40
|
+
"storefront"
|
|
41
|
+
],
|
|
21
42
|
"dependencies": {
|
|
22
|
-
"@heroicons/react": "^2.2.0"
|
|
23
|
-
|
|
43
|
+
"@heroicons/react": "^2.2.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
24
47
|
},
|
|
25
48
|
"devDependencies": {
|
|
26
49
|
"@types/node": "^24.7.2",
|
package/readme.md
CHANGED
|
@@ -377,8 +377,9 @@ interface IHeroContent {
|
|
|
377
377
|
imageUrl: string;
|
|
378
378
|
mobileImageUrl: string;
|
|
379
379
|
videoUrl?: string;
|
|
380
|
-
|
|
381
|
-
|
|
380
|
+
measurementId?: string; // GA4 measurement ID (from app config)
|
|
381
|
+
sectionId?: string; // Section ID for GA4 event params
|
|
382
|
+
sectionType?: SectionType;
|
|
382
383
|
styling?: IHeroStyling; // v2.0+ enhanced styling
|
|
383
384
|
}
|
|
384
385
|
```
|
|
@@ -434,8 +435,9 @@ interface IAnnouncementContent {
|
|
|
434
435
|
buttonLink: string;
|
|
435
436
|
announcementHtml: string;
|
|
436
437
|
counterEndDate?: string;
|
|
437
|
-
|
|
438
|
-
|
|
438
|
+
measurementId?: string; // GA4 measurement ID (from app config)
|
|
439
|
+
sectionId?: string; // Section ID for GA4 event params
|
|
440
|
+
sectionType?: SectionType;
|
|
439
441
|
styling?: IAnnouncementStyling; // v2.0+ enhanced styling
|
|
440
442
|
}
|
|
441
443
|
```
|