mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-05 06:13:14 +00:00
feat: harden block inputs (batch 1)
Hardened 1st batch of five homepage blocks by normalizing config types, clamping unsafe values, and guarding bad inputs so user‑supplied params fail safe without template errors. Changes coerce_bool.html and coerce_int.html: added shared coercion helpers for consistent, safe defaults across blocks. block.html: sanitized content/design inputs, robust user_groups parsing, safer role/affiliation/interest/link handling, and CTA URL validation. block.html: guarded avatar/banner/name/headings/bio, safe button/link resolution, and resilient parsing for affiliations, education, interests, and social links. block.html: hardened filters (kinds/folders/tags), safer sort/order handling, clamped numeric params, and archive link safety/fallbacks. block.html and block.html: defensive content rendering, clamped overlay opacity, and safe button link handling. schema.json, schema.json, schema.json: added view enum entries, added new_tab, and Biome formatting applied.
This commit is contained in:
1 parent
3651909ac2
commit
98f0d201c1
10 files changed
+995
-305
No files matched your search
@@ -5,14 +5,21 @@
|
||||
{{/* Initialise */}}
|
||||
{{ $page := .wcPage }}
|
||||
{{ $block := .wcBlock }}
|
||||
{{ $view := $block.design.view | default "card" }}
|
||||
{{ $content := $block.content }}
|
||||
{{ if not (reflect.IsMap $content) }}{{ $content = dict }}{{ end }}
|
||||
{{ $design := $block.design }}
|
||||
{{ if not (reflect.IsMap $design) }}{{ $design = dict }}{{ end }}
|
||||
|
||||
{{ $items_offset := $block.content.offset | default 0 }}
|
||||
{{ $items_count := $block.content.count }}
|
||||
{{ $view := "card" }}
|
||||
{{ $view_raw := index $design "view" }}
|
||||
{{ if eq (printf "%T" $view_raw) "string" }}
|
||||
{{ $view = lower (strings.TrimSpace $view_raw) }}
|
||||
{{ end }}
|
||||
|
||||
{{ $items_offset := partial "functions/coerce_int" (dict "value" (index $content "offset") "default" 0 "min" 0) }}
|
||||
{{ $items_count := partial "functions/coerce_int" (dict "value" (index $content "count") "default" 5 "min" 0) }}
|
||||
{{ if eq $items_count 0 }}
|
||||
{{ $items_count = 65535 }}
|
||||
{{ else }}
|
||||
{{ $items_count = $items_count | default 5 }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Query */}}
|
||||
@@ -20,22 +27,78 @@
|
||||
{{ $archive_page := "" }}
|
||||
|
||||
{{/* Filters */}}
|
||||
{{ $kinds := $block.content.filters.kinds | default (slice "page") }}
|
||||
{{ $filters := index $content "filters" }}
|
||||
{{ if not (reflect.IsMap $filters) }}{{ $filters = dict }}{{ end }}
|
||||
|
||||
{{ $kinds := slice }}
|
||||
{{ $kinds_raw := index $filters "kinds" }}
|
||||
{{ if reflect.IsSlice $kinds_raw }}
|
||||
{{ range $kinds_raw }}
|
||||
{{ $kind := strings.TrimSpace (printf "%v" .) }}
|
||||
{{ if $kind }}
|
||||
{{ $kinds = $kinds | append $kind }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" $kinds_raw) "string" }}
|
||||
{{ $kind := strings.TrimSpace $kinds_raw }}
|
||||
{{ if $kind }}
|
||||
{{ $kinds = slice $kind }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if eq (len $kinds) 0 }}
|
||||
{{ $kinds = slice "page" }}
|
||||
{{ end }}
|
||||
{{ $query = where $query "Kind" "in" $kinds }}
|
||||
|
||||
{{ if $block.content.page_type }}
|
||||
{{ $query = where $query "Type" $block.content.page_type }}
|
||||
{{ $archive_page = site.GetPage "Section" $block.content.page_type }}
|
||||
{{ $page_type := "" }}
|
||||
{{ $page_type_raw := index $content "page_type" }}
|
||||
{{ if eq (printf "%T" $page_type_raw) "string" }}
|
||||
{{ $page_type = strings.TrimSpace $page_type_raw }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.folders }}
|
||||
{{ $folders := $block.content.filters.folders }}
|
||||
{{ if $page_type }}
|
||||
{{ $query = where $query "Type" $page_type }}
|
||||
{{ $archive_page = site.GetPage "Section" $page_type }}
|
||||
{{ end }}
|
||||
|
||||
{{ $folders_raw := index $filters "folders" }}
|
||||
{{ $folders := slice }}
|
||||
{{ if reflect.IsSlice $folders_raw }}
|
||||
{{ range $folders_raw }}
|
||||
{{ $folder := strings.TrimSpace (printf "%v" .) }}
|
||||
{{ if $folder }}
|
||||
{{ $folders = $folders | append $folder }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" $folders_raw) "string" }}
|
||||
{{ $folder := strings.TrimSpace $folders_raw }}
|
||||
{{ if $folder }}
|
||||
{{ $folders = slice $folder }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len $folders) 0 }}
|
||||
{{ $query = where $query "Section" "in" $folders }}
|
||||
{{/* Init archive page to main folder */}}
|
||||
{{ $main_folder := index $folders 0 }}
|
||||
{{ $archive_page = site.GetPage "Section" $main_folder }}
|
||||
{{ $main_folder := strings.TrimSpace (printf "%v" (index $folders 0)) }}
|
||||
{{ if $main_folder }}
|
||||
{{ $archive_page = site.GetPage "Section" $main_folder }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.tags }}
|
||||
{{ $tags := $block.content.filters.tags }}
|
||||
|
||||
{{ $tags_raw := index $filters "tags" }}
|
||||
{{ $tags := slice }}
|
||||
{{ if reflect.IsSlice $tags_raw }}
|
||||
{{ range $tags_raw }}
|
||||
{{ $tag := strings.TrimSpace (printf "%v" .) }}
|
||||
{{ if $tag }}
|
||||
{{ $tags = $tags | append $tag }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" $tags_raw) "string" }}
|
||||
{{ $tag := strings.TrimSpace $tags_raw }}
|
||||
{{ if $tag }}
|
||||
{{ $tags = slice $tag }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len $tags) 0 }}
|
||||
{{ $selected := slice }}
|
||||
{{ range $tags }}
|
||||
{{ $tag_page := site.GetPage (printf "tags/%s" (urlize .)) }}
|
||||
@@ -45,47 +108,110 @@
|
||||
{{ end }}
|
||||
{{ if gt (len $selected) 0 }}
|
||||
{{ $query = $query | intersect $selected }}
|
||||
{{ $archive_page = site.GetPage (printf "tags/%s" (urlize (index $tags 0))) }}
|
||||
{{ $first_tag := index $tags 0 }}
|
||||
{{ $archive_page = site.GetPage (printf "tags/%s" (urlize $first_tag)) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.tag }}
|
||||
{{ $archive_page = site.GetPage (printf "tags/%s" (urlize $block.content.filters.tag)) }}
|
||||
{{ $query = $query | intersect $archive_page.Pages }}
|
||||
|
||||
{{ $tag := "" }}
|
||||
{{ $tag_raw := index $filters "tag" }}
|
||||
{{ if eq (printf "%T" $tag_raw) "string" }}
|
||||
{{ $tag = strings.TrimSpace $tag_raw }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.category }}
|
||||
{{ $archive_page = site.GetPage (printf "categories/%s" (urlize $block.content.filters.category)) }}
|
||||
{{ $query = $query | intersect $archive_page.Pages }}
|
||||
{{ if $tag }}
|
||||
{{ with site.GetPage (printf "tags/%s" (urlize $tag)) }}
|
||||
{{ $archive_page = . }}
|
||||
{{ $query = $query | intersect .Pages }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.publication_type }}
|
||||
{{ $archive_page = site.GetPage (printf "publication_types/%s" $block.content.filters.publication_type) }}
|
||||
{{ $query = $query | intersect $archive_page.Pages }}
|
||||
|
||||
{{ $category := "" }}
|
||||
{{ $category_raw := index $filters "category" }}
|
||||
{{ if eq (printf "%T" $category_raw) "string" }}
|
||||
{{ $category = strings.TrimSpace $category_raw }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.exclude_publication_type }}
|
||||
{{ $query = $query | complement (site.GetPage (printf "publication_types/%s" $block.content.filters.exclude_publication_type)).Pages }}
|
||||
{{ if $category }}
|
||||
{{ with site.GetPage (printf "categories/%s" (urlize $category)) }}
|
||||
{{ $archive_page = . }}
|
||||
{{ $query = $query | intersect .Pages }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.author }}
|
||||
{{ $archive_page = site.GetPage (printf "authors/%s" (urlize $block.content.filters.author)) }}
|
||||
{{ $query = $query | intersect $archive_page.Pages }}
|
||||
|
||||
{{ $publication_type := "" }}
|
||||
{{ $publication_type_raw := index $filters "publication_type" }}
|
||||
{{ if eq (printf "%T" $publication_type_raw) "string" }}
|
||||
{{ $publication_type = strings.TrimSpace $publication_type_raw }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.featured_only }}
|
||||
{{ if $publication_type }}
|
||||
{{ with site.GetPage (printf "publication_types/%s" $publication_type) }}
|
||||
{{ $archive_page = . }}
|
||||
{{ $query = $query | intersect .Pages }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $exclude_publication_type := "" }}
|
||||
{{ $exclude_publication_type_raw := index $filters "exclude_publication_type" }}
|
||||
{{ if eq (printf "%T" $exclude_publication_type_raw) "string" }}
|
||||
{{ $exclude_publication_type = strings.TrimSpace $exclude_publication_type_raw }}
|
||||
{{ end }}
|
||||
{{ if $exclude_publication_type }}
|
||||
{{ with site.GetPage (printf "publication_types/%s" $exclude_publication_type) }}
|
||||
{{ $query = $query | complement .Pages }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $author_filter := "" }}
|
||||
{{ $author_filter_raw := index $filters "author" }}
|
||||
{{ if eq (printf "%T" $author_filter_raw) "string" }}
|
||||
{{ $author_filter = strings.TrimSpace $author_filter_raw }}
|
||||
{{ end }}
|
||||
{{ if $author_filter }}
|
||||
{{ with site.GetPage (printf "authors/%s" (urlize $author_filter)) }}
|
||||
{{ $archive_page = . }}
|
||||
{{ $query = $query | intersect .Pages }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $featured_only := partial "functions/coerce_bool" (dict "value" (index $filters "featured_only") "default" false) }}
|
||||
{{ if $featured_only }}
|
||||
{{ $query = where $query "Params.featured" "==" true }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.exclude_featured }}
|
||||
{{ $exclude_featured := partial "functions/coerce_bool" (dict "value" (index $filters "exclude_featured") "default" false) }}
|
||||
{{ if $exclude_featured }}
|
||||
{{ $query = where $query "Params.featured" "!=" true }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.exclude_past }}
|
||||
{{ $exclude_past := partial "functions/coerce_bool" (dict "value" (index $filters "exclude_past") "default" false) }}
|
||||
{{ if $exclude_past }}
|
||||
{{ $query = where $query "Date" ">=" now }}
|
||||
{{ end }}
|
||||
{{ if $block.content.filters.exclude_future }}
|
||||
{{ $exclude_future := partial "functions/coerce_bool" (dict "value" (index $filters "exclude_future") "default" false) }}
|
||||
{{ if $exclude_future }}
|
||||
{{ $query = where $query "Date" "<" now }}
|
||||
{{ end }}
|
||||
|
||||
{{ $count := len $query }}
|
||||
|
||||
{{/* Sort */}}
|
||||
{{ $sort_by := $block.content.sort_by | default "Date" }}
|
||||
{{ $sort_by := "Date" }}
|
||||
{{ $sort_by_raw := index $content "sort_by" }}
|
||||
{{ if eq (printf "%T" $sort_by_raw) "string" }}
|
||||
{{ $sort_by_raw = strings.TrimSpace $sort_by_raw }}
|
||||
{{ if ne $sort_by_raw "" }}
|
||||
{{ $sort_by = $sort_by_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $sort_by = partial "functions/get_sort_by_parameter" $sort_by }}
|
||||
{{ $sort_ascending := $block.content.sort_ascending | default (eq $block.content.order "asc") | default false }}
|
||||
{{ $sort_ascending := false }}
|
||||
{{ if isset $content "sort_ascending" }}
|
||||
{{ $sort_ascending = partial "functions/coerce_bool" (dict "value" (index $content "sort_ascending") "default" false) }}
|
||||
{{ else }}
|
||||
{{ $order_raw := index $content "order" }}
|
||||
{{ if eq (printf "%T" $order_raw) "string" }}
|
||||
{{ if eq (lower (strings.TrimSpace $order_raw)) "asc" }}
|
||||
{{ $sort_ascending = true }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $sort_order := cond $sort_ascending "asc" "desc" }}
|
||||
{{ $query = sort $query $sort_by $sort_order }}
|
||||
|
||||
@@ -104,28 +230,32 @@
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $columns := $block.design.columns | default "2" }}
|
||||
{{ $columns := partial "functions/coerce_int" (dict "value" (index $design "columns") "default" 2 "min" 1 "max" 4) }}
|
||||
{{ $title := "" }}
|
||||
{{ with index $content "title" }}{{ $title = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
{{ $text := "" }}
|
||||
{{ with index $content "text" }}{{ $text = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
|
||||
{{ if $block.content.title }}
|
||||
{{ if $title }}
|
||||
<div class="flex flex-col items-center max-w-prose mx-auto gap-3 justify-center px-6 md:px-0">
|
||||
|
||||
<div class="mb-6 text-3xl font-bold text-gray-900 dark:text-white">
|
||||
{{ $block.content.title | emojify | $page.RenderString }}
|
||||
{{ $title }}
|
||||
</div>
|
||||
|
||||
{{ with $block.content.text }}<p>{{ . | emojify | $page.RenderString }}</p>{{ end }}
|
||||
{{ with $text }}<p>{{ . }}</p>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<div class="flex flex-col items-center px-6">
|
||||
|
||||
{{ $config := dict
|
||||
"columns" ($block.design.columns | default 2)
|
||||
"columns" $columns
|
||||
"len" (len $query)
|
||||
"fill_image" ($block.design.fill_image | default true)
|
||||
"show_date" ($block.design.show_date | default true)
|
||||
"show_read_time" ($block.design.show_read_time | default true)
|
||||
"show_read_more" ($block.design.show_read_more | default true)
|
||||
"fill_image" (partial "functions/coerce_bool" (dict "value" (index $design "fill_image") "default" true))
|
||||
"show_date" (partial "functions/coerce_bool" (dict "value" (index $design "show_date") "default" true))
|
||||
"show_read_time" (partial "functions/coerce_bool" (dict "value" (index $design "show_read_time") "default" true))
|
||||
"show_read_more" (partial "functions/coerce_bool" (dict "value" (index $design "show_read_more") "default" true))
|
||||
}}
|
||||
{{ partial "functions/render_view" (dict "fragment" "start" "page" $block "item" . "view" $view "config" $config) }}
|
||||
|
||||
@@ -138,15 +268,30 @@
|
||||
</div>
|
||||
|
||||
{{/* Archive link */}}
|
||||
{{ $show_archive_link := $block.content.archive.enable | default (gt $count $items_count) }}
|
||||
{{ $archive := index $content "archive" }}
|
||||
{{ if not (reflect.IsMap $archive) }}{{ $archive = dict }}{{ end }}
|
||||
{{ $show_archive_link := gt $count $items_count }}
|
||||
{{ if isset $archive "enable" }}
|
||||
{{ $show_archive_link = partial "functions/coerce_bool" (dict "value" (index $archive "enable") "default" false) }}
|
||||
{{ end }}
|
||||
{{ if $show_archive_link | and $archive_page }}
|
||||
|
||||
{{ $archive_link := "" }}
|
||||
{{ if $block.content.archive.link }}
|
||||
{{ $archive_link = $block.content.archive.link | relLangURL }}
|
||||
{{ else }}
|
||||
{{ $archive_link_raw := index $archive "link" }}
|
||||
{{ if eq (printf "%T" $archive_link_raw) "string" }}
|
||||
{{ $archive_link = strings.TrimSpace $archive_link_raw }}
|
||||
{{ end }}
|
||||
{{ if not $archive_link }}
|
||||
{{ $archive_link = $archive_page.RelPermalink }}
|
||||
{{ end }}
|
||||
{{ if $archive_link }}
|
||||
{{ $scheme := (urls.Parse $archive_link).Scheme }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $archive_link "#") }}
|
||||
{{ $archive_link = $archive_link | relLangURL }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Localisation */}}
|
||||
{{ $items_type := $archive_page.Type }}
|
||||
@@ -161,13 +306,20 @@
|
||||
{{ $i18n = "more_pages" }}
|
||||
{{ end }}
|
||||
|
||||
{{ $archive_text := $block.content.archive.text | default (i18n $i18n) | default "See all" }}
|
||||
{{ $archive_text := "" }}
|
||||
{{ $archive_text_raw := index $archive "text" }}
|
||||
{{ if eq (printf "%T" $archive_text_raw) "string" }}
|
||||
{{ $archive_text = strings.TrimSpace $archive_text_raw }}
|
||||
{{ end }}
|
||||
{{ if not $archive_text }}
|
||||
{{ $archive_text = i18n $i18n | default "See all" }}
|
||||
{{ end }}
|
||||
|
||||
<div class="container mx-auto max-w-screen-lg px-8 xl:px-5 pb-5 lg:pb-8">
|
||||
<div class="mt-10 flex justify-center">
|
||||
<a
|
||||
class="relative inline-flex items-center gap-1 rounded-md border border-gray-300 bg-white px-3 py-2 pl-4 text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-20 dark:border-gray-500 dark:bg-gray-800 dark:text-gray-300"
|
||||
href="{{ $archive_link }}">
|
||||
href="{{ $archive_link | safeURL }}">
|
||||
<span>{{ $archive_text | emojify }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"properties": {
|
||||
"view": {
|
||||
"type": "string",
|
||||
"enum": ["card", "compact", "showcase", "citation", "list", "masonry"],
|
||||
"enum": ["card", "compact", "showcase", "citation", "list", "masonry", "article-grid", "date-title-summary", "slides-gallery"],
|
||||
"description": "Display view type",
|
||||
"default": "card"
|
||||
},
|
||||
|
||||
@@ -6,14 +6,52 @@
|
||||
{{ $page := .wcPage }}
|
||||
{{ $block := .wcBlock }}
|
||||
|
||||
{{ $title := $block.content.title | emojify | $page.RenderString }}
|
||||
{{ $text := $block.content.text | emojify | $page.RenderString | safeHTML }}
|
||||
{{ $card_class := $block.design.card.css_class }}
|
||||
{{ $card_style := $block.design.card.css_style }}
|
||||
{{ $content := $block.content }}
|
||||
{{ if not (reflect.IsMap $content) }}{{ $content = dict }}{{ end }}
|
||||
{{ $design := $block.design }}
|
||||
{{ if not (reflect.IsMap $design) }}{{ $design = dict }}{{ end }}
|
||||
{{ $card := index $design "card" }}
|
||||
{{ if not (reflect.IsMap $card) }}{{ $card = dict }}{{ end }}
|
||||
|
||||
{{ $title := "" }}
|
||||
{{ with index $content "title" }}{{ $title = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
{{ $text := "" }}
|
||||
{{ with index $content "text" }}{{ $text = printf "%v" . | emojify | $page.RenderString | safeHTML }}{{ end }}
|
||||
{{ $card_class := "" }}
|
||||
{{ $card_class_raw := index $card "css_class" }}
|
||||
{{ if eq (printf "%T" $card_class_raw) "string" }}
|
||||
{{ $card_class = $card_class_raw }}
|
||||
{{ end }}
|
||||
{{ $card_style := "" }}
|
||||
{{ $card_style_raw := index $card "css_style" }}
|
||||
{{ if eq (printf "%T" $card_style_raw) "string" }}
|
||||
{{ $card_style = $card_style_raw }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Contrast and accessibility controls */}}
|
||||
{{ $text_color_mode := $block.design.card.text_color | default "auto" }}
|
||||
{{ $overlay_opacity := $block.design.card.overlay_opacity | default 0.15 }}
|
||||
{{ $text_color_mode := "auto" }}
|
||||
{{ $text_color_raw := index $card "text_color" }}
|
||||
{{ if eq (printf "%T" $text_color_raw) "string" }}
|
||||
{{ $text_color_candidate := lower (strings.TrimSpace $text_color_raw) }}
|
||||
{{ if in (slice "auto" "light" "dark") $text_color_candidate }}
|
||||
{{ $text_color_mode = $text_color_candidate }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $overlay_opacity := 0.15 }}
|
||||
{{ $overlay_raw := index $card "overlay_opacity" }}
|
||||
{{ if ne $overlay_raw nil }}
|
||||
{{ $overlay_type := printf "%T" $overlay_raw }}
|
||||
{{ if or (eq $overlay_type "int") (eq $overlay_type "int8") (eq $overlay_type "int16") (eq $overlay_type "int32") (eq $overlay_type "int64") (eq $overlay_type "uint") (eq $overlay_type "uint8") (eq $overlay_type "uint16") (eq $overlay_type "uint32") (eq $overlay_type "uint64") (eq $overlay_type "float32") (eq $overlay_type "float64") }}
|
||||
{{ $overlay_opacity = float $overlay_raw }}
|
||||
{{ else if eq $overlay_type "string" }}
|
||||
{{ $overlay_trimmed := strings.TrimSpace $overlay_raw }}
|
||||
{{ if gt (len (findRE "^-?[0-9]*\\.?[0-9]+$" $overlay_trimmed)) 0 }}
|
||||
{{ $overlay_opacity = float $overlay_trimmed }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if lt $overlay_opacity 0 }}{{ $overlay_opacity = 0 }}{{ end }}
|
||||
{{ if gt $overlay_opacity 1 }}{{ $overlay_opacity = 1 }}{{ end }}
|
||||
|
||||
{{/* Auto-detect text color based on background */}}
|
||||
{{ $text_classes := "" }}
|
||||
@@ -27,8 +65,19 @@
|
||||
{{ else }}
|
||||
{{/* Auto mode - detect based on gradient colors */}}
|
||||
{{ $is_dark_bg := false }}
|
||||
{{ with $block.design.background.gradient.start }}
|
||||
{{ if or (hasPrefix . "primary-7") (hasPrefix . "primary-8") (hasPrefix . "primary-9") (hasPrefix . "secondary-7") (hasPrefix . "secondary-8") (hasPrefix . "secondary-9") }}
|
||||
{{ $gradient_start := "" }}
|
||||
{{ $bg := index $design "background" }}
|
||||
{{ if and $bg (reflect.IsMap $bg) }}
|
||||
{{ $gradient := index $bg "gradient" }}
|
||||
{{ if and $gradient (reflect.IsMap $gradient) }}
|
||||
{{ $start_raw := index $gradient "start" }}
|
||||
{{ if eq (printf "%T" $start_raw) "string" }}
|
||||
{{ $gradient_start = $start_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $gradient_start }}
|
||||
{{ if or (hasPrefix $gradient_start "primary-7") (hasPrefix $gradient_start "primary-8") (hasPrefix $gradient_start "primary-9") (hasPrefix $gradient_start "secondary-7") (hasPrefix $gradient_start "secondary-8") (hasPrefix $gradient_start "secondary-9") }}
|
||||
{{ $is_dark_bg = true }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -52,59 +101,78 @@
|
||||
style="{{ $custom_style | safeCSS }}">
|
||||
|
||||
{{/* Modern Typography Hierarchy - Responsive text colors */}}
|
||||
{{ with $title }}
|
||||
<h2 class="{{ $text_classes }} text-4xl sm:text-5xl lg:text-6xl xl:text-7xl font-bold tracking-tight leading-tight">
|
||||
{{ $title }}
|
||||
{{ . }}
|
||||
</h2>
|
||||
{{ end }}
|
||||
|
||||
{{/* Enhanced Text with Better Spacing - Responsive colors */}}
|
||||
{{ with $text }}
|
||||
<div class="{{ $text_classes }}/80 mt-6 text-lg sm:text-xl lg:text-2xl max-w-3xl leading-relaxed font-light">
|
||||
{{ $text }}
|
||||
</div>
|
||||
|
||||
{{ with $block.content.button }}
|
||||
{{/* Modern 2025 Button Container */}}
|
||||
<div class="flex mt-10">
|
||||
{{ $button_link := .url }}
|
||||
{{ $button_scheme := (urls.Parse $button_link).Scheme }}
|
||||
{{ $button_target := "" }}
|
||||
{{ $new_tab := .new_tab | default false }}
|
||||
{{ if not $button_scheme }}
|
||||
{{/* Check if it's a hash fragment (page anchor) */}}
|
||||
{{ if hasPrefix $button_link "#" }}
|
||||
{{/* Keep hash fragments as-is */}}
|
||||
{{ $button_link = $button_link }}
|
||||
{{ else }}
|
||||
{{/* Apply relLangURL for relative paths */}}
|
||||
{{ $button_link = $button_link | relLangURL }}
|
||||
{{ end }}
|
||||
{{ else if in (slice "http" "https") $button_scheme }}
|
||||
{{ $new_tab = true }}
|
||||
{{ end }}
|
||||
{{/* Apply new tab if requested or external link */}}
|
||||
{{ if $new_tab }}
|
||||
{{ $button_target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
|
||||
{{/* 2025 Glassmorphism Button */}}
|
||||
<a href="{{ $button_link | safeURL }}"
|
||||
{{ $button_target | safeHTMLAttr }}
|
||||
class="group relative inline-flex items-center gap-3 px-8 py-4 text-lg font-semibold {{ $button_classes }} transition-all duration-300 ease-out hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-500/50 dark:focus-visible:ring-white/50 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent">
|
||||
|
||||
{{/* Button Background with Glassmorphism */}}
|
||||
<div class="absolute inset-0 bg-white/95 dark:bg-gray-900/95 backdrop-blur-md rounded-2xl ring-1 ring-gray-200/30 dark:ring-white/30 shadow-lg group-hover:bg-white dark:group-hover:bg-gray-800 group-hover:ring-gray-300/50 dark:group-hover:ring-white/50 group-hover:shadow-2xl transition-all duration-300"></div>
|
||||
|
||||
{{/* Button Content */}}
|
||||
<span class="relative z-10">{{ .text }}</span>
|
||||
{{ with .icon }}
|
||||
<span class="relative z-10 transition-transform duration-300 group-hover:translate-x-1">
|
||||
{{ partial "functions/get_icon" (dict "name" . "attributes" "style=\"height: 1.25em\" class='inline-block'") }}
|
||||
</span>
|
||||
{{ end }}
|
||||
|
||||
{{/* Subtle glow effect */}}
|
||||
<div class="absolute inset-0 bg-white/20 dark:bg-white/10 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 blur-xl"></div>
|
||||
</a>
|
||||
{{ . }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ $button_raw := index $content "button" }}
|
||||
{{ if and $button_raw (reflect.IsMap $button_raw) }}
|
||||
{{ $button_text := "" }}
|
||||
{{ $button_url := "" }}
|
||||
{{ $button_icon := "" }}
|
||||
{{ $button_new_tab := partial "functions/coerce_bool" (dict "value" (index $button_raw "new_tab") "default" false) }}
|
||||
{{ with index $button_raw "text" }}{{ $button_text = printf "%v" . }}{{ end }}
|
||||
{{ with index $button_raw "url" }}{{ $button_url = printf "%v" . }}{{ end }}
|
||||
{{ with index $button_raw "icon" }}
|
||||
{{ if eq (printf "%T" .) "string" }}
|
||||
{{ $button_icon = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $button_text = strings.TrimSpace $button_text }}
|
||||
{{ $button_url = strings.TrimSpace $button_url }}
|
||||
{{ if and $button_text $button_url }}
|
||||
{{/* Modern 2025 Button Container */}}
|
||||
<div class="flex mt-10">
|
||||
{{ $button_link := $button_url }}
|
||||
{{ $button_scheme := (urls.Parse $button_link).Scheme }}
|
||||
{{ $button_target := "" }}
|
||||
{{ if not $button_scheme }}
|
||||
{{/* Check if it's a hash fragment (page anchor) */}}
|
||||
{{ if not (hasPrefix $button_link "#") }}
|
||||
{{/* Apply relLangURL for relative paths */}}
|
||||
{{ $button_link = $button_link | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $button_link) ".pdf" }}
|
||||
{{ $button_new_tab = true }}
|
||||
{{ end }}
|
||||
{{ else if in (slice "http" "https") $button_scheme }}
|
||||
{{ $button_new_tab = true }}
|
||||
{{ end }}
|
||||
{{/* Apply new tab if requested or external link */}}
|
||||
{{ if $button_new_tab }}
|
||||
{{ $button_target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
|
||||
{{/* 2025 Glassmorphism Button */}}
|
||||
<a href="{{ $button_link | safeURL }}"
|
||||
{{ $button_target | safeHTMLAttr }}
|
||||
class="group relative inline-flex items-center gap-3 px-8 py-4 text-lg font-semibold {{ $button_classes }} transition-all duration-300 ease-out hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-500/50 dark:focus-visible:ring-white/50 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent">
|
||||
|
||||
{{/* Button Background with Glassmorphism */}}
|
||||
<div class="absolute inset-0 bg-white/95 dark:bg-gray-900/95 backdrop-blur-md rounded-2xl ring-1 ring-gray-200/30 dark:ring-white/30 shadow-lg group-hover:bg-white dark:group-hover:bg-gray-800 group-hover:ring-gray-300/50 dark:group-hover:ring-white/50 group-hover:shadow-2xl transition-all duration-300"></div>
|
||||
|
||||
{{/* Button Content */}}
|
||||
<span class="relative z-10">{{ $button_text }}</span>
|
||||
{{ if $button_icon }}
|
||||
<span class="relative z-10 transition-transform duration-300 group-hover:translate-x-1">
|
||||
{{ partial "functions/get_icon" (dict "name" $button_icon "attributes" "style=\"height: 1.25em\" class='inline-block'") }}
|
||||
</span>
|
||||
{{ end }}
|
||||
|
||||
{{/* Subtle glow effect */}}
|
||||
<div class="absolute inset-0 bg-white/20 dark:bg-white/10 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 blur-xl"></div>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
@@ -41,6 +41,11 @@
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "Optional button icon (icon pack format: pack/icon-name)"
|
||||
},
|
||||
"new_tab": {
|
||||
"type": "boolean",
|
||||
"description": "Open the button link in a new tab",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["url", "text"],
|
||||
|
||||
@@ -5,14 +5,20 @@
|
||||
{{/* Initialise */}}
|
||||
{{ $page := .wcPage }}
|
||||
{{ $block := .wcBlock }}
|
||||
{{ $title := $block.content.title | emojify | $page.RenderString }}
|
||||
{{ $text := $block.content.text | emojify | $page.RenderString | safeHTML }}
|
||||
{{ $content := $block.content }}
|
||||
{{ if not (reflect.IsMap $content) }}{{ $content = dict }}{{ end }}
|
||||
{{ $title := "" }}
|
||||
{{ with index $content "title" }}{{ $title = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
{{ $text := "" }}
|
||||
{{ with index $content "text" }}{{ $text = printf "%v" . | emojify | $page.RenderString | safeHTML }}{{ end }}
|
||||
|
||||
<div class="flex flex-col items-center max-w-prose mx-auto gap-3 justify-center px-6">
|
||||
|
||||
{{ with $title }}
|
||||
<div class="mb-6 text-3xl font-bold text-gray-900 dark:text-white">
|
||||
{{ $title }}
|
||||
{{ . }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{/* We use DIV rather than P to support `prose` class and multiple paragraphs */}}
|
||||
{{ with $text }}<div class="prose prose-slate lg:prose-xl dark:prose-invert max-w-prose">{{ . }}</div>{{ end }}
|
||||
|
||||
@@ -6,16 +6,45 @@
|
||||
{{ $page := .wcPage }}
|
||||
{{ $block := .wcBlock }}
|
||||
|
||||
{{ $author := $block.content.username | default "me" }}
|
||||
{{ $content := $block.content }}
|
||||
{{ if not (reflect.IsMap $content) }}{{ $content = dict }}{{ end }}
|
||||
{{ $design := $block.design }}
|
||||
{{ if not (reflect.IsMap $design) }}{{ $design = dict }}{{ end }}
|
||||
|
||||
{{ $author := "me" }}
|
||||
{{ $authorRaw := index $content "username" }}
|
||||
{{ if eq (printf "%T" $authorRaw) "string" }}
|
||||
{{ $author = $authorRaw }}
|
||||
{{ end }}
|
||||
{{ $profile := partial "functions/get_author_profile" $author }}
|
||||
{{ $avatar := $profile.avatar }}
|
||||
{{ $headings := index $content "headings" }}
|
||||
{{ if not (reflect.IsMap $headings) }}{{ $headings = dict }}{{ end }}
|
||||
|
||||
{{/* Avatar customization parameters */}}
|
||||
{{ $avatar_size := $block.design.avatar.size | default "large" }}
|
||||
{{ $avatar_shape := $block.design.avatar.shape | default "circle" }}
|
||||
{{ $avatar_size := "large" }}
|
||||
{{ $avatar_shape := "circle" }}
|
||||
{{ $avatar_config := index $design "avatar" }}
|
||||
{{ if and $avatar_config (reflect.IsMap $avatar_config) }}
|
||||
{{ $avatar_size_raw := index $avatar_config "size" }}
|
||||
{{ if eq (printf "%T" $avatar_size_raw) "string" }}
|
||||
{{ $avatar_size = $avatar_size_raw }}
|
||||
{{ end }}
|
||||
{{ $avatar_shape_raw := index $avatar_config "shape" }}
|
||||
{{ if eq (printf "%T" $avatar_shape_raw) "string" }}
|
||||
{{ $avatar_shape = $avatar_shape_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Profile name typography */}}
|
||||
{{ $name_size := $block.design.name.size | default "lg" }}
|
||||
{{ $name_size := "lg" }}
|
||||
{{ $name_config := index $design "name" }}
|
||||
{{ if and $name_config (reflect.IsMap $name_config) }}
|
||||
{{ $name_size_raw := index $name_config "size" }}
|
||||
{{ if eq (printf "%T" $name_size_raw) "string" }}
|
||||
{{ $name_size = $name_size_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $name_class_map := dict
|
||||
"xs" "text-xl sm:text-2xl lg:text-3xl"
|
||||
"sm" "text-2xl sm:text-3xl lg:text-4xl"
|
||||
@@ -35,6 +64,24 @@
|
||||
{{ $shape_classes := dict "circle" "rounded-full" "square" "rounded-none" "rounded" "rounded-lg" }}
|
||||
{{ $shape_class := index $shape_classes $avatar_shape | default "rounded-full" }}
|
||||
|
||||
{{ $banner_filename := "" }}
|
||||
{{ $banner_config := index $design "banner" }}
|
||||
{{ if and $banner_config (reflect.IsMap $banner_config) }}
|
||||
{{ $banner_raw := index $banner_config "filename" }}
|
||||
{{ if eq (printf "%T" $banner_raw) "string" }}
|
||||
{{ $banner_filename = $banner_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $bio_style := "" }}
|
||||
{{ $bio_config := index $design "biography" }}
|
||||
{{ if and $bio_config (reflect.IsMap $bio_config) }}
|
||||
{{ $bio_style_raw := index $bio_config "style" }}
|
||||
{{ if eq (printf "%T" $bio_style_raw) "string" }}
|
||||
{{ $bio_style = $bio_style_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<div class="resume-biography py-8 px-4 sm:py-12 md:py-16 lg:py-24 xl:py-32 relative overflow-hidden">
|
||||
{{/* Add subtle top gradient for navbar transition */}}
|
||||
<div class="max-w-7xl mx-auto">
|
||||
@@ -45,33 +92,42 @@
|
||||
|
||||
{{/* Banner Image */}}
|
||||
{{ $img := "" }}
|
||||
{{with $block.design.banner.filename}}
|
||||
{{- $img = resources.Get (path.Join "media" .) -}}
|
||||
{{ if $img }}
|
||||
<div class="relative w-full h-64 rounded-2xl overflow-hidden shadow-xl mb-8">
|
||||
{{ if ne $img.MediaType.SubType "gif" }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $img
|
||||
"mode" "fill"
|
||||
"aspect_ratio" "16:9"
|
||||
"sizes" (slice 768 1024 1366 1920 2560)
|
||||
) }}
|
||||
<img class="absolute inset-0 w-full h-full object-cover"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="100vw"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
alt="" />
|
||||
{{ else }}
|
||||
{{- $img = $img.Process "webp" -}}
|
||||
<img class="absolute inset-0 w-full h-full object-cover"
|
||||
src="{{$img.RelPermalink}}"
|
||||
alt="" />
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if $banner_filename }}
|
||||
{{- $img = resources.Get (path.Join "media" $banner_filename) -}}
|
||||
{{ if $img }}
|
||||
<div class="relative w-full h-64 rounded-2xl overflow-hidden shadow-xl mb-8">
|
||||
{{ if ne $img.MediaType.SubType "gif" }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $img
|
||||
"mode" "fill"
|
||||
"aspect_ratio" "16:9"
|
||||
"sizes" (slice 768 1024 1366 1920 2560)
|
||||
) }}
|
||||
<img class="absolute inset-0 w-full h-full object-cover"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="100vw"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
alt="" />
|
||||
{{ else }}
|
||||
{{- $img = $img.Process "webp" -}}
|
||||
<img class="absolute inset-0 w-full h-full object-cover"
|
||||
src="{{$img.RelPermalink}}"
|
||||
alt="" />
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Avatar */}}
|
||||
{{ $status_icon := "" }}
|
||||
{{ $status := $profile.status }}
|
||||
{{ if and $status (reflect.IsMap $status) }}
|
||||
{{ $status_icon_raw := index $status "icon" }}
|
||||
{{ if eq (printf "%T" $status_icon_raw) "string" }}
|
||||
{{ $status_icon = $status_icon_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $avatar }}
|
||||
<div class="avatar-wrapper {{ if $img }}-mt-24{{else}}mb-4{{end}}" style="width: {{$display_size}}px; height: {{$display_size}}px;">
|
||||
{{ $avatar_image := $avatar.Fill (printf "%sx%s Center" $generation_size $generation_size) }}
|
||||
@@ -81,7 +137,7 @@
|
||||
width="{{$display_size}}"
|
||||
height="{{$display_size}}"
|
||||
style="width: {{$display_size}}px; height: {{$display_size}}px; object-fit: cover;">
|
||||
{{with $profile.status.icon}}<span class="avatar-emoji">{{.|emojify}}</span>{{end}}
|
||||
{{ with $status_icon }}<span class="avatar-emoji">{{ . | emojify }}</span>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
@@ -91,52 +147,141 @@
|
||||
{{ $profile.title }}
|
||||
</h1>
|
||||
|
||||
{{- with $profile.pronouns -}}
|
||||
{{ $pronouns := "" }}
|
||||
{{ with $profile.pronouns }}
|
||||
{{ if eq (printf "%T" .) "string" }}
|
||||
{{ $pronouns = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ with $pronouns }}
|
||||
<p class="text-lg text-gray-600 dark:text-gray-400">({{ . }})</p>
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ $role_text := "" }}
|
||||
{{ with $profile.role }}
|
||||
{{ if eq (printf "%T" .) "string" }}
|
||||
{{ $role_text = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ with $role_text }}
|
||||
<p class="text-2xl font-semibold text-primary-600 dark:text-primary-400">{{ . | markdownify | emojify }}</p>
|
||||
{{ end }}
|
||||
|
||||
{{ range $profile.affiliations }}
|
||||
<p class="text-lg text-gray-700 dark:text-gray-300">
|
||||
{{ with .url }}<a href="{{ . }}" target="_blank" rel="noopener" class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors">{{ end }}
|
||||
{{ .name }}
|
||||
{{ if .url }}</a>{{ end }}
|
||||
</p>
|
||||
{{ $affiliations_raw := $profile.affiliations }}
|
||||
{{ $affiliations := slice }}
|
||||
{{ if reflect.IsSlice $affiliations_raw }}
|
||||
{{ $affiliations = $affiliations_raw }}
|
||||
{{ else if eq (printf "%T" $affiliations_raw) "string" }}
|
||||
{{ $affiliations = slice $affiliations_raw }}
|
||||
{{ end }}
|
||||
{{ range $affiliations }}
|
||||
{{ $aff_name := "" }}
|
||||
{{ $aff_url := "" }}
|
||||
{{ if reflect.IsMap . }}
|
||||
{{ $nameRaw := index . "name" | default (index . "title") }}
|
||||
{{ if ne $nameRaw nil }}
|
||||
{{ $aff_name = printf "%v" $nameRaw }}
|
||||
{{ end }}
|
||||
{{ $urlRaw := index . "url" }}
|
||||
{{ if eq (printf "%T" $urlRaw) "string" }}
|
||||
{{ $aff_url = $urlRaw }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" .) "string" }}
|
||||
{{ $aff_name = . }}
|
||||
{{ end }}
|
||||
{{ $aff_name = strings.TrimSpace $aff_name }}
|
||||
{{ $aff_url = strings.TrimSpace $aff_url }}
|
||||
{{ if $aff_name }}
|
||||
<p class="text-lg text-gray-700 dark:text-gray-300">
|
||||
{{ if $aff_url }}
|
||||
{{ $link := $aff_url }}
|
||||
{{ $scheme := (urls.Parse $link).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $link "#") }}
|
||||
{{ $link = $link | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $link) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<a href="{{ $link | safeURL }}" {{ $target | safeHTMLAttr }} class="hover:text-primary-600 dark:hover:text-primary-400 transition-colors">{{ $aff_name }}</a>
|
||||
{{ else }}
|
||||
{{ $aff_name }}
|
||||
{{ end }}
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{/* Social Icons */}}
|
||||
{{ $links_raw := $profile.links }}
|
||||
{{ $links := slice }}
|
||||
{{ if reflect.IsSlice $links_raw }}
|
||||
{{ $links = $links_raw }}
|
||||
{{ else if eq (printf "%T" $links_raw) "string" }}
|
||||
{{ $links = slice $links_raw }}
|
||||
{{ end }}
|
||||
{{ if gt (len $links) 0 }}
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
{{ range $profile.links }}
|
||||
{{ $link := .url | default .link }}
|
||||
{{ $scheme := (urls.Parse $link).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ $link = (.url | default .link) | relLangURL }}
|
||||
{{ if eq (path.Ext $link) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ range $links }}
|
||||
{{ $linkUrl := "" }}
|
||||
{{ $iconName := "hero/link" }}
|
||||
{{ $linkLabel := "" }}
|
||||
{{ if reflect.IsMap . }}
|
||||
{{ $urlRaw := index . "url" | default (index . "link") }}
|
||||
{{ if eq (printf "%T" $urlRaw) "string" }}
|
||||
{{ $linkUrl = $urlRaw }}
|
||||
{{ end }}
|
||||
{{ $iconRaw := index . "icon" }}
|
||||
{{ if eq (printf "%T" $iconRaw) "string" }}
|
||||
{{ $iconName = $iconRaw }}
|
||||
{{ end }}
|
||||
{{ $labelRaw := index . "label" }}
|
||||
{{ if eq (printf "%T" $labelRaw) "string" }}
|
||||
{{ $linkLabel = $labelRaw }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" .) "string" }}
|
||||
{{ $linkUrl = . }}
|
||||
{{ end }}
|
||||
{{ $linkUrl = strings.TrimSpace $linkUrl }}
|
||||
{{ if $linkUrl }}
|
||||
{{ $scheme := (urls.Parse $linkUrl).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $linkUrl "#") }}
|
||||
{{ $linkUrl = $linkUrl | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $linkUrl) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<a href="{{ $linkUrl | safeURL }}" {{ $target | safeHTMLAttr }}
|
||||
aria-label="{{ $iconName }}"
|
||||
{{ if $linkLabel }} title="{{ $linkLabel }}"{{ end }}
|
||||
class="w-12 h-12 flex items-center justify-center rounded-full bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400
|
||||
shadow-md hover:shadow-xl hover:scale-110 hover:-translate-y-1
|
||||
transition-all duration-300 border border-gray-200 dark:border-gray-700">
|
||||
{{ partial "functions/get_icon" (dict "name" $iconName "attributes" "class=\"w-6 h-6\"") }}
|
||||
</a>
|
||||
{{ end }}
|
||||
<a href="{{ $link | safeURL }}" {{ $target | safeHTMLAttr }}
|
||||
aria-label="{{ .icon }}"
|
||||
{{ with .label }} title="{{.}}"{{ end }}
|
||||
class="w-12 h-12 flex items-center justify-center rounded-full bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400
|
||||
shadow-md hover:shadow-xl hover:scale-110 hover:-translate-y-1
|
||||
transition-all duration-300 border border-gray-200 dark:border-gray-700">
|
||||
{{ partial "functions/get_icon" (dict "name" .icon "attributes" "class=\"w-6 h-6\"") }}
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{/* Right Column: Flexible span */}}
|
||||
<div class="md:col-span-8">
|
||||
|
||||
{{/* Bio Section */}}
|
||||
{{ $bio_content := ($block.content.text | emojify | $page.RenderString) | default $profile.bio }}
|
||||
{{ $bio_content := "" }}
|
||||
{{ $bio_override_raw := index $content "text" }}
|
||||
{{ if ne $bio_override_raw nil }}
|
||||
{{ $bio_content = printf "%v" $bio_override_raw | emojify | $page.RenderString }}
|
||||
{{ end }}
|
||||
{{ if not $bio_content }}
|
||||
{{ with $profile.bio }}{{ $bio_content = printf "%v" . }}{{ end }}
|
||||
{{ end }}
|
||||
{{ with $bio_content }}
|
||||
<div class="mb-12">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
@@ -145,11 +290,18 @@
|
||||
</div>
|
||||
{{ $i18n_about := i18n "about_me" }}
|
||||
{{ $about_default := cond (eq $i18n_about "about_me") "Professional Summary" $i18n_about }}
|
||||
{{ $about_heading := $block.content.headings.about | default $about_default }}
|
||||
{{ $about_heading := $about_default }}
|
||||
{{ $about_heading_raw := index $headings "about" }}
|
||||
{{ if eq (printf "%T" $about_heading_raw) "string" }}
|
||||
{{ $about_heading_raw = strings.TrimSpace $about_heading_raw }}
|
||||
{{ if ne $about_heading_raw "" }}
|
||||
{{ $about_heading = $about_heading_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<h2 class="text-xl sm:text-2xl lg:text-3xl font-bold text-gray-900 dark:text-white tracking-tight">{{ $about_heading }}</h2>
|
||||
</div>
|
||||
<div class="prose prose-lg dark:prose-invert text-gray-800 dark:text-gray-200 text-base sm:text-lg leading-relaxed">
|
||||
<div class="bio-text" {{ with $block.design.biography.style }}{{ (printf "style=\"%s\"" .) | safe.HTMLAttr }}{{end}}>
|
||||
<div class="bio-text" {{ if $bio_style }}{{ (printf "style=\"%s\"" $bio_style) | safeHTMLAttr }}{{ end }}>
|
||||
{{ . }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -157,78 +309,148 @@
|
||||
{{ end }}
|
||||
|
||||
{{/* CV Download Button */}}
|
||||
{{ with $block.content.button }}
|
||||
<div class="mb-16">
|
||||
<a href="{{.url}}" target="_blank"
|
||||
class="inline-flex items-center px-8 py-4 bg-gradient-to-r from-primary-600 to-secondary-600 hover:from-primary-700 hover:to-secondary-700 text-white font-bold text-lg rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-300">
|
||||
{{ partial "functions/get_icon" (dict "name" "document-arrow-down" "attributes" "class='w-5 h-5 mr-3'") }}
|
||||
{{.text}}
|
||||
</a>
|
||||
</div>
|
||||
{{ $button_raw := index $content "button" }}
|
||||
{{ if and $button_raw (reflect.IsMap $button_raw) }}
|
||||
{{ $button_text := "" }}
|
||||
{{ $button_url := "" }}
|
||||
{{ with index $button_raw "text" }}{{ $button_text = printf "%v" . }}{{ end }}
|
||||
{{ with index $button_raw "url" }}{{ $button_url = printf "%v" . }}{{ end }}
|
||||
{{ $button_text = strings.TrimSpace $button_text }}
|
||||
{{ $button_url = strings.TrimSpace $button_url }}
|
||||
{{ if and $button_text $button_url }}
|
||||
{{ $button_link := $button_url }}
|
||||
{{ $scheme := (urls.Parse $button_link).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $button_link "#") }}
|
||||
{{ $button_link = $button_link | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $button_link) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<div class="mb-16">
|
||||
<a href="{{ $button_link | safeURL }}" {{ $target | safeHTMLAttr }}
|
||||
class="inline-flex items-center px-8 py-4 bg-gradient-to-r from-primary-600 to-secondary-600 hover:from-primary-700 hover:to-secondary-700 text-white font-bold text-lg rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-300">
|
||||
{{ partial "functions/get_icon" (dict "name" "document-arrow-down" "attributes" "class='w-5 h-5 mr-3'") }}
|
||||
{{ $button_text }}
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Education Section */}}
|
||||
{{ with $profile.education }}
|
||||
{{ $education_raw := $profile.education }}
|
||||
{{ $education := slice }}
|
||||
{{ if reflect.IsSlice $education_raw }}
|
||||
{{ $education = $education_raw }}
|
||||
{{ end }}
|
||||
{{ if gt (len $education) 0 }}
|
||||
<div class="mb-16">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="flex-shrink-0 w-12 h-12 bg-primary-100 dark:bg-primary-900/50 rounded-full flex items-center justify-center">
|
||||
{{ partial "functions/get_icon" (dict "name" "academic-cap" "attributes" "class='w-6 h-6 text-primary-600 dark:text-primary-400'") }}
|
||||
</div>
|
||||
{{ $education_heading := $block.content.headings.education | default (i18n "education") }}
|
||||
{{ $education_heading := i18n "education" }}
|
||||
{{ $education_heading_raw := index $headings "education" }}
|
||||
{{ if eq (printf "%T" $education_heading_raw) "string" }}
|
||||
{{ $education_heading_raw = strings.TrimSpace $education_heading_raw }}
|
||||
{{ if ne $education_heading_raw "" }}
|
||||
{{ $education_heading = $education_heading_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<h3 class="text-xl sm:text-2xl lg:text-3xl font-bold text-gray-900 dark:text-white tracking-tight">{{ $education_heading | markdownify }}</h3>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
{{ range . }}
|
||||
{{ range $education }}
|
||||
{{ if reflect.IsMap . }}
|
||||
{{ $degree := "" }}
|
||||
{{ $degreeRaw := index . "degree" | default (index . "area") }}
|
||||
{{ if ne $degreeRaw nil }}{{ $degree = printf "%v" $degreeRaw }}{{ end }}
|
||||
{{ $year := "" }}
|
||||
{{ with index . "year" }}{{ $year = printf "%v" . }}{{ end }}
|
||||
{{ $start := "" }}
|
||||
{{ with index . "start" }}{{ $start = printf "%v" . }}{{ end }}
|
||||
{{ $end := "" }}
|
||||
{{ with index . "end" }}{{ $end = printf "%v" . }}{{ end }}
|
||||
{{ $institution := "" }}
|
||||
{{ with index . "institution" }}{{ $institution = printf "%v" . }}{{ end }}
|
||||
{{ $icon := "" }}
|
||||
{{ $iconRaw := index . "icon" }}
|
||||
{{ if eq (printf "%T" $iconRaw) "string" }}
|
||||
{{ $icon = $iconRaw }}
|
||||
{{ end }}
|
||||
<div class="group h-full flex flex-col bg-gradient-to-br from-white/90 to-primary-50/30 dark:from-gray-800/90 dark:to-primary-900/20 rounded-xl p-6 shadow-md hover:shadow-xl transition-all duration-300 border border-transparent hover:border-primary-200 dark:hover:border-primary-800 backdrop-blur-md">
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-shrink-0 w-12 h-12 bg-white dark:bg-gray-800 ring-1 ring-gray-200 dark:ring-gray-700 rounded-full flex items-center justify-center shadow-md">
|
||||
{{ if .icon }}
|
||||
{{ partial "functions/get_icon" (dict "name" .icon "attributes" "class='w-6 h-6 text-primary-600 dark:text-primary-400'") }}
|
||||
{{else}}
|
||||
{{ if $icon }}
|
||||
{{ partial "functions/get_icon" (dict "name" $icon "attributes" "class='w-6 h-6 text-primary-600 dark:text-primary-400'") }}
|
||||
{{ else }}
|
||||
{{ partial "functions/get_icon" (dict "name" "academic-cap" "attributes" "class='w-6 h-6 text-primary-600 dark:text-primary-400'") }}
|
||||
{{end}}
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="flex-1 flex-grow">
|
||||
{{ $degree := .degree | default .area }}
|
||||
{{ if $degree }}
|
||||
<p class="text-xl font-bold text-gray-900 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
|
||||
{{ $degree }}
|
||||
</p>
|
||||
{{ if .year }}
|
||||
<p class="text-gray-500 dark:text-gray-400 text-sm mt-2">{{ .year }}</p>
|
||||
{{ else if .start }}
|
||||
{{ end }}
|
||||
{{ if $year }}
|
||||
<p class="text-gray-500 dark:text-gray-400 text-sm mt-2">{{ $year }}</p>
|
||||
{{ else if $start }}
|
||||
<p class="text-gray-500 dark:text-gray-400 text-sm mt-2">
|
||||
{{ .start }}
|
||||
{{ if .end }}<br>{{ .end }}{{ end }}
|
||||
{{ $start }}
|
||||
{{ if $end }}<br>{{ $end }}{{ end }}
|
||||
</p>
|
||||
{{ end }}
|
||||
<p class="text-gray-700 dark:text-gray-300 text-base">{{ .institution }}</p>
|
||||
{{ if $institution }}
|
||||
<p class="text-gray-700 dark:text-gray-300 text-base">{{ $institution }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{/* Interests Section */}}
|
||||
{{ with $profile.interests }}
|
||||
{{ $interests_raw := $profile.interests }}
|
||||
{{ $interests := slice }}
|
||||
{{ if reflect.IsSlice $interests_raw }}
|
||||
{{ $interests = $interests_raw }}
|
||||
{{ else if eq (printf "%T" $interests_raw) "string" }}
|
||||
{{ $interests = slice $interests_raw }}
|
||||
{{ end }}
|
||||
{{ if gt (len $interests) 0 }}
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
<div class="flex-shrink-0 w-12 h-12 bg-primary-100 dark:bg-primary-900/50 rounded-full flex items-center justify-center">
|
||||
{{ partial "functions/get_icon" (dict "name" "sparkles" "attributes" "class='w-6 h-6 text-primary-600 dark:text-primary-400'") }}
|
||||
</div>
|
||||
{{ $interests_heading := $block.content.headings.interests | default (i18n "interests") }}
|
||||
{{ $interests_heading := i18n "interests" }}
|
||||
{{ $interests_heading_raw := index $headings "interests" }}
|
||||
{{ if eq (printf "%T" $interests_heading_raw) "string" }}
|
||||
{{ $interests_heading_raw = strings.TrimSpace $interests_heading_raw }}
|
||||
{{ if ne $interests_heading_raw "" }}
|
||||
{{ $interests_heading = $interests_heading_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white tracking-tight">{{ $interests_heading | markdownify }}</h3>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
{{ range . }}
|
||||
{{ range $interests }}
|
||||
{{ $interest := strings.TrimSpace (printf "%v" .) }}
|
||||
{{ if $interest }}
|
||||
<span class="inline-block bg-primary-50 dark:bg-gray-800 text-primary-800 dark:text-primary-200 text-base font-medium px-4 py-2 rounded-full border border-primary-200/50 dark:border-primary-800/50
|
||||
hover:bg-primary-100
|
||||
dark:hover:bg-primary-500 dark:hover:text-gray-900 dark:hover:border-primary-500
|
||||
transition-all duration-200 cursor-default">
|
||||
{{ . | markdownify | emojify }}
|
||||
{{ $interest | markdownify | emojify }}
|
||||
</span>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
@@ -5,16 +5,38 @@
|
||||
{{/* Initialise */}}
|
||||
{{ $page := .wcPage }}
|
||||
{{ $block := .wcBlock }}
|
||||
{{ $show_social := $block.design.show_social | default true }}
|
||||
{{ $show_interests := $block.design.show_interests | default false }}
|
||||
{{ $show_role := $block.design.show_role | default true }}
|
||||
{{ $show_organizations := $block.design.show_organizations | default true }}
|
||||
{{ $max_interests := $block.design.max_interests | default 3 }}
|
||||
{{ $align := $block.design.align | default "center" }}
|
||||
{{ $max_columns := $block.design.max_columns | default 4 }}
|
||||
{{ $show_empty_groups := $block.design.show_empty_groups | default false }}
|
||||
{{ $sort_field := $block.content.sort_by | default "name_family" }}
|
||||
{{ $sort_asc := $block.content.sort_ascending | default true }}
|
||||
|
||||
{{ $content := $block.content }}
|
||||
{{ if not (reflect.IsMap $content) }}{{ $content = dict }}{{ end }}
|
||||
{{ $design := $block.design }}
|
||||
{{ if not (reflect.IsMap $design) }}{{ $design = dict }}{{ end }}
|
||||
|
||||
{{ $show_social := partial "functions/coerce_bool" (dict "value" (index $design "show_social") "default" true) }}
|
||||
{{ $show_interests := partial "functions/coerce_bool" (dict "value" (index $design "show_interests") "default" false) }}
|
||||
{{ $show_role := partial "functions/coerce_bool" (dict "value" (index $design "show_role") "default" true) }}
|
||||
{{ $show_organizations := partial "functions/coerce_bool" (dict "value" (index $design "show_organizations") "default" true) }}
|
||||
{{ $show_empty_groups := partial "functions/coerce_bool" (dict "value" (index $design "show_empty_groups") "default" false) }}
|
||||
{{ $max_interests_int := partial "functions/coerce_int" (dict "value" (index $design "max_interests") "default" 3 "min" 0) }}
|
||||
{{ $max_columns_int := partial "functions/coerce_int" (dict "value" (index $design "max_columns") "default" 4 "min" 2 "max" 4) }}
|
||||
|
||||
{{ $align := "center" }}
|
||||
{{ $align_raw := index $design "align" }}
|
||||
{{ if eq (printf "%T" $align_raw) "string" }}
|
||||
{{ $align_candidate := lower (strings.TrimSpace $align_raw) }}
|
||||
{{ if in (slice "center" "left") $align_candidate }}
|
||||
{{ $align = $align_candidate }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $sort_field := "name_family" }}
|
||||
{{ $sort_field_raw := index $content "sort_by" }}
|
||||
{{ if eq (printf "%T" $sort_field_raw) "string" }}
|
||||
{{ $sort_field_raw = strings.TrimSpace $sort_field_raw }}
|
||||
{{ if ne $sort_field_raw "" }}
|
||||
{{ $sort_field = $sort_field_raw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $sort_asc := partial "functions/coerce_bool" (dict "value" (index $content "sort_ascending") "default" true) }}
|
||||
{{ $sort_field_lc := lower $sort_field }}
|
||||
{{ if hasPrefix $sort_field_lc "params." }}
|
||||
{{/* Backwards compatibility with Params.* used when authors were pages */}}
|
||||
@@ -22,36 +44,15 @@
|
||||
{{ $sort_field_lc = lower $sort_field }}
|
||||
{{ end }}
|
||||
|
||||
{{ $title := $block.content.title | emojify | $page.RenderString }}
|
||||
{{ $subtitle := $block.content.subtitle | emojify | $page.RenderString }}
|
||||
{{ $text := $block.content.text | emojify | $page.RenderString }}
|
||||
{{ $title := "" }}
|
||||
{{ with index $content "title" }}{{ $title = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
{{ $subtitle := "" }}
|
||||
{{ with index $content "subtitle" }}{{ $subtitle = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
{{ $text := "" }}
|
||||
{{ with index $content "text" }}{{ $text = printf "%v" . | emojify | $page.RenderString }}{{ end }}
|
||||
{{ $alignClass := cond (eq $align "left") "items-start text-left" "items-center text-center" }}
|
||||
{{ $ctaAlignClass := cond (eq $align "left") "text-left" "text-center" }}
|
||||
|
||||
{{/* Safe numeric handling with clamping using type checks */}}
|
||||
{{ $max_columns_int := 4 }}
|
||||
{{ $max_columns_type := printf "%T" $max_columns }}
|
||||
{{ if or (eq $max_columns_type "int") (eq $max_columns_type "int64") (eq $max_columns_type "float64") (eq $max_columns_type "float32") (eq $max_columns_type "uint") (eq $max_columns_type "uint64") }}
|
||||
{{ $max_columns_int = int $max_columns }}
|
||||
{{ else if eq $max_columns_type "string" }}
|
||||
{{ if gt (len (findRE "^[0-9]+$" $max_columns)) 0 }}
|
||||
{{ $max_columns_int = int $max_columns }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if lt $max_columns_int 2 }}{{ $max_columns_int = 2 }}{{ end }}
|
||||
{{ if gt $max_columns_int 4 }}{{ $max_columns_int = 4 }}{{ end }}
|
||||
|
||||
{{ $max_interests_int := 3 }}
|
||||
{{ $max_interests_type := printf "%T" $max_interests }}
|
||||
{{ if or (eq $max_interests_type "int") (eq $max_interests_type "int64") (eq $max_interests_type "float64") (eq $max_interests_type "float32") (eq $max_interests_type "uint") (eq $max_interests_type "uint64") }}
|
||||
{{ $max_interests_int = int $max_interests }}
|
||||
{{ else if eq $max_interests_type "string" }}
|
||||
{{ if gt (len (findRE "^[0-9]+$" $max_interests)) 0 }}
|
||||
{{ $max_interests_int = int $max_interests }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if lt $max_interests_int 0 }}{{ $max_interests_int = 0 }}{{ end }}
|
||||
|
||||
{{ $columnsClasses := dict
|
||||
"2" "grid grid-cols-1 gap-8 sm:grid-cols-2"
|
||||
"3" "grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3"
|
||||
@@ -91,9 +92,17 @@
|
||||
{{ $allProfiles = $allProfiles | append (dict "profile" $profile "profileData" $profileData) }}
|
||||
{{ end }}
|
||||
|
||||
{{ $groups := $block.content.user_groups }}
|
||||
{{ $groups_raw := index $content "user_groups" }}
|
||||
{{ $groups := slice }}
|
||||
{{ if reflect.IsSlice $groups_raw }}
|
||||
{{ $groups = $groups_raw }}
|
||||
{{ else if eq (printf "%T" $groups_raw) "string" }}
|
||||
{{ $groups = slice $groups_raw }}
|
||||
{{ else if and $groups_raw (reflect.IsMap $groups_raw) }}
|
||||
{{ $groups = slice $groups_raw }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $groups }}
|
||||
{{ if gt (len $groups) 0 }}
|
||||
{{ range $i, $groupItem := $groups }}
|
||||
{{ $groupName := "" }}
|
||||
{{ $groupSortField := $sort_field }}
|
||||
@@ -101,12 +110,22 @@
|
||||
{{ $groupSortFieldLc := "" }}
|
||||
{{ $itemIsMap := reflect.IsMap $groupItem }}
|
||||
{{ if $itemIsMap }}
|
||||
{{ $groupName = index $groupItem "name" | default (index $groupItem "title") | default "" }}
|
||||
{{ $groupSortField = index $groupItem "sort_by" | default $groupSortField }}
|
||||
{{ $groupSortAsc = index $groupItem "sort_ascending" | default $groupSortAsc }}
|
||||
{{ $groupNameRaw := index $groupItem "name" | default (index $groupItem "title") }}
|
||||
{{ if ne $groupNameRaw nil }}
|
||||
{{ $groupName = printf "%v" $groupNameRaw }}
|
||||
{{ end }}
|
||||
{{ $groupSortFieldRaw := index $groupItem "sort_by" }}
|
||||
{{ if eq (printf "%T" $groupSortFieldRaw) "string" }}
|
||||
{{ $groupSortFieldRaw = strings.TrimSpace $groupSortFieldRaw }}
|
||||
{{ if ne $groupSortFieldRaw "" }}
|
||||
{{ $groupSortField = $groupSortFieldRaw }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $groupSortAsc = partial "functions/coerce_bool" (dict "value" (index $groupItem "sort_ascending") "default" $groupSortAsc) }}
|
||||
{{ else }}
|
||||
{{ $groupName = $groupItem }}
|
||||
{{ $groupName = printf "%v" $groupItem }}
|
||||
{{ end }}
|
||||
{{ $groupName = strings.TrimSpace $groupName }}
|
||||
{{ if eq (trim $groupName " ") "" }}
|
||||
{{/* Skip groups with empty names to avoid matching everything */}}
|
||||
{{ continue }}
|
||||
@@ -122,10 +141,17 @@
|
||||
{{ $groupProfiles := slice }}
|
||||
{{ range $allProfiles }}
|
||||
{{ $profile := .profile }}
|
||||
{{ $uRaw := $profile.user_groups | default (slice) }}
|
||||
{{ $u := slice }}
|
||||
{{ range $uRaw }}
|
||||
{{ $u = $u | append (printf "%v" .) }}
|
||||
{{ $uRaw := $profile.user_groups }}
|
||||
{{ if reflect.IsSlice $uRaw }}
|
||||
{{ range $uRaw }}
|
||||
{{ $u = $u | append (printf "%v" .) }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" $uRaw) "string" }}
|
||||
{{ $trimmedGroup := strings.TrimSpace $uRaw }}
|
||||
{{ if ne $trimmedGroup "" }}
|
||||
{{ $u = slice $trimmedGroup }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if gt (len (intersect $u (slice (printf "%v" $groupName)))) 0 }}
|
||||
{{ $profileData := .profileData }}
|
||||
@@ -212,37 +238,67 @@
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
{{ $affiliations_raw := $profile.affiliations }}
|
||||
{{ $affiliations := slice }}
|
||||
{{ if reflect.IsSlice $affiliations_raw }}
|
||||
{{ $affiliations = $affiliations_raw }}
|
||||
{{ else if eq (printf "%T" $affiliations_raw) "string" }}
|
||||
{{ $affiliations = slice $affiliations_raw }}
|
||||
{{ end }}
|
||||
{{ $first_affiliation := "" }}
|
||||
{{ if gt (len $affiliations) 0 }}
|
||||
{{ $aff := index $affiliations 0 }}
|
||||
{{ if reflect.IsMap $aff }}
|
||||
{{ $affNameRaw := index $aff "name" | default (index $aff "title") }}
|
||||
{{ if ne $affNameRaw nil }}
|
||||
{{ $first_affiliation = printf "%v" $affNameRaw }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" $aff) "string" }}
|
||||
{{ $first_affiliation = $aff }}
|
||||
{{ end }}
|
||||
{{ $first_affiliation = strings.TrimSpace $first_affiliation }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $show_role }}
|
||||
<div class="text-gray-600 dark:text-gray-400 mb-3">
|
||||
{{ $roleText := "" }}
|
||||
{{ with $profile.role }}{{ $roleText = . }}{{ end }}
|
||||
{{ if and (not $roleText) $profile.affiliations }}
|
||||
{{ range first 1 $profile.affiliations }}
|
||||
{{ $roleText = .name }}
|
||||
{{ with $profile.role }}
|
||||
{{ if eq (printf "%T" .) "string" }}
|
||||
{{ $roleText = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if and (not $roleText) $first_affiliation }}
|
||||
{{ $roleText = $first_affiliation }}
|
||||
{{ end }}
|
||||
{{ with $roleText }}<p class="font-medium">{{ . }}</p>{{ end }}
|
||||
{{ if and $show_organizations $profile.affiliations }}
|
||||
{{ range first 1 $profile.affiliations }}
|
||||
<p class="text-sm mt-1">{{ .name }}</p>
|
||||
{{ end }}
|
||||
{{ if and $show_organizations $first_affiliation }}
|
||||
<p class="text-sm mt-1">{{ $first_affiliation }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ with $profile.bio }}
|
||||
{{ $bio := "" }}
|
||||
{{ with $profile.bio }}{{ $bio = printf "%v" . }}{{ end }}
|
||||
{{ with $bio }}
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">
|
||||
{{ . }}
|
||||
</p>
|
||||
{{ end }}
|
||||
|
||||
{{ if and $show_interests $profile.interests }}
|
||||
{{ $interests_raw := $profile.interests }}
|
||||
{{ $interests := slice }}
|
||||
{{ if reflect.IsSlice $interests_raw }}
|
||||
{{ $interests = $interests_raw }}
|
||||
{{ else if eq (printf "%T" $interests_raw) "string" }}
|
||||
{{ $interests = slice $interests_raw }}
|
||||
{{ end }}
|
||||
{{ if and $show_interests (gt (len $interests) 0) }}
|
||||
<div class="mb-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{{ if gt $max_interests_int 0 }}
|
||||
{{ range first $max_interests_int $profile.interests }}
|
||||
{{ range first $max_interests_int $interests }}
|
||||
<span class="inline-block bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs px-2 py-1 rounded">
|
||||
{{ . }}
|
||||
{{ printf "%v" . }}
|
||||
</span>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -250,26 +306,55 @@
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ if and $show_social $profile.links }}
|
||||
{{ $links_raw := $profile.links }}
|
||||
{{ $links := slice }}
|
||||
{{ if reflect.IsSlice $links_raw }}
|
||||
{{ $links = $links_raw }}
|
||||
{{ else if eq (printf "%T" $links_raw) "string" }}
|
||||
{{ $links = slice $links_raw }}
|
||||
{{ end }}
|
||||
{{ if and $show_social (gt (len $links) 0) }}
|
||||
<div class="flex gap-3 pt-2 border-t border-gray-200 dark:border-gray-700">
|
||||
{{ range $profile.links }}
|
||||
{{ $l := .url | default .link }}
|
||||
{{ $scheme := (urls.Parse $l).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ $l = (.url | default .link) | relLangURL }}
|
||||
{{ if eq (path.Ext $l) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
|
||||
<a href="{{ $l | safeURL }}"
|
||||
{{ $target | safeHTMLAttr }}
|
||||
aria-label="{{ .icon }}"
|
||||
{{ with .label }} title="{{ . }}"{{ end }}
|
||||
class="text-gray-500 hover:text-primary-600 dark:text-gray-400 dark:hover:text-primary-400 transition-colors">
|
||||
{{ partial "functions/get_icon" (dict "name" .icon "attributes" "class=\"w-5 h-5\"") }}
|
||||
</a>
|
||||
{{ range $links }}
|
||||
{{ $linkUrl := "" }}
|
||||
{{ $iconName := "hero/link" }}
|
||||
{{ $linkLabel := "" }}
|
||||
{{ if reflect.IsMap . }}
|
||||
{{ $urlRaw := index . "url" | default (index . "link") }}
|
||||
{{ if eq (printf "%T" $urlRaw) "string" }}
|
||||
{{ $linkUrl = $urlRaw }}
|
||||
{{ end }}
|
||||
{{ $iconRaw := index . "icon" }}
|
||||
{{ if eq (printf "%T" $iconRaw) "string" }}
|
||||
{{ $iconName = $iconRaw }}
|
||||
{{ end }}
|
||||
{{ $labelRaw := index . "label" }}
|
||||
{{ if eq (printf "%T" $labelRaw) "string" }}
|
||||
{{ $linkLabel = $labelRaw }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" .) "string" }}
|
||||
{{ $linkUrl = . }}
|
||||
{{ end }}
|
||||
{{ $linkUrl = strings.TrimSpace $linkUrl }}
|
||||
{{ if $linkUrl }}
|
||||
{{ $scheme := (urls.Parse $linkUrl).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $linkUrl "#") }}
|
||||
{{ $linkUrl = $linkUrl | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $linkUrl) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<a href="{{ $linkUrl | safeURL }}"
|
||||
{{ $target | safeHTMLAttr }}
|
||||
aria-label="{{ $iconName }}"
|
||||
{{ if $linkLabel }} title="{{ $linkLabel }}"{{ end }}
|
||||
class="text-gray-500 hover:text-primary-600 dark:text-gray-400 dark:hover:text-primary-400 transition-colors">
|
||||
{{ partial "functions/get_icon" (dict "name" $iconName "attributes" "class=\"w-5 h-5\"") }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -347,45 +432,115 @@
|
||||
{{ $profile.title }}
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
{{ $affiliations_raw := $profile.affiliations }}
|
||||
{{ $affiliations := slice }}
|
||||
{{ if reflect.IsSlice $affiliations_raw }}
|
||||
{{ $affiliations = $affiliations_raw }}
|
||||
{{ else if eq (printf "%T" $affiliations_raw) "string" }}
|
||||
{{ $affiliations = slice $affiliations_raw }}
|
||||
{{ end }}
|
||||
{{ $first_affiliation := "" }}
|
||||
{{ if gt (len $affiliations) 0 }}
|
||||
{{ $aff := index $affiliations 0 }}
|
||||
{{ if reflect.IsMap $aff }}
|
||||
{{ $affNameRaw := index $aff "name" | default (index $aff "title") }}
|
||||
{{ if ne $affNameRaw nil }}
|
||||
{{ $first_affiliation = printf "%v" $affNameRaw }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" $aff) "string" }}
|
||||
{{ $first_affiliation = $aff }}
|
||||
{{ end }}
|
||||
{{ $first_affiliation = strings.TrimSpace $first_affiliation }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $show_role }}
|
||||
<div class="text-gray-600 dark:text-gray-400 mb-3">
|
||||
{{ with $profile.role }}<p class="font-medium">{{ . }}</p>{{ end }}
|
||||
{{ if and $show_organizations $profile.affiliations }}
|
||||
{{ range first 1 $profile.affiliations }}
|
||||
<p class="text-sm mt-1">{{ .name }}</p>
|
||||
{{ $roleText := "" }}
|
||||
{{ with $profile.role }}
|
||||
{{ if eq (printf "%T" .) "string" }}
|
||||
{{ $roleText = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if and (not $roleText) $first_affiliation }}
|
||||
{{ $roleText = $first_affiliation }}
|
||||
{{ end }}
|
||||
{{ with $roleText }}<p class="font-medium">{{ . }}</p>{{ end }}
|
||||
{{ if and $show_organizations $first_affiliation }}
|
||||
<p class="text-sm mt-1">{{ $first_affiliation }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ with $profile.bio }}
|
||||
{{ $bio := "" }}
|
||||
{{ with $profile.bio }}{{ $bio = printf "%v" . }}{{ end }}
|
||||
{{ with $bio }}
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">{{ . }}</p>
|
||||
{{ end }}
|
||||
{{ if and $show_interests $profile.interests }}
|
||||
|
||||
{{ $interests_raw := $profile.interests }}
|
||||
{{ $interests := slice }}
|
||||
{{ if reflect.IsSlice $interests_raw }}
|
||||
{{ $interests = $interests_raw }}
|
||||
{{ else if eq (printf "%T" $interests_raw) "string" }}
|
||||
{{ $interests = slice $interests_raw }}
|
||||
{{ end }}
|
||||
{{ if and $show_interests (gt (len $interests) 0) }}
|
||||
<div class="mb-4">
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{{ if gt $max_interests_int 0 }}
|
||||
{{ range first $max_interests_int $profile.interests }}
|
||||
<span class="inline-block bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs px-2 py-1 rounded">{{ . }}</span>
|
||||
{{ range first $max_interests_int $interests }}
|
||||
<span class="inline-block bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs px-2 py-1 rounded">{{ printf "%v" . }}</span>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if and $show_social $profile.links }}
|
||||
|
||||
{{ $links_raw := $profile.links }}
|
||||
{{ $links := slice }}
|
||||
{{ if reflect.IsSlice $links_raw }}
|
||||
{{ $links = $links_raw }}
|
||||
{{ else if eq (printf "%T" $links_raw) "string" }}
|
||||
{{ $links = slice $links_raw }}
|
||||
{{ end }}
|
||||
{{ if and $show_social (gt (len $links) 0) }}
|
||||
<div class="flex gap-3 pt-2 border-t border-gray-200 dark:border-gray-700">
|
||||
{{ range $profile.links }}
|
||||
{{ $l := .url | default .link }}
|
||||
{{ $scheme := (urls.Parse $l).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ $l = (.url | default .link) | relLangURL }}
|
||||
{{ if eq (path.Ext $l) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<a href="{{ $l | safeURL }}" {{ $target | safeHTMLAttr }} aria-label="{{ .icon }}" {{ with .label }} title="{{ . }}"{{ end }} class="text-gray-500 hover:text-primary-600 dark:text-gray-400 dark:hover:text-primary-400 transition-colors">
|
||||
{{ partial "functions/get_icon" (dict "name" .icon "attributes" "class=\"w-5 h-5\"") }}
|
||||
</a>
|
||||
{{ range $links }}
|
||||
{{ $linkUrl := "" }}
|
||||
{{ $iconName := "hero/link" }}
|
||||
{{ $linkLabel := "" }}
|
||||
{{ if reflect.IsMap . }}
|
||||
{{ $urlRaw := index . "url" | default (index . "link") }}
|
||||
{{ if eq (printf "%T" $urlRaw) "string" }}
|
||||
{{ $linkUrl = $urlRaw }}
|
||||
{{ end }}
|
||||
{{ $iconRaw := index . "icon" }}
|
||||
{{ if eq (printf "%T" $iconRaw) "string" }}
|
||||
{{ $iconName = $iconRaw }}
|
||||
{{ end }}
|
||||
{{ $labelRaw := index . "label" }}
|
||||
{{ if eq (printf "%T" $labelRaw) "string" }}
|
||||
{{ $linkLabel = $labelRaw }}
|
||||
{{ end }}
|
||||
{{ else if eq (printf "%T" .) "string" }}
|
||||
{{ $linkUrl = . }}
|
||||
{{ end }}
|
||||
{{ $linkUrl = strings.TrimSpace $linkUrl }}
|
||||
{{ if $linkUrl }}
|
||||
{{ $scheme := (urls.Parse $linkUrl).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $linkUrl "#") }}
|
||||
{{ $linkUrl = $linkUrl | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $linkUrl) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<a href="{{ $linkUrl | safeURL }}" {{ $target | safeHTMLAttr }} aria-label="{{ $iconName }}" {{ if $linkLabel }} title="{{ $linkLabel }}"{{ end }} class="text-gray-500 hover:text-primary-600 dark:text-gray-400 dark:hover:text-primary-400 transition-colors">
|
||||
{{ partial "functions/get_icon" (dict "name" $iconName "attributes" "class=\"w-5 h-5\"") }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -397,15 +552,41 @@
|
||||
{{ end }}
|
||||
|
||||
{{/* Call to Action */}}
|
||||
{{ with $block.content.cta }}
|
||||
<div class="mt-12 {{ $ctaAlignClass }}">
|
||||
<a href="{{ .url }}" class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transition-all duration-300">
|
||||
{{ .text }}
|
||||
{{ with .icon }}
|
||||
{{ partial "functions/get_icon" (dict "name" . "attributes" "class=\"ml-2 w-5 h-5\"") }}
|
||||
{{ $cta_raw := index $content "cta" }}
|
||||
{{ if and $cta_raw (reflect.IsMap $cta_raw) }}
|
||||
{{ $cta_text := "" }}
|
||||
{{ $cta_url := "" }}
|
||||
{{ $cta_icon := "" }}
|
||||
{{ with index $cta_raw "text" }}{{ $cta_text = printf "%v" . }}{{ end }}
|
||||
{{ with index $cta_raw "url" }}{{ $cta_url = printf "%v" . }}{{ end }}
|
||||
{{ with index $cta_raw "icon" }}
|
||||
{{ if eq (printf "%T" .) "string" }}
|
||||
{{ $cta_icon = . }}
|
||||
{{ end }}
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ $cta_text = strings.TrimSpace $cta_text }}
|
||||
{{ $cta_url = strings.TrimSpace $cta_url }}
|
||||
{{ if and $cta_text $cta_url }}
|
||||
{{ $cta_link := $cta_url }}
|
||||
{{ $scheme := (urls.Parse $cta_link).Scheme }}
|
||||
{{ $target := "" }}
|
||||
{{ if not $scheme }}
|
||||
{{ if not (hasPrefix $cta_link "#") }}
|
||||
{{ $cta_link = $cta_link | relLangURL }}
|
||||
{{ end }}
|
||||
{{ if eq (path.Ext $cta_link) ".pdf" }}{{ $target = "target=\"_blank\" rel=\"noopener\"" }}{{ end }}
|
||||
{{ else if in (slice "http" "https") $scheme }}
|
||||
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
|
||||
{{ end }}
|
||||
<div class="mt-12 {{ $ctaAlignClass }}">
|
||||
<a href="{{ $cta_link | safeURL }}" {{ $target | safeHTMLAttr }} class="inline-flex items-center px-6 py-3 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transition-all duration-300">
|
||||
{{ $cta_text }}
|
||||
{{ if $cta_icon }}
|
||||
{{ partial "functions/get_icon" (dict "name" $cta_icon "attributes" "class=\"ml-2 w-5 h-5\"") }}
|
||||
{{ end }}
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
@@ -108,7 +108,7 @@
|
||||
},
|
||||
"show_interests": {
|
||||
"type": "boolean",
|
||||
"description": "Display up to three interests per profile",
|
||||
"description": "Display interests per profile (capped by max_interests)",
|
||||
"default": false
|
||||
},
|
||||
"show_social": {
|
||||
@@ -152,7 +152,13 @@
|
||||
"title": "Meet the team",
|
||||
"subtitle": "Research + Engineering",
|
||||
"text": "We combine safety research with high-velocity engineering.",
|
||||
"user_groups": ["Principal Investigators", "PhD Students"],
|
||||
"user_groups": [
|
||||
"Principal Investigators",
|
||||
"Research Engineers",
|
||||
{ "name": "Alumni", "sort_by": "graduation_year", "sort_ascending": false }
|
||||
],
|
||||
"sort_by": "name_family",
|
||||
"sort_ascending": true,
|
||||
"cta": {
|
||||
"text": "Join us",
|
||||
"url": "/apply",
|
||||
@@ -162,8 +168,12 @@
|
||||
"design": {
|
||||
"show_role": true,
|
||||
"show_organizations": true,
|
||||
"show_interests": false,
|
||||
"show_social": true
|
||||
"show_interests": true,
|
||||
"max_interests": 3,
|
||||
"show_social": true,
|
||||
"align": "left",
|
||||
"max_columns": 3,
|
||||
"show_empty_groups": false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{{ $value := .value }}
|
||||
{{ $result := .default }}
|
||||
{{ if ne $value nil }}
|
||||
{{ $valueType := printf "%T" $value }}
|
||||
{{ if eq $valueType "bool" }}
|
||||
{{ $result = $value }}
|
||||
{{ else if or (eq $valueType "int") (eq $valueType "int8") (eq $valueType "int16") (eq $valueType "int32") (eq $valueType "int64") (eq $valueType "uint") (eq $valueType "uint8") (eq $valueType "uint16") (eq $valueType "uint32") (eq $valueType "uint64") }}
|
||||
{{ $result = ne (int $value) 0 }}
|
||||
{{ else if or (eq $valueType "float32") (eq $valueType "float64") }}
|
||||
{{ $result = ne (float $value) 0 }}
|
||||
{{ else if eq $valueType "string" }}
|
||||
{{ $trimmed := lower (strings.TrimSpace $value) }}
|
||||
{{ if in (slice "1" "true" "yes" "y" "on") $trimmed }}
|
||||
{{ $result = true }}
|
||||
{{ else if in (slice "0" "false" "no" "n" "off") $trimmed }}
|
||||
{{ $result = false }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ return $result }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{ $value := .value }}
|
||||
{{ $result := .default }}
|
||||
{{ if ne $value nil }}
|
||||
{{ $valueType := printf "%T" $value }}
|
||||
{{ if or (eq $valueType "int") (eq $valueType "int8") (eq $valueType "int16") (eq $valueType "int32") (eq $valueType "int64") (eq $valueType "uint") (eq $valueType "uint8") (eq $valueType "uint16") (eq $valueType "uint32") (eq $valueType "uint64") (eq $valueType "float32") (eq $valueType "float64") }}
|
||||
{{ $result = int $value }}
|
||||
{{ else if eq $valueType "string" }}
|
||||
{{ $trimmed := strings.TrimSpace $value }}
|
||||
{{ if gt (len (findRE "^-?[0-9]+$" $trimmed)) 0 }}
|
||||
{{ $result = int $trimmed }}
|
||||
{{ else if gt (len (findRE "^-?[0-9]*\\.[0-9]+$" $trimmed)) 0 }}
|
||||
{{ $result = int (float $trimmed) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if isset . "min" }}
|
||||
{{ if lt $result .min }}
|
||||
{{ $result = .min }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if isset . "max" }}
|
||||
{{ if gt $result .max }}
|
||||
{{ $result = .max }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ return $result }}
|
||||
Reference in new issue
Block a user