@marvalt/wparser 0.1.56 → 0.1.58

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/index.cjs CHANGED
@@ -1874,11 +1874,31 @@ function extractTextAlign(block) {
1874
1874
  }
1875
1875
  /**
1876
1876
  * Extract font size from block
1877
+ * Checks both fontSize attribute and style.typography.fontSize
1877
1878
  */
1878
- function extractFontSize(block) {
1879
+ function extractFontSize(block, context) {
1879
1880
  const attrs = block.attributes || {};
1881
+ // Check inherited font size from context (from parent group)
1882
+ if (context && context.inheritedFontSize) {
1883
+ return context.inheritedFontSize;
1884
+ }
1885
+ // Check style.typography.fontSize (can be a preset name or CSS value)
1886
+ const styleFontSize = attrs['style']?.typography?.fontSize;
1887
+ if (styleFontSize) {
1888
+ if (typeof styleFontSize === 'string') {
1889
+ return styleFontSize;
1890
+ }
1891
+ // If it's an object, it might have a value property
1892
+ if (typeof styleFontSize === 'object' && styleFontSize.value) {
1893
+ return styleFontSize.value;
1894
+ }
1895
+ }
1896
+ // Check fontSize attribute (preset name like 'small', 'medium', etc.)
1880
1897
  const fontSize = attrs['fontSize'];
1881
- return typeof fontSize === 'string' ? fontSize : null;
1898
+ if (typeof fontSize === 'string') {
1899
+ return fontSize;
1900
+ }
1901
+ return null;
1882
1902
  }
1883
1903
  /**
1884
1904
  * Convert image URL to Cloudflare variant if it's a Cloudflare URL
@@ -2156,17 +2176,46 @@ function extractGradientBackground(block) {
2156
2176
  /**
2157
2177
  * Extract and map text color from block attributes
2158
2178
  * Uses colorMapper from context to convert WordPress theme colors to app CSS classes
2179
+ * Also checks style.color.text and inherited colors from parent groups
2159
2180
  *
2160
2181
  * @param block - WordPress block to extract text color from
2161
- * @param context - Render context containing optional colorMapper
2182
+ * @param context - Render context containing optional colorMapper and themePalette
2162
2183
  * @returns CSS class string (e.g., 'text-white') or null if no mapping
2163
2184
  */
2164
2185
  function extractTextColor(block, context) {
2165
2186
  const attrs = block.attributes || {};
2166
- const wpColorName = attrs['textColor'] || attrs['text'];
2187
+ // Check inherited text color from context (from parent group)
2188
+ if (context.inheritedTextColor) {
2189
+ return context.inheritedTextColor;
2190
+ }
2191
+ // Check style.color.text (can be a color name or CSS variable)
2192
+ const styleTextColor = attrs['style']?.color?.text;
2193
+ let wpColorName = null;
2194
+ if (styleTextColor) {
2195
+ if (typeof styleTextColor === 'string') {
2196
+ wpColorName = styleTextColor;
2197
+ }
2198
+ else if (typeof styleTextColor === 'object' && styleTextColor.color) {
2199
+ wpColorName = styleTextColor.color;
2200
+ }
2201
+ }
2202
+ // Fall back to textColor or text attribute
2203
+ if (!wpColorName) {
2204
+ wpColorName = attrs['textColor'] || attrs['text'];
2205
+ }
2167
2206
  if (!wpColorName || typeof wpColorName !== 'string') {
2168
2207
  return null;
2169
2208
  }
2209
+ // If it's a CSS variable or hex color, return it directly
2210
+ if (wpColorName.startsWith('var(') || wpColorName.startsWith('#')) {
2211
+ return `text-[${wpColorName}]`;
2212
+ }
2213
+ // Get theme palette from context if available
2214
+ const themePalette = context?.themePalette || context.registry?.themePalette || context.registry?.wpStyles?.theme_palette;
2215
+ if (themePalette && themePalette[wpColorName]) {
2216
+ // Return inline style class with actual color value
2217
+ return `text-[${themePalette[wpColorName]}]`;
2218
+ }
2170
2219
  // Special handling for common WordPress color names when used as text color
2171
2220
  // These mappings take precedence because text color semantics differ from background color
2172
2221
  const textColorMap = {
@@ -2341,6 +2390,9 @@ const Paragraph = ({ block, context }) => {
2341
2390
  // Extract text color if specified, otherwise inherit from parent (CSS variables handle default)
2342
2391
  const textColor = extractTextColor(block, context);
2343
2392
  const textColorClass = textColor || ''; // Don't hardcode - let CSS variables handle default
2393
+ // Extract font size if specified
2394
+ const fontSize = extractFontSize(block, context);
2395
+ const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
2344
2396
  // Check if innerHTML contains HTML elements (like links, strong, em, etc.)
2345
2397
  const hasHTML = block.innerHTML && /<[a-z][\s\S]*>/i.test(block.innerHTML);
2346
2398
  // Check if content contains shortcodes (check both HTML and text content)
@@ -2376,24 +2428,28 @@ const Paragraph = ({ block, context }) => {
2376
2428
  const hasBlockLevelContent = React.Children.toArray(parts).some((part) => isBlockLevelElement(part));
2377
2429
  if (hasBlockLevelContent) {
2378
2430
  // Render block-level content without <p> wrapper, but add spacing wrapper
2379
- return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textColorClass), children: parts });
2431
+ const fontSize = extractFontSize(block, context);
2432
+ const fontSizeClass = fontSize ? getFontSizeClasses(fontSize) : '';
2433
+ return jsxRuntimeExports.jsx("div", { className: buildClassName(spacing, textColorClass, fontSizeClass), children: parts });
2380
2434
  }
2381
2435
  // Render shortcode parts inside paragraph (shortcodes are processed as React components)
2382
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass), children: parts });
2436
+ return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: parts });
2383
2437
  }
