$prop) { if (!is_array($prop)) { $resolved[$key] = $prop; continue; } $type = $prop['type'] ?? 'static'; if ($type === 'static') { $resolved[$key] = $prop['value'] ?? ''; } elseif ($type === 'dynamic' && $post_data) { $source = $prop['source'] ?? ''; $resolved[$key] = PlaceholderRenderer::get_value($source, $post_data); } else { $resolved[$key] = $prop['value'] ?? ''; } } return $resolved; } // ======================================== // Section Renderers // ======================================== /** * Helper to generate style attribute string */ private static function generate_style_attr($styles) { if (empty($styles)) return ''; $css = []; if (!empty($styles['color'])) $css[] = "color: {$styles['color']}"; if (!empty($styles['backgroundColor'])) $css[] = "background-color: {$styles['backgroundColor']}"; if (!empty($styles['fontSize'])) $css[] = "font-size: {$styles['fontSize']}"; // Note: assumes value has unit or is handled by class, but inline style works for specific values // Add more mapping if needed, or rely on frontend to send valid CSS values return empty($css) ? '' : 'style="' . implode(';', $css) . '"'; } /** * Render Hero section */ public static function render_hero($props, $layout, $color_scheme, $id, $element_styles = [], $section_styles = []) { $title = esc_html($props['title'] ?? ''); $subtitle = esc_html($props['subtitle'] ?? ''); $image = esc_url($props['image'] ?? ''); $cta_text = esc_html($props['cta_text'] ?? ''); $cta_url = esc_url($props['cta_url'] ?? ''); // Section Styles (Background & Spacing) $bg_color = $section_styles['backgroundColor'] ?? ''; $bg_image = $section_styles['backgroundImage'] ?? ''; $overlay_opacity = $section_styles['backgroundOverlay'] ?? 0; $pt = $section_styles['paddingTop'] ?? ''; $pb = $section_styles['paddingBottom'] ?? ''; $height_preset = $section_styles['heightPreset'] ?? ''; $section_css = ""; if ($bg_color) $section_css .= "background-color: {$bg_color};"; if ($bg_image) $section_css .= "background-image: url('{$bg_image}'); background-size: cover; background-position: center;"; if ($pt) $section_css .= "padding-top: {$pt};"; if ($pb) $section_css .= "padding-bottom: {$pb};"; if ($height_preset === 'screen') $section_css .= "min-height: 100vh; display: flex; align-items: center;"; $section_attr = $section_css ? "style=\"{$section_css}\"" : ""; $html = "
"; // Overlay if ($overlay_opacity > 0) { $opacity = $overlay_opacity / 100; $html .= "
"; } // Element Styles $title_style = self::generate_style_attr($element_styles['title'] ?? []); $subtitle_style = self::generate_style_attr($element_styles['subtitle'] ?? []); $cta_style = self::generate_style_attr($element_styles['cta_text'] ?? []); // Button // Image (if not background) if ($image && !$bg_image && $layout !== 'default') { $html .= "\"{$title}\""; } $html .= '
'; if ($title) { $html .= "

{$title}

"; } if ($subtitle) { $html .= "

{$subtitle}

