feat: add action_dropdown macro with loading state support

- Create action_dropdown macro in dropdowns.html supporting:
  - Loading/disabled state via loading_var parameter
  - Custom loading label
  - Icon support
  - Primary/secondary variants
- Update code quality dashboard to use new macro
- Add Dropdowns section to components page with examples:
  - Basic dropdown
  - Action dropdown with loading state
  - Context menu (3-dot)
  - Variant showcase (primary, secondary, ghost)

Architecture validation now passes with 0 errors and 0 warnings.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-21 21:13:08 +01:00
parent d50b154823
commit 245040d256
4 changed files with 225 additions and 38 deletions

View File

@@ -2136,6 +2136,139 @@ goToPage(n) { if (n !== '...' && n >= 1 && n <= this.totalPages) { this.paginati
</div>
</section>
<!-- DROPDOWNS SECTION -->
<section id="dropdowns" class="scroll-mt-24">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-6 flex items-center">
<span x-html="$icon('chevron-down', 'w-6 h-6 mr-2 text-purple-600 dark:text-purple-400')"></span>
Dropdowns
</h2>
<div class="mb-6 p-4 bg-blue-50 dark:bg-gray-700 border border-blue-200 dark:border-gray-600 rounded-lg">
<div class="flex items-start">
<span x-html="$icon('information-circle', 'w-5 h-5 text-blue-600 dark:text-blue-400 mr-2 flex-shrink-0 mt-0.5')"></span>
<div>
<p class="text-sm text-blue-800 dark:text-gray-200">
<strong>Dropdown Macros:</strong> Use macros from <code class="bg-blue-100 dark:bg-gray-600 px-1 rounded">shared/macros/dropdowns.html</code> for consistent dropdown styling.
</p>
</div>
</div>
</div>
<!-- Basic Dropdown -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Basic Dropdown</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">Standard dropdown with items. Uses its own Alpine.js state.</p>
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 mb-3">
{% from 'shared/macros/dropdowns.html' import dropdown, dropdown_item %}
{% call dropdown('Actions', 'demoDropdown1') %}
{{ dropdown_item('Edit', "alert('Edit clicked')", icon='pencil') }}
{{ dropdown_item('Duplicate', "alert('Duplicate clicked')", icon='duplicate') }}
{{ dropdown_item('Delete', "alert('Delete clicked')", icon='trash', variant='danger') }}
{% endcall %}
</div>
<button @click="copyCode(`{% raw %}{% from 'shared/macros/dropdowns.html' import dropdown, dropdown_item %}
{% call dropdown('Actions', 'isDropdownOpen') %}
{{ dropdown_item('Edit', 'edit()', icon='pencil') }}
{{ dropdown_item('Duplicate', 'duplicate()', icon='duplicate') }}
{{ dropdown_item('Delete', 'delete()', icon='trash', variant='danger') }}
{% endcall %}{% endraw %}`)" class="text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
Copy Code
</button>
</div>
<!-- Action Dropdown with Loading State -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Action Dropdown with Loading State</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">Dropdown that supports disabled/loading state. Uses external Alpine.js state from parent component.</p>
<div x-data="{ demoLoading: false, demoDropdownOpen: false }" class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 mb-3">
<div class="flex items-center gap-4">
{% from 'shared/macros/dropdowns.html' import action_dropdown, dropdown_item %}
{% call action_dropdown(
label='Run Action',
loading_label='Processing...',
open_var='demoDropdownOpen',
loading_var='demoLoading',
icon='play'
) %}
{{ dropdown_item('Option 1', "demoDropdownOpen = false; demoLoading = true; setTimeout(() => demoLoading = false, 2000)") }}
{{ dropdown_item('Option 2', "demoDropdownOpen = false; alert('Option 2')") }}
{{ dropdown_item('Option 3', "demoDropdownOpen = false; alert('Option 3')") }}
{% endcall %}
<span class="text-xs text-gray-500">(Click Option 1 to see loading state)</span>
</div>
</div>
<button @click="copyCode(`{% raw %}{% from 'shared/macros/dropdowns.html' import action_dropdown, dropdown_item %}
{% call action_dropdown(
label='Run Scan',
loading_label='Scanning...',
open_var='scanDropdownOpen',
loading_var='scanning',
icon='search'
) %}
{{ dropdown_item('Run All', 'runScan(\"all\"); scanDropdownOpen = false') }}
{{ dropdown_item('Run Selected', 'runScan(\"selected\"); scanDropdownOpen = false') }}
{% endcall %}{% endraw %}`)" class="text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
Copy Code
</button>
</div>
<!-- Context Menu -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Context Menu (3-dot)</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">Icon-only dropdown commonly used for row actions in tables.</p>
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 mb-3">
{% from 'shared/macros/dropdowns.html' import context_menu, dropdown_item, dropdown_divider %}
{% call context_menu('demoContext', 'demoContextOpen') %}
{{ dropdown_item('View', "alert('View')", icon='eye') }}
{{ dropdown_item('Edit', "alert('Edit')", icon='pencil') }}
{{ dropdown_divider() }}
{{ dropdown_item('Delete', "alert('Delete')", icon='trash', variant='danger') }}
{% endcall %}
</div>
<button @click="copyCode(`{% raw %}{% from 'shared/macros/dropdowns.html' import context_menu, dropdown_item, dropdown_divider %}
{% call context_menu('rowMenu', 'isMenuOpen') %}
{{ dropdown_item('View', 'view()', icon='eye') }}
{{ dropdown_item('Edit', 'edit()', icon='pencil') }}
{{ dropdown_divider() }}
{{ dropdown_item('Delete', 'delete()', icon='trash', variant='danger') }}
{% endcall %}{% endraw %}`)" class="text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
Copy Code
</button>
</div>
<!-- Dropdown Variants -->
<div>
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Dropdown Variants</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">Primary, secondary, and ghost variants.</p>
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 mb-3 flex gap-4">
{% from 'shared/macros/dropdowns.html' import dropdown, dropdown_item %}
{% call dropdown('Primary', 'demoPrimary', variant='primary') %}
{{ dropdown_item('Option 1', "alert('1')") }}
{{ dropdown_item('Option 2', "alert('2')") }}
{% endcall %}
{% call dropdown('Secondary', 'demoSecondary', variant='secondary') %}
{{ dropdown_item('Option 1', "alert('1')") }}
{{ dropdown_item('Option 2', "alert('2')") }}
{% endcall %}
{% call dropdown('Ghost', 'demoGhost', variant='ghost') %}
{{ dropdown_item('Option 1', "alert('1')") }}
{{ dropdown_item('Option 2', "alert('2')") }}
{% endcall %}
</div>
<button @click="copyCode(`{% raw %}{% call dropdown('Label', 'isOpen', variant='primary') %}...{% endcall %}
{% call dropdown('Label', 'isOpen', variant='secondary') %}...{% endcall %}
{% call dropdown('Label', 'isOpen', variant='ghost') %}...{% endcall %}{% endraw %}`)" class="text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
Copy Code
</button>
</div>
</div>
</section>
<!-- CARDS SECTION -->
<section id="cards" class="scroll-mt-24">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">