2384
2438
  // If innerHTML contains HTML elements but no shortcodes, render it directly (preserves links, formatting, etc.)
2385
2439
  if (hasHTML && block.innerHTML) {
2386
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass), dangerouslySetInnerHTML: { __html: htmlContent } });
2440
+ return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), dangerouslySetInnerHTML: { __html: htmlContent } });
2387
2441
  }
2388
2442
  // No HTML and no shortcodes, just render plain text content
2389
- return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass), children: textContent });
2443
+ return jsxRuntimeExports.jsx("p", { className: buildClassName(spacing, textAlign, textColorClass, fontSizeClass), children: textContent });
2390
2444
  };
2391
2445
  const Heading = ({ block, children, context }) => {
2392
2446
  const attrs = block.attributes || {};
2393
2447
  const { level = 2 } = attrs;
2394
2448
  const content = getBlockTextContent(block);
2395
2449
  const textAlign = getTextAlignClasses(attrs['textAlign']);
2396
- const fontSize = getFontSizeClasses(attrs['fontSize']);
2450
+ // Extract font size - check both fontSize attribute and style.typography.fontSize
2451
+ const extractedFontSize = extractFontSize(block, context);
2452
+ const fontSize = extractedFontSize ? getFontSizeClasses(extractedFontSize) : '';
2397
2453
  const Tag = `h${Math.min(Math.max(Number(level) || 2, 1), 6)}`;
2398
2454
  // Use CSS variables for font sizes (defined in index.css) instead of Tailwind classes
2399
2455
  // Only use Tailwind class if explicitly set via fontSize attribute
@@ -2491,6 +2547,44 @@ const ListItem = ({ block, children, context }) => {
2491
2547
  return jsxRuntimeExports.jsx("li", { className: textColorClass || undefined, children: content || children });
2492
2548
  };
2493
2549
  const Group = ({ block, children, context }) => {
2550
+ // If innerHTML exists, WordPress has already rendered the block wrapper with all its classes
2551
+ // Extract the wrapper div attributes and use them, then render children inside
2552
+ if (block.innerHTML) {
2553
+ // Extract the opening div tag with all attributes
2554
+ const openingDivMatch = block.innerHTML.match(/<div([^>]*)>/i);
2555
+ if (openingDivMatch) {
2556
+ const divAttributes = openingDivMatch[1];
2557
+ // Parse class attribute to preserve WordPress classes
2558
+ const classMatch = divAttributes.match(/class=["']([^"']+)["']/);
2559
+ const styleMatch = divAttributes.match(/style=["']([^"']+)["']/);
2560
+ // Build props for the div
2561
+ const divProps = {};
2562
+ if (classMatch) {
2563
+ divProps.className = classMatch[1];
2564
+ }
2565
+ if (styleMatch) {
2566
+ // Parse inline styles into React style object
2567
+ const styles = {};
2568
+ styleMatch[1].split(';').forEach(style => {
2569
+ const [key, value] = style.split(':').map(s => s.trim());
2570
+ if (key && value) {
2571
+ // Convert CSS property names to camelCase
2572
+ const cssKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
2573
+ styles[cssKey] = value;
2574
+ }
2575
+ });
2576
+ if (Object.keys(styles).length > 0) {
2577
+ divProps.style = styles;
2578
+ }
2579
+ }
2580
+ // Render with preserved WordPress classes/styles and children
2581
+ return React.createElement('div', divProps, children);
2582
+ }
2583
+ // If we can't parse, fall back to rendering innerHTML as-is
2584
+ // This will preserve structure but children may be duplicated
2585
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2586
+ }
2587
+ // No innerHTML - use custom rendering logic
2494
2588
  const attrs = block.attributes || {};
2495
2589
  const align = attrs['align'];
2496
2590
  // Layout can be an object with type property, or nested structure
@@ -2760,19 +2854,41 @@ function createDefaultRegistry(colorMapper, spacingConfig) {
2760
2854
  },
2761
2855
  // Navigation block - renders WordPress navigation menu
2762
2856
  'core/navigation': ({ block, context }) => {
2763
- const attrs = block.attributes || {};
2764
- const layout = attrs['layout'];
2765
- const orientation = layout?.orientation || 'horizontal';
2766
- const ariaLabel = attrs['ariaLabel'];
2767
- // Render innerHTML which WordPress provides (contains the menu HTML)
2857
+ // Render innerHTML which WordPress provides (contains the menu HTML with all classes)
2768
2858
  if (block.innerHTML) {
2769
- const navClasses = orientation === 'vertical'
2770
- ? 'flex flex-col space-y-2'
2771
- : 'flex flex-wrap gap-4';
2772
- return (jsxRuntimeExports.jsx("nav", { className: navClasses, "aria-label": ariaLabel || 'Navigation', dangerouslySetInnerHTML: { __html: block.innerHTML } }));
2859
+ // Extract the nav element from innerHTML to preserve WordPress classes
2860
+ // WordPress renders: <nav class="...">...</nav>
2861
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2773
2862
  }
2774
2863
  // Fallback: render empty nav
2775
- return jsxRuntimeExports.jsx("nav", { className: "flex flex-wrap gap-4", "aria-label": ariaLabel || 'Navigation' });
2864
+ return jsxRuntimeExports.jsx("nav", { "aria-label": "Navigation" });
2865
+ },
2866
+ // Site Title block - renders site title
2867
+ 'core/site-title': ({ block, context }) => {
2868
+ // Render innerHTML which WordPress provides
2869
+ if (block.innerHTML) {
2870
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2871
+ }
2872
+ // Fallback: render site name from context if available
2873
+ return null;
2874
+ },
2875
+ // Site Tagline block - renders site tagline/description
2876
+ 'core/site-tagline': ({ block, context }) => {
2877
+ // Render innerHTML which WordPress provides
2878
+ if (block.innerHTML) {
2879
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2880
+ }
2881
+ // Fallback: render site description from context if available
2882
+ return null;
2883
+ },
2884
+ // Post Date block - renders post date
2885
+ 'core/post-date': ({ block, context }) => {
2886
+ // Render innerHTML which WordPress provides
2887
+ if (block.innerHTML) {
2888
+ return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: block.innerHTML } });
2889
+ }
2890
+ // Fallback: render current date
2891
+ return null;
2776
2892
  },
2777
2893
  };
2778
2894
  return {