{#
macros/interactive.html - Interactive UI component macros for APEP.
Import with: {% from "apep/macros/interactive.html" import btn, hero_btn, download_btn, theme_switch %}
Macros:
btn(label, variant, size, url, disabled)
hero_btn(label, url, subtitle, variant)
download_btn(label, file_path, variant)
theme_switch(variant)
Author: sora7672
#}
{% from "apep/macros/helper.html" import alert %}
{#
Renders a button or, when url is provided and the button is not disabled,
an anchor tag styled as a button. Disabled anchors always render as
since anchor elements do not support the disabled attribute natively.
@param label {string} - button text
@param variant {string} - "primary" | "secondary" | "ghost" | "danger", default "primary"
@param size {string} - "sm" | "md" | "lg", default "md"
@param url {string} - when set and disabled is false, renders as . Optional.
@param disabled {bool} - adds disabled and aria-disabled attributes. Forces
even when url is provided. Default false.
#}
{% macro btn(label, variant="primary", size="md", url=None, disabled=False) %}
{% if not label %}
{{ alert("btn: label is required", "Pass a non-empty string as the first argument.") }}
{% elif variant not in ["primary", "secondary", "ghost", "danger"] %}
{{ alert("btn: invalid variant \"" ~ variant ~ "\"", "Allowed values: \"primary\", \"secondary\", \"ghost\", \"danger\".") }}
{% elif size not in ["sm", "md", "lg"] %}
{{ alert("btn: invalid size \"" ~ size ~ "\"", "Allowed values: \"sm\", \"md\", \"lg\".") }}
{% else %}
{% if url and not disabled %}
{{ label }}
{% else %}
{{ label }}
{% endif %}
{% endif %}
{% endmacro %}
{#
Renders a large call-to-action button with an optional subtitle line below it.
Always renders as an anchor tag. Use the standard btn macro for non-hero actions.
@param label {string} - main button text
@param url {string} - link target, default "#"
@param subtitle {string} - small muted text rendered below the button. Optional.
@param variant {string} - "primary" | "secondary", default "primary"
#}
{% macro hero_btn(label, url="#", subtitle=None, variant="primary") %}
{% if not label %}
{{ alert("hero_btn: label is required", "Pass a non-empty string as the first argument.") }}
{% elif variant not in ["primary", "secondary"] %}
{{ alert("hero_btn: invalid variant \"" ~ variant ~ "\"", "Allowed values: \"primary\", \"secondary\".") }}
{% else %}
{{ label }}
{% if subtitle %}
{{ subtitle }}
{% endif %}
{% endif %}
{% endmacro %}
{#
Renders a download button in one of three visual variants.
The file_path is passed as a data attribute and handled by download-btn.js,
which resolves the path under /downloads/ and triggers the browser download.
External URLs in the "minimal" variant trigger a confirm dialog before download.
@param label {string} - displayed button text
@param file_path {string} - path to the file, resolved under /downloads/ by download-btn.js
@param variant {string} - "default" | "icon" | "minimal", default "default"
default - full button with filetype icon
icon - compact icon + label, no wrapper
minimal - compact button, download triggered entirely via JS
#}
{% macro download_btn(label, file_path, variant="default") %}
{% if not label %}
{{ alert("download_btn: label is required", "Pass a non-empty string as the first argument.") }}
{% elif not file_path %}
{{ alert("download_btn: file_path is required", "Pass a valid file path as the second argument.") }}
{% elif variant not in ["default", "icon", "minimal"] %}
{{ alert("download_btn: invalid variant \"" ~ variant ~ "\"", "Allowed values: \"default\", \"icon\", \"minimal\".") }}
{% else %}
{% if variant == "minimal" %}
{{ label }}
↓
{% elif variant == "icon" %}
{{ label }}
{% else %}
{{ label }}
{% endif %}
{% endif %}
{% endmacro %}
{#
Renders a light/dark theme toggle in one of three variants.
The active theme is persisted and restored by theme-switch.js.
theme-switch.js is loaded globally by the backend to prevent flash-of-wrong-theme on page load.
All variants are detected via querySelectorAll("[data-theme-btn]") or
"[data-theme-checkbox]" - placing multiple variants on one page is supported.
@param variant {string} - "icon" | "slider" | "floating", default "icon"
icon - plain button, place anywhere in layout
slider - toggle slider with animated thumb
floating - fixed-position button, bottom-right by default
@param position {string} - only used for variant "floating"
"top-left" | "top-right" | "bottom-left" | "bottom-right"
default "bottom-right"
#}
{% macro theme_switch(variant="floating", position="top-right") %}
{% if variant not in ["icon", "slider", "toggle", "floating"] %}
{{ alert("theme_switch: invalid variant \"" ~ variant ~ "\"", "Allowed values: \"icon\", \"slider\", \"toggle\", \"floating\".") }}
{% elif variant == "floating" and position not in ["top-left", "top-right", "bottom-left", "bottom-right"] %}
{{ alert("theme_switch: invalid position \"" ~ position ~ "\"", "Allowed values: \"top-left\", \"top-right\", \"bottom-left\", \"bottom-right\".") }}
{% else %}
{% if variant == "floating" %}
{% elif variant == "slider" or variant == "toggle" %}
{% else %}
{% endif %}
{% endif %}
{% endmacro %}