|
| 1 | +{% from "_macros/vf_basic-section.jinja" import basic_section_title, vf_basic_section_blocks %} |
| 2 | +{% from "_macros/shared/vf_section_top_rule.jinja" import vf_section_top_rule %} |
| 3 | + |
1 | 4 | # Params |
2 | | -# title_text (string) (required): Title of the rich vertical list |
3 | | -# list_item_tick_style (string) (optional): Type of list item tick styling. Options are "bullet", "tick", "number". |
4 | | -# is_flipped (boolean) (optional): Whether the list items are flipped so image is on the left and the text is on the right. Defaults to false. |
5 | | -# Slots |
6 | | -# description: Paragraph-style description content |
7 | | -# logo_section Logo section block |
8 | | -# list_item_[1-7]: List item content, assumed to be li.p-list__item |
9 | | -# image (required) |
| 5 | +# title (dict) (required): {text, link_attrs?} — rendered via basic_section_title (always renders <h2>). |
| 6 | +# items (array) (optional): Array of {type, item} dicts rendered via vf_basic_section_blocks. |
| 7 | +# Supported types: description, list, code-block, logo-block, cta-block. |
| 8 | +# Entries with any other type are silently dropped. |
| 9 | +# media (dict) (required): Media column config. Keys: |
| 10 | +# - type (string): "image" | "video". Defaults to "image". |
| 11 | +# - ratio.large (string): "16-9" | "3-2" | "1-1" | "2-3" | "auto-height". Defaults to "3-2". |
| 12 | +# - ratio.medium_small (string): "16-9" | "3-2" | "1-1". Defaults to "3-2". |
| 13 | +# - fit (string): "cover" | "contain". Defaults to "cover". |
| 14 | +# - attrs (dict): Passthrough HTML attributes for the <img> or <iframe>. |
| 15 | +# is_flipped (bool) (optional): Swap content and media columns. Defaults to false. |
| 16 | +# padding (string) (optional): "deep" | "shallow" | "default". Defaults to "default". |
| 17 | +# top_rule_variant (string) (optional): "default" | "muted". Defaults to "default". |
| 18 | +# attrs (dict) (optional): HTML attrs for the <section>. |
10 | 19 | {% macro vf_rich_vertical_list( |
11 | | - title_text, |
12 | | - list_item_tick_style="", |
13 | | - is_flipped=false |
| 20 | + title={}, |
| 21 | + items=[], |
| 22 | + media={}, |
| 23 | + is_flipped=false, |
| 24 | + padding="default", |
| 25 | + top_rule_variant="default", |
| 26 | + attrs={} |
14 | 27 | ) -%} |
15 | | - {% set description_content = caller('description') %} |
16 | | - {% set has_description = description_content|trim|length > 0 %} |
17 | | - {% set logo_section_content = caller('logo_section') %} |
18 | | - {% set has_logo_section = logo_section_content|trim|length > 0 %} |
19 | | - {% set cta_content = caller('cta') %} |
20 | | - {% set has_cta = cta_content|trim|length > 0 %} |
21 | | - {% set has_list = caller('list_item_1')|trim|length > 0 %} |
22 | | - {% set image_content = caller('image') %} |
23 | | - {% set max_list_items = 7 %} |
24 | | - |
25 | | - {% set list_item_tick_style=list_item_tick_style|trim|lower %} |
26 | | - {% if list_item_tick_style|length > 0 and list_item_tick_style not in ['bullet', 'tick', 'number'] %} |
27 | | - {% set list_item_tick_style = '' %} |
28 | | - {% endif %} |
| 28 | + {#- Normalise & validate padding -#} |
| 29 | + {%- set padding = padding | string | trim | lower -%} |
| 30 | + {%- if padding not in ['deep', 'shallow', 'default'] -%}{%- set padding = 'default' -%}{%- endif -%} |
| 31 | + {%- set padding_classes = 'p-section--' ~ padding -%} |
| 32 | + {%- if padding == 'default' -%}{%- set padding_classes = 'p-section' -%}{%- endif -%} |
29 | 33 |
|
30 | | - {% if list_item_tick_style == "bullet" %} |
31 | | - {% set list_item_tick_class = "has-bullet" %} |
32 | | - {% elif list_item_tick_style == "tick" %} |
33 | | - {% set list_item_tick_class = "is-ticked" %} |
34 | | - {% endif %} |
| 34 | + {#- Normalise & validate top_rule_variant -#} |
| 35 | + {%- set top_rule_variant = top_rule_variant | string | trim | lower -%} |
| 36 | + {%- if top_rule_variant not in ['default', 'muted'] -%}{%- set top_rule_variant = 'default' -%}{%- endif -%} |
35 | 37 |
|
36 | | - {% set list_element_type = "ul" %} |
37 | | - {% if list_item_tick_style == "number" %} |
38 | | - {% set list_element_type = "ol" %} |
39 | | - {% endif %} |
| 38 | + {#- Normalise & validate media config -#} |
| 39 | + {%- set media_type = (media.get('type', 'image') | string | trim | lower) -%} |
| 40 | + {%- if media_type not in ['image', 'video'] -%}{%- set media_type = 'image' -%}{%- endif -%} |
40 | 41 |
|
41 | | - {#- |
42 | | - Construct list of list items using caller in the top-level macro |
43 | | - The _text_column_contents macro will not have access to the caller block, so we need to extract the list items here. |
44 | | - -#} |
45 | | - {% set list_items = [] %} |
46 | | - {% if has_list %} |
47 | | - {% for list_item_index in range(1, max_list_items + 1) %} |
48 | | - {% set list_item_content = caller('list_item_' + list_item_index|string) %} |
49 | | - {% set has_list_item_content = list_item_content|trim|length > 0 %} |
50 | | - {% if has_list_item_content %} |
51 | | - {{ list_items.append(list_item_content) or ""}} |
52 | | - {% endif %} |
53 | | - {% endfor %} |
54 | | - {% endif %} |
| 42 | + {%- set media_ratio = media.get('ratio', {}) -%} |
| 43 | + {%- set media_ratio_large = (media_ratio.get('large', '3-2') | string | trim | lower) -%} |
| 44 | + {%- set valid_large_ratios = ['16-9', '3-2', '1-1', '2-3', 'auto-height'] -%} |
| 45 | + {%- if media_ratio_large not in valid_large_ratios -%}{%- set media_ratio_large = '3-2' -%}{%- endif -%} |
55 | 46 |
|
56 | | - {%- macro _text_column_contents(list_items) %} |
57 | | - {#- Mandatory title -#} |
58 | | - <div class="p-section--shallow"> |
59 | | - <h2>{{ title_text }}</h2> |
60 | | - </div> |
61 | | - |
62 | | - {%- if has_logo_section %} |
63 | | - {#- Optional logo section -#} |
64 | | - <div class="p-section--shallow"> |
65 | | - <div class="u-fixed-width"> |
66 | | - {{- logo_section_content -}} |
67 | | - </div> |
68 | | - </div> |
69 | | - {%- endif -%} |
| 47 | + {%- set media_ratio_medium_small = (media_ratio.get('medium_small', '3-2') | string | trim | lower) -%} |
| 48 | + {#- 'auto-height' is intentionally excluded — it requires side-by-side columns, but medium/small layouts stack -#} |
| 49 | + {%- set valid_medium_small_ratios = ['16-9', '3-2', '1-1'] -%} |
| 50 | + {%- if media_ratio_medium_small not in valid_medium_small_ratios -%}{%- set media_ratio_medium_small = '3-2' -%}{%- endif -%} |
70 | 51 |
|
71 | | - {%- if has_description %} |
72 | | - {#- Optional description -#} |
73 | | - <div class="p-section--shallow"> |
74 | | - {{- description_content -}} |
75 | | - </div> |
76 | | - {%- endif -%} |
| 52 | + {%- set media_fit = (media.get('fit', 'cover') | string | trim | lower) -%} |
| 53 | + {%- if media_fit not in ['cover', 'contain'] -%}{%- set media_fit = 'cover' -%}{%- endif -%} |
77 | 54 |
|
78 | | - {%- if list_items|length > 0 %} |
79 | | - {#- Optional list -#} |
80 | | - <{{ list_element_type }} class="p-list--divided"> |
81 | | - {% for list_item in list_items %} |
82 | | - <li class="p-list__item {{ list_item_tick_class }}"> |
83 | | - {{- list_item -}} |
84 | | - </li> |
85 | | - {% endfor %} |
86 | | - </{{ list_element_type }}> |
87 | | - {%- endif -%} |
| 55 | + {%- set media_attrs = media.get('attrs', {}) -%} |
| 56 | + {%- set is_auto_height = (media_ratio_large == 'auto-height') -%} |
88 | 57 |
|
89 | | - {%- if has_cta %} |
90 | | - {#- Optional CTA block -#} |
91 | | - <div class="p-cta-block"> |
92 | | - {{- cta_content -}} |
93 | | - </div> |
94 | | - {%- endif -%} |
| 58 | + {#- Constrain items to the curated allow-list. Disallowed types are silently dropped. -#} |
| 59 | + {%- set allowed_item_types = ['description', 'list', 'code-block', 'logo-block', 'cta-block'] -%} |
| 60 | + {%- set filtered_items = items | selectattr('type', 'in', allowed_item_types) | list -%} |
95 | 61 |
|
| 62 | + {%- macro _rich_list_image(ratio_large, ratio_medium_small, fit, attrs) %} |
| 63 | + {%- set is_cover = (fit == 'cover') -%} |
| 64 | + {#- The image-container CSS uses 'square' for the 1:1 ratio class, not '1-1'. -#} |
| 65 | + {%- set ratio_large_class = 'square' if ratio_large == '1-1' else ratio_large -%} |
| 66 | + {%- set ratio_medium_small_class = 'square' if ratio_medium_small == '1-1' else ratio_medium_small -%} |
| 67 | + {%- set classes = 'p-image-container--' ~ ratio_large_class ~ '-on-large' -%} |
| 68 | + {%- set classes = classes ~ ' p-image-container--' ~ ratio_medium_small_class ~ '-on-medium' -%} |
| 69 | + {%- set classes = classes ~ ' p-image-container--' ~ ratio_medium_small_class ~ '-on-small' -%} |
| 70 | + {%- if is_cover -%}{%- set classes = classes ~ ' is-cover' -%}{%- endif -%} |
| 71 | + <div class="{{ classes }}"> |
| 72 | + <img class="p-image-container__image{%- if 'class' in attrs %} {{ attrs['class'] }}{%- endif %}" |
| 73 | + {%- for attr, value in attrs.items() -%} |
| 74 | + {%- if attr != 'class' %} {{ attr }}="{{ value }}"{%- endif -%} |
| 75 | + {%- endfor -%} |
| 76 | + /> |
| 77 | + </div> |
96 | 78 | {%- endmacro -%} |
97 | 79 |
|
98 | | - {%- macro _image_column_contents() %} |
99 | | - {#- Mandatory image -#} |
100 | | - <div class="p-section--shallow"> |
101 | | - {{- image_content -}} |
| 80 | + {%- macro _rich_list_video(attrs) %} |
| 81 | + <div class="u-embedded-media"> |
| 82 | + <iframe class="u-embedded-media__element{%- if 'class' in attrs %} {{ attrs['class'] }}{%- endif %}" |
| 83 | + {%- for attr, value in attrs.items() -%} |
| 84 | + {%- if attr != 'class' %} {{ attr }}="{{ value }}"{%- endif -%} |
| 85 | + {%- endfor -%} |
| 86 | + ></iframe> |
102 | 87 | </div> |
103 | 88 | {%- endmacro -%} |
104 | 89 |
|
105 | | - <div class="p-section"> |
106 | | - <div class="grid-row--50-50-on-large"> |
107 | | - <hr> |
108 | | - {% if not is_flipped -%} |
109 | | - <div class="grid-col"> |
110 | | - {{- _text_column_contents(list_items) -}} |
111 | | - </div> |
112 | | - <div class="grid-col"> |
113 | | - {{- _image_column_contents() -}} |
114 | | - </div> |
| 90 | + {%- macro _media_column_contents( |
| 91 | + media_type, media_ratio_large, media_ratio_medium_small, media_fit, media_attrs, is_auto_height |
| 92 | + ) -%} |
| 93 | + {%- if is_auto_height -%} |
| 94 | + <div> |
| 95 | + {%- else -%} |
| 96 | + <div class="p-section--shallow"> |
| 97 | + {%- endif -%} |
| 98 | + {%- if media_type == 'video' -%} |
| 99 | + {{ _rich_list_video(media_attrs) }} |
115 | 100 | {%- else -%} |
116 | | - {#- For flipped layout, place the image contents in the first column and the text in the second column -#} |
117 | | - <div class="grid-col"> |
118 | | - {{- _image_column_contents() -}} |
119 | | - </div> |
120 | | - <div class="grid-col"> |
121 | | - {{- _text_column_contents(list_items) -}} |
122 | | - </div> |
| 101 | + {{ _rich_list_image(media_ratio_large, media_ratio_medium_small, media_fit, media_attrs) }} |
123 | 102 | {%- endif -%} |
124 | 103 | </div> |
125 | | - </div> |
| 104 | + {%- endmacro -%} |
126 | 105 |
|
| 106 | + {%- macro _content_column_contents(title, items) -%} |
| 107 | + {{ basic_section_title(title) }} |
| 108 | + {{ vf_basic_section_blocks(items=items) }} |
| 109 | + {%- endmacro -%} |
| 110 | + |
| 111 | +<section class="{{ padding_classes }}{%- if 'class' in attrs %} {{ attrs['class'] }}{%- endif -%}" |
| 112 | + {%- for attr, value in attrs.items() -%} |
| 113 | + {%- if attr != 'class' %} {{ attr }}="{{ value }}"{%- endif -%} |
| 114 | + {%- endfor -%} |
| 115 | +> |
| 116 | + <div class="grid-row--50-50-on-large"> |
| 117 | + {{ vf_section_top_rule(top_rule_variant) }} |
| 118 | + {%- if not is_flipped -%} |
| 119 | + <div class="grid-col">{{ _content_column_contents(title, filtered_items) }}</div> |
| 120 | + <div class="grid-col">{{ _media_column_contents(media_type, media_ratio_large, media_ratio_medium_small, media_fit, media_attrs, is_auto_height) }}</div> |
| 121 | + {%- else -%} |
| 122 | + <div class="grid-col">{{ _media_column_contents(media_type, media_ratio_large, media_ratio_medium_small, media_fit, media_attrs, is_auto_height) }}</div> |
| 123 | + <div class="grid-col">{{ _content_column_contents(title, filtered_items) }}</div> |
| 124 | + {%- endif -%} |
| 125 | + </div> |
| 126 | +</section> |
127 | 127 | {%- endmacro %} |
0 commit comments