- Replace custom sidebar/icon nav with standard tabs_nav macro - Add horizontal scroll for mobile (overflow-x-auto) - Reduce tab spacing on mobile (space-x-4 vs space-x-8 on desktop) - Content panels now take full width below tabs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
79 lines
2.8 KiB
HTML
79 lines
2.8 KiB
HTML
{# app/templates/shared/macros/tabs.html #}
|
|
{# Tab navigation components for consistent UI across admin pages #}
|
|
|
|
{#
|
|
Tab navigation wrapper (standalone)
|
|
Usage:
|
|
{% call tabs_nav() %}
|
|
{{ tab_button('tab1', 'Tab 1', icon='home') }}
|
|
{{ tab_button('tab2', 'Tab 2', icon='cog') }}
|
|
{% endcall %}
|
|
#}
|
|
{% macro tabs_nav(tab_var='activeTab', class='') %}
|
|
<div class="mb-6 {{ class }}">
|
|
<div class="border-b border-gray-200 dark:border-gray-700">
|
|
<nav class="-mb-px flex space-x-4 md:space-x-8 overflow-x-auto">
|
|
{{ caller() }}
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{#
|
|
Tab navigation wrapper (inline, for use inside cards/flex containers)
|
|
Usage:
|
|
<div class="flex justify-between">
|
|
{% call tabs_inline() %}
|
|
{{ tab_button('all', 'All', count_var='items.length') }}
|
|
{% endcall %}
|
|
<!-- other content like search -->
|
|
</div>
|
|
#}
|
|
{% macro tabs_inline(tab_var='activeTab') %}
|
|
<div class="flex space-x-2 border-b border-gray-200 dark:border-gray-700">
|
|
{{ caller() }}
|
|
</div>
|
|
{% endmacro %}
|
|
|
|
{#
|
|
Individual tab button
|
|
Args:
|
|
id: Tab identifier (used for activeTab comparison)
|
|
label: Display text
|
|
tab_var: Alpine.js variable name for active tab (default: 'activeTab')
|
|
icon: Optional icon name (uses $icon helper)
|
|
count_var: Optional Alpine.js variable for count badge
|
|
onclick: Optional custom click handler (overrides default tab switching)
|
|
#}
|
|
{% macro tab_button(id, label, tab_var='activeTab', icon=none, count_var=none, onclick=none) %}
|
|
<button
|
|
@click="{{ onclick if onclick else (tab_var ~ " = '" ~ id ~ "'") }}"
|
|
:class="{{ tab_var }} === '{{ id }}' ? 'border-purple-500 text-purple-600 dark:text-purple-400' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300'"
|
|
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm transition-colors"
|
|
>
|
|
{%- if icon %}<span x-html="$icon('{{ icon }}', 'inline w-5 h-5 mr-2')"></span>{% endif -%}
|
|
{{ label }}
|
|
{%- if count_var %}<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-gray-100 dark:bg-gray-700" x-text="{{ count_var }}"></span>{% endif -%}
|
|
</button>
|
|
{% endmacro %}
|
|
|
|
{#
|
|
Tab panel wrapper (for content that shows/hides based on active tab)
|
|
Usage:
|
|
{{ tab_panel('tab1') }}
|
|
<div>Tab 1 content</div>
|
|
{{ endtab_panel() }}
|
|
|
|
Or simply use x-show directly:
|
|
<div x-show="activeTab === 'tab1'" x-transition>
|
|
Content
|
|
</div>
|
|
#}
|
|
{% macro tab_panel(id, tab_var='activeTab', transition=true) %}
|
|
<div x-show="{{ tab_var }} === '{{ id }}'"{% if transition %} x-transition{% endif %}>
|
|
{% endmacro %}
|
|
|
|
{% macro endtab_panel() %}
|
|
</div>
|
|
{% endmacro %}
|