vanilla-framework 4.38.0 → 4.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,240 @@
1
+ {% from "_macros/shared/vf_section_top_rule.jinja" import vf_section_top_rule %}
2
+
3
+ {#-
4
+ Params
5
+ - body
6
+ - size ("small" | "medium" | "large")
7
+ - text (str)
8
+ -#}
9
+ {%- macro _quote_wrapper_body_block(body={}) -%}
10
+ {%- set quote_size = body.get("size", "") | trim | lower -%}
11
+ {%- set quote_text = body.get("text", "") | trim -%}
12
+
13
+ {# Translate quote size param to quote heading level #}
14
+ {% if quote_size == 'large' %}
15
+ {% set quote_heading_level = 2 %}
16
+ {% elif quote_size == 'small' %}
17
+ {% set quote_heading_level = 6 %}
18
+ {% else %}
19
+ {% set quote_heading_level = 4 %}
20
+ {% endif %}
21
+
22
+ {%- if quote_text | length > 0 -%}
23
+ <p class="p-heading--{{ quote_heading_level }}">
24
+ <i>
25
+ &#8220;{{ quote_text }}&#8221;
26
+ </i>
27
+ </p>
28
+ {%- endif -%}
29
+ {%- endmacro %}
30
+
31
+ {#-
32
+ Params
33
+ - citation
34
+ - name
35
+ - organisation
36
+ - title
37
+ -#}
38
+ {%- macro _quote_wrapper_citation_block(citation={}) -%}
39
+ {%- set citation_source_name = citation.get("name", "") | trim -%}
40
+ {%- set citation_source_title = citation.get("title", "") | trim -%}
41
+ {%- set citation_source_organisation = citation.get("organisation", "") | trim -%}
42
+
43
+ {%- set has_citation_source_name = citation_source_name | length > 0 -%}
44
+ {%- set has_citation_source_title = citation_source_title | length > 0 -%}
45
+ {%- set has_citation_source_organisation = citation_source_organisation | length > 0 -%}
46
+ {%- set has_citation = has_citation_source_name or has_citation_source_title or has_citation_source_organisation -%}
47
+
48
+ {%- if has_citation -%}
49
+ {#- Optional citation block -#}
50
+ <p>
51
+ {% if has_citation_source_name -%}
52
+ {#- Optional citation source name -#}
53
+ <strong>{{ citation_source_name }}</strong>
54
+ {% if has_citation_source_title or has_citation_source_organisation -%}
55
+ {#- If the citation name is followed by title and/or organisation, add a line break -#}
56
+ <br>
57
+ {% endif -%}
58
+ {% endif -%}
59
+ {% if has_citation_source_title or has_citation_source_organisation -%}
60
+ {#- Optional citation source title and/or organisation -#}
61
+ <span class="u-text--muted">
62
+ {% if has_citation_source_title -%}
63
+ {#- Optional citation source title -#}
64
+ {{ citation_source_title }}
65
+ {%- if has_citation_source_organisation %}
66
+ {#- Add a line break between the title and org if both are present -#}
67
+ <br>
68
+ {%- endif %}
69
+ {% endif %}
70
+ {%- if has_citation_source_organisation -%}
71
+ {#- Optional citation source organisation -#}
72
+ {{ citation_source_organisation }}
73
+ {%- endif %}
74
+ </span>
75
+ {%- endif %}
76
+ </p>
77
+ {% endif %}
78
+ {%- endmacro -%}
79
+
80
+ {#-
81
+ Params
82
+ - heading
83
+ - title
84
+ - text
85
+ - link
86
+ - html
87
+ #}
88
+ {%- macro _quote_wrapper_heading_block(heading={}) %}
89
+ {%- set heading_title_text = heading.get("title", {}).get("text", "") | trim -%}
90
+ {%- set heading_link_html = heading.get("link", {}).get("html", "") | trim -%}
91
+ {%- set has_heading_title = heading_title_text | length > 0 -%}
92
+ {%- set has_heading_link = heading_link_html | length > 0 -%}
93
+ {%- set has_heading_row = has_heading_title or has_heading_link -%}
94
+
95
+ {% if has_heading_row -%}
96
+ {#- Optional heading -#}
97
+ <div class="p-section--shallow">
98
+ <div class="grid-row">
99
+ {%- if has_heading_title %}
100
+ {#- Optional heading text -#}
101
+ <div class="grid-col-2">
102
+ <h2 class="p-muted-heading">{{ heading_title_text }}</h2>
103
+ </div>
104
+ {%- endif -%}
105
+
106
+ {%- if has_heading_link %}
107
+ {#- Optional heading link -#}
108
+ <div class="grid-col-2 grid-col-start-large-7">
109
+ <p class="p-text--default">
110
+ {{ heading_link_html }}
111
+ </p>
112
+ </div>
113
+ {% endif -%}
114
+ </div>
115
+ </div>
116
+ {% endif -%}
117
+ {%- endmacro %}
118
+
119
+ {#-
120
+ Params
121
+ - cta
122
+ - html (String)
123
+ -#}
124
+ {%- macro _quote_wrapper_cta_block(cta={}) -%}
125
+ {%- set cta_html = cta.get("html", "") | trim -%}
126
+ {% if cta_html | length > 0 %}
127
+ {#- Optional CTA block -#}
128
+ <div class="p-cta-block">
129
+ {{- cta_html -}}
130
+ </div>
131
+ {%- endif -%}
132
+ {%- endmacro -%}
133
+
134
+ {#-
135
+ This macro exposes the main contents of the quote wrapper pattern.
136
+ this includes the citation, body (quote itself), CTA, and image.
137
+ Params
138
+ - contents
139
+ - citation
140
+ - name
141
+ - organisation
142
+ - title
143
+ - body
144
+ - size ("small" | "medium" | "large")
145
+ - text (String)
146
+ - cta
147
+ - html (String)
148
+ - image
149
+ - html (String)
150
+ #}
151
+ {%- macro _quote_wrapper_block_contents_column(contents={}) -%}
152
+ {%- set citation = contents.get("citation", {}) -%}
153
+ {%- set body = contents.get("body", {}) -%}
154
+ {%- set cta = contents.get("cta", {}) -%}
155
+ {%- set image = contents.get("image", {}) -%}
156
+ {%- set has_citation = (citation.get("name") or citation.get("title") or citation.get("organisation")) | trim | length > 0 -%}
157
+ {%- set has_cta = cta.get("html") | trim | length > 0 -%}
158
+ {%- set has_image = image.get("html") | trim | length > 0 -%}
159
+ {% if has_citation -%}
160
+ {#- When a citation is present, wrap the quote and citation in a nested grid to space them properly -#}
161
+ <div class="grid-row">
162
+ <div class="grid-col-4">
163
+ {{ _quote_wrapper_body_block(body=body) }}
164
+ </div>
165
+ <div class="grid-col-2">
166
+ {{ _quote_wrapper_citation_block(citation=citation) }}
167
+ </div>
168
+ </div>
169
+ {% else -%}
170
+ {#- When no citation is present, display quote body without a nested grid -#}
171
+ {{ _quote_wrapper_body_block(body=body) }}
172
+ {% endif -%}
173
+
174
+ {#- Optional CTA and/or image block -#}
175
+ {{ _quote_wrapper_cta_block(cta=cta) }}
176
+
177
+ {#- Optional image block -#}
178
+ {{ image.get("html", "") }}
179
+ {%- endmacro -%}
180
+
181
+ {#-
182
+ Params
183
+ - image (required)
184
+ - html (String)
185
+ -#}
186
+ {%- macro _quote_wrapper_block_signpost_column(signpost={}) -%}
187
+ {%- set signpost_html = signpost.get("image", {}).get("html", "") | trim -%}
188
+ {%- if signpost_html | length > 0 -%}
189
+ <div class="p-section--shallow">
190
+ {{ signpost_html | safe }}
191
+ </div>
192
+ {%- endif -%}
193
+ {%- endmacro -%}
194
+
195
+ {#-
196
+ Params
197
+ - signpost (optional)
198
+ - image
199
+ - html (String)
200
+ - contents (required)
201
+ - body
202
+ - size ("small" | "medium" | "large")
203
+ - text (str)
204
+ - citation
205
+ - name
206
+ - organisation
207
+ - title
208
+ - cta
209
+ - html (String)
210
+ - image
211
+ - html (String)
212
+ - has_divider (boolean) (optional): Whether to show a divider above the contents column when it is stacked with preceding content. Defaults to false.
213
+ - Note: passing a signpost will force this to True.
214
+ -#}
215
+ {%- macro vf_quote_block(signpost={}, contents={}) -%}
216
+ {%- set signpost_html = signpost.get("image", {}).get("html", "") | trim -%}
217
+ {%- set has_signpost = signpost_html | length > 0 -%}
218
+ <div class="grid-row">
219
+ {%- if has_signpost -%}
220
+ <div class="grid-col-2 grid-col-medium-1">
221
+ {%- if contents.get("has_divider", False) -%}
222
+ <hr class="p-rule--muted u-hide--medium u-hide--large"/>
223
+ {%- endif -%}
224
+ {#- Optional signpost image -#}
225
+ {{- _quote_wrapper_block_signpost_column(signpost=signpost) -}}
226
+ </div>
227
+ {%- endif -%}
228
+
229
+ <div class="grid-col-6 grid-col-start-large-3 grid-col-medium-3 grid-col-start-medium-2">
230
+ {%- if contents.get("has_divider", False) -%}
231
+ <hr class="p-rule--muted u-hide--small"/>
232
+ {%- endif -%}
233
+ {{- _quote_wrapper_block_contents_column(contents=contents) -}}
234
+ </div>
235
+ </div>
236
+ {%- endmacro -%}
237
+
1
238
  {#
2
239
  Params
3
240
  - title_text (string) (optional): The text to be displayed as the heading
@@ -28,146 +265,48 @@
28
265
  {% set has_heading_row = has_title or has_heading_link %}
29
266
  {% set signpost_image_content = caller('signpost_image') %}
30
267
  {% set has_signpost_image = signpost_image_content|trim|length > 0 %}
31
- {% set has_citation_source_name = citation_source_name_text|trim|length > 0 %}
32
- {% set has_citation_source_title = citation_source_title_text|trim|length > 0 %}
33
- {% set has_citation_source_organisation = citation_source_organisation_text|trim|length > 0 %}
34
- {% set has_citation = has_citation_source_name or has_citation_source_title or has_citation_source_organisation %}
35
- {% set cta_content = caller('cta') %}
36
- {% set has_cta = cta_content|trim|length > 0 %}
37
- {% set image_content = caller('image') %}
38
- {% set has_image = image_content|trim|length > 0 %}
39
- {% set quote_size = quote_size|trim|lower %}
40
-
41
- {# Translate quote size param to quote heading level #}
42
- {% if quote_size == 'large' %}
43
- {% set quote_heading_level = 2 %}
44
- {% elif quote_size == 'small' %}
45
- {% set quote_heading_level = 6 %}
46
- {% else %}
47
- {% set quote_heading_level = 4 %}
48
- {% endif %}
49
-
50
- {%- macro _quote_body() -%}
51
- <div class="p-section--shallow">
52
- <p class="p-heading--{{ quote_heading_level }}">
53
- <i>
54
- &#8220;{{ quote_text }}&#8221;
55
- </i>
56
- </p>
57
- </div>
58
- {%- endmacro %}
59
-
60
- {%- macro _citation_block() -%}
61
- {%- if has_citation -%}
62
- {#- Optional citation block -#}
63
- <p>
64
- {% if has_citation_source_name -%}
65
- {#- Optional citation source name -#}
66
- <strong>{{ citation_source_name_text }}</strong>
67
- {% if has_citation_source_title or has_citation_source_organisation -%}
68
- {#- If the citation name is followed by title and/or organisation, add a line break -#}
69
- <br>
70
- {% endif -%}
71
- {% endif -%}
72
- {% if has_citation_source_title or has_citation_source_organisation -%}
73
- {#- Optional citation source title and/or organisation -#}
74
- <span class="u-text--muted">
75
- {% if has_citation_source_title -%}
76
- {#- Optional citation source title -#}
77
- {{ citation_source_title_text }}
78
- {%- if has_citation_source_organisation %}
79
- {#- Add a line break between the title and org if both are present -#}
80
- <br>
81
- {%- endif %}
82
- {% endif %}
83
- {%- if has_citation_source_organisation -%}
84
- {#- Optional citation source organisation -#}
85
- {{ citation_source_organisation_text }}
86
- {%- endif %}
87
- </span>
88
- {%- endif %}
89
- </p>
90
- {% endif %}
91
- {%- endmacro -%}
92
-
93
- {%- macro _heading_block() %}
94
- {% if has_heading_row -%}
95
- {#- Optional heading -#}
96
- <div class="p-section--shallow">
97
- <hr class="p-rule--highlight is-fixed-width">
98
- <div class="row">
99
- {%- if has_title %}
100
- {#- Optional heading text -#}
101
- <div class="col-3 col-medium-2">
102
- <h2 class="p-muted-heading">{{ title_text }}</h2>
103
- </div>
104
- {%- endif -%}
105
-
106
- {%- if has_heading_link %}
107
- {#- Optional heading link -#}
108
- <div class="col-3 col-medium-4 col-start-large-10 col-start-medium-3">
109
- <p class="p-text--default">
110
- {{ heading_link_content }}
111
- </p>
112
- </div>
113
- {% endif -%}
114
- </div>
115
- </div>
116
- {% endif -%}
117
- {%- endmacro %}
118
268
 
119
269
  <div class="p-section{% if is_shallow %}--shallow{% endif %}">
120
- {{- _heading_block() -}}
121
- <div class="row">
122
- {% if has_signpost_image -%}
123
- {% if not has_heading_row %}
124
- {#-
125
- If a signpost is present, but no heading row, the signpost is the first piece of content in the pattern on small.
126
- So, we place a standard rule above the signpost to separate it from the preceding section.
127
- -#}
128
- <hr class="p-rule u-hide--medium u-hide--large">
129
- {% endif %}
130
- {#- Optional signpost image -#}
131
- <div class="col-3 col-medium-2">
132
- <div class="p-section--shallow">
133
- {{ signpost_image_content }}
134
- </div>
135
- </div>
136
- {% endif -%}
137
-
138
- <div class="col-9 col-start-large-4 col-medium-4 col-start-medium-3">
139
- <hr class="p-rule--muted">
140
- {% if has_citation -%}
141
- {#- When a citation is present, wrap the quote and citation in a nested grid to space them properly -#}
142
- <div class="row">
143
- <div class="col-6">
144
- {{ _quote_body() }}
145
- </div>
146
- <div class="col-3">
147
- <hr class="p-rule--muted u-hide--large">
148
- {{ _citation_block() }}
149
- </div>
150
- </div>
151
- {% else -%}
152
- {#- When no citation is present, display quote body without a nested grid -#}
153
- {{ _quote_body() }}
154
- {% endif -%}
155
-
156
- {%- if has_cta or has_image -%}
157
- {#- Optional CTA and/or image block -#}
158
- {%- if has_cta %}
159
- {#- Optional CTA block -#}
160
- <div class="p-cta-block">
161
- {{ cta_content }}
162
- </div>
163
- {% endif -%}
164
-
165
- {% if has_image -%}
166
- {#- Optional image block -#}
167
- {{ image_content }}
168
- {% endif -%}
169
- {% endif -%}
170
- </div>
171
- </div>
270
+ {#- pattern-level separator -#}
271
+ {%- if has_heading_row -%}
272
+ {{ vf_section_top_rule(top_rule_variant="default", is_fixed_width=True) }}
273
+ {%- endif -%}
274
+ {{- _quote_wrapper_heading_block(
275
+ heading={
276
+ "title": {
277
+ "text": title_text
278
+ },
279
+ "link": {
280
+ "html": heading_link_content
281
+ }
282
+ }
283
+ ) -}}
284
+ {#- TODO use `content.has_divider` only for non-first quote blocks once we support multiple quotes per pattern. -#}
285
+ {#- TODO add shallow spacing between each quote block in the pattern once we support multiple quotes per pattern. -#}
286
+ {{- vf_quote_block(
287
+ signpost={
288
+ "image": {
289
+ "html": signpost_image_content
290
+ }
291
+ },
292
+ contents={
293
+ "body": {
294
+ "size": quote_size,
295
+ "text": quote_text
296
+ },
297
+ "citation": {
298
+ "name": citation_source_name_text,
299
+ "title": citation_source_title_text,
300
+ "organisation": citation_source_organisation_text
301
+ },
302
+ "image": {
303
+ "html": caller('image')
304
+ },
305
+ "cta": {
306
+ "html": caller('cta')
307
+ },
308
+ "has_divider": not has_heading_row
309
+ }
310
+ ) -}}
172
311
  </div>
173
312
  {% endmacro -%}
@@ -0,0 +1,238 @@
1
+ {% from "_macros/shared/vf_section_top_rule.jinja" import vf_section_top_rule %}
2
+ {% from "_macros/shared/vf_cta-block.jinja" import vf_cta_block %}
3
+ {% from "_macros/shared/vf_description-block.jinja" import vf_description_block %}
4
+ {% from "_macros/shared/vf_tabs.jinja" import vf_tabs %}
5
+ {% from "_macros/vf_quote-wrapper.jinja" import vf_quote_block %}
6
+ {% from "_macros/shared/vf_linked-logo-block.jinja" import vf_linked_logo_block %}
7
+ {% from "_macros/shared/vf_logo-block.jinja" import vf_logo_block %}
8
+ {% from "_macros/shared/vf_divided-section-block.jinja" import vf_divided_section_block %}
9
+ {% from "_macros/shared/vf_blog-block.jinja" import vf_blog_block %}
10
+ {% from "_macros/vf_basic-section.jinja" import vf_basic_section_blocks %}
11
+
12
+ {#-
13
+ - tab (Object) - Configuration object with the following properties:
14
+ - type (String) - The type of content block. Must be one of:
15
+ - "quote" - Quote block (full-width only). See _tab_section_quote
16
+ - "linked-logo" - Linked logo block. See vf_linked_logo_block
17
+ - "logo-block" - Logo block. See vf_logo_block
18
+ - "divided-section" - Divided section block. See vf_divided_section_block
19
+ - "blog" - Blog articles block. See vf_blog_block
20
+ - "basic-section" - Basic section with content blocks. See vf_basic_section_blocks
21
+ - item (Object) - Configuration specific to the block type.
22
+ The structure depends on the type selected (see referenced macros).
23
+ - tab_html (String) - HTML content for the tab label
24
+ -#}
25
+ {%- macro _tab_section_tab(tab={}) -%}
26
+ {%- set type = tab.get("type", "") | trim -%}
27
+ {%- set item = tab.get("item", {}) -%}
28
+ {% if type == "quote" %}
29
+ {{ vf_quote_block(signpost=item.get('signpost', {}), contents=item.get('contents', {})) }}
30
+ {% elif type == "linked-logo" %}
31
+ {{ vf_linked_logo_block({'links': item.get('links', [])}) }}
32
+ {% elif type == "logo-block" %}
33
+ {{ vf_logo_block({'logos': item.get('logos', []), 'is_fixed_width': item.get('is_fixed_width', true)}) }}
34
+ {% elif type == "divided-section" %}
35
+ {{ vf_divided_section_block(blocks=item.get('blocks', [])) }}
36
+ {% elif type == "blog" %}
37
+ <div class="p-blog grid-row">
38
+ {{ vf_blog_block(articles=item.get('articles', []), template_config=item.get('template_config', {})) }}
39
+ </div>
40
+ {% elif type == "basic-section" %}
41
+ {{ vf_basic_section_blocks(items=item.get('items', [])) }}
42
+ {% endif %}
43
+ {%- endmacro -%}
44
+
45
+ {#-
46
+ A higher-order component that renders a section with a title, optional description,
47
+ optional call-to-action, and a tabbed interface containing various content blocks.
48
+
49
+ This pattern combines a top heading area with tabs that can contain different types of
50
+ content blocks (quotes, logos, blog articles, etc.). The allowed block types vary by layout.
51
+
52
+ Parameters:
53
+ - title (Object) - Configuration for the title. Properties:
54
+ - text (String, Required) - The main title text (rendered as h2 by default)
55
+ - link_attrs (Object, Optional) - Attributes for the title link (e.g., href, class)
56
+ - heading_level (Number, Optional) - Heading level for the title (2, 3, or 4). Default: 2
57
+
58
+ - description (Object, Optional) - Configuration for the secondary description column.
59
+ Passed directly to vf_description_block. Properties:
60
+ - content (String) - The description text or HTML content
61
+ - type (String, Optional) - "text" or "html". Default: "text"
62
+
63
+ - cta (Object, Optional) - Call-to-action configuration passed to vf_cta_block. Properties:
64
+ - primary (Object) - Primary button configuration
65
+ - secondaries (Array) - Array of secondary button configurations
66
+ - link (Object) - Text link configuration
67
+
68
+ - layout (String, Optional) - Layout variant. One of:
69
+ - "full-width" - Tabs span full width. Allows: quote, linked-logo, logo-block, blog
70
+ - "50-50" - Tabs in right 50% column. Allows: linked-logo, logo-block, divided-section, blog, basic-section
71
+ - "25-75" - Tabs in right 75% column. Allows: linked-logo, logo-block, blog
72
+ Default: "50-50"
73
+
74
+ - padding (String, Optional) - Padding variant. One of:
75
+ - "deep" - Uses p-section--deep
76
+ - "shallow" - Uses p-section--shallow
77
+ - "default" - Uses p-section
78
+ Default: "default"
79
+
80
+ - top_rule_variant (String, Optional) - Horizontal rule variant above title. One of:
81
+ - "default" - Standard horizontal rule
82
+ - "muted" - Muted horizontal rule
83
+ - "none" - No rule
84
+ Default: "default"
85
+
86
+ - tabs (Array, Required) - Array of tab configurations. Each tab object should have:
87
+ - type (String, Required) - Block type: "quote", "linked-logo", "logo-block", "divided-section", "blog", or "basic-section"
88
+ - item (Object, Required) - Configuration specific to the block type
89
+ - tab_html (String, Required) - HTML for the tab control
90
+
91
+ - attrs (Object, Optional) - Dictionary of attributes to apply to the section element.
92
+
93
+ Note: Some block types are only allowed in certain layouts (see allowed_blocks_per_layout).
94
+ Unsupported blocks for a layout will silently be skipped.
95
+ -#}
96
+ {%- macro vf_tab_section(
97
+ title={},
98
+ description={},
99
+ cta={},
100
+ layout="50-50",
101
+ padding="default",
102
+ top_rule_variant="default",
103
+ tabs=[],
104
+ attrs={}
105
+ ) -%}
106
+ {#- Marshall values from parameters into variables, apply safe defaults, and trim contents -#}
107
+ {%- set title_text = title.get("text", "") | trim -%}
108
+ {%- set title_link_attrs = title.get("link_attrs", {}) -%}
109
+ {%- set title_heading_level = title.get("heading_level", 2) -%}
110
+ {%- set padding = padding | trim -%}
111
+
112
+ {#- Store the existence of optional content -#}
113
+ {%- set title_is_link = title_link_attrs.items() | length > 0 -%}
114
+ {%- set has_description = description.get("content", "") | trim | length > 0 -%}
115
+ {%- set has_cta = cta.get("primary") or cta.get("secondaries") or cta.get("link") -%}
116
+
117
+ {#- Input validation -#}
118
+ {%- if title_heading_level not in [2, 3, 4] -%}
119
+ {%- set title_heading_level = 2 -%}
120
+ {%- endif -%}
121
+
122
+ {%- if padding not in ["deep", "shallow", "default"] -%}
123
+ {%- set padding = "default" -%}
124
+ {%- endif -%}
125
+
126
+ {%- set padding_classes = "p-section--" + padding -%}
127
+ {%- if padding == "default" -%}
128
+ {%- set padding_classes = "p-section" -%}
129
+ {%- endif -%}
130
+
131
+ {%- if layout not in ["50-50", "25-75", "full-width"] -%}
132
+ {%- set layout = "full-width" -%}
133
+ {%- endif -%}
134
+
135
+ {%- if layout == "full-width" -%}
136
+ {%- set tabs_column_count = 8 -%}
137
+ {%- elif layout == "50-50" -%}
138
+ {%- set tabs_column_count = 4 -%}
139
+ {%- elif layout == "25-75" -%}
140
+ {%- set tabs_column_count = 6 -%}
141
+ {%- endif -%}
142
+
143
+ {%- set tabs_column_start = 8 - tabs_column_count + 1 -%}
144
+ {%- set title_row_has_two_columns = has_description or has_cta or (layout == "50-50") -%}
145
+ {%- set tabs_in_title_row = not has_description and not has_cta and layout == "50-50" -%}
146
+
147
+ {#- Configuration: which block types are allowed per layout.
148
+ If a layout key is present, only those block types will be rendered
149
+ for that layout. If a layout is not present in the mapping, all
150
+ block types are permitted. This provides an easy place to control
151
+ designer constraints (e.g. quote only allowed in full-width).
152
+ -#}
153
+ {%- set allowed_blocks_per_layout = {
154
+ '50-50': [ 'linked-logo', 'logo-block', 'divided-section', 'blog', 'basic-section' ],
155
+ '25-75': [ 'linked-logo', 'logo-block', 'blog' ],
156
+ 'full-width': [ 'quote', 'linked-logo', 'logo-block', 'blog' ]
157
+ } -%}
158
+
159
+ {#- Build a list of rendered tabs for the `vf_tabs` helper -#}
160
+ {%- set ns = namespace(rendered_tabs=[]) -%}
161
+ {%- for tab in tabs -%}
162
+ {%- set type = tab.get('type', '') | trim -%}
163
+
164
+ {#- Enforce allowed blocks per layout if configured - replace `continue` (unsupported) with a render flag -#}
165
+ {%- set render_tab = true -%}
166
+ {%- if layout in allowed_blocks_per_layout -%}
167
+ {%- set allowed = allowed_blocks_per_layout[layout] -%}
168
+ {%- if not (type in allowed) -%}
169
+ {%- set render_tab = false -%}
170
+ {%- endif -%}
171
+ {%- endif -%}
172
+
173
+ {%- if render_tab -%}
174
+ {#-
175
+ Prepend the section title text to the name.
176
+ This helps ensure uniqueness of tab section tab names across an entire page,
177
+ in case multiple tab sections are used in a single page.
178
+ The user still needs to ensure that the page's section titles are unique.
179
+ -#}
180
+ {%- set name = title_text | trim | lower | replace(' ', '_') -%}
181
+ {%- set content_html = _tab_section_tab(tab=tab) -%}
182
+ {%- set ns.rendered_tabs = ns.rendered_tabs + [ { 'name': name, 'tab_html': tab.get('tab_html', ''), 'content_html': content_html } ] -%}
183
+ {%- endif -%}
184
+ {%- endfor -%}
185
+
186
+
187
+ <section
188
+ class="{{ padding_classes }} {{ attrs.get("class", "") }}"
189
+ {%- for attr, value in attrs.items() -%}
190
+ {% if attr != "class" %}
191
+ {{ attr }}="{{ value }}"
192
+ {%- endif -%}
193
+ {%- endfor -%}
194
+ >
195
+ {% if not tabs_in_title_row %}
196
+ <div class="p-section--shallow">
197
+ {% endif %}
198
+ <div class="grid-row{% if title_row_has_two_columns %}--50-50-on-large{% endif %}">
199
+ {{ vf_section_top_rule(top_rule_variant) }}
200
+ <div class="grid-col">
201
+ {%- if title_is_link -%}
202
+ <a {% for attr, value in title_link_attrs.items() %} {{ attr }}="{{ value }}" {% endfor %}>
203
+ {%- endif -%}
204
+ <h{{ title_heading_level }}>
205
+ {{ title_text }}
206
+ </h{{ title_heading_level }}>
207
+ {%- if title_is_link -%}
208
+ </a>
209
+ {%- endif -%}
210
+ </div>
211
+ {%- if title_row_has_two_columns -%}
212
+ <div class="grid-col">
213
+ {{ vf_description_block(
214
+ type=description.get("type"),
215
+ content=description.get("content")
216
+ )}}
217
+ {{ vf_cta_block(
218
+ primary=cta.get("primary"),
219
+ secondaries=cta.get("secondaries"),
220
+ link=cta.get("link")
221
+ ) }}
222
+ {#- On 50-50, if there is no description or CTA, the tabs live in the same row as the title. -#}
223
+ {% if tabs_in_title_row %}
224
+ {{ vf_tabs(list={ 'tabs': ns.rendered_tabs }) }}
225
+ {% endif %}
226
+ </div>
227
+ {%- endif -%}
228
+ </div>
229
+ {% if not tabs_in_title_row %}
230
+ </div>
231
+ <div class="grid-row">
232
+ <div class="grid-col-{{ tabs_column_count }} grid-col-start-large-{{ tabs_column_start }}">
233
+ {{ vf_tabs(list={ 'tabs': ns.rendered_tabs }) }}
234
+ </div>
235
+ </div>
236
+ {% endif %}
237
+ </section>
238
+ {%- endmacro -%}