mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 16:31:08 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
@@ -1,93 +1,61 @@
|
||||
{% comment %}
|
||||
Reusable pagination component with accessibility and responsive design.
|
||||
Usage: {% include 'components/pagination.html' with page_obj=page_obj %}
|
||||
Pagination Component
|
||||
====================
|
||||
|
||||
A reusable pagination component with accessibility features and HTMX support.
|
||||
|
||||
Purpose:
|
||||
Renders pagination controls for paginated querysets. Supports both
|
||||
standard page navigation and HTMX-powered dynamic updates.
|
||||
|
||||
Usage Examples:
|
||||
Standard pagination:
|
||||
{% include 'components/pagination.html' with page_obj=page_obj %}
|
||||
|
||||
HTMX-enabled pagination:
|
||||
{% include 'components/pagination.html' with page_obj=page_obj use_htmx=True hx_target='#results' %}
|
||||
|
||||
Custom styling:
|
||||
{% include 'components/pagination.html' with page_obj=page_obj size='sm' %}
|
||||
|
||||
Parameters:
|
||||
- page_obj: Django Page object from paginator (required)
|
||||
- use_htmx: Enable HTMX for dynamic updates (optional, default: False)
|
||||
- hx_target: HTMX target selector (optional, default: '#results')
|
||||
- hx_swap: HTMX swap strategy (optional, default: 'innerHTML')
|
||||
- hx_push_url: Whether to push URL to history (optional, default: 'true')
|
||||
- size: Size variant 'sm', 'md', 'lg' (optional, default: 'md')
|
||||
- show_info: Show "Showing X to Y of Z" info (optional, default: True)
|
||||
- base_url: Base URL for pagination (optional, default: request.path)
|
||||
|
||||
Dependencies:
|
||||
- Tailwind CSS for styling
|
||||
- HTMX (optional, for dynamic pagination)
|
||||
|
||||
Accessibility:
|
||||
- Uses nav element with aria-label="Pagination"
|
||||
- Current page marked with aria-current="page"
|
||||
- Previous/Next buttons have aria-labels
|
||||
- Disabled buttons use aria-disabled
|
||||
{% endcomment %}
|
||||
|
||||
{% if page_obj.has_other_pages %}
|
||||
<nav class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6" aria-label="Pagination">
|
||||
<div class="hidden sm:block">
|
||||
<p class="text-sm text-gray-700">
|
||||
Showing
|
||||
<span class="font-medium">{{ page_obj.start_index }}</span>
|
||||
to
|
||||
<span class="font-medium">{{ page_obj.end_index }}</span>
|
||||
of
|
||||
<span class="font-medium">{{ page_obj.paginator.count }}</span>
|
||||
results
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex justify-between sm:justify-end">
|
||||
{% if page_obj.has_previous %}
|
||||
<a
|
||||
href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ page_obj.previous_page_number }}"
|
||||
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
|
||||
aria-label="Go to previous page"
|
||||
>
|
||||
<svg class="mr-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
Previous
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-400 bg-gray-100 cursor-not-allowed">
|
||||
<svg class="mr-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
Previous
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<!-- Page numbers for larger screens -->
|
||||
<div class="hidden md:flex">
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if num == page_obj.number %}
|
||||
<span class="relative inline-flex items-center px-4 py-2 border border-blue-500 bg-blue-50 text-sm font-medium text-blue-600 mx-1">
|
||||
{{ num }}
|
||||
</span>
|
||||
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
||||
<a
|
||||
href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ num }}"
|
||||
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 mx-1 transition-colors"
|
||||
aria-label="Go to page {{ num }}"
|
||||
>
|
||||
{{ num }}
|
||||
</a>
|
||||
{% elif num == 1 or num == page_obj.paginator.num_pages %}
|
||||
<a
|
||||
href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ num }}"
|
||||
class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 mx-1 transition-colors"
|
||||
aria-label="Go to page {{ num }}"
|
||||
>
|
||||
{{ num }}
|
||||
</a>
|
||||
{% elif num == page_obj.number|add:'-4' or num == page_obj.number|add:'4' %}
|
||||
<span class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-500 mx-1">
|
||||
...
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a
|
||||
href="?{% if request.GET.urlencode %}{{ request.GET.urlencode }}&{% endif %}page={{ page_obj.next_page_number }}"
|
||||
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
|
||||
aria-label="Go to next page"
|
||||
>
|
||||
Next
|
||||
<svg class="ml-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-400 bg-gray-100 cursor-not-allowed">
|
||||
Next
|
||||
<svg class="ml-2 h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
{% with use_htmx=use_htmx|default:False hx_target=hx_target|default:'#results' hx_swap=hx_swap|default:'innerHTML' size=size|default:'md' show_info=show_info|default:True %}
|
||||
|
||||
{# Size-based classes #}
|
||||
{% if size == 'sm' %}
|
||||
{% with btn_padding='px-2 py-1 text-xs' info_class='text-xs' %}
|
||||
{% include 'components/pagination_inner.html' %}
|
||||
{% endwith %}
|
||||
{% elif size == 'lg' %}
|
||||
{% with btn_padding='px-5 py-3 text-base' info_class='text-base' %}
|
||||
{% include 'components/pagination_inner.html' %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
{% with btn_padding='px-4 py-2 text-sm' info_class='text-sm' %}
|
||||
{% include 'components/pagination_inner.html' %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user