mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-16 00:14:29 +00:00
feat: convert remaining Landing Page blocks from Go HTML to React
This commit is contained in:
1 parent
266d092fac
commit
ef8a34e33a
26 files changed
+1088
-1063
No files matched your search
@@ -1,32 +0,0 @@
|
||||
{{/* Hugo Blox: Hero Block - Preact Wrapper */}}
|
||||
{{/* Single source of truth - Preact component handles everything */}}
|
||||
{{/* Documentation: https://hugoblox.com/blocks/ */}}
|
||||
{{/* License: https://github.com/HugoBlox/kit/blob/main/LICENSE.md */}}
|
||||
|
||||
{{/* Initialize variables */}}
|
||||
{{ $page := .wcPage }}
|
||||
{{ $block := .wcBlock }}
|
||||
{{ $block_id := .wcIdentifier | default (printf "hero-%d" (now.Unix)) }}
|
||||
|
||||
{{/* Prepare props for the Preact component */}}
|
||||
{{ $props := dict
|
||||
"content" $block.content
|
||||
"design" $block.design
|
||||
"id" $block_id
|
||||
}}
|
||||
|
||||
{{/* Convert props to JSON for client-side rendering */}}
|
||||
{{ $propsJSON := $props | jsonify }}
|
||||
|
||||
{{/* Render a container that Preact will populate */}}
|
||||
{{/* The component will be rendered immediately on page load */}}
|
||||
<div
|
||||
id="{{ $block_id }}"
|
||||
class="hero-block-container"
|
||||
data-props='{{ $propsJSON | safeHTML }}'
|
||||
data-hero-render="immediate"
|
||||
>
|
||||
{{/* Empty - Preact renders everything */}}
|
||||
</div>
|
||||
|
||||
{{/* Preact script loading is handled by libraries.html */}}
|
||||
@@ -17,8 +17,7 @@
|
||||
"type" $block_type
|
||||
}}
|
||||
|
||||
{{/* If an icon is specified, fetch its SVG data and add it to the props */}}
|
||||
{{/* TODO: Generalize this to work with all block icon fields */}}
|
||||
{{/* Resolve icon for primary_action (hero, cta blocks) */}}
|
||||
{{ with $block.content.primary_action.icon }}
|
||||
{{ $icon_data := partial "functions/get_icon_data" (dict "name" .) }}
|
||||
{{ if $icon_data }}
|
||||
@@ -26,12 +25,129 @@
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Resolve icon for single button (cta-card) */}}
|
||||
{{ with $block.content.button.icon }}
|
||||
{{ $icon_data := partial "functions/get_icon_data" (dict "name" .) }}
|
||||
{{ if $icon_data }}
|
||||
{{ $props = merge $props (dict "button_icon_svg" $icon_data) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Resolve per-item icons (stats, features, etc.) into an icon_svgs map */}}
|
||||
{{ $icon_svgs := dict }}
|
||||
{{ with $block.content.items }}
|
||||
{{ range . }}
|
||||
{{ with .icon }}
|
||||
{{ if not (index $icon_svgs .) }}
|
||||
{{ $icon_data := partial "functions/get_icon_data" (dict "name" .) }}
|
||||
{{ if $icon_data }}
|
||||
{{ $icon_svgs = merge $icon_svgs (dict . $icon_data) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len $icon_svgs) 0 }}
|
||||
{{ $props = merge $props (dict "icon_svgs" $icon_svgs) }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Resolve per-item images via Hugo's image pipeline */}}
|
||||
{{/* Thumbnail mode (testimonials): Fill 96x96 webp */}}
|
||||
{{/* Responsive mode (cta-image-paragraph): srcset with breakpoints */}}
|
||||
{{ $item_images := dict }}
|
||||
{{ with $block.content.items }}
|
||||
{{ range $idx, $item := . }}
|
||||
{{ with .image }}
|
||||
{{ $image_path := strings.TrimSpace (printf "%v" .) }}
|
||||
{{ $is_remote := or (strings.HasPrefix $image_path "http://") (strings.HasPrefix $image_path "https://") }}
|
||||
|
||||
{{/* Resolve the image resource */}}
|
||||
{{ $image_resource := false }}
|
||||
{{ $fallback_url := "" }}
|
||||
|
||||
{{ if $is_remote }}
|
||||
{{ $remote := try (resources.GetRemote $image_path) }}
|
||||
{{ if and $remote (not $remote.Err) }}
|
||||
{{ $image_resource = $remote.Value }}
|
||||
{{ else }}
|
||||
{{ $fallback_url = $image_path }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ $normalized := $image_path }}
|
||||
{{ $normalized = strings.TrimPrefix "/" $normalized }}
|
||||
{{ $normalized = strings.TrimPrefix "./" $normalized }}
|
||||
{{ $normalized = strings.TrimPrefix "assets/" $normalized }}
|
||||
{{ $normalized = strings.TrimPrefix "media/" $normalized }}
|
||||
{{ $normalized = path.Clean $normalized }}
|
||||
|
||||
{{ if and $normalized (ne $normalized ".") }}
|
||||
{{ with $page }}
|
||||
{{ $image_resource = (.Resources.ByType "image").GetMatch $normalized }}
|
||||
{{ end }}
|
||||
{{ if not $image_resource }}
|
||||
{{ $image_resource = resources.Get (path.Join "media" $normalized) }}
|
||||
{{ end }}
|
||||
{{ if not $image_resource }}
|
||||
{{ $fallback_url = printf "/media/%s" $normalized }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $img_data := dict }}
|
||||
{{ if $image_resource }}
|
||||
{{ if eq $block_type "cta-image-paragraph" }}
|
||||
{{/* Responsive mode: generate srcset via process_responsive_image */}}
|
||||
{{ if ne $image_resource.MediaType.SubType "gif" }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $image_resource
|
||||
"mode" "fit"
|
||||
"sizes" (slice 400 600 800 1200)
|
||||
) }}
|
||||
{{ $img_data = dict "srcset" $responsive.srcset "src" $responsive.fallback.RelPermalink "width" $responsive.fallback.Width "height" $responsive.fallback.Height }}
|
||||
{{ else }}
|
||||
{{ $img_data = dict "src" $image_resource.RelPermalink "width" $image_resource.Width "height" $image_resource.Height }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{/* Thumbnail mode (testimonials, etc.): Fill 96x96 */}}
|
||||
{{ $processed := $image_resource.Process "Fill 96x96 Center webp" }}
|
||||
{{ $img_data = dict "src" $processed.RelPermalink }}
|
||||
{{ end }}
|
||||
{{ else if $fallback_url }}
|
||||
{{ $img_data = dict "src" $fallback_url }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $img_data }}
|
||||
{{ $item_images = merge $item_images (dict (printf "%d" $idx) $img_data) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len $item_images) 0 }}
|
||||
{{ $props = merge $props (dict "item_images" $item_images) }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Resolve per-item feature_icon (cta-image-paragraph) */}}
|
||||
{{ with $block.content.items }}
|
||||
{{ range . }}
|
||||
{{ with .feature_icon }}
|
||||
{{ if not (index $icon_svgs .) }}
|
||||
{{ $icon_data := partial "functions/get_icon_data" (dict "name" .) }}
|
||||
{{ if $icon_data }}
|
||||
{{ $icon_svgs = merge $icon_svgs (dict . $icon_data) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len $icon_svgs) 0 }}
|
||||
{{ $props = merge $props (dict "icon_svgs" $icon_svgs) }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Convert props to JSON for client-side rendering */}}
|
||||
{{ $propsJSON := $props | jsonify }}
|
||||
|
||||
{{/* Render a container that Preact will populate */}}
|
||||
<div
|
||||
id="{{ $block_id }}"
|
||||
class="{{ $block_type }}-block-container preact-block"
|
||||
data-block-type="{{ $block_type }}"
|
||||
data-props='{{ $propsJSON | safeHTML }}'
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
{{ $branding := partialCached "functions/get_branding" . "branding" }}
|
||||
{{ $logo := partialCached "functions/get_logo" (dict "constraint" "max_height" "size" 36) "navbar_logo" }}
|
||||
{{- $show_logo := site.Params.hugoblox.header.show_logo | default true -}}
|
||||
{{- $show_title := site.Params.hugoblox.header.show_title | default true -}}
|
||||
{{- $navbar_title := site.Params.hugoblox.header.title | default $branding.name -}}
|
||||
<header id="site-header" class="header">
|
||||
<nav class="navbar px-3 flex justify-start">
|
||||
<div class="order-0 h-full">
|
||||
<!-- Brand -->
|
||||
<a class="navbar-brand" href="{{ site.Home.RelPermalink }}" title="{{ $branding.site_title }}">
|
||||
{{/* Auto-detect logo from assets/media/logo.{svg,png} */}}
|
||||
{{ if $show_logo }}
|
||||
{{ with $logo.resource }}
|
||||
{{ if $logo.is_svg }}
|
||||
{{- .Content | safeHTML -}}
|
||||
@@ -20,8 +24,9 @@
|
||||
alt="{{ $branding.name }}" />
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{/* Display brand name */}}
|
||||
{{ $branding.name }}
|
||||
{{ if $show_title }}{{ $navbar_title }}{{ end }}
|
||||
</a>
|
||||
</div>
|
||||
<!-- Mobile menu -->
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
{{ if ne $bg_img.MediaType.SubType "svg" }}
|
||||
{{ $bg_img = $bg_img.Fit "1920x1920 webp" }}
|
||||
{{ end }}
|
||||
{{ $image_layer := printf "url('%s')" $bg_img.Permalink }}
|
||||
{{ $image_layer := printf "url('%s')" $bg_img.RelPermalink }}
|
||||
{{ $background_layers = $background_layers | append $image_layer }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -27,32 +27,55 @@ and Tailwind's real filesystem scanner.
|
||||
{{ $paths = $paths | append `assets/js/preact-built/**/*.js` }}
|
||||
|
||||
{{/*
|
||||
3. Inline sources: extract class attributes from Preact JSX/TSX and embed them
|
||||
directly so Tailwind can always see them regardless of FS layout.
|
||||
3. Comprehensive class extraction from Preact JSX/TSX source files.
|
||||
|
||||
Hugo's virtual filesystem mounts block JSX/TSX into assets/js/hbx/blocks/.
|
||||
Tailwind's @source file paths only work on the real filesystem, but these
|
||||
files exist only in Hugo's virtual FS (or in a module cache for remote modules).
|
||||
So we read the file content via resources.Match and extract class candidates inline.
|
||||
|
||||
Strategy: Extract ALL string literal content — not just class= attributes.
|
||||
This catches classes in any pattern:
|
||||
- Static attrs: class="gap-8 items-center"
|
||||
- JSX expressions: class={"gap-8 items-center"}
|
||||
- Template literals: class={`gap-8 ${cond ? "md:grid" : ""}`}
|
||||
- Utility fns: cn("gap-8", condition && "md:grid-cols-2")
|
||||
|
||||
Tailwind's scanner automatically ignores non-class tokens (import paths,
|
||||
error messages, etc.), so false positives are harmless.
|
||||
*/}}
|
||||
{{ $jsx_files := resources.Match "js/hbx/**/*.jsx" }}
|
||||
{{ $tsx_files := resources.Match "js/hbx/**/*.tsx" }}
|
||||
{{ $all_jsx := resources.Match "js/hbx/**/*.jsx" }}
|
||||
{{ $all_tsx := resources.Match "js/hbx/**/*.tsx" }}
|
||||
|
||||
{{ $extracted_classes := slice }}
|
||||
{{ range $f := $jsx_files }}
|
||||
{{ range $f := $all_jsx | append $all_tsx }}
|
||||
{{ $code := $f.Content }}
|
||||
{{ $matches := findRE `class(?:Name)?\s*=\s*"([^"]+)"` $code }}
|
||||
{{ range $m := $matches }}
|
||||
{{ $cls := replaceRE `^class(?:Name)?\s*=\s*"([^"]+)"$` `$1` $m }}
|
||||
{{ $classes := split $cls " " }}
|
||||
{{ range $class := $classes }}
|
||||
{{ $extracted_classes = $extracted_classes | append $class }}
|
||||
|
||||
{{/* A) Extract content of all double-quoted strings */}}
|
||||
{{ $dq_matches := findRE `"[^"]*"` $code }}
|
||||
{{ range $m := $dq_matches }}
|
||||
{{ $str := strings.TrimPrefix `"` (strings.TrimSuffix `"` $m) }}
|
||||
{{ range split $str " " }}
|
||||
{{ $t := strings.TrimSpace . }}
|
||||
{{/* Filter out tokens with unsafe characters (braces, quotes, parens) */}}
|
||||
{{ if and $t (not (findRE `[\{\}\(\)"';]` $t)) }}
|
||||
{{ $extracted_classes = $extracted_classes | append $t }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ range $f := $tsx_files }}
|
||||
{{ $code := $f.Content }}
|
||||
{{ $matches := findRE `class(?:Name)?\s*=\s*"([^"]+)"` $code }}
|
||||
{{ range $m := $matches }}
|
||||
{{ $cls := replaceRE `^class(?:Name)?\s*=\s*"([^"]+)"$` `$1` $m }}
|
||||
{{ $classes := split $cls " " }}
|
||||
{{ range $class := $classes }}
|
||||
{{ $extracted_classes = $extracted_classes | append $class }}
|
||||
|
||||
{{/* B) Extract static parts of template literals (backtick strings) */}}
|
||||
{{/* Removes ${...} interpolations, keeping surrounding text */}}
|
||||
{{ $bt_matches := findRE "`[^`]*`" $code }}
|
||||
{{ range $m := $bt_matches }}
|
||||
{{ $str := strings.TrimPrefix "`" (strings.TrimSuffix "`" $m) }}
|
||||
{{ $static := replaceRE `\$\{[^}]*\}` " " $str }}
|
||||
{{ range split $static " " }}
|
||||
{{ $t := strings.TrimSpace . }}
|
||||
{{/* Filter out tokens with unsafe characters (braces, quotes, parens) */}}
|
||||
{{ if and $t (not (findRE `[\{\}\(\)"';]` $t)) }}
|
||||
{{ $extracted_classes = $extracted_classes | append $t }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
Reference in new issue
Block a user