Skip to content

Commit 8c3c764

Browse files
authored
Updated SnippetList new feature. (#592)
1 parent c735163 commit 8c3c764

File tree

15 files changed

+219
-163
lines changed

15 files changed

+219
-163
lines changed

base/components/components.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@
77
from base.templatetags.base_templatetags import querystring
88

99

10+
@register("icon")
11+
class Icon(Component):
12+
13+
class Kwargs(BaseModel):
14+
kind: Literal["heart", "bookmark"]
15+
color: str
16+
label: str
17+
18+
template_file = "icon.html"
19+
20+
def get_template_data(self, args, kwargs, slots, context):
21+
return {
22+
"kind": kwargs.kind,
23+
"label": kwargs.label,
24+
"color": kwargs.color,
25+
"classes": "w-[18px] h-[18px]",
26+
}
27+
28+
1029
class PaginationItem(BaseModel):
1130
kind: Literal["current", "ellipsis", "number"]
1231
text: Optional[str | int] = None

base/components/icon.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{% if kind == 'heart' %}
2+
<svg {% html_attrs aria-label=label class=classes fill=color %} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M305 151.1L320 171.8L335 151.1C360 116.5 400.2 96 442.9 96C516.4 96 576 155.6 576 229.1L576 231.7C576 343.9 436.1 474.2 363.1 529.9C350.7 539.3 335.5 544 320 544C304.5 544 289.2 539.4 276.9 529.9C203.9 474.2 64 343.9 64 231.7L64 229.1C64 155.6 123.6 96 197.1 96C239.8 96 280 116.5 305 151.1z"/></svg>
3+
{% elif kind == 'bookmark' %}
4+
<svg {% html_attrs aria-label=label class=classes fill=color %} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M192 64C156.7 64 128 92.7 128 128L128 544C128 555.5 134.2 566.2 144.2 571.8C154.2 577.4 166.5 577.3 176.4 571.4L320 485.3L463.5 571.4C473.4 577.3 485.7 577.5 495.7 571.8C505.7 566.1 512 555.5 512 544L512 128C512 92.7 483.3 64 448 64L192 64z"/></svg>
5+
{% endif %}

base/main.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from .pagination import PAGE_VAR, Pagination
2+
3+
4+
class ObjectList:
5+
pagination_class = Pagination
6+
7+
def __init__(self, request, model, queryset, list_per_page):
8+
self.model = model
9+
self.queryset = queryset
10+
self.list_per_page = list_per_page
11+
self.params = dict(request.GET.lists())
12+
if PAGE_VAR in self.params:
13+
del self.params[PAGE_VAR]
14+
self.result_objects = self.get_objects(request)
15+
16+
def __iter__(self):
17+
return iter(self.result_objects)
18+
19+
def paginate(self, request, queryset):
20+
pagination = self.pagination_class(
21+
request,
22+
self.model,
23+
queryset,
24+
self.list_per_page,
25+
)
26+
self.pagination = pagination
27+
return pagination.get_objects()
28+
29+
def get_objects(self, request):
30+
paginate_result = self.paginate(request, self.queryset)
31+
return paginate_result

cab/components/components.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from django_components import Component, register
2+
from pydantic import BaseModel
3+
4+
from base.main import ObjectList
5+
6+
7+
@register("snippet_list")
8+
class SnippetListComponent(Component):
9+
10+
template_file = "snippet_list.html"
11+
12+
class Kwargs(BaseModel):
13+
snippet_list: ObjectList
14+
model_config = {"arbitrary_types_allowed": True}
15+
16+
def get_template_data(self, args, kwargs, slots, context):
17+
return {
18+
"snippet_list": kwargs.snippet_list.result_objects,
19+
}

cab/components/snippet_list.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<section aria-labelledby="snippet-list-title">
2+
<h2 id="snippet-list-title" class="sr-only">Snippet List</h2>
3+
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 auto-rows-fr gap-8">
4+
{% for snippet in snippet_list %}
5+
<article class="flex flex-col flex-nowrap shadow transition-all duration-500 font-common hover:-translate-y-2 hover:shadow-xl">
6+
<header class="flex flex-auto flex-col p-4 mb-0 bg-transparent">
7+
<h3 class="my-2 bg-none truncate"><a class="text-2xl font-title text-base-black no-underline font-bold" href="{{ snippet.get_absolute_url }}" title="{{ snippet.title }}">{{ snippet.title }}</a></h3>
8+
<p class="line-clamp-6 my-6 font-text text-base">{{ snippet.description }}</p>
9+
<div class="mt-auto">
10+
<ul aria-label="tag list" class="m-0 my-2 inline-flex gap-2 flex-wrap list-none text-sm">
11+
{% for tag in snippet.tags.all %}
12+
<li class="py-1.5 px-2 border-2 text-base-white-400 bg-base-green-400 border-base-green-400 rounded-lg">{{ tag.name }}</li>
13+
{% endfor %}
14+
</ul>
15+
<div class="my-2 flex justify-between">
16+
<time aria-label="updated date">{{ snippet.updated_date|date:'D d M Y' }}</time>
17+
<a class="inline-flex items-center gap-2 cursor-pointer group underline underline-offset-4" href="{{ snippet.get_absolute_url }}">
18+
<span class="transition-colors">Read More</span>
19+
<svg class="w-6 h-6 group-hover:text-base-green-400" viewBox="0 0 24 24" fill="none">
20+
<line class="stroke-current stroke-2 -translate-x-2 opacity-0 group-hover:translate-x-0 group-hover:opacity-100 transition-all duration-300" x1="4" y1="12" x2="16" y2="12"/>
21+
<path class="stroke-current stroke-2 fill-none stroke-linecap-round stroke-linejoin-round group-hover:translate-x-1 transition-transform duration-300" d="M15 8l4 4-4 4"/>
22+
</svg>
23+
</a>
24+
</div>
25+
</div>
26+
</header>
27+
<footer class="p-4 flex items-center justify-between border-t-1 border-gray-300 before:content-none after:content-none">
28+
<div>
29+
<strong>Author: </strong>
30+
<a href="{{ snippet.author.get_absolute_url }}" class="underline underline-offset-4 hover:text-base-orange-400">{{ snippet.author.username }}</a>
31+
</div>
32+
<ul class="flex m-0 gap-4 font-text">
33+
<li class="flex items-center gap-2">
34+
{% component 'icon' kind='heart' label='rating' color='red' / %}
35+
<span>{{ snippet.rating_score }}</span>
36+
</li>
37+
<li class="flex items-center gap-2">
38+
{% component 'icon' kind='bookmark' label='bookmark' color='#ffd700' / %}
39+
<span>{{ snippet.bookmark_count }}</span>
40+
</li>
41+
</ul>
42+
</footer>
43+
</article>
44+
{% endfor %}
45+
</div>
46+
</section>

cab/utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from django.utils.safestring import mark_safe
88
from markdown import markdown as markdown_func
99

10+
from base.main import ObjectList
1011
from base.pagination import Pagination
1112

1213

@@ -35,23 +36,29 @@ def object_list(
3536
hits
3637
number of objects, total
3738
"""
39+
from cab.models import Snippet
40+
3841
if extra_context is None:
3942
extra_context = {}
4043
queryset = queryset._clone()
4144
model = queryset.model
4245
opts = model._meta
4346
if paginate_by:
44-
pagination = Pagination(request, model, queryset, paginate_by)
45-
object_list = pagination.get_objects()
47+
if queryset.model == Snippet:
48+
object_list = ObjectList(request, queryset.model, queryset, 15)
49+
pagination = object_list.pagination
50+
else:
51+
pagination = Pagination(request, model, queryset, paginate_by)
52+
object_list = pagination.get_objects()
4653

4754
context = {
48-
"%s_list" % template_object_name: object_list,
55+
"object_list": object_list,
4956
"pagination": pagination,
5057
"hits": pagination.result_count,
5158
}
5259
else:
5360
context = {
54-
"%s_list" % template_object_name: object_list,
61+
"object_list": queryset,
5562
}
5663
if not allow_empty and len(queryset) == 0:
5764
raise Http404

djangosnippets/templates/base.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
<link rel="shortcut icon" type="image/x-icon" href="{% static "img/favicon.ico" %}" />
77
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css">
88
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/themes/smoothness/jquery-ui.css" />
9+
<link rel="preconnect" href="https://fonts.googleapis.com">
10+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11+
<link href="https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Raleway:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
912
<link rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml" title="Feed of latest snippets" />
1013
<link rel="stylesheet" href="{% static "css/main.css" %}" type="text/css" />
1114
{% block feeds %}{% endblock %}
@@ -35,7 +38,7 @@
3538
</ul>
3639
</nav>
3740
</div></header>
38-
<div id="main">
41+
<div id="main" class="container mb-auto mx-auto w-7/8">
3942
{% block secondary_nav %}
4043
<nav id="subnav">
4144
<ul>
@@ -59,14 +62,16 @@
5962

6063
<div id="base-container">
6164
<h1>{% block content_header %}{% endblock %}</h1>
62-
<div id="content">
65+
{% with current_url_name=request.resolver_match.url_name %}
66+
<div id="content" class="{% if current_url_name == 'home' %}w-1/2{% else %}w-full{% endif %}">
6367
{% block content %}
6468
{% endblock %}
6569
</div>
66-
<div id="sidebar">
70+
<div id="sidebar" class="{% if current_url_name == 'home' %}w-1/2{% else %}hidden md:block md:w-1/4{% endif %}">
6771
{% block sidebar %}
6872
{% endblock %}
6973
</div>
74+
{% endwith %}
7075
{% block extra_content %}
7176
{% endblock %}
7277
</div>

djangosnippets/templates/cab/partials/most_bookmarked.html

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,11 @@
44

55
<h1>Most bookmarked snippets{% if months %} last {{ months }} months{% endif %}</h1>
66

7-
<div id="content">
7+
<div id="content" class="w-full">
88

99

1010
{% if object_list %}
11-
<table class="snippet_list">
12-
<thead>
13-
<tr>
14-
<th>Title</th>
15-
<th>Author</th>
16-
<th>Tags</th>
17-
<th>Publication date</th>
18-
<th>Rating</th>
19-
</tr>
20-
</thead>
21-
<tbody>
22-
{% for snippet in object_list %}
23-
<tr>
24-
<td><a href="{{ snippet.get_absolute_url }}">{% if not snippet.title|strip %}Untitled{% else %}{{ snippet.title }}{% endif %}</a></td>
25-
<td><a href="{{ snippet.author.get_absolute_url }}">{{ snippet.author.username }}</a></td>
26-
<td>{% for tag in snippet.tags.all %}<a href="{% url 'cab_snippet_matches_tag' tag.slug %}">{{ tag.name }}</a> {% endfor %}</td>
27-
<td>{{ snippet.pub_date }}</td>
28-
<td><span class="rating-{% if snippet.rating_score >= 0 %}positive{% else %}negative{% endif %}">{% if snippet.rating_score >= 0 %}+{% endif %}{{ snippet.rating_score }}</span></td>
29-
</tr>
30-
{% endfor %}
31-
</tbody>
32-
</table>
11+
{% component 'snippet_list' snippet_list=object_list / %}
3312
{% component "pagination" pagination_obj=pagination / %}
3413
<p class="count">{{ hits }} snippet{{ hits|pluralize }} posted so far.</p>
3514
{% else %}
@@ -38,18 +17,18 @@ <h1>Most bookmarked snippets{% if months %} last {{ months }} months{% endif %}<
3817
</div>
3918

4019

41-
<div id="sidebar">
42-
<p>You're looking at the most-bookmarked snippets on the site; {% if user.is_authenticated %}you can help useful snippets show up here by clicking the "bookmark this snippet" link on their pages and ading them to <a href="{% url 'cab_user_bookmarks' %}">your bookmarks</a>{% else %}if you'd like to help useful snippets show up here, <a href="{% url 'account_login' %}">sign up for an account</a> and you'll get your own bookmarks list{% endif %}.</p>
43-
44-
<nav class="filter">
45-
<h3>Filter by date</h3>
46-
<ul>
47-
<li{% if not months %} class="active"{% endif %}><a href="{{ request.path }}">Any time</a></li>
48-
<li{% if months == 3 %} class="active"{% endif %}><a href="{{ request.path }}?months=3">3 months</a></li>
49-
<li{% if months == 6 %} class="active"{% endif %}><a href="{{ request.path }}?months=6">6 months</a></li>
50-
<li{% if months == 12 %} class="active"{% endif %}><a href="{{ request.path }}?months=12">1 year</a></li>
51-
</ul>
52-
</nav>
53-
54-
<p><a rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml"><i class="fa fa-fw fa-rss-square"></i>Feed of latest snippets</a></p>
55-
</div>
20+
{#<div id="sidebar" class="hidden md:block md:w-1/4">#}
21+
{#<p>You're looking at the most-bookmarked snippets on the site; {% if user.is_authenticated %}you can help useful snippets show up here by clicking the "bookmark this snippet" link on their pages and ading them to <a href="{% url 'cab_user_bookmarks' %}">your bookmarks</a>{% else %}if you'd like to help useful snippets show up here, <a href="{% url 'account_login' %}">sign up for an account</a> and you'll get your own bookmarks list{% endif %}.</p>#}
22+
{##}
23+
{# <nav class="filter">#}
24+
{# <h3>Filter by date</h3>#}
25+
{# <ul>#}
26+
{# <li{% if not months %} class="active"{% endif %}><a href="{{ request.path }}">Any time</a></li>#}
27+
{# <li{% if months == 3 %} class="active"{% endif %}><a href="{{ request.path }}?months=3">3 months</a></li>#}
28+
{# <li{% if months == 6 %} class="active"{% endif %}><a href="{{ request.path }}?months=6">6 months</a></li>#}
29+
{# <li{% if months == 12 %} class="active"{% endif %}><a href="{{ request.path }}?months=12">1 year</a></li>#}
30+
{# </ul>#}
31+
{# </nav>#}
32+
{##}
33+
{# <p><a rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml"><i class="fa fa-fw fa-rss-square"></i>Feed of latest snippets</a></p>#}
34+
{#</div>#}

djangosnippets/templates/cab/partials/tag_list.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
{% load core_tags %}
33
<h1>All tags</h1>
4-
<div id="content">
4+
<div id="content" class="w-full md:w-3/4">
55
{% if object_list %}
66
<ul>
77
{% for tag in object_list %}

djangosnippets/templates/cab/partials/top_rated.html

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,29 @@
44

55

66
<h1>Top-rated snippets{% if months %} last {{ months }} months{% endif %}</h1>
7-
<div id="content">
7+
<div id="content" class="w-full">
88
{% if object_list %}
9-
<table class="snippet_list">
10-
<thead>
11-
<tr>
12-
<th>Title</th>
13-
<th>Author</th>
14-
<th>Tags</th>
15-
<th>Publication date</th>
16-
<th>Rating</th>
17-
</tr>
18-
</thead>
19-
<tbody>
20-
{% for snippet in object_list %}
21-
<tr>
22-
<td><a href="{{ snippet.get_absolute_url }}">{% if not snippet.title|strip %}Untitled{% else %}{{ snippet.title }}{% endif %}</a></td>
23-
<td><a href="{{ snippet.author.get_absolute_url }}">{{ snippet.author.username }}</a></td>
24-
<td>{% for tag in snippet.tags.all %}<a href="{% url 'cab_snippet_matches_tag' tag.slug %}">{{ tag.name }}</a> {% endfor %}</td>
25-
<td>{{ snippet.pub_date }}</td>
26-
<td><span class="rating-{% if snippet.rating_score >= 0 %}positive{% else %}negative{% endif %}">{% if snippet.rating_score >= 0 %}+{% endif %}{{ snippet.rating_score }}</span></td>
27-
</tr>
28-
{% endfor %}
29-
</tbody>
30-
</table>
9+
{% component 'snippet_list' snippet_list=object_list / %}
3110
{% component "pagination" pagination_obj=pagination / %}
3211
<p class="count">{{ hits }} snippet{{ hits|pluralize }} posted so far.</p>
3312
{% else %}
3413
<p class="empty">No snippets posted yet.</p>
3514
{% endif %}
3615
</div>
3716

38-
<div id="sidebar">
39-
<p>You're looking at the top-rated snippets currently on the site; {% if user.is_authenticated %}if you'd like to contribute, just click the ratings link in the sidebar of a particular snippet's page; you can rate any snippet "useful" or "not useful"{% else %}if you'd like to contribute, <a href="{% url 'account_login' %}">sign up for an account</a> and you'll be able to rate any snippet you see{% endif %}.</p>
40-
41-
<nav class="filter">
42-
<h3>Filter by date</h3>
43-
<ul>
44-
<li{% if not months %} class="active"{% endif %}><a href="{{ request.path }}">Any time</a></li>
45-
<li{% if months == 3 %} class="active"{% endif %}><a href="{{ request.path }}?months=3">3 months</a></li>
46-
<li{% if months == 6 %} class="active"{% endif %}><a href="{{ request.path }}?months=6">6 months</a></li>
47-
<li{% if months == 12 %} class="active"{% endif %}><a href="{{ request.path }}?months=12">1 year</a></li>
48-
</ul>
49-
</nav>
50-
51-
<p><a rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml"><i class="fa fa-fw fa-rss-square"></i>Feed of latest snippets</a></p>
52-
53-
</div>
17+
{#<div id="sidebar" class="hidden md:block md:w-1/4">#}
18+
{#<p>You're looking at the top-rated snippets currently on the site; {% if user.is_authenticated %}if you'd like to contribute, just click the ratings link in the sidebar of a particular snippet's page; you can rate any snippet "useful" or "not useful"{% else %}if you'd like to contribute, <a href="{% url 'account_login' %}">sign up for an account</a> and you'll be able to rate any snippet you see{% endif %}.</p>#}
19+
{##}
20+
{#<nav class="filter">#}
21+
{# <h3>Filter by date</h3>#}
22+
{# <ul>#}
23+
{# <li{% if not months %} class="active"{% endif %}><a href="{{ request.path }}">Any time</a></li>#}
24+
{# <li{% if months == 3 %} class="active"{% endif %}><a href="{{ request.path }}?months=3">3 months</a></li>#}
25+
{# <li{% if months == 6 %} class="active"{% endif %}><a href="{{ request.path }}?months=6">6 months</a></li>#}
26+
{# <li{% if months == 12 %} class="active"{% endif %}><a href="{{ request.path }}?months=12">1 year</a></li>#}
27+
{# </ul>#}
28+
{#</nav>#}
29+
{##}
30+
{#<p><a rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml"><i class="fa fa-fw fa-rss-square"></i>Feed of latest snippets</a></p>#}
31+
{##}
32+
{#</div>#}

0 commit comments

Comments
 (0)