fragment-headless-sdk 2.0.0 → 2.1.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/components/Hero/DesktopHero.d.ts +10 -2
- package/dist/components/Hero/DesktopHero.js +39 -42
- package/dist/components/Hero/MobileHero.d.ts +7 -2
- package/dist/components/Hero/MobileHero.js +27 -38
- package/dist/components/Hero/index.js +12 -15
- package/dist/styles/fragment-sdk.css +31 -0
- package/dist/utils/hero-resolvers.d.ts +62 -0
- package/dist/utils/hero-resolvers.js +107 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +3 -2
- package/readme.md +259 -4
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { IHeroContent } from "../../types";
|
|
3
|
-
|
|
3
|
+
import { resolveHeroColors, resolveHeroTypography } from "../../utils/hero-resolvers";
|
|
4
|
+
interface HeroViewProps {
|
|
4
5
|
buttonHref?: string;
|
|
5
6
|
content: IHeroContent;
|
|
6
|
-
|
|
7
|
+
colors: ReturnType<typeof resolveHeroColors>;
|
|
8
|
+
contentWidthClass: string;
|
|
9
|
+
typography: ReturnType<typeof resolveHeroTypography>;
|
|
10
|
+
position: "left" | "center" | "right";
|
|
11
|
+
height: string;
|
|
12
|
+
}
|
|
13
|
+
export default function DesktopHero({ buttonHref, content, colors, contentWidthClass, typography, position, height, }: HeroViewProps): React.JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -1,45 +1,42 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
export default function DesktopHero({ buttonHref, content, }) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return (React.createElement("div", { className: containerClass, style: containerStyle, ...(containerAttributes ?? {}) },
|
|
38
|
-
content?.imageUrl && (React.createElement("img", { src: content.imageUrl, alt: content.title || "Hero", className: imageClass, style: imageStyle, ...(imageAttributes ?? {}) })),
|
|
39
|
-
React.createElement("div", { className: contentWrapperClass, style: contentWrapperStyle, ...(contentWrapperAttributes ?? {}) },
|
|
40
|
-
React.createElement("div", { className: contentInnerClass, style: contentInnerStyle, ...(contentInnerAttributes ?? {}) },
|
|
41
|
-
content?.title && (React.createElement("h1", { className: titleClass, style: titleStyle, ...(titleAttributes ?? {}) }, content.title)),
|
|
42
|
-
content?.description && (React.createElement("div", { className: descriptionClass, style: descriptionStyle, ...(descriptionAttributes ?? {}), dangerouslySetInnerHTML: { __html: content.description } })),
|
|
2
|
+
import { DEFAULT_CONTENT_WIDTH_CLASS, joinClassNames, renderText, } from "../../utils/hero-resolvers";
|
|
3
|
+
export default function DesktopHero({ buttonHref, content, colors, contentWidthClass, typography, position, height, }) {
|
|
4
|
+
const getPositionClasses = () => {
|
|
5
|
+
switch (position) {
|
|
6
|
+
case "center":
|
|
7
|
+
return "items-center text-center";
|
|
8
|
+
case "right":
|
|
9
|
+
return "items-end text-right";
|
|
10
|
+
case "left":
|
|
11
|
+
default:
|
|
12
|
+
return "items-start text-left";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return (React.createElement("div", { className: `relative ${height} gap-4 w-full`, style: { backgroundColor: colors.background } },
|
|
16
|
+
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" })) : (
|
|
17
|
+
/* Image Background */
|
|
18
|
+
content?.imageUrl && (React.createElement("img", { src: content.imageUrl, alt: content.title || "Hero", className: "absolute inset-0 z-0 object-cover w-full h-full" }))),
|
|
19
|
+
React.createElement("div", { className: `relative z-10 mx-auto flex h-full max-w-screen-xl flex-col justify-center px-10 ${getPositionClasses()} xl:px-4` },
|
|
20
|
+
React.createElement("div", { className: joinClassNames("max-w-full", contentWidthClass || DEFAULT_CONTENT_WIDTH_CLASS) },
|
|
21
|
+
renderText({
|
|
22
|
+
fontSize: typography.title.fontSize,
|
|
23
|
+
lineHeight: typography.title.lineHeight,
|
|
24
|
+
text: content?.title,
|
|
25
|
+
className: "mt-4",
|
|
26
|
+
color: colors.title,
|
|
27
|
+
font: typography.title.font,
|
|
28
|
+
}),
|
|
29
|
+
renderText({
|
|
30
|
+
fontSize: typography.description.fontSize,
|
|
31
|
+
lineHeight: typography.description.lineHeight,
|
|
32
|
+
text: content?.description,
|
|
33
|
+
className: "mt-4",
|
|
34
|
+
color: colors.text,
|
|
35
|
+
font: typography.description.font,
|
|
36
|
+
}),
|
|
43
37
|
content?.buttonLink && content?.buttonText && (React.createElement("a", { href: buttonHref, target: "_blank", rel: "noopener noreferrer", className: "no-underline" },
|
|
44
|
-
React.createElement("div", { className:
|
|
38
|
+
React.createElement("div", { className: "mt-6 inline-block rounded-md px-8 py-2 text-2xl font-semibold drop-shadow-lg transition-all duration-200 hover:opacity-90", style: {
|
|
39
|
+
color: colors.buttonText,
|
|
40
|
+
backgroundColor: colors.buttonBackground,
|
|
41
|
+
} }, content.buttonText)))))));
|
|
45
42
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { IHeroContent } from "../../types";
|
|
3
|
-
|
|
3
|
+
import { resolveHeroColors, resolveHeroTypography } from "../../utils/hero-resolvers";
|
|
4
|
+
interface MobileHeroProps {
|
|
4
5
|
buttonHref?: string;
|
|
5
6
|
content: IHeroContent;
|
|
6
|
-
|
|
7
|
+
colors: ReturnType<typeof resolveHeroColors>;
|
|
8
|
+
typography: ReturnType<typeof resolveHeroTypography>;
|
|
9
|
+
}
|
|
10
|
+
export default function MobileHero({ buttonHref, content, colors, typography, }: MobileHeroProps): React.JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -1,41 +1,30 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
export default function MobileHero({ buttonHref, content, }) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
resolveToken(styling, "buttonColor");
|
|
26
|
-
const buttonTextColor = resolveTokenByCategory(styling, "colors", "buttonText") ||
|
|
27
|
-
resolveToken(styling, "buttonTextColor");
|
|
28
|
-
const buttonClass = mergeSlotClasses("mb-2 rounded-md px-6 py-2 text-lg font-semibold drop-shadow-lg transition-all duration-200 hover:bg-gray-800", styling, "mobileButton", "button");
|
|
29
|
-
const buttonStyle = mergeSlotStyles({
|
|
30
|
-
backgroundColor: buttonBgColor,
|
|
31
|
-
color: buttonTextColor,
|
|
32
|
-
}, styling, "mobileButton", "button");
|
|
33
|
-
const buttonAttributes = mergeSlotAttributes(styling, "mobileButton", "button");
|
|
34
|
-
return (React.createElement("div", { className: containerClass, style: containerStyle, ...(containerAttributes ?? {}) },
|
|
35
|
-
content?.title && (React.createElement("h1", { className: titleClass, style: titleStyle, ...(titleAttributes ?? {}) }, content.title)),
|
|
36
|
-
(content?.mobileImageUrl || content?.imageUrl) && (React.createElement("div", { className: imageWrapperClass, style: imageWrapperStyle, ...(imageWrapperAttributes ?? {}) },
|
|
37
|
-
React.createElement("img", { src: content.mobileImageUrl || content.imageUrl || "", alt: content.title || "Hero", className: imageClass, style: imageStyle, ...(imageAttributes ?? {}) }))),
|
|
38
|
-
content?.description && (React.createElement("div", { className: descriptionClass, style: descriptionStyle, ...(descriptionAttributes ?? {}), dangerouslySetInnerHTML: { __html: content.description } })),
|
|
2
|
+
import { renderText, } from "../../utils/hero-resolvers";
|
|
3
|
+
export default function MobileHero({ buttonHref, content, colors, typography, }) {
|
|
4
|
+
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 } },
|
|
5
|
+
renderText({
|
|
6
|
+
fontSize: typography.title.fontSize,
|
|
7
|
+
lineHeight: typography.title.lineHeight,
|
|
8
|
+
text: content?.title,
|
|
9
|
+
className: "px-4 drop-shadow-xl text-center",
|
|
10
|
+
color: colors.title,
|
|
11
|
+
font: typography.title.font,
|
|
12
|
+
}),
|
|
13
|
+
content?.videoUrl ? (React.createElement("div", { className: "w-full" },
|
|
14
|
+
React.createElement("video", { src: content.videoUrl, autoPlay: true, muted: true, loop: true, playsInline: true, className: "h-full w-full object-cover" }))) : content?.mobileImageUrl ? (React.createElement("div", { className: "w-full" },
|
|
15
|
+
React.createElement("img", { src: content.mobileImageUrl, alt: content.title || "Hero", className: "h-full w-full object-cover" }))) : content?.imageUrl ? (React.createElement("div", { className: "w-full" },
|
|
16
|
+
React.createElement("img", { src: content.imageUrl, alt: content.title || "Hero", className: "h-full w-full object-cover" }))) : null,
|
|
17
|
+
renderText({
|
|
18
|
+
fontSize: typography.description.fontSize,
|
|
19
|
+
lineHeight: typography.description.lineHeight,
|
|
20
|
+
text: content?.description,
|
|
21
|
+
className: "px-4 drop-shadow-lg text-center mt-4",
|
|
22
|
+
color: colors.text,
|
|
23
|
+
font: typography.description.font,
|
|
24
|
+
}),
|
|
39
25
|
content?.buttonLink && content?.buttonText && (React.createElement("a", { href: buttonHref, target: "_blank", rel: "noopener noreferrer", className: "no-underline" },
|
|
40
|
-
React.createElement("div", { className:
|
|
26
|
+
React.createElement("div", { className: "mb-2 rounded-md px-6 py-2 text-lg font-semibold drop-shadow-lg transition-all duration-200 hover:opacity-90", style: {
|
|
27
|
+
color: colors.buttonText,
|
|
28
|
+
backgroundColor: colors.buttonBackground,
|
|
29
|
+
} }, content.buttonText)))));
|
|
41
30
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from "react";
|
|
2
|
-
import { buildClickUrl, fireImpressionWhenVisible
|
|
2
|
+
import { buildClickUrl, fireImpressionWhenVisible } from "../../utils";
|
|
3
|
+
import { resolveContentWidthClass, resolveHeight, resolveHeroColors, resolveHeroTypography, resolvePosition, } from "../../utils/hero-resolvers";
|
|
3
4
|
import DesktopHero from "./DesktopHero";
|
|
4
5
|
import MobileHero from "./MobileHero";
|
|
5
6
|
export default function Hero({ content }) {
|
|
@@ -14,18 +15,14 @@ export default function Hero({ content }) {
|
|
|
14
15
|
: undefined;
|
|
15
16
|
if (!content)
|
|
16
17
|
return null;
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
React.createElement("div", { className: desktopWrapperClass, ...(desktopWrapperAttributes ?? {}) },
|
|
28
|
-
React.createElement(DesktopHero, { content: content, buttonHref: signedButtonHref })),
|
|
29
|
-
React.createElement("div", { className: mobileWrapperClass, ...(mobileWrapperAttributes ?? {}) },
|
|
30
|
-
React.createElement(MobileHero, { content: content, buttonHref: signedButtonHref }))));
|
|
18
|
+
const colors = resolveHeroColors(content);
|
|
19
|
+
const contentWidthClass = resolveContentWidthClass(content);
|
|
20
|
+
const typography = resolveHeroTypography(content);
|
|
21
|
+
const position = resolvePosition(content);
|
|
22
|
+
const height = resolveHeight(content);
|
|
23
|
+
return (React.createElement("div", { className: "bg-black", ref: ref, style: { backgroundColor: colors.background } },
|
|
24
|
+
React.createElement("div", { className: "hidden lg:block" },
|
|
25
|
+
React.createElement(DesktopHero, { content: content, buttonHref: signedButtonHref, colors: colors, contentWidthClass: contentWidthClass, typography: typography, position: position, height: height })),
|
|
26
|
+
React.createElement("div", { className: "block lg:hidden" },
|
|
27
|
+
React.createElement(MobileHero, { content: content, buttonHref: signedButtonHref, colors: colors, typography: typography }))));
|
|
31
28
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&family=Roboto:wght@300;400;500;700&family=Open+Sans:wght@300;400;600;700&family=Lato:wght@300;400;700&family=Montserrat:wght@300;400;500;700&family=Inter:wght@300;400;500;700&family=Poppins:wght@300;400;600;700&family=Raleway:wght@300;400;500;700&family=Playfair+Display:wght@400;700&family=Merriweather:wght@300;400;700&family=Oswald:wght@300;400;500;700&display=swap");
|
|
2
|
+
|
|
3
|
+
/* Custom animations for fragment-headless-sdk */
|
|
4
|
+
@keyframes marquee {
|
|
5
|
+
0% {
|
|
6
|
+
transform: translateX(0%);
|
|
7
|
+
}
|
|
8
|
+
100% {
|
|
9
|
+
transform: translateX(-100%);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Utility classes for marquee animations */
|
|
14
|
+
.animate-marquee {
|
|
15
|
+
animation: marquee 25s linear infinite;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Make hero/announcement prose links inherit the surrounding text color (v2.1+) */
|
|
19
|
+
.prose {
|
|
20
|
+
--tw-prose-links: currentColor;
|
|
21
|
+
--tw-prose-links-hover: currentColor;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.prose :where(a):not(:where([class~="not-prose"] *)) {
|
|
25
|
+
color: currentColor;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.prose :where(a):not(:where([class~="not-prose"] *)):hover {
|
|
29
|
+
color: currentColor;
|
|
30
|
+
text-decoration: underline;
|
|
31
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { IHeroContent } from "../types";
|
|
3
|
+
export interface HeroResolvedColors {
|
|
4
|
+
title: string;
|
|
5
|
+
text: string;
|
|
6
|
+
buttonBackground: string;
|
|
7
|
+
buttonText: string;
|
|
8
|
+
background: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_COLORS: HeroResolvedColors;
|
|
11
|
+
export declare const DEFAULT_CONTENT_WIDTH_CLASS = "w-2/5";
|
|
12
|
+
export declare const DEFAULT_HEIGHT_CLASS = "h-[400px]";
|
|
13
|
+
export declare const FONT_FAMILY_MAP: {
|
|
14
|
+
readonly geist: "\"Geist\", -apple-system, BlinkMacSystemFont, sans-serif";
|
|
15
|
+
readonly georgia: "\"Georgia\", \"Times New Roman\", serif";
|
|
16
|
+
readonly geistMono: "\"Geist Mono\", \"SFMono-Regular\", Menlo, monospace";
|
|
17
|
+
readonly roboto: "\"Roboto\", sans-serif";
|
|
18
|
+
readonly openSans: "\"Open Sans\", sans-serif";
|
|
19
|
+
readonly lato: "\"Lato\", sans-serif";
|
|
20
|
+
readonly montserrat: "\"Montserrat\", sans-serif";
|
|
21
|
+
readonly inter: "\"Inter\", sans-serif";
|
|
22
|
+
readonly poppins: "\"Poppins\", sans-serif";
|
|
23
|
+
readonly raleway: "\"Raleway\", sans-serif";
|
|
24
|
+
readonly playfair: "\"Playfair Display\", serif";
|
|
25
|
+
readonly merriweather: "\"Merriweather\", serif";
|
|
26
|
+
readonly oswald: "\"Oswald\", sans-serif";
|
|
27
|
+
};
|
|
28
|
+
export type FontKey = keyof typeof FONT_FAMILY_MAP;
|
|
29
|
+
export declare const DEFAULT_TYPOGRAPHY: {
|
|
30
|
+
titleFontSize: string;
|
|
31
|
+
titleLineHeight: string;
|
|
32
|
+
titleFont: FontKey;
|
|
33
|
+
descriptionFontSize: string;
|
|
34
|
+
descriptionLineHeight: string;
|
|
35
|
+
descriptionFont: FontKey;
|
|
36
|
+
};
|
|
37
|
+
export interface HeroTypographySettings {
|
|
38
|
+
title: {
|
|
39
|
+
fontSize: string;
|
|
40
|
+
lineHeight: string;
|
|
41
|
+
font: FontKey;
|
|
42
|
+
};
|
|
43
|
+
description: {
|
|
44
|
+
fontSize: string;
|
|
45
|
+
lineHeight: string;
|
|
46
|
+
font: FontKey;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export declare const joinClassNames: (...classes: Array<string | false | null | undefined>) => string;
|
|
50
|
+
export declare function resolveHeroColors(content: IHeroContent): HeroResolvedColors;
|
|
51
|
+
export declare function resolveContentWidthClass(content: IHeroContent): string;
|
|
52
|
+
export declare function resolvePosition(content: IHeroContent): "left" | "center" | "right";
|
|
53
|
+
export declare function resolveHeight(content: IHeroContent): string;
|
|
54
|
+
export declare function resolveHeroTypography(content: IHeroContent): HeroTypographySettings;
|
|
55
|
+
export declare function renderText({ fontSize, lineHeight, text, className, color, font, }: {
|
|
56
|
+
fontSize: string;
|
|
57
|
+
lineHeight: string;
|
|
58
|
+
text: string | null | undefined;
|
|
59
|
+
className?: string;
|
|
60
|
+
color?: string;
|
|
61
|
+
font: FontKey;
|
|
62
|
+
}): React.JSX.Element | null;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export const DEFAULT_COLORS = {
|
|
3
|
+
title: "#ffffff",
|
|
4
|
+
text: "#ffffff",
|
|
5
|
+
buttonBackground: "#ffffff",
|
|
6
|
+
buttonText: "#000000",
|
|
7
|
+
background: "#000000",
|
|
8
|
+
};
|
|
9
|
+
export const DEFAULT_CONTENT_WIDTH_CLASS = "w-2/5";
|
|
10
|
+
export const DEFAULT_HEIGHT_CLASS = "h-[400px]";
|
|
11
|
+
export const FONT_FAMILY_MAP = {
|
|
12
|
+
geist: '"Geist", -apple-system, BlinkMacSystemFont, sans-serif',
|
|
13
|
+
georgia: '"Georgia", "Times New Roman", serif',
|
|
14
|
+
geistMono: '"Geist Mono", "SFMono-Regular", Menlo, monospace',
|
|
15
|
+
roboto: '"Roboto", sans-serif',
|
|
16
|
+
openSans: '"Open Sans", sans-serif',
|
|
17
|
+
lato: '"Lato", sans-serif',
|
|
18
|
+
montserrat: '"Montserrat", sans-serif',
|
|
19
|
+
inter: '"Inter", sans-serif',
|
|
20
|
+
poppins: '"Poppins", sans-serif',
|
|
21
|
+
raleway: '"Raleway", sans-serif',
|
|
22
|
+
playfair: '"Playfair Display", serif',
|
|
23
|
+
merriweather: '"Merriweather", serif',
|
|
24
|
+
oswald: '"Oswald", sans-serif',
|
|
25
|
+
};
|
|
26
|
+
export const DEFAULT_TYPOGRAPHY = {
|
|
27
|
+
titleFontSize: "text-5xl",
|
|
28
|
+
titleLineHeight: "leading-none",
|
|
29
|
+
titleFont: "roboto",
|
|
30
|
+
descriptionFontSize: "text-3xl",
|
|
31
|
+
descriptionLineHeight: "leading-relaxed",
|
|
32
|
+
descriptionFont: "roboto",
|
|
33
|
+
};
|
|
34
|
+
export const joinClassNames = (...classes) => classes.filter(Boolean).join(" ");
|
|
35
|
+
const fallbackColor = (value, fallback) => typeof value === "string" && value.trim().length > 0 ? value : fallback;
|
|
36
|
+
export function resolveHeroColors(content) {
|
|
37
|
+
const tokens = content?.styling?.tokens?.colors ??
|
|
38
|
+
{};
|
|
39
|
+
return {
|
|
40
|
+
title: fallbackColor(tokens.title, DEFAULT_COLORS.title),
|
|
41
|
+
text: fallbackColor(tokens.text, DEFAULT_COLORS.text),
|
|
42
|
+
buttonBackground: fallbackColor(tokens.button ?? tokens.buttonBackground, DEFAULT_COLORS.buttonBackground),
|
|
43
|
+
buttonText: fallbackColor(tokens.buttonText, DEFAULT_COLORS.buttonText),
|
|
44
|
+
background: fallbackColor(tokens.background, DEFAULT_COLORS.background),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function resolveContentWidthClass(content) {
|
|
48
|
+
const layout = content?.styling?.tokens?.layout ?? {};
|
|
49
|
+
const rawClass = layout.contentWidthClass;
|
|
50
|
+
if (typeof rawClass === "string" && rawClass.trim().length > 0) {
|
|
51
|
+
return rawClass.trim();
|
|
52
|
+
}
|
|
53
|
+
return DEFAULT_CONTENT_WIDTH_CLASS;
|
|
54
|
+
}
|
|
55
|
+
export function resolvePosition(content) {
|
|
56
|
+
const layout = content?.styling?.tokens?.layout ?? {};
|
|
57
|
+
const rawPosition = layout.position;
|
|
58
|
+
if (rawPosition === "center" ||
|
|
59
|
+
rawPosition === "right" ||
|
|
60
|
+
rawPosition === "left") {
|
|
61
|
+
return rawPosition;
|
|
62
|
+
}
|
|
63
|
+
return "left";
|
|
64
|
+
}
|
|
65
|
+
export function resolveHeight(content) {
|
|
66
|
+
const layout = content?.styling?.tokens?.layout ?? {};
|
|
67
|
+
const rawHeight = layout.height;
|
|
68
|
+
if (typeof rawHeight === "string" && rawHeight.trim().length > 0) {
|
|
69
|
+
return rawHeight.trim();
|
|
70
|
+
}
|
|
71
|
+
return DEFAULT_HEIGHT_CLASS;
|
|
72
|
+
}
|
|
73
|
+
export function resolveHeroTypography(content) {
|
|
74
|
+
const typography = content?.styling?.tokens?.typography;
|
|
75
|
+
return {
|
|
76
|
+
title: {
|
|
77
|
+
fontSize: typography?.titleFontSize ?? DEFAULT_TYPOGRAPHY.titleFontSize,
|
|
78
|
+
lineHeight: typography?.titleLineHeight ?? DEFAULT_TYPOGRAPHY.titleLineHeight,
|
|
79
|
+
font: typography?.titleFont ?? DEFAULT_TYPOGRAPHY.titleFont,
|
|
80
|
+
},
|
|
81
|
+
description: {
|
|
82
|
+
fontSize: typography?.descriptionFontSize ??
|
|
83
|
+
DEFAULT_TYPOGRAPHY.descriptionFontSize,
|
|
84
|
+
lineHeight: typography?.descriptionLineHeight ??
|
|
85
|
+
DEFAULT_TYPOGRAPHY.descriptionLineHeight,
|
|
86
|
+
font: typography?.descriptionFont ??
|
|
87
|
+
DEFAULT_TYPOGRAPHY.descriptionFont,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function renderText({ fontSize, lineHeight, text, className, color, font, }) {
|
|
92
|
+
if (!text || text.trim().length === 0) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const baseClasses = "drop-shadow-lg";
|
|
96
|
+
const combinedClasses = className
|
|
97
|
+
? `${baseClasses} ${fontSize} ${lineHeight} ${className}`
|
|
98
|
+
: `${baseClasses} ${fontSize} ${lineHeight}`;
|
|
99
|
+
return React.createElement("div", {
|
|
100
|
+
className: combinedClasses,
|
|
101
|
+
style: {
|
|
102
|
+
color,
|
|
103
|
+
fontFamily: FONT_FAMILY_MAP[font] ?? FONT_FAMILY_MAP.geist,
|
|
104
|
+
},
|
|
105
|
+
dangerouslySetInnerHTML: { __html: text },
|
|
106
|
+
});
|
|
107
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fragment-headless-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
".": {
|
|
9
9
|
"import": "./dist/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts"
|
|
11
|
-
}
|
|
11
|
+
},
|
|
12
|
+
"./styles": "./dist/styles/fragment-sdk.css"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"dist"
|
package/readme.md
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
The official SDK for integrating with Fragment-Shopify CMS. Provides React components, TypeScript types, and utilities for rendering published sections in headless Shopify storefronts.
|
|
4
4
|
|
|
5
|
-
## ✨ What's New in v2.
|
|
5
|
+
## ✨ What's New in v2.1.0
|
|
6
|
+
|
|
7
|
+
🎨 **Enhanced Hero Styling System** - New hero resolvers utility with advanced typography, positioning, and layout controls
|
|
8
|
+
🔤 **Advanced Typography** - Built-in font family support with granular control over sizes and line heights
|
|
9
|
+
📐 **Flexible Layout Controls** - Content positioning (left/center/right), width management, and height configuration
|
|
10
|
+
🎯 **Developer Experience** - Enhanced TypeScript interfaces and utility functions for better development workflow
|
|
11
|
+
|
|
12
|
+
### Previous Major Release (v2.0.0)
|
|
6
13
|
|
|
7
14
|
🎨 **Revolutionary Styling System** - Complete overhaul with advanced theming, responsive design, and scalable architecture
|
|
8
15
|
🏗️ **Component-Specific Types** - Enhanced type safety with dedicated slot interfaces
|
|
@@ -46,6 +53,15 @@ Fragment-Shopify App (CMS) → API Endpoint → fragment-headless-sdk (Consumer)
|
|
|
46
53
|
- 🔄 **Legacy Compatibility**: Seamless migration path from v1.x styling
|
|
47
54
|
- 🎪 **CSS-in-JS Support**: Integration with styled-components, emotion, etc.
|
|
48
55
|
|
|
56
|
+
### Hero Resolvers System (v2.1+)
|
|
57
|
+
|
|
58
|
+
- 🔤 **Advanced Typography**: Built-in font family support (Roboto, Inter, Montserrat, etc.)
|
|
59
|
+
- 📐 **Layout Controls**: Content positioning (left/center/right), width, and height management
|
|
60
|
+
- 🎨 **Smart Color Resolution**: Intelligent fallback handling for color tokens
|
|
61
|
+
- 🛠️ **Utility Functions**: Helper functions for class joining, text rendering, and style resolution
|
|
62
|
+
- 📝 **Type Safety**: Enhanced TypeScript interfaces for all styling options
|
|
63
|
+
- 🔄 **Backward Compatible**: Works seamlessly with existing Hero implementations
|
|
64
|
+
|
|
49
65
|
---
|
|
50
66
|
|
|
51
67
|
## 📦 Installation
|
|
@@ -104,6 +120,30 @@ module.exports = {
|
|
|
104
120
|
@tailwind utilities;
|
|
105
121
|
```
|
|
106
122
|
|
|
123
|
+
### 4. Import Fragment SDK Styles
|
|
124
|
+
|
|
125
|
+
**Required** - Import the SDK's CSS file for proper typography and animations:
|
|
126
|
+
|
|
127
|
+
#### Option A: JavaScript/TypeScript Import
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// In your main app file (e.g., _app.tsx, layout.tsx, or main.tsx)
|
|
131
|
+
import "fragment-headless-sdk/styles";
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Option B: CSS Import
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
/* In your global CSS file */
|
|
138
|
+
@import "fragment-headless-sdk/dist/styles/fragment-sdk.css";
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
This provides:
|
|
142
|
+
|
|
143
|
+
- **Google Fonts** for all supported font families (Roboto, Inter, Montserrat, etc.)
|
|
144
|
+
- **Marquee animations** for announcement components
|
|
145
|
+
- **Improved link styling** for prose content
|
|
146
|
+
|
|
107
147
|
---
|
|
108
148
|
|
|
109
149
|
## 🚀 Quick Start
|
|
@@ -417,6 +457,172 @@ slots: {
|
|
|
417
457
|
|
|
418
458
|
---
|
|
419
459
|
|
|
460
|
+
## 🎨 Hero Resolvers System (v2.1+)
|
|
461
|
+
|
|
462
|
+
The Hero Resolvers system provides advanced utilities for customizing Hero components with enhanced typography, layout, and styling controls.
|
|
463
|
+
|
|
464
|
+
### Typography Control
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { resolveHeroTypography, FontKey } from "fragment-headless-sdk";
|
|
468
|
+
|
|
469
|
+
// Enhanced typography configuration
|
|
470
|
+
const heroContent = {
|
|
471
|
+
title: "Welcome to Our Store",
|
|
472
|
+
description: "Discover amazing products",
|
|
473
|
+
styling: {
|
|
474
|
+
tokens: {
|
|
475
|
+
typography: {
|
|
476
|
+
// Title typography
|
|
477
|
+
titleFont: "montserrat" as FontKey,
|
|
478
|
+
titleFontSize: "text-6xl",
|
|
479
|
+
titleLineHeight: "leading-tight",
|
|
480
|
+
|
|
481
|
+
// Description typography
|
|
482
|
+
descriptionFont: "inter" as FontKey,
|
|
483
|
+
descriptionFontSize: "text-xl",
|
|
484
|
+
descriptionLineHeight: "leading-relaxed",
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// Resolve typography settings
|
|
491
|
+
const typography = resolveHeroTypography(heroContent);
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Layout & Positioning
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import {
|
|
498
|
+
resolvePosition,
|
|
499
|
+
resolveContentWidthClass,
|
|
500
|
+
resolveHeight,
|
|
501
|
+
} from "fragment-headless-sdk";
|
|
502
|
+
|
|
503
|
+
const heroContent = {
|
|
504
|
+
title: "Centered Hero",
|
|
505
|
+
styling: {
|
|
506
|
+
tokens: {
|
|
507
|
+
layout: {
|
|
508
|
+
position: "center", // "left" | "center" | "right"
|
|
509
|
+
contentWidth: "max-w-4xl", // Tailwind width classes
|
|
510
|
+
height: "min-h-screen", // Custom height classes
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// Resolve layout settings
|
|
517
|
+
const position = resolvePosition(heroContent); // "center"
|
|
518
|
+
const contentWidth = resolveContentWidthClass(heroContent); // "max-w-4xl"
|
|
519
|
+
const height = resolveHeight(heroContent); // "min-h-screen"
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Color Resolution
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import { resolveHeroColors } from "fragment-headless-sdk";
|
|
526
|
+
|
|
527
|
+
const heroContent = {
|
|
528
|
+
title: "Styled Hero",
|
|
529
|
+
styling: {
|
|
530
|
+
tokens: {
|
|
531
|
+
colors: {
|
|
532
|
+
title: "#ffffff",
|
|
533
|
+
text: "#f0f0f0",
|
|
534
|
+
button: "#007bff",
|
|
535
|
+
buttonText: "#ffffff",
|
|
536
|
+
background: "#1a1a1a",
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// Resolve colors with intelligent fallbacks
|
|
543
|
+
const colors = resolveHeroColors(heroContent);
|
|
544
|
+
// Returns: { title: "#ffffff", text: "#f0f0f0", buttonBackground: "#007bff", buttonText: "#ffffff", background: "#1a1a1a" }
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Built-in Font Families
|
|
548
|
+
|
|
549
|
+
The system includes support for popular web fonts:
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
type FontKey =
|
|
553
|
+
| "roboto"
|
|
554
|
+
| "open-sans"
|
|
555
|
+
| "lato"
|
|
556
|
+
| "montserrat"
|
|
557
|
+
| "poppins"
|
|
558
|
+
| "inter"
|
|
559
|
+
| "nunito-sans"
|
|
560
|
+
| "source-sans-pro";
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Utility Functions
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
import { joinClassNames, renderText } from "fragment-headless-sdk";
|
|
567
|
+
|
|
568
|
+
// Safe class name joining
|
|
569
|
+
const classes = joinClassNames("text-xl", "font-bold", null, "text-center");
|
|
570
|
+
// Returns: "text-xl font-bold text-center"
|
|
571
|
+
|
|
572
|
+
// Unified text rendering
|
|
573
|
+
const titleElement = renderText({
|
|
574
|
+
fontSize: "text-4xl",
|
|
575
|
+
lineHeight: "leading-tight",
|
|
576
|
+
text: "Hero Title",
|
|
577
|
+
className: "font-bold",
|
|
578
|
+
color: "#ffffff",
|
|
579
|
+
font: "montserrat",
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Complete Example
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
import { Hero } from "fragment-headless-sdk";
|
|
587
|
+
|
|
588
|
+
const advancedHeroContent = {
|
|
589
|
+
title: "Advanced Hero Section",
|
|
590
|
+
description: "With enhanced typography and layout controls",
|
|
591
|
+
buttonText: "Get Started",
|
|
592
|
+
buttonLink: "/signup",
|
|
593
|
+
imageUrl: "https://example.com/hero.jpg",
|
|
594
|
+
|
|
595
|
+
styling: {
|
|
596
|
+
tokens: {
|
|
597
|
+
colors: {
|
|
598
|
+
title: "#ffffff",
|
|
599
|
+
text: "#e2e8f0",
|
|
600
|
+
button: "#3b82f6",
|
|
601
|
+
buttonText: "#ffffff",
|
|
602
|
+
background: "#1e293b",
|
|
603
|
+
},
|
|
604
|
+
typography: {
|
|
605
|
+
titleFont: "montserrat",
|
|
606
|
+
titleFontSize: "text-5xl lg:text-7xl",
|
|
607
|
+
titleLineHeight: "leading-tight",
|
|
608
|
+
descriptionFont: "inter",
|
|
609
|
+
descriptionFontSize: "text-lg lg:text-xl",
|
|
610
|
+
descriptionLineHeight: "leading-relaxed",
|
|
611
|
+
},
|
|
612
|
+
layout: {
|
|
613
|
+
position: "center",
|
|
614
|
+
contentWidth: "max-w-4xl",
|
|
615
|
+
height: "min-h-screen",
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
<Hero content={advancedHeroContent} />;
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
420
626
|
## 📡 API Reference
|
|
421
627
|
|
|
422
628
|
### `fetchResource<T>(params)`
|
|
@@ -725,6 +931,54 @@ fragment-headless-sdk/
|
|
|
725
931
|
|
|
726
932
|
## 🔄 Migration Guide
|
|
727
933
|
|
|
934
|
+
### From v2.0 to v2.1
|
|
935
|
+
|
|
936
|
+
**No Breaking Changes** - v2.1 is fully backward compatible!
|
|
937
|
+
|
|
938
|
+
All existing Hero components continue to work exactly as before. The new Hero Resolvers system is completely opt-in.
|
|
939
|
+
|
|
940
|
+
#### New Features Available (Optional)
|
|
941
|
+
|
|
942
|
+
```typescript
|
|
943
|
+
// v2.0 style (continues to work)
|
|
944
|
+
const heroContent = {
|
|
945
|
+
title: "My Hero",
|
|
946
|
+
styling: {
|
|
947
|
+
tokens: {
|
|
948
|
+
colors: {
|
|
949
|
+
title: "#ffffff",
|
|
950
|
+
button: "#007bff",
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
},
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
// v2.1 enhanced style (new features)
|
|
957
|
+
const heroContent = {
|
|
958
|
+
title: "My Hero",
|
|
959
|
+
styling: {
|
|
960
|
+
tokens: {
|
|
961
|
+
colors: {
|
|
962
|
+
title: "#ffffff",
|
|
963
|
+
button: "#007bff",
|
|
964
|
+
},
|
|
965
|
+
// NEW: Typography controls
|
|
966
|
+
typography: {
|
|
967
|
+
titleFont: "montserrat",
|
|
968
|
+
titleFontSize: "text-6xl",
|
|
969
|
+
titleLineHeight: "leading-tight",
|
|
970
|
+
},
|
|
971
|
+
// NEW: Layout controls
|
|
972
|
+
layout: {
|
|
973
|
+
position: "center",
|
|
974
|
+
contentWidth: "max-w-4xl",
|
|
975
|
+
height: "min-h-screen",
|
|
976
|
+
},
|
|
977
|
+
},
|
|
978
|
+
},
|
|
979
|
+
};
|
|
980
|
+
```
|
|
981
|
+
|
|
728
982
|
### From v1.x to v2.0
|
|
729
983
|
|
|
730
984
|
**No Breaking Changes** - v2.0 is fully backward compatible!
|
|
@@ -810,12 +1064,13 @@ MIT License - See LICENSE file for details.
|
|
|
810
1064
|
|
|
811
1065
|
## 🎉 What's Next?
|
|
812
1066
|
|
|
813
|
-
The v2.
|
|
1067
|
+
The v2.1 Hero Resolvers system and v2.0 styling foundation provide unlimited customization and scalability. Future enhancements may include:
|
|
814
1068
|
|
|
815
1069
|
- Visual styling editor integration
|
|
816
1070
|
- Animation and transition presets
|
|
817
|
-
- Advanced layout system
|
|
1071
|
+
- Advanced layout system for all components
|
|
818
1072
|
- Component composition utilities
|
|
819
1073
|
- Design system generator
|
|
1074
|
+
- Enhanced Announcement component resolvers
|
|
820
1075
|
|
|
821
|
-
**Ready to build beautiful, scalable headless experiences!** 🚀
|
|
1076
|
+
**Ready to build beautiful, scalable headless experiences with advanced typography and layout controls!** 🚀
|