"; } if ($cta_text && $cta_url) { $html .= "{$cta_text}"; } $html .= '
'; $html .= '
'; return $html; } /** * Universal Row Renderer (Shared logic for Content & ImageText) */ private static function render_universal_row($props, $layout, $color_scheme, $element_styles, $options = []) { $title = esc_html($props['title']['value'] ?? ($props['title'] ?? '')); $text = $props['text']['value'] ?? ($props['text'] ?? ($props['content']['value'] ?? ($props['content'] ?? ''))); // Handle both props/values $image = esc_url($props['image']['value'] ?? ($props['image'] ?? '')); // Options $has_image = !empty($image); $image_pos = $layout ?: 'left'; // Element Styles $title_style = self::generate_style_attr($element_styles['title'] ?? []); $text_style = self::generate_style_attr($element_styles['text'] ?? ($element_styles['content'] ?? [])); // Wrapper Classes $wrapper_class = "wn-max-w-7xl wn-mx-auto wn-px-4"; $grid_class = "wn-mx-auto"; if ($has_image && in_array($image_pos, ['left', 'right', 'image-left', 'image-right'])) { $grid_class .= " wn-grid wn-grid-cols-1 wn-lg-grid-cols-2 wn-gap-12 wn-items-center"; } else { $grid_class .= " wn-max-w-4xl"; } $html = "
"; $html .= "
"; // Image Output $image_html = ""; if ($current_pos_right = ($image_pos === 'right' || $image_pos === 'image-right')) { $order_class = 'wn-lg-order-last'; } else { $order_class = 'wn-lg-order-first'; } if ($has_image) { $image_html = "
"; $image_html .= "\"{$title}\""; $image_html .= "
"; } // Content Output $content_html = "
"; if ($title) { $content_html .= "

{$title}

"; } if ($text) { // Apply prose classes similar to React $content_html .= "
{$text}
"; } $content_html .= "
"; // Render based on order (Grid handles order via CSS classes for left/right, but fallback for DOM order) if ($has_image) { // For grid layout, we output both. CSS order handles visual. $html .= $image_html . $content_html; } else { $html .= $content_html; } $html .= "
"; return $html; } /** * Render Content section (for post body, rich text) */ public static function render_content($props, $layout, $color_scheme, $id, $element_styles = [], $section_styles = []) { $content = $props['content'] ?? ''; // Apply WordPress content filters (shortcodes, autop, etc.) $content = apply_filters('the_content', $content); // Normalize prop structure for universal renderer if needed if (is_string($props['content'])) { $props['content'] = ['value' => $content]; } else { $props['content']['value'] = $content; } // Section Styles (Background) $bg_color = $section_styles['backgroundColor'] ?? ''; $padding = $section_styles['paddingTop'] ?? ''; $height_preset = $section_styles['heightPreset'] ?? ''; $css = ""; if($bg_color) $css .= "background-color:{$bg_color};"; // Height Logic if ($height_preset === 'screen') { $css .= "min-height: 100vh; display: flex; align-items: center;"; $padding = '5rem'; // Default padding for screen to avoid edge collision } elseif ($height_preset === 'small') { $padding = '2rem'; } elseif ($height_preset === 'large') { $padding = '8rem'; } elseif ($height_preset === 'medium') { $padding = '4rem'; } if($padding) $css .= "padding:{$padding} 0;"; $style_attr = $css ? "style=\"{$css}\"" : ""; $inner_html = self::render_universal_row($props, 'left', $color_scheme, $element_styles); return "
{$inner_html}
"; } /** * Render Image + Text section */ public static function render_image_text($props, $layout, $color_scheme, $id, $element_styles = [], $section_styles = []) { $bg_color = $section_styles['backgroundColor'] ?? ''; $padding = $section_styles['paddingTop'] ?? ''; $height_preset = $section_styles['heightPreset'] ?? ''; $css = ""; if($bg_color) $css .= "background-color:{$bg_color};"; // Height Logic if ($height_preset === 'screen') { $css .= "min-height: 100vh; display: flex; align-items: center;"; $padding = '5rem'; } elseif ($height_preset === 'small') { $padding = '2rem'; } elseif ($height_preset === 'large') { $padding = '8rem'; } elseif ($height_preset === 'medium') { $padding = '4rem'; } if($padding) $css .= "padding:{$padding} 0;"; $style_attr = $css ? "style=\"{$css}\"" : ""; $inner_html = self::render_universal_row($props, $layout, $color_scheme, $element_styles); return "
{$inner_html}
"; } /** * Render Feature Grid section */ public static function render_feature_grid($props, $layout, $color_scheme, $id, $element_styles = []) { $heading = esc_html($props['heading'] ?? ''); $items = $props['items'] ?? []; $html = "
"; if ($heading) { $html .= "

{$heading}

"; } // Feature Item Styles (Card) $item_style_attr = self::generate_style_attr($element_styles['feature_item'] ?? []); // BG, Border, Shadow handled by CSS classes mostly, but colors here $item_bg = $element_styles['feature_item']['backgroundColor'] ?? ''; $html .= '
'; foreach ($items as $item) { $item_title = esc_html($item['title'] ?? ''); $item_desc = esc_html($item['description'] ?? ''); $item_icon = esc_html($item['icon'] ?? ''); // Allow overriding item specific style if needed, but for now global $html .= "
"; // Render Icon SVG if ($item_icon) { $icon_svg = self::get_icon_svg($item_icon); if ($icon_svg) { $html .= "
{$icon_svg}
"; } } if ($item_title) { // Feature title style $f_title_style = self::generate_style_attr($element_styles['feature_title'] ?? []); $html .= "

{$item_title}

"; } if ($item_desc) { // Feature description style $f_desc_style = self::generate_style_attr($element_styles['feature_description'] ?? []); $html .= "

{$item_desc}

"; } $html .= '
'; } $html .= '
'; $html .= '
'; return $html; } /** * Render CTA Banner section */ public static function render_cta_banner($props, $layout, $color_scheme, $id, $element_styles = []) { $title = esc_html($props['title'] ?? ''); $text = esc_html($props['text'] ?? ''); $button_text = esc_html($props['button_text'] ?? ''); $button_url = esc_url($props['button_url'] ?? ''); $html = "
"; $html .= '
'; if ($title) { $html .= "

{$title}

"; } if ($text) { $html .= "

{$text}

"; } if ($button_text && $button_url) { $html .= "{$button_text}"; } $html .= '
'; $html .= '
'; return $html; } /** * Render Contact Form section */ public static function render_contact_form($props, $layout, $color_scheme, $id, $element_styles = []) { $title = esc_html($props['title'] ?? ''); $webhook_url = esc_url($props['webhook_url'] ?? ''); $redirect_url = esc_url($props['redirect_url'] ?? ''); $fields = $props['fields'] ?? ['name', 'email', 'message']; // Extract styles $btn_bg = $element_styles['button']['backgroundColor'] ?? ''; $btn_color = $element_styles['button']['color'] ?? ''; $field_bg = $element_styles['fields']['backgroundColor'] ?? ''; $field_color = $element_styles['fields']['color'] ?? ''; $btn_style = ""; if ($btn_bg) $btn_style .= "background-color: {$btn_bg};"; if ($btn_color) $btn_style .= "color: {$btn_color};"; $btn_attr = $btn_style ? "style=\"{$btn_style}\"" : ""; $field_style = ""; if ($field_bg) $field_style .= "background-color: {$field_bg};"; if ($field_color) $field_style .= "color: {$field_color};"; $field_attr = $field_style ? "style=\"{$field_style}\"" : ""; $html = "
"; if ($title) { $html .= "

{$title}

"; } // Form is rendered but won't work for bots (they just see the structure) $html .= '
'; foreach ($fields as $field) { $field_label = ucfirst(str_replace('_', ' ', $field)); $html .= '
'; $html .= ""; if ($field === 'message') { $html .= ""; } else { $html .= ""; } $html .= '
'; } $html .= ""; $html .= '
'; $html .= '
'; return $html; } /** * Helper to get SVG for known icons */ private static function get_icon_svg($name) { $icons = [ 'Star' => '', 'Zap' => '', 'Shield' => '', 'Heart' => '', 'Award' => '', 'Clock' => '', 'Truck' => '', 'User' => '', 'Settings' => '', ]; return $icons[$name] ?? $icons['Star']; } /** * Generic section fallback */ public static function render_generic($props, $type, $id) { $content = ''; foreach ($props as $key => $value) { if (is_string($value)) { $content .= "
" . wp_kses_post($value) . "
"; } } return "
{$content}
"; } }