@marvalt/wparser 0.1.25 → 0.1.29

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/README.md CHANGED
@@ -8,6 +8,15 @@ Static-only WordPress page parser and renderer for React apps.
8
8
  - Auto-hero uses featured image unless `[HEROSECTION]` is present
9
9
  - Extensible: future adapters (Elementor/Divi) via registry
10
10
 
11
+ ## Documentation
12
+
13
+ Comprehensive documentation is available in the `docs/` folder:
14
+
15
+ - **[STYLING.md](./docs/STYLING.md)** - How WordPress and applications control styling
16
+ - **[COLOR_MAPPING.md](./docs/COLOR_MAPPING.md)** - Color mapping system for background colors
17
+ - **[SPACING.md](./docs/SPACING.md)** - Spacing system and customization
18
+ - **[CUSTOMIZATION.md](./docs/CUSTOMIZATION.md)** - Complete customization guide
19
+
11
20
  ## Installation
12
21
 
13
22
  Peer deps: React 18+
package/dist/index.cjs CHANGED
@@ -1390,7 +1390,16 @@ function renderBlock(block, registry, key, options, page) {
1390
1390
  const children = block.innerBlocks && block.innerBlocks.length
1391
1391
  ? block.innerBlocks.map((child, i) => renderBlock(child, registry, `${key}-${i}`, options, page))
1392
1392
  : undefined;
1393
- const node = Renderer({ block, children, context: { registry, page, colorMapper: registry.colorMapper } });
1393
+ const node = Renderer({
1394
+ block,
1395
+ children,
1396
+ context: {
1397
+ registry,
1398
+ page,
1399
+ colorMapper: registry.colorMapper,
1400
+ spacingConfig: registry.spacingConfig,
1401
+ }
1402
+ });
1394
1403
  if (options?.debugWrappers) {
1395
1404
  return (jsxRuntimeExports.jsx("div", { "data-block": block.name, className: "wp-block", children: node }, key));
1396
1405
  }
@@ -2050,6 +2059,36 @@ function extractBackgroundColor(block, context) {
2050
2059
  // Fallback: return null (no background applied)
2051
2060
  return null;
2052
2061
  }
2062
+ /**
2063
+ * Extract spacer height from block attributes or innerHTML
2064
+ * Returns height in pixels, or null if not found
2065
+ */
2066
+ function extractSpacerHeight(block) {
2067
+ const attrs = block.attributes || {};
2068
+ // First, try to get height from attributes
2069
+ const height = attrs['height'];
2070
+ if (typeof height === 'number') {
2071
+ return height;
2072
+ }
2073
+ if (typeof height === 'string') {
2074
+ // Parse "100px" or "100" to number
2075
+ const match = height.match(/^(\d+)/);
2076
+ if (match) {
2077
+ return parseInt(match[1], 10);
2078
+ }
2079
+ }
2080
+ // Fall back to parsing innerHTML for style="height:100px"
2081
+ if (block.innerHTML) {
2082
+ const styleMatch = block.innerHTML.match(/style=["']([^"']+)["']/i);
2083
+ if (styleMatch) {
2084
+ const heightMatch = styleMatch[1].match(/height:\s*(\d+)px/i);
2085
+ if (heightMatch) {
2086
+ return parseInt(heightMatch[1], 10);
2087
+ }
2088
+ }
2089
+ }
2090
+ return null;
2091
+ }
2053
2092
 
2054
2093
  /**
2055
2094
  * Style mapping utilities
@@ -2143,10 +2182,25 @@ function buildClassName(...classes) {
2143
2182
  .trim();
2144
2183
  }
2145
2184
 
2185
+ // Helper function to get spacing value with fallback
2186
+ // Only accepts string keys (excludes 'heading' which is an object)
2187
+ function getSpacing(spacingConfig, key, fallback) {
2188
+ return spacingConfig?.[key] || fallback;
2189
+ }
2190
+ // Helper function to get heading spacing
2191
+ function getHeadingSpacing(spacingConfig, level) {
2192
+ const headingConfig = spacingConfig?.heading;
2193
+ if (level === 1)
2194
+ return headingConfig?.h1 || 'mt-10 mb-8';
2195
+ if (level === 2)
2196
+ return headingConfig?.h2 || 'mt-8 mb-6';
2197
+ return headingConfig?.h3 || 'mt-6 mb-4';
2198
+ }
2146
2199
  const Paragraph = ({ block, context }) => {
2147
2200
  const content = getBlockTextContent(block);
2148
2201
  const attrs = block.attributes || {};
2149
2202
  const textAlign = getTextAlignClasses(attrs['align']);
2203
+ const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'paragraph', 'my-6');
2150
2204
  // Check if content contains shortcodes
2151
2205
  const hasShortcodes = /\[(\w+)/.test(content);
2152
2206
  if (hasShortcodes && context.registry.shortcodes) {
@@ -2177,14 +2231,14 @@ const Paragraph = ({ block, context }) => {
2177
2231
  };
2178
2232
  const hasBlockLevelContent = React.Children.toArray(parts).some((part) => isBlockLevelElement(part));
2179
2233
  if (hasBlockLevelContent) {
2180
- // Render block-level content without <p> wrapper
2181
- return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: parts });
2234
+ // Render block-level content without <p> wrapper, but add spacing wrapper
2235
+ return jsxRuntimeExports.jsx("div", { className: spacing, children: parts });
2182
2236
  }
2183
- return jsxRuntimeExports.jsx("p", { className: buildClassName('text-gray-700 my-4', textAlign), children: parts });
2237
+ return jsxRuntimeExports.jsx("p", { className: buildClassName('text-gray-700', spacing, textAlign), children: parts });
2184
2238
  }
2185
- return jsxRuntimeExports.jsx("p", { className: buildClassName('text-gray-700 my-4', textAlign), children: content });
2239
+ return jsxRuntimeExports.jsx("p", { className: buildClassName('text-gray-700', spacing, textAlign), children: content });
2186
2240
  };
2187
- const Heading = ({ block, children }) => {
2241
+ const Heading = ({ block, children, context }) => {
2188
2242
  const attrs = block.attributes || {};
2189
2243
  const { level = 2 } = attrs;
2190
2244
  const content = getBlockTextContent(block);
@@ -2193,12 +2247,11 @@ const Heading = ({ block, children }) => {
2193
2247
  const Tag = `h${Math.min(Math.max(Number(level) || 2, 1), 6)}`;
2194
2248
  // Default heading sizes if fontSize not specified
2195
2249
  const sizeClass = fontSize || (level === 1 ? 'text-4xl' : level === 2 ? 'text-3xl' : level === 3 ? 'text-2xl' : 'text-xl');
2196
- // Add spacing: mt for top (except first heading), mb for bottom
2197
- // Use larger bottom margin for headings to create visual separation
2198
- const spacingClass = level === 1 ? 'mt-8 mb-6' : level === 2 ? 'mt-6 mb-4' : 'mt-4 mb-3';
2250
+ // Get spacing from config with improved defaults
2251
+ const spacingClass = getHeadingSpacing(context.spacingConfig || context.registry.spacingConfig, level);
2199
2252
  return (jsxRuntimeExports.jsx(Tag, { className: buildClassName('font-bold text-gray-900', sizeClass, textAlign, spacingClass), children: children ?? content }));
2200
2253
  };
2201
- const Image = ({ block }) => {
2254
+ const Image = ({ block, context }) => {
2202
2255
  const imageAttrs = getImageAttributes(block);
2203
2256
  if (!imageAttrs.url)
2204
2257
  return null;
@@ -2209,13 +2262,15 @@ const Image = ({ block }) => {
2209
2262
  const height = imageAttrs.height;
2210
2263
  imageUrl = getCloudflareVariantUrl(imageUrl, { width, height });
2211
2264
  }
2212
- return (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: imageAttrs.alt, width: imageAttrs.width, height: imageAttrs.height, className: "w-full h-auto rounded-lg object-cover my-4", style: { maxWidth: '100%', height: 'auto' }, loading: "lazy" }));
2265
+ const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'image', 'my-6');
2266
+ return (jsxRuntimeExports.jsx("img", { src: imageUrl, alt: imageAttrs.alt, width: imageAttrs.width, height: imageAttrs.height, className: buildClassName('w-full h-auto rounded-lg object-cover', spacing), style: { maxWidth: '100%', height: 'auto' }, loading: "lazy" }));
2213
2267
  };
2214
- const List = ({ block, children }) => {
2268
+ const List = ({ block, children, context }) => {
2215
2269
  const attrs = block.attributes || {};
2216
2270
  const { ordered } = attrs;
2217
2271
  const Tag = ordered ? 'ol' : 'ul';
2218
- return React.createElement(Tag, { className: 'list-disc pl-6 space-y-2 text-gray-700 my-4' }, children);
2272
+ const spacing = getSpacing(context.spacingConfig || context.registry.spacingConfig, 'list', 'my-6');
2273
+ return React.createElement(Tag, { className: buildClassName('list-disc pl-6 space-y-2 text-gray-700', spacing) }, children);
2219
2274
  };
2220
2275
  const ListItem = ({ children }) => {
2221
2276
  return jsxRuntimeExports.jsx("li", { className: "text-gray-700", children: children });
@@ -2230,7 +2285,11 @@ const Group = ({ block, children, context }) => {
2230
2285
  // Determine if this is a section-level group (has alignment) or content-level
2231
2286
  const isSection = align === 'full' || align === 'wide';
2232
2287
  const containerClass = getContainerClasses(align, layout);
2233
- const spacingClass = isSection ? getSectionSpacingClasses() : getContentSpacingClasses();
2288
+ // Get spacing from config or use defaults
2289
+ const spacingConfig = context.spacingConfig || context.registry.spacingConfig;
2290
+ const spacingClass = isSection
2291
+ ? (spacingConfig?.section || getSectionSpacingClasses())
2292
+ : (spacingConfig?.content || getContentSpacingClasses());
2234
2293
  // Ensure container class is always applied for constrained groups
2235
2294
  const finalContainerClass = layout?.type === 'constrained' && align === 'wide'
2236
2295
  ? 'container'
@@ -2394,7 +2453,7 @@ const Fallback = ({ block, children }) => {
2394
2453
  // Minimal fallback; do not render innerHTML directly in v1 for safety
2395
2454
  return jsxRuntimeExports.jsx("div", { "data-unknown-block": block.name, children: children });
2396
2455
  };
2397
- function createDefaultRegistry(colorMapper) {
2456
+ function createDefaultRegistry(colorMapper, spacingConfig) {
2398
2457
  const renderers = {
2399
2458
  'core/paragraph': Paragraph,
2400
2459
  'core/heading': Heading,
@@ -2431,12 +2490,22 @@ function createDefaultRegistry(colorMapper) {
2431
2490
  const html = block.innerHTML || '';
2432
2491
  return jsxRuntimeExports.jsx("div", { dangerouslySetInnerHTML: { __html: html } });
2433
2492
  },
2493
+ // Spacer block - adds vertical spacing
2494
+ 'core/spacer': ({ block }) => {
2495
+ const height = extractSpacerHeight(block);
2496
+ if (height && height > 0) {
2497
+ return jsxRuntimeExports.jsx("div", { style: { height: `${height}px` }, "aria-hidden": "true" });
2498
+ }
2499
+ // Default fallback if height not found
2500
+ return jsxRuntimeExports.jsx("div", { style: { height: '100px' }, "aria-hidden": "true" });
2501
+ },
2434
2502
  };
2435
2503
  return {
2436
2504
  renderers,
2437
2505
  shortcodes: {}, // Empty by default - apps extend this
2438
2506
  fallback: Fallback,
2439
2507
  colorMapper,
2508
+ spacingConfig,
2440
2509
  };
2441
2510
  }
2442
2511
  // Legacy function for backward compatibility - use getBlockTextContent instead
@@ -2526,8 +2595,8 @@ function findMatchingMapping(block, mappings) {
2526
2595
  * const registry = createEnhancedRegistry(mappings);
2527
2596
  * ```
2528
2597
  */
2529
- function createEnhancedRegistry(mappings = [], baseRegistry, colorMapper) {
2530
- const base = baseRegistry || createDefaultRegistry(colorMapper);
2598
+ function createEnhancedRegistry(mappings = [], baseRegistry, colorMapper, spacingConfig) {
2599
+ const base = baseRegistry || createDefaultRegistry(colorMapper, spacingConfig);
2531
2600
  // Create enhanced renderers that check patterns first
2532
2601
  const enhancedRenderers = {
2533
2602
  ...base.renderers,
@@ -2575,6 +2644,8 @@ function createEnhancedRegistry(mappings = [], baseRegistry, colorMapper) {
2575
2644
  matchBlock,
2576
2645
  // Use provided colorMapper or inherit from base registry
2577
2646
  colorMapper: colorMapper || base.colorMapper,
2647
+ // Use provided spacingConfig or inherit from base registry
2648
+ spacingConfig: spacingConfig || base.spacingConfig,
2578
2649
  };
2579
2650
  }
2580
2651
 
@@ -2767,6 +2838,7 @@ exports.extractImageUrlWithFallback = extractImageUrlWithFallback;
2767
2838
  exports.extractMediaPosition = extractMediaPosition;
2768
2839
  exports.extractMinHeight = extractMinHeight;
2769
2840
  exports.extractOverlayColor = extractOverlayColor;
2841
+ exports.extractSpacerHeight = extractSpacerHeight;
2770
2842
  exports.extractSubtitleFromInnerBlocks = extractSubtitleFromInnerBlocks;
2771
2843
  exports.extractTextAlign = extractTextAlign;
2772
2844
  exports.extractTextAlignFromInnerBlocks = extractTextAlignFromInnerBlocks;