vanilla-framework 4.38.0 → 4.40.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.
@@ -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 -%}