Column Header Icons¶
Added in: Milestone 2 (Phase 2)
Column header icons provide a flexible system for adding interactive elements to table column headers. While commonly used for sorting indicators, the system is extensible - any plugin can add custom icons for any purpose.
Overview¶
The column header icon system allows plugins to: - Add clickable icons to column headers - Provide dynamic behavior based on current state - Control icon display with conditional logic - Order multiple icons per column
ColumnHeaderIcon Dataclass¶
The ColumnHeaderIcon dataclass defines a column header icon:
from dataclasses import dataclass
from typing import Optional, Callable
@dataclass
class ColumnHeaderIcon:
"""Configuration for a column header icon."""
icon_template: Callable[[object, str], str] # Icon template path
url: Callable[[object, str], str] # Icon URL
title: Callable[[object, str], str] # Tooltip text
css_class: str = '' # CSS classes for icon
order: int = 100 # Display order
condition: Optional[Callable[[object, str], bool]] = None # Display condition
identifier: Optional[str] = None # Unique ID
Callable Attributes¶
All main attributes are callables that receive (view, column_name):
icon_template(view, column_name) → str
- Returns path to icon template
- Example: 'djadmin/icons/sort-up.html'
url(view, column_name) → str
- Returns URL for icon link/button
- Example: '/djadmin/products/?ordering=name'
title(view, column_name) → str
- Returns tooltip text
- Example: 'Sort by name (ascending)'
condition(view, column_name) → bool (optional) - Returns whether icon should display - Example: Check if column is orderable
Other Attributes¶
css_class (optional)
- CSS classes applied to icon link
- Example: 'sort-icon active'
order (optional, default: 100) - Controls icon display order when multiple icons exist - Lower values appear first
identifier (optional) - Unique string identifier for the icon - Useful for debugging
Plugin Hook¶
Plugins register column header icons via the djadmin_get_column_header_icons hook:
# myapp/djadmin_hooks.py
from djadmin.plugins import hookimpl
from djadmin.dataclasses import ColumnHeaderIcon
from djadmin.actions.list_view import ListAction
@hookimpl
def djadmin_get_column_header_icons(action):
"""Register column header icons for ListView."""
def get_icon_template(view, column_name):
# Dynamic icon selection based on state
if is_active(view, column_name):
return 'myapp/icons/active.html'
return 'myapp/icons/inactive.html'
def get_icon_url(view, column_name):
# Generate URL for icon action
return view.model_admin.get_action_url(column_name)
def get_icon_title(view, column_name):
return f"Toggle {column_name}"
def should_display(view, column_name):
# Only show for certain columns
return hasattr(view.model_admin, 'toggleable_columns') and \
column_name in view.model_admin.toggleable_columns
return {
ListAction: [
ColumnHeaderIcon(
icon_template=get_icon_template,
url=get_icon_url,
title=get_icon_title,
condition=should_display,
order=50,
identifier='toggle_icon',
)
]
}
Built-in Icons: Sort Indicators (djadmin-filters)¶
The djadmin-filters plugin uses column header icons for sorting:
# djadmin_filters/djadmin_hooks.py (simplified)
def get_sort_icon_template(view, column_name):
"""Determine which icon to show based on current sort state."""
current_ordering = view.request.GET.get('ordering', '')
if current_ordering == column_name:
return 'djadmin/icons/sort-up.html' # Ascending
elif current_ordering == f'-{column_name}':
return 'djadmin/icons/sort-down.html' # Descending
else:
return 'djadmin/icons/sort.html' # Neutral
def get_sort_url(view, column_name):
"""Generate URL that toggles sort state."""
from django.template import Template, Context
current_ordering = view.request.GET.get('ordering', '')
# Toggle logic: none → asc → desc → none
if current_ordering == column_name:
new_ordering = f'-{column_name}' # Switch to descending
elif current_ordering == f'-{column_name}':
new_ordering = None # Clear ordering
else:
new_ordering = column_name # Set ascending
# Use querystring tag to preserve other params
template = Template("{% load djadmin_tags %}{% querystring ordering=new_ordering %}")
return template.render(Context({
'request': view.request,
'new_ordering': new_ordering,
}))
def get_sort_title(view, column_name):
"""Generate tooltip text."""
current_ordering = view.request.GET.get('ordering', '')
if current_ordering == column_name:
return f"Sort by {column_name} (descending)"
elif current_ordering == f'-{column_name}':
return f"Clear sorting"
else:
return f"Sort by {column_name}"
def should_display_sort_icon(view, column_name):
"""Only show for columns with order=True."""
column = view.get_column_by_name(column_name)
return column and column.order and column.order.enabled
Template Rendering¶
Icons are rendered in column headers via the render_column_header_icons template tag:
{# djadmin/templates/djadmin/{app}/{model}_list.html or actions/list.html #}
{% load djadmin_tags %}
<thead>
<tr>
{% for column in columns %}
<th>
{{ column.label }}
{% render_column_header_icons column.field %}
</th>
{% endfor %}
</tr>
</thead>
The template tag renders the icons using this template:
{# djadmin/templates/djadmin/includes/_column_header_icons.html #}
{% if icons %}
<span class="column-header-icons">
{% for icon in icons %}
<a href="{{ icon.url }}"
title="{{ icon.title }}"
class="column-header-icon {{ icon.css_class }}">
{% include icon.icon_template %}
</a>
{% endfor %}
</span>
{% endif %}
Styling¶
Column header icons use the theme's styles:
/* Icon container */
.column-header-icons {
display: inline-flex;
gap: 0.25rem;
margin-left: 0.5rem;
align-items: center;
}
/* Individual icon link */
.column-header-icon {
display: inline-flex;
align-items: center;
color: #6b7280;
transition: color 0.2s;
}
.column-header-icon:hover {
color: #111827;
}
/* Dark mode */
.dark .column-header-icon {
color: #9ca3af;
}
.dark .column-header-icon:hover {
color: #f9fafb;
}
/* Icon SVG sizing */
.column-header-icon svg {
width: 1rem;
height: 1rem;
}
Example: Help Icon¶
Add help icons to column headers with documentation links:
# myapp/djadmin_hooks.py
from djadmin.plugins import hookimpl
from djadmin.dataclasses import ColumnHeaderIcon
from djadmin.actions.list_view import ListAction
@hookimpl
def djadmin_get_column_header_icons(action):
"""Add help icons to columns with documentation."""
def get_help_url(view, column_name):
# Get help URL from model admin configuration
help_urls = getattr(view.model_admin, 'column_help_urls', {})
return help_urls.get(column_name, '#')
def get_help_title(view, column_name):
return f"Help for {column_name}"
def has_help(view, column_name):
help_urls = getattr(view.model_admin, 'column_help_urls', {})
return column_name in help_urls
return {
ListAction: [
ColumnHeaderIcon(
icon_template=lambda v, c: 'myapp/icons/help.html',
url=get_help_url,
title=get_help_title,
condition=has_help,
order=200, # After sort icons
css_class='help-icon',
identifier='column_help',
)
]
}
Usage in ModelAdmin:
from djadmin import ModelAdmin, register
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = ['name', 'sku', 'price']
# Map column names to help URLs
column_help_urls = {
'sku': 'https://docs.example.com/sku',
'price': 'https://docs.example.com/pricing',
}
Icon Template Example¶
Icon templates should be simple SVG includes:
{# myapp/templates/myapp/icons/help.html #}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round"
stroke-linejoin="round"
d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" />
</svg>
See Also¶
- Sidebar Widgets - Add widgets to sidebar
- Ordering (djadmin-filters) - Built-in sort icons
- Plugin Development Guide - Creating plugins
- Hook Reference - All available hooks