Compare commits

...

56 Commits

Author SHA1 Message Date
Daniel Sheppard
49bcf57834 Update Style 2025-02-19 14:58:34 -06:00
Daniel Sheppard
01cb7beb68 Update bundle 2025-02-13 10:51:48 -06:00
Daniel Sheppard
762ebe23f9 Merge branch '9583-add_column_specific_search_field_to_tables' of https://github.com/netbox-community/netbox into 9583-add_column_specific_search_field_to_tables 2025-02-13 10:50:13 -06:00
Daniel Sheppard
714495486b Merge branch 'feature' of https://github.com/netbox-community/netbox into 9583-add_column_specific_search_field_to_tables 2025-02-13 10:48:14 -06:00
Daniel Sheppard
e7649f0cc2 Fix test error 2025-01-20 18:45:28 -06:00
Daniel Sheppard
434cf37988 Fix test error and OOB-swap 2025-01-20 18:27:26 -06:00
Daniel Sheppard
451a1f498d Remove debug 2025-01-20 17:35:20 -06:00
Daniel Sheppard
694fcd3a79 Fix assets 2025-01-20 17:31:16 -06:00
Daniel Sheppard
7e5d8eaf05 Debugging 2025-01-20 17:21:29 -06:00
Daniel Sheppard
1958965c36 More bundle debugging 2025-01-20 17:16:33 -06:00
Daniel Sheppard
e12ae3acde Figure out what is failing 2025-01-20 17:11:11 -06:00
Daniel Sheppard
a28871296c Fix assets 2025-01-20 17:07:25 -06:00
Daniel Sheppard
ee83895e2a Fix bundles 2025-01-20 16:42:59 -06:00
Daniel Sheppard
764c676ffc Fix bundles 2025-01-20 15:54:07 -06:00
Daniel Sheppard
92f6f426b3 Removed unused import 2025-01-20 14:51:15 -06:00
Daniel Sheppard
4381ea9265 Merge Head 2025-01-20 14:24:47 -06:00
Daniel Sheppard
92c3bbc9db Correct issue with filter_form not passing through render_table from django_tables2 2025-01-20 14:21:41 -06:00
Daniel Sheppard
8d65973c82 Simplify table header filter rendering 2025-01-20 13:13:19 -06:00
Daniel Sheppard
882aa30043 Simplify filter generation 2025-01-20 12:06:26 -06:00
Daniel Sheppard
f2c74db761 Update display of filtering chits 2025-01-20 12:03:41 -06:00
Daniel Sheppard
852535e3d4 Merge in updates from feature 2025-01-20 11:41:26 -06:00
Jeremy Stretch
012e815f3b Merge branch 'feature' into 9583-add_column_specific_search_field_to_tables 2024-12-06 15:59:06 -05:00
Daniel Sheppard
d6ab7b7378 Update bundle 2024-10-16 22:24:48 -05:00
Daniel Sheppard
77cb8ac817 Merge branch 'feature' into 9583-add_column_specific_search_field_to_tables 2024-10-16 22:13:01 -05:00
Daniel Sheppard
b12ce97474 Update to latest feature 2024-07-01 22:27:15 -05:00
Daniel Sheppard
0ad1db9912 Regnerate CSS 2024-06-12 23:22:13 -05:00
Daniel Sheppard
f1306783dc Merge branch 'feature' of https://github.com/netbox-community/netbox into 9583-add_column_specific_search_field_to_tables
# Conflicts:
#	netbox/project-static/dist/netbox.css
#	netbox/templates/htmx/table.html
#	netbox/templates/inc/table_htmx.html
2024-06-12 23:10:25 -05:00
Daniel Sheppard
30e653197e Add a table form override and add a table column remap option 2024-06-12 23:01:33 -05:00
Daniel Sheppard
fd38255d31 Rename swap variable back and remove join 2024-04-25 13:04:37 -05:00
Daniel Sheppard
5830ae9e9a Rename variable for doing OOB swaps on the table 2024-04-25 12:53:54 -05:00
Daniel Sheppard
479c69bd2d Fix up duplication of template chits when rendering HTMX 2024-04-22 22:42:42 -05:00
Daniel Sheppard
1c995fa5f9 Perform OOB swap for filter badges 2024-04-15 08:43:51 -05:00
Daniel Sheppard
35cff12974 Apply suggestions from Arthur 2024-04-15 08:10:40 -05:00
Daniel Sheppard
a9aa0cbaa0 Merge in latest feature 2024-04-15 07:30:28 -05:00
Daniel Sheppard
8ad79a64ae Update CSS 2024-03-21 23:18:31 -05:00
Daniel Sheppard
a422a3cd98 Modify logic for table column filtering to further isolate the column filtering and the filterset tab 2024-03-21 23:09:30 -05:00
Daniel Sheppard
8a7df0b98d Update netbox/utilities/templatetags/form_helpers.py
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-21 22:37:06 -05:00
Daniel Sheppard
f257f4aad4 Apply suggestions from code review
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-21 22:18:34 -05:00
Jeremy Stretch
25a4e9448c Merge branch 'feature' into 9583-add_column_specific_search_field_to_tables 2024-03-20 15:02:12 -04:00
Jeremy Stretch
77bfd620c3 Merge branch 'feature' into 9583-add_column_specific_search_field_to_tables 2024-03-07 08:50:17 -05:00
Daniel Sheppard
84151cbc1a Fix tom-select errors related to field id. Break out render_field function for column filters 2024-02-13 15:23:14 -06:00
Daniel Sheppard
0309796bbb Fix extraneous __all__ entry 2024-02-12 16:09:02 -06:00
Daniel Sheppard
f81f76f862 Optimizations 2024-02-12 16:00:19 -06:00
Daniel Sheppard
c3f1a9601c Update CSS after merge 2024-02-12 09:49:15 -06:00
Daniel Sheppard
4ae6683abc Merge in recent feture changes 2024-02-12 09:44:30 -06:00
Daniel Sheppard
06c1aff04f Fix test failure 2024-02-12 08:17:53 -06:00
Daniel Sheppard
5f69666b7b Change dropdown position and fix test failure 2024-02-11 21:37:33 -06:00
Daniel Sheppard
b54cfd6ba9 Update CSS 2024-02-11 21:17:06 -06:00
Daniel Sheppard
3243ebd1dd Merge remote-tracking branch 'origin/9583-add_column_specific_search_field_to_tables' into 9583-add_column_specific_search_field_to_tables 2024-02-09 16:37:47 -06:00
Daniel Sheppard
03f67f373f CSS update for dropdown 2024-02-09 16:36:13 -06:00
Daniel Sheppard
50557c0f9d Final work on #9583 for basic functionality 2024-02-09 16:35:09 -06:00
Daniel Sheppard
cc423f5071 Apply some quick fixes and add comments 2024-02-07 13:48:32 -06:00
Daniel Sheppard
664a0eba6d Preliminary work on 9583. 2024-02-07 13:40:20 -06:00
Daniel Sheppard
f7294f7087 Preliminary work on 9583. 2024-02-07 13:37:57 -06:00
Daniel Sheppard
e762755e80 HTMX work on 9583. 2024-02-07 12:05:11 -06:00
Daniel Sheppard
4c39516253 Preliminary work on 9583. 2024-02-02 15:49:32 -06:00
12 changed files with 135 additions and 27 deletions

View File

@@ -44,6 +44,7 @@ class BaseTable(tables.Table):
:param user: Personalize table display for the given user (optional). Has no effect if AnonymousUser is passed.
"""
exempt_columns = ()
filterset_form = None
class Meta:
attrs = {

View File

@@ -170,6 +170,20 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
# Render the objects table
table = self.get_table(self.queryset, request, has_bulk_actions)
# Check for filterset_form(s) on this view and/or the table, if a form exists:
# * If both exist, initialize both
# * If a filterset form for the table exists, only initialize the table filterset_form
# * If a filterset form exists for the view, initialize the filterset form
# * Apply to the table for use by the table and initialize a separate instance of the form for use by the table
# column filters
# * Otherwise set to None
if self.filterset_form:
filterset_form = self.filterset_form(request.GET)
table.filterset_form = self.filterset_form(request.GET)
else:
filterset_form = None
table.filterset_form = None
# If this is an HTMX request, return only the rendered table HTML
if htmx_partial(request):
if request.GET.get('embedded', False):
@@ -179,15 +193,15 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
table.columns.hide('pk')
return render(request, 'htmx/table.html', {
'table': table,
'filter_form': filterset_form,
'model': model,
'actions': actions,
})
context = {
'model': model,
'table': table,
'actions': actions,
'filter_form': self.filterset_form(request.GET) if self.filterset_form else None,
'filter_form': filterset_form,
'prerequisite_model': get_prerequisite_model(self.queryset),
**self.get_extra_context(request),
}

File diff suppressed because one or more lines are too long

View File

@@ -29,6 +29,23 @@ span.color-label {
opacity: 0;
}
// Override bootstrap "dropdown" positioning and display for column filters
.column-filter {
position: static;
display: inline;
}
// Override mdi font-size to adjust filter icon size
.column-filter.dropdown > .dropdown-toggle > .mdi-filter-settings {
font-size: .625rem;
}
.column-filter.dropdown > .dropdown-menu {
max-width: 300px;
}
.column-filter.dropdown-toggle:after { content: none }
// NetBox edition text
.netbox-edition {
letter-spacing: .15rem;

View File

@@ -66,9 +66,8 @@ Context:
{# Object list tab #}
<div class="tab-pane show active" id="object-list" role="tabpanel" aria-labelledby="object-list-tab">
{# Applied filters #}
{% if filter_form %}
{% if not request.htmx %}
{# Applied filters #}
{% applied_filters model filter_form request.GET %}
{% endif %}

View File

@@ -3,6 +3,11 @@
{% load buttons %}
{% load render_table from django_tables2 %}
{% if request.htmx %}
{# OOB Swaps to update various components #}
{% applied_filters model filter_form request.GET %}
{% endif %}
<div class="htmx-container table-responsive">
{% with preferences|get_key:"pagination.placement" as paginator_placement %}
{% if paginator_placement == 'top' or paginator_placement == 'both' %}

View File

@@ -5,7 +5,7 @@
<div class="col-auto d-print-none">
<div class="input-group input-group-flat me-2 quicksearch" hx-disinherit="hx-select hx-swap">
<input type="search" results="5" name="q" id="quicksearch" class="form-control" placeholder="{% trans "Quick search" %}"
hx-get="{{ request.full_path }}" hx-target="#object_list" hx-trigger="keyup changed delay:500ms, search"/>
hx-get="" hx-target="#object_list" hx-trigger="keyup changed delay:500ms, search"/>
<span class="input-group-text py-1">
<a href="#" id="quicksearch_clear" class="invisible text-secondary"><i class="mdi mdi-close-circle"></i></a>
</span>

View File

@@ -1,4 +1,5 @@
{% load django_tables2 %}
{% load form_helpers %}
{% load i18n %}
<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %} hx-disinherit="hx-target hx-select" hx-swap="outerHTML">
{% if table.show_header %}
@@ -15,12 +16,15 @@
<a href="#"
hx-get="{{ table.htmx_url }}{% querystring table.prefixed_order_by_field='' %}"
class="text-danger"
{% if not table.embedded %}hx-push-url="true"{% endif %}
title="{% trans "Clear ordering" %}"
><i class="mdi mdi-close"></i></a>
</div>
{% endif %}
{% render_table_filter_field column.name table request%}
<a href="#"
hx-get="{{ table.htmx_url }}{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}"
hx-get="{{ table.htmx_url }}{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}"
{% if not table.embedded %}hx-push-url="true"{% endif %}
>{{ column.header }}</a>
</th>
{% else %}

View File

@@ -0,0 +1,11 @@
{% load form_helpers %}
{% if field %}
<div class="column-filter dropdown">
<a href="#" class="dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside"><i class="mdi mdi-filter-settings"></i></a>
<div class="dropdown-menu">
<div class="px-3 py-3">
{% include "form_helpers/render_field.html" %}
</div>
</div>
</div>
{% endif %}

View File

@@ -1,20 +1,22 @@
{% load i18n %}
{% if applied_filters %}
<div class="mb-3">
{% for filter in applied_filters %}
<a href="{{ filter.link_url }}" class="badge rounded-pill text-bg-primary text-decoration-none me-1">
<i class="mdi mdi-close"></i> {{ filter.link_text }}
</a>
{% endfor %}
{% if applied_filters|length > 1 %}
<a href="?" class="badge rounded-pill text-bg-danger text-decoration-none me-1">
<i class="mdi mdi-tag-off"></i> {% trans "Clear all" %}
</a>
{% endif %}
{% if save_link %}
<a href="{{ save_link }}" class="badge rounded-pill text-bg-success text-decoration-none me-1">
<i class="mdi mdi-content-save"></i> {% trans "Save" %}
</a>
{% endif %}
</div>
{% endif %}
<div id="applied_filters_pane" hx-swap-oob="true">
{% if applied_filters %}
<div class="mb-3">
{% for filter in applied_filters %}
<a href="{{ filter.link_url }}" class="badge rounded-pill text-bg-primary text-decoration-none me-1">
<i class="mdi mdi-close"></i> {{ filter.link_text }}
</a>
{% endfor %}
{% if applied_filters|length > 1 %}
<a href="?" class="badge rounded-pill text-bg-danger text-decoration-none me-1">
<i class="mdi mdi-tag-off"></i> {% trans "Clear all" %}
</a>
{% endif %}
{% if save_link %}
<a href="{{ save_link }}" class="badge rounded-pill text-bg-success text-decoration-none me-1">
<i class="mdi mdi-content-save"></i> {% trans "Save" %}
</a>
{% endif %}
</div>
{% endif %}
</div>

View File

@@ -7,10 +7,12 @@ __all__ = (
'render_custom_fields',
'render_errors',
'render_field',
'render_table_filter_field',
'render_form',
'widget_type',
)
from utilities.templatetags.helpers import querystring
register = template.Library()
@@ -30,6 +32,11 @@ def getfield(form, fieldname):
return None
@register.filter()
def get_filter_field(form, fieldname):
return getfield(form, f'{fieldname}') or getfield(form, f'{fieldname}_id')
@register.filter(name='widget_type')
def widget_type(field):
"""
@@ -47,6 +54,7 @@ def widget_type(field):
# Inclusion tags
#
@register.inclusion_tag('form_helpers/render_fieldset.html')
def render_fieldset(form, fieldset):
"""
@@ -109,6 +117,7 @@ def render_field(field, bulk_nullable=False, label=None):
"""
Render a single form field from template
"""
return {
'field': field,
'label': label or field.label,
@@ -116,6 +125,49 @@ def render_field(field, bulk_nullable=False, label=None):
}
@register.inclusion_tag('form_helpers/render_table_filter_field.html')
def render_table_filter_field(fieldname, table, request):
"""
Render a single form field for table column filters from template
"""
url = ""
field = None
# Does this table have a filterset form?
if hasattr(table, 'filterset_form') and table.filterset_form is not None:
# Get the filterset field
field = get_filter_field(table.filterset_form, fieldname)
# Return if no filterset field
if field is None:
return {}
# Handle filter forms
if table:
# Build kwargs for querystring function
kwargs = {field.name: None}
# Build request url
if request and table.htmx_url:
url = table.htmx_url + querystring(request, **kwargs)
elif request:
url = querystring(request, **kwargs)
# Set HTMX args
if hasattr(field.field, 'widget'):
field.field.widget.attrs.update({
'id': f'table_filter_id_{field.name}',
'hx-get': url if url else '#',
'hx-push-url': "true",
'hx-trigger': 'hidden.bs.dropdown from:closest .dropdown'
})
return {
'field': field,
'label': None,
'bulk_nullable': False,
}
@register.inclusion_tag('form_helpers/render_custom_fields.html')
def render_custom_fields(form):
"""

View File

@@ -270,6 +270,8 @@ def applied_filters(context, model, form, query_params):
Display the active filters for a given filter form.
"""
user = context['request'].user
if not form:
return
form.is_valid() # Ensure cleaned_data has been set
applied_filters = []
@@ -304,6 +306,7 @@ def applied_filters(context, model, form, query_params):
save_link = f"{url}?object_types={object_type}&parameters={quote(parameters)}"
return {
'request': context['request'],
'applied_filters': applied_filters,
'save_link': save_link,
}