diff --git a/base/components/components.py b/base/components/components.py index 9cc72c7d..7efce844 100644 --- a/base/components/components.py +++ b/base/components/components.py @@ -3,6 +3,7 @@ from django_components import Component, register from pydantic import BaseModel +from base.main import TAB_VAR, ObjectList from base.pagination import PAGE_VAR, Pagination from base.templatetags.base_templatetags import querystring @@ -68,3 +69,35 @@ def get_template_data(self, args, kwargs, slots, context): "next_page_link": next_page_link, "page_elements": page_elements, } + + +class TabItem(BaseModel): + text: str + is_current: bool + attrs: Optional[dict] + + +@register("sorting_tabs") +class SortingTabs(Component): + template_file = "sorting_tabs.html" + + class Kwargs(BaseModel): + object_list: ObjectList + model_config = {"arbitrary_types_allowed": True} + + def create_tab(self, object_list, tab): + verbose_text = tab.replace("_", " ").capitalize() + is_current = tab == object_list.current_tab + link = querystring(None, {**object_list.params, TAB_VAR: tab}) + attrs = {"href": link} + if is_current: + attrs["aria-selected"] = "true" + return TabItem(text=verbose_text, is_current=is_current, attrs=attrs) + + def get_template_data(self, args, kwargs, slots, context): + object_list = kwargs.object_list + tabs = [self.create_tab(object_list, tab) for tab in object_list.sorting_tabs] + return { + "tabs": tabs, + "object_list": object_list, + } diff --git a/base/components/sorting_tabs.html b/base/components/sorting_tabs.html new file mode 100644 index 00000000..99a39c94 --- /dev/null +++ b/base/components/sorting_tabs.html @@ -0,0 +1,8 @@ + diff --git a/base/main.py b/base/main.py index ec29a514..0ce5f1cd 100644 --- a/base/main.py +++ b/base/main.py @@ -1,21 +1,42 @@ from .pagination import PAGE_VAR, Pagination +TAB_VAR = "tab" + class ObjectList: pagination_class = Pagination + base_ordering = () + sorting_tabs = {} def __init__(self, request, model, queryset, list_per_page): self.model = model + self.opts = model._meta self.queryset = queryset self.list_per_page = list_per_page - self.params = dict(request.GET.lists()) + self.params = dict(request.GET.dict()) + self.current_tab = self.params.get(TAB_VAR, None) + if self.opts.ordering: + self.base_ordering = self.opts.ordering if PAGE_VAR in self.params: del self.params[PAGE_VAR] - self.result_objects = self.get_objects(request) + self.result_objects = self.get_objects(request, queryset) def __iter__(self): return iter(self.result_objects) + def tab_sort(self, queryset): + result_queryset = queryset + if self.current_tab: + sort_value = self.sorting_tabs[self.current_tab] + result_queryset = result_queryset.order_by(*sort_value, *self.base_ordering) + else: + for tab_name, tab_order in self.sorting_tabs.items(): + if tab_order == self.base_ordering: + self.current_tab = tab_name + break + + return result_queryset + def paginate(self, request, queryset): pagination = self.pagination_class( request, @@ -26,6 +47,7 @@ def paginate(self, request, queryset): self.pagination = pagination return pagination.get_objects() - def get_objects(self, request): - paginate_result = self.paginate(request, self.queryset) + def get_objects(self, request, queryset): + tab_result = self.tab_sort(queryset) + paginate_result = self.paginate(request, tab_result) return paginate_result diff --git a/cab/main.py b/cab/main.py new file mode 100644 index 00000000..79ac08e5 --- /dev/null +++ b/cab/main.py @@ -0,0 +1,10 @@ +from base.main import ObjectList + + +class SnippetList(ObjectList): + sorting_tabs = { + "newest": ("-pub_date",), + "latest_updated": ("-updated_date",), + "highest_rated": ("-rating_score",), + "most_bookmarked": ("-bookmark_count",), + } diff --git a/cab/utils.py b/cab/utils.py index 34dafd87..48e93c54 100644 --- a/cab/utils.py +++ b/cab/utils.py @@ -7,9 +7,10 @@ from django.utils.safestring import mark_safe from markdown import markdown as markdown_func -from base.main import ObjectList from base.pagination import Pagination +from .main import SnippetList + def object_list( request, @@ -45,7 +46,7 @@ def object_list( opts = model._meta if paginate_by: if queryset.model == Snippet: - object_list = ObjectList(request, queryset.model, queryset, 15) + object_list = SnippetList(request, queryset.model, queryset, 15) pagination = object_list.pagination else: pagination = Pagination(request, model, queryset, paginate_by) diff --git a/djangosnippets/templates/base.html b/djangosnippets/templates/base.html index 2441d9f7..d5eb8d8f 100644 --- a/djangosnippets/templates/base.html +++ b/djangosnippets/templates/base.html @@ -8,7 +8,7 @@ - + {% block feeds %}{% endblock %} @@ -62,6 +62,7 @@

{% block content_header %}{% endblock %}

+ {% block new_content_header %}{% endblock %} {% with current_url_name=request.resolver_match.url_name %}
{% block content %} diff --git a/djangosnippets/templates/cab/snippet_list.html b/djangosnippets/templates/cab/snippet_list.html index 1d8823c1..e3a5b914 100644 --- a/djangosnippets/templates/cab/snippet_list.html +++ b/djangosnippets/templates/cab/snippet_list.html @@ -2,17 +2,22 @@ {% load core_tags %} {% load static %} {% block bodyclass %}snippet-list{% endblock %} -{% block head_title %}All snippets{% if months %} last {{ months }} months{% endif %}{% endblock %} +{% block head_title %}Snippet list{% endblock %} -{% block content_header %}All snippets{% if months %} last {{ months }} months{% endif %}{% endblock %} +{% block new_content_header %} +
+

{{ hits }} snippet{{ hits|pluralize }}

+
+
+ {% component 'sorting_tabs' object_list=object_list / %} +
+
+{% endblock %} {% block content %} {% if object_list %} {% component 'snippet_list' snippet_list=object_list / %} {% component 'pagination' pagination_obj=pagination / %} -

{{ hits }} snippet{{ hits|pluralize }} posted so far.

- {% else %} -

No snippets posted yet.

{% endif %} {% endblock %} diff --git a/djangosnippets/templates/cab/user_detail.html b/djangosnippets/templates/cab/user_detail.html index 1c903bb3..310e9349 100644 --- a/djangosnippets/templates/cab/user_detail.html +++ b/djangosnippets/templates/cab/user_detail.html @@ -7,14 +7,21 @@ {% block head_title %}Snippets by {{ author.username }}{% if months %}, last {{ months }} months{% endif %}{% endblock %} -{% block content_header %}Snippets by {{ author.username }}{% if months %}, last {{ months }} months{% endif %}{% endblock %} +{% block new_content_header %} +
+

Snippet{{ hits|pluralize }} by {{ author.username }}

+
+
+ {% component 'sorting_tabs' object_list=object_list / %} +
+
+{% endblock %} {% block content %} {% if object_list %} {% component 'snippet_list' snippet_list=object_list / %} {% cache 600 author_detail_sidebar author.username %} -

{% if request.user.username == author.username %}You've{% else %}{{ author.username }} has{% endif %} posted {{ author.snippet_set.count }} snippet{{ author.snippet_set.count|pluralize }}.

{% endcache %} {% component "pagination" pagination_obj=pagination / %} {% else %} diff --git a/theme/static_src/src/styles.css b/theme/static_src/src/styles.css index 910e1ee2..3e94d371 100644 --- a/theme/static_src/src/styles.css +++ b/theme/static_src/src/styles.css @@ -7,6 +7,7 @@ --color-base-gray-400: #7a7a7a; --color-base-green-400: #256918; --color-base-green-800: #12330c; + --font-header: "Libertinus Sans", sans-serif; --font-title: "Playfair Display", sans-serif; --font-text: "Nunito", sans-serif; --font-common: "Raleway", sans-serif;