2026-10-23 08:35:52 +08:00
{# trade-pro macro library: passthrough copy of the shared
themes/_professional_components.html with English strings, plus the
trade-pro inline SVG icon set (Task 9). Icons are decorative
server-side inlines: no JS dependency, currentColor strokes. #}
{# ── SVG icon set (Task 9) ──────────────────────────────────────────────
Style contract: 24× 24 viewBox, stroke=currentColor, stroke-width 1.5,
fill none, round caps/joins — the same modern line language as the
ported cms design. Every glyph is ONE < path > (subpaths allowed) so the
d attribute survives autoescaping without |safe. Unknown names fall
back to arrow-right instead of raising (page code typos must never
take a build down). #}
{% set _tp_icon_paths={
'arrow-right':'M5 12h14M12 5l7 7-7 7',
'arrow-left':'M19 12H5M12 19l-7-7 7-7',
'chevron-down':'M6 9l6 6 6-6',
'check':'M20 6 9 17 4 12',
'phone':'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z',
'mail':'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2Zm18 2-10 7L2 6',
'location':'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0ZM12 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z',
'download':'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3',
'search':'M11 3a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM21 21l-4.35-4.35',
'quote':'M8 3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2 1 1 0 0 0-1 1v1a1 1 0 0 0 1 1 2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2 1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1 2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4ZM16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a1 1 0 0 1-1 1 2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2 1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1 2 2 0 0 0 2-2V5a2 2 0 0 0-2-2h-4Z'
} %}
{% macro icon(name, size=None, cls='') -%}
{%- set _d=_tp_icon_paths.get(name) or _tp_icon_paths['arrow-right'] -%}
< svg class = "sf-tp-icon{% if cls %} {{ cls }}{% endif %}" width = "{{ size if size else 'var(--sf-tp-icon-size-md, 20px)' }}" height = "{{ size if size else 'var(--sf-tp-icon-size-md, 20px)' }}" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "1.5" stroke-linecap = "round" stroke-linejoin = "round" aria-hidden = "true" focusable = "false" >< path d = "{{ _d }}" /></ svg >
{%- endmacro %}
2026-10-22 18:19:30 +08:00
{% macro cover(item, kind='product') %}
{% if item.cover %}< img class = "pro-photo" src = "{{ safe_image(item.cover) }}" alt = "{{ item.title }}" loading = "lazy" >
{% else %}< div class = "pro-art pro-art-{{ kind }}" >< span class = "pro-art-ring" ></ span >< span class = "pro-art-center" > {{ (item.title or 'SF')[:2] }}</ span >< span class = "pro-art-label" > {{ kind|upper }}</ span ></ div > {% endif %}
{% endmacro %}
{% macro card(item, kind='product', position=0) %}
< a class = "pro-card pro-card-{{ kind }}" href = "{{ safe_href(item.route) }}" data-category = "{{ fields_of(item).get('category','') }}" data-brand = "{{ fields_of(item).get('brand','') }}" >
< div class = "pro-card-cover" > {{ cover(item,kind) }}</ div >
< div class = "pro-card-body" >< div class = "pro-card-meta" >< span > {{ fields_of(item).get('brand') or fields_of(item).get('category') or {'product':'PRODUCT','vehicle':'VEHICLE','post':'NEWS','case':'CASE','brand':'BRAND','service':'SERVICE'}.get(item.type,'CONTENT') }}</ span >< span > {{ '%02d'|format(position+1) }}</ span ></ div >
2026-10-23 08:35:52 +08:00
< h3 > {{ item.title }}</ h3 >< p > {{ (item.summary or '')[:128] }}</ p >< span class = "pro-card-link" > View details {{ icon('arrow-right') }}</ span ></ div >
2026-10-22 18:19:30 +08:00
</ a >
{% endmacro %}
{% macro grid(items,kind='product') %}< div class = "pro-grid pro-grid-{{ kind }}" > {% for item in items %}{{ card(item,kind,loop.index0) }}{% else %}< div class = "pro-empty" > No published content in this section yet.</ div > {% endfor %}</ div > {% endmacro %}
2026-10-23 08:35:52 +08:00
{% macro section_title(eyebrow,heading,href='') %}< div class = "pro-section-head" >< div >< span class = "pro-eyebrow" > {{ eyebrow }}</ span >< h2 > {{ heading }}</ h2 ></ div > {% if href %}< a class = "pro-text-link" href = "{{ href }}" > Browse all {{ icon('arrow-right') }}</ a > {% endif %}</ div > {% endmacro %}
{% macro filters() %}< div class = "pro-filter-panel" >< label >< span > Search</ span >< span class = "pro-filter-search-box" > {{ icon('search') }}< input type = "search" data-pro-filter-search placeholder = "Filter by name or content" ></ span ></ label >< label >< span > Brand</ span >< select data-pro-filter-brand >< option value = "" > All brands</ option ></ select ></ label >< label >< span > Category</ span >< select data-pro-filter-category >< option value = "" > All categories</ option ></ select ></ label >< span class = "pro-filter-count" data-pro-filter-count role = "status" ></ span ></ div > {% endmacro %}
2026-10-23 09:00:50 +08:00
{% macro footer_links(nav) %}< ul > {% for item in nav[:7] %}< li >< a href = "{{item.route}}" > {{item.title}} {{ icon('arrow-right', cls='sf-tp-icon--sm') }}</ a ></ li > {% endfor %}</ ul > {% endmacro %}
2026-10-22 18:19:30 +08:00
{% macro body_copy(content,page) %}{% if content %}< div class = "pro-rich" > {{ content }}</ div > {% elif page.summary %}< p class = "pro-lead" > {{ page.summary }}</ p > {% else %}< p class = "pro-muted" > Complete this page's content in the admin console.</ p > {% endif %}{% endmacro %}