cf-elements 1.0.2 → 1.0.4
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/cf-elements.js +145 -7
- package/package.json +1 -1
package/cf-elements.js
CHANGED
|
@@ -99,6 +99,116 @@
|
|
|
99
99
|
}
|
|
100
100
|
})();
|
|
101
101
|
|
|
102
|
+
// ==========================================================================
|
|
103
|
+
// GOOGLE FONTS - Auto-load fonts from font attributes
|
|
104
|
+
// ==========================================================================
|
|
105
|
+
|
|
106
|
+
// System fonts that don't need to be loaded from Google
|
|
107
|
+
const SYSTEM_FONTS = new Set([
|
|
108
|
+
'sans-serif', 'serif', 'monospace', 'cursive', 'fantasy', 'system-ui',
|
|
109
|
+
'ui-sans-serif', 'ui-serif', 'ui-monospace', 'ui-rounded',
|
|
110
|
+
'arial', 'helvetica', 'times new roman', 'times', 'courier new', 'courier',
|
|
111
|
+
'verdana', 'georgia', 'palatino', 'garamond', 'bookman', 'tahoma',
|
|
112
|
+
'trebuchet ms', 'arial black', 'impact', 'comic sans ms', 'inherit'
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
// Track which fonts have been loaded to avoid duplicates
|
|
116
|
+
const loadedFonts = new Set();
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Extract Google Font names from the document
|
|
120
|
+
* Scans font attributes on cf-* elements and styleguide data
|
|
121
|
+
*/
|
|
122
|
+
function extractFontsFromDocument() {
|
|
123
|
+
const fonts = new Set();
|
|
124
|
+
|
|
125
|
+
// 1. Extract from font attributes on elements
|
|
126
|
+
document.querySelectorAll('[font]').forEach(el => {
|
|
127
|
+
const font = el.getAttribute('font');
|
|
128
|
+
if (font) {
|
|
129
|
+
// Clean and add font name
|
|
130
|
+
const cleanFont = font.replace(/["']/g, '').split(',')[0].trim();
|
|
131
|
+
if (cleanFont && !SYSTEM_FONTS.has(cleanFont.toLowerCase())) {
|
|
132
|
+
fonts.add(cleanFont);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// 2. Extract from styleguide data if present
|
|
138
|
+
const styleguideEl = document.getElementById('cf-styleguide-data');
|
|
139
|
+
if (styleguideEl) {
|
|
140
|
+
try {
|
|
141
|
+
const data = JSON.parse(styleguideEl.textContent);
|
|
142
|
+
if (data.typography) {
|
|
143
|
+
const { headlineFont, subheadlineFont, contentFont } = data.typography;
|
|
144
|
+
[headlineFont, subheadlineFont, contentFont].forEach(font => {
|
|
145
|
+
if (font && !SYSTEM_FONTS.has(font.toLowerCase())) {
|
|
146
|
+
fonts.add(font);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
} catch (e) {
|
|
151
|
+
// Ignore parse errors
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 3. Extract from inline font-family styles in cf-* elements
|
|
156
|
+
document.querySelectorAll('cf-headline, cf-subheadline, cf-paragraph, cf-button').forEach(el => {
|
|
157
|
+
const style = el.getAttribute('style') || '';
|
|
158
|
+
const match = style.match(/font-family:\s*["']?([^"';,]+)/i);
|
|
159
|
+
if (match) {
|
|
160
|
+
const font = match[1].trim();
|
|
161
|
+
if (font && !SYSTEM_FONTS.has(font.toLowerCase())) {
|
|
162
|
+
fonts.add(font);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return fonts;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Inject Google Fonts stylesheet into document head
|
|
172
|
+
*/
|
|
173
|
+
function injectGoogleFonts(fonts) {
|
|
174
|
+
if (!fonts || fonts.size === 0) return;
|
|
175
|
+
|
|
176
|
+
// Filter out already loaded fonts
|
|
177
|
+
const newFonts = Array.from(fonts).filter(f => !loadedFonts.has(f));
|
|
178
|
+
if (newFonts.length === 0) return;
|
|
179
|
+
|
|
180
|
+
// Mark as loaded
|
|
181
|
+
newFonts.forEach(f => loadedFonts.add(f));
|
|
182
|
+
|
|
183
|
+
// Build Google Fonts URL
|
|
184
|
+
const fontParams = newFonts
|
|
185
|
+
.map(font => font.replace(/ /g, '+'))
|
|
186
|
+
.join('&family=');
|
|
187
|
+
|
|
188
|
+
const url = `https://fonts.googleapis.com/css2?family=${fontParams}:wght@300;400;500;600;700;800;900&display=swap`;
|
|
189
|
+
|
|
190
|
+
// Check if already loaded
|
|
191
|
+
if (document.querySelector(`link[href^="https://fonts.googleapis.com"][href*="${newFonts[0].replace(/ /g, '+')}"]`)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Inject link tag
|
|
196
|
+
const link = document.createElement('link');
|
|
197
|
+
link.id = 'cf-google-fonts';
|
|
198
|
+
link.rel = 'stylesheet';
|
|
199
|
+
link.href = url;
|
|
200
|
+
document.head.appendChild(link);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Auto-load Google Fonts from document
|
|
205
|
+
* Called before element rendering
|
|
206
|
+
*/
|
|
207
|
+
function loadGoogleFonts() {
|
|
208
|
+
const fonts = extractFontsFromDocument();
|
|
209
|
+
injectGoogleFonts(fonts);
|
|
210
|
+
}
|
|
211
|
+
|
|
102
212
|
// ==========================================================================
|
|
103
213
|
// CONSTANTS & MAPPINGS
|
|
104
214
|
// ==========================================================================
|
|
@@ -944,9 +1054,10 @@
|
|
|
944
1054
|
* bg-style - Background image style: cover, cover-center (default), parallax, w100, w100h100, no-repeat, repeat, repeat-x, repeat-y
|
|
945
1055
|
* gradient - CSS gradient
|
|
946
1056
|
* overlay - Overlay color (rgba)
|
|
947
|
-
* text-color - Default text color
|
|
1057
|
+
* text-color - Default text color (inherited by child elements)
|
|
948
1058
|
* link-color - Default link color
|
|
949
|
-
* font-
|
|
1059
|
+
* font - Default font family (e.g., "Roboto") - inherited by child elements
|
|
1060
|
+
* font-family - Alias for font
|
|
950
1061
|
* font-weight - Default font weight
|
|
951
1062
|
* header-code - Custom header HTML/scripts
|
|
952
1063
|
* footer-code - Custom footer HTML/scripts
|
|
@@ -961,7 +1072,8 @@
|
|
|
961
1072
|
const overlay = attr(this, "overlay");
|
|
962
1073
|
const textColor = attr(this, "text-color", "#334155");
|
|
963
1074
|
const linkColor = attr(this, "link-color", "#3b82f6");
|
|
964
|
-
|
|
1075
|
+
// Support both "font" (simple) and "font-family" (explicit) attributes
|
|
1076
|
+
const font = attr(this, "font") || attr(this, "font-family");
|
|
965
1077
|
const fontWeight = attr(this, "font-weight");
|
|
966
1078
|
const headerCode = attr(this, "header-code");
|
|
967
1079
|
const footerCode = attr(this, "footer-code");
|
|
@@ -971,8 +1083,15 @@
|
|
|
971
1083
|
width: "100%",
|
|
972
1084
|
"min-height": "100vh",
|
|
973
1085
|
position: "relative",
|
|
1086
|
+
// Apply text color and font for CSS inheritance
|
|
1087
|
+
color: textColor,
|
|
974
1088
|
};
|
|
975
1089
|
|
|
1090
|
+
// Apply font-family for inheritance by child elements
|
|
1091
|
+
if (font) {
|
|
1092
|
+
styles["font-family"] = `"${font}", sans-serif`;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
976
1095
|
if (gradient) {
|
|
977
1096
|
styles["background"] = gradient;
|
|
978
1097
|
} else if (bg) {
|
|
@@ -998,7 +1117,7 @@
|
|
|
998
1117
|
|
|
999
1118
|
// Build optional data attributes for settings
|
|
1000
1119
|
let optionalAttrs = "";
|
|
1001
|
-
if (
|
|
1120
|
+
if (font) optionalAttrs += ` data-font="${font}"`;
|
|
1002
1121
|
if (fontWeight) optionalAttrs += ` data-font-weight="${fontWeight}"`;
|
|
1003
1122
|
if (headerCode)
|
|
1004
1123
|
optionalAttrs += ` data-header-code="${encodeURIComponent(
|
|
@@ -2058,6 +2177,7 @@
|
|
|
2058
2177
|
*/
|
|
2059
2178
|
class CFHeadline extends CFElement {
|
|
2060
2179
|
render() {
|
|
2180
|
+
const elementId = attr(this, "element-id");
|
|
2061
2181
|
const size = attr(this, "size", "48px");
|
|
2062
2182
|
const weight = attr(this, "weight", "bold");
|
|
2063
2183
|
const font = attr(this, "font");
|
|
@@ -2103,6 +2223,7 @@
|
|
|
2103
2223
|
// Build data attributes for round-trip conversion
|
|
2104
2224
|
// Store original size (preset or px) for reliable roundtrip
|
|
2105
2225
|
let dataAttrs = 'data-type="Headline/V1"';
|
|
2226
|
+
if (elementId) dataAttrs += ` data-element-id="${elementId}"`;
|
|
2106
2227
|
dataAttrs += ` data-size="${size}"`;
|
|
2107
2228
|
dataAttrs += ` data-weight="${weight}"`;
|
|
2108
2229
|
if (hasExplicitColor && color) {
|
|
@@ -2119,6 +2240,9 @@
|
|
|
2119
2240
|
if (icon) dataAttrs += ` data-icon="${icon}"`;
|
|
2120
2241
|
if (icon && iconAlign !== "left") dataAttrs += ` data-icon-align="${iconAlign}"`;
|
|
2121
2242
|
|
|
2243
|
+
// Build ID attribute for scroll-to and show-hide targeting
|
|
2244
|
+
const idAttr = elementId ? ` id="${elementId}"` : "";
|
|
2245
|
+
|
|
2122
2246
|
// Build icon HTML if present
|
|
2123
2247
|
let iconHtml = "";
|
|
2124
2248
|
if (icon) {
|
|
@@ -2136,7 +2260,7 @@
|
|
|
2136
2260
|
dataAttrs += animationAttrs;
|
|
2137
2261
|
|
|
2138
2262
|
this.outerHTML = `
|
|
2139
|
-
<div ${dataAttrs} style="${buildStyle(wrapperStyles)}">
|
|
2263
|
+
<div${idAttr} ${dataAttrs} style="${buildStyle(wrapperStyles)}">
|
|
2140
2264
|
<${tag} style="${buildStyle(textStyles)}">${textWithIcon}</${tag}>
|
|
2141
2265
|
</div>
|
|
2142
2266
|
`;
|
|
@@ -2149,6 +2273,7 @@
|
|
|
2149
2273
|
*/
|
|
2150
2274
|
class CFSubheadline extends CFElement {
|
|
2151
2275
|
render() {
|
|
2276
|
+
const elementId = attr(this, "element-id");
|
|
2152
2277
|
const size = attr(this, "size", "24px");
|
|
2153
2278
|
const weight = attr(this, "weight", "normal");
|
|
2154
2279
|
const font = attr(this, "font");
|
|
@@ -2192,6 +2317,7 @@
|
|
|
2192
2317
|
// Build data attributes for round-trip conversion
|
|
2193
2318
|
// Store original size (preset or px) for reliable roundtrip
|
|
2194
2319
|
let dataAttrs = 'data-type="SubHeadline/V1"';
|
|
2320
|
+
if (elementId) dataAttrs += ` data-element-id="${elementId}"`;
|
|
2195
2321
|
dataAttrs += ` data-size="${size}"`;
|
|
2196
2322
|
dataAttrs += ` data-weight="${weight}"`;
|
|
2197
2323
|
if (hasExplicitColor && color) {
|
|
@@ -2208,6 +2334,9 @@
|
|
|
2208
2334
|
if (icon) dataAttrs += ` data-icon="${icon}"`;
|
|
2209
2335
|
if (icon && iconAlign !== "left") dataAttrs += ` data-icon-align="${iconAlign}"`;
|
|
2210
2336
|
|
|
2337
|
+
// Build ID attribute for scroll-to and show-hide targeting
|
|
2338
|
+
const idAttr = elementId ? ` id="${elementId}"` : "";
|
|
2339
|
+
|
|
2211
2340
|
// Build icon HTML if present
|
|
2212
2341
|
let iconHtml = "";
|
|
2213
2342
|
if (icon) {
|
|
@@ -2225,7 +2354,7 @@
|
|
|
2225
2354
|
dataAttrs += animationAttrs;
|
|
2226
2355
|
|
|
2227
2356
|
this.outerHTML = `
|
|
2228
|
-
<div ${dataAttrs} style="${buildStyle(wrapperStyles)}">
|
|
2357
|
+
<div${idAttr} ${dataAttrs} style="${buildStyle(wrapperStyles)}">
|
|
2229
2358
|
<${tag} style="${buildStyle(textStyles)}">${textWithIcon}</${tag}>
|
|
2230
2359
|
</div>
|
|
2231
2360
|
`;
|
|
@@ -2238,6 +2367,7 @@
|
|
|
2238
2367
|
*/
|
|
2239
2368
|
class CFParagraph extends CFElement {
|
|
2240
2369
|
render() {
|
|
2370
|
+
const elementId = attr(this, "element-id");
|
|
2241
2371
|
const size = attr(this, "size", "16px");
|
|
2242
2372
|
const weight = attr(this, "weight", "normal");
|
|
2243
2373
|
const font = attr(this, "font");
|
|
@@ -2289,6 +2419,7 @@
|
|
|
2289
2419
|
// Build data attributes for round-trip conversion
|
|
2290
2420
|
// Store original size (preset or px) for reliable roundtrip
|
|
2291
2421
|
let dataAttrs = 'data-type="Paragraph/V1"';
|
|
2422
|
+
if (elementId) dataAttrs += ` data-element-id="${elementId}"`;
|
|
2292
2423
|
dataAttrs += ` data-size="${size}"`;
|
|
2293
2424
|
dataAttrs += ` data-weight="${weight}"`;
|
|
2294
2425
|
if (hasExplicitColor && color) {
|
|
@@ -2307,6 +2438,9 @@
|
|
|
2307
2438
|
if (icon) dataAttrs += ` data-icon="${icon}"`;
|
|
2308
2439
|
if (icon && iconAlign !== "left") dataAttrs += ` data-icon-align="${iconAlign}"`;
|
|
2309
2440
|
|
|
2441
|
+
// Build ID attribute for scroll-to and show-hide targeting
|
|
2442
|
+
const idAttr = elementId ? ` id="${elementId}"` : "";
|
|
2443
|
+
|
|
2310
2444
|
// Build icon HTML if present
|
|
2311
2445
|
let iconHtml = "";
|
|
2312
2446
|
if (icon) {
|
|
@@ -2324,7 +2458,7 @@
|
|
|
2324
2458
|
dataAttrs += animationAttrs;
|
|
2325
2459
|
|
|
2326
2460
|
this.outerHTML = `
|
|
2327
|
-
<div ${dataAttrs} style="${buildStyle(wrapperStyles)}">
|
|
2461
|
+
<div${idAttr} ${dataAttrs} style="${buildStyle(wrapperStyles)}">
|
|
2328
2462
|
<p style="${buildStyle(textStyles)}">${textWithIcon}</p>
|
|
2329
2463
|
</div>
|
|
2330
2464
|
`;
|
|
@@ -4588,6 +4722,7 @@
|
|
|
4588
4722
|
// Auto-initialize when DOM is ready
|
|
4589
4723
|
if (document.readyState === "loading") {
|
|
4590
4724
|
document.addEventListener("DOMContentLoaded", () => {
|
|
4725
|
+
loadGoogleFonts(); // Load fonts before rendering
|
|
4591
4726
|
styleguideManager.init();
|
|
4592
4727
|
brandAssetsManager.init();
|
|
4593
4728
|
initFunnelWind();
|
|
@@ -4600,6 +4735,7 @@
|
|
|
4600
4735
|
} else {
|
|
4601
4736
|
// DOM already ready, use requestAnimationFrame to ensure all elements are parsed
|
|
4602
4737
|
requestAnimationFrame(() => {
|
|
4738
|
+
loadGoogleFonts(); // Load fonts before rendering
|
|
4603
4739
|
styleguideManager.init();
|
|
4604
4740
|
brandAssetsManager.init();
|
|
4605
4741
|
initFunnelWind();
|
|
@@ -4616,12 +4752,14 @@
|
|
|
4616
4752
|
init: initFunnelWind,
|
|
4617
4753
|
initAnimations: initAnimations,
|
|
4618
4754
|
loadAnimateCSS: loadAnimateCSS,
|
|
4755
|
+
loadGoogleFonts: loadGoogleFonts,
|
|
4619
4756
|
initVideoBackgrounds: initVideoBackgrounds,
|
|
4620
4757
|
elements: elements,
|
|
4621
4758
|
StyleguideManager: styleguideManager,
|
|
4622
4759
|
BrandAssetsManager: brandAssetsManager,
|
|
4623
4760
|
initStyleguide: (data) => {
|
|
4624
4761
|
styleguideManager.init(data);
|
|
4762
|
+
loadGoogleFonts(); // Load fonts from styleguide
|
|
4625
4763
|
initFunnelWind();
|
|
4626
4764
|
},
|
|
4627
4765
|
initBrandAssets: (data) => {
|
package/package.json
CHANGED