linkify game, edition, purchase, session references
Django CI/CD / test (push) Successful in 1m0s Details
Django CI/CD / build-and-push (push) Has been cancelled Details

also add link styles for links in a table row
This commit is contained in:
Lukáš Kucharczyk 2024-09-02 20:04:21 +02:00
parent b8258e2937
commit e067e65bce
Signed by: lukas
SSH Key Fingerprint: SHA256:vMuSwvwAvcT6htVAioMP7rzzwMQNi3roESyhv+nAxeg
7 changed files with 174 additions and 85 deletions

View File

@ -27,6 +27,20 @@ def Popover(
return result return result
HTMLAttribute = tuple[str, str]
HTMLTag = str
def A(attributes: list[HTMLAttribute], children: list[HTMLTag] | HTMLTag) -> HTMLTag:
if isinstance(children, str):
children = [children]
childrenBlob = "\n".join(children)
attributesList = [f'{name} = "{value}"' for name, value in attributes]
attributesBlob = " ".join(attributesList)
tag: str = f"<a {attributesBlob}>{childrenBlob}</a>"
return mark_safe(tag)
def safe_division(numerator: int | float, denominator: int | float) -> int | float: def safe_division(numerator: int | float, denominator: int | float) -> int | float:
""" """
Divides without triggering division by zero exception. Divides without triggering division by zero exception.

View File

@ -1386,6 +1386,10 @@ input:checked + .toggle-bg {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.mb-6 {
margin-bottom: 1.5rem;
}
.mb-8 { .mb-8 {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
@ -1394,10 +1398,6 @@ input:checked + .toggle-bg {
margin-inline-end: 0.5rem; margin-inline-end: 0.5rem;
} }
.ml-1 {
margin-left: 0.25rem;
}
.mr-4 { .mr-4 {
margin-right: 1rem; margin-right: 1rem;
} }
@ -1422,14 +1422,6 @@ input:checked + .toggle-bg {
margin-top: 1rem; margin-top: 1rem;
} }
.mb-5 {
margin-bottom: 1.25rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
.block { .block {
display: block; display: block;
} }
@ -1479,10 +1471,6 @@ input:checked + .toggle-bg {
height: 0.625rem; height: 0.625rem;
} }
.h-3 {
height: 0.75rem;
}
.h-4 { .h-4 {
height: 1rem; height: 1rem;
} }
@ -1543,10 +1531,6 @@ input:checked + .toggle-bg {
width: 16rem; width: 16rem;
} }
.w-7 {
width: 1.75rem;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@ -1705,12 +1689,6 @@ input:checked + .toggle-bg {
margin-left: calc(-1px * calc(1 - var(--tw-space-x-reverse))); margin-left: calc(-1px * calc(1 - var(--tw-space-x-reverse)));
} }
.space-x-1 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.25rem * var(--tw-space-x-reverse));
margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse)));
}
.space-x-2 > :not([hidden]) ~ :not([hidden]) { .space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0; --tw-space-x-reverse: 0;
margin-right: calc(0.5rem * var(--tw-space-x-reverse)); margin-right: calc(0.5rem * var(--tw-space-x-reverse));
@ -1764,10 +1742,6 @@ input:checked + .toggle-bg {
border-radius: 0.25rem; border-radius: 0.25rem;
} }
.rounded-full {
border-radius: 9999px;
}
.rounded-lg { .rounded-lg {
border-radius: 0.5rem; border-radius: 0.5rem;
} }
@ -1886,11 +1860,6 @@ input:checked + .toggle-bg {
background-color: rgb(5 122 85 / var(--tw-bg-opacity)); background-color: rgb(5 122 85 / var(--tw-bg-opacity));
} }
.bg-violet-600 {
--tw-bg-opacity: 1;
background-color: rgb(124 58 237 / var(--tw-bg-opacity));
}
.bg-white { .bg-white {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@ -2000,10 +1969,6 @@ input:checked + .toggle-bg {
vertical-align: top; vertical-align: top;
} }
.font-condensed {
font-family: IBM Plex Sans Condensed, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
.font-mono { .font-mono {
font-family: IBM Plex Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-family: IBM Plex Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
} }
@ -2174,6 +2139,18 @@ input:checked + .toggle-bg {
text-decoration-color: #64748b; text-decoration-color: #64748b;
} }
.decoration-wavy {
text-decoration-style: wavy;
}
.underline-offset-2 {
text-underline-offset: 2px;
}
.underline-offset-4 {
text-underline-offset: 4px;
}
.opacity-0 { .opacity-0 {
opacity: 0; opacity: 0;
} }
@ -2239,6 +2216,12 @@ input:checked + .toggle-bg {
transition-duration: 150ms; transition-duration: 150ms;
} }
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-200 { .duration-200 {
transition-duration: 200ms; transition-duration: 200ms;
} }
@ -2247,6 +2230,14 @@ input:checked + .toggle-bg {
transition-duration: 300ms; transition-duration: 300ms;
} }
.duration-1000 {
transition-duration: 1000ms;
}
.duration-500 {
transition-duration: 500ms;
}
.ease-in { .ease-in {
transition-timing-function: cubic-bezier(0.4, 0, 1, 1); transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
} }
@ -2573,11 +2564,6 @@ textarea:disabled:is(.dark *) {
background-color: rgb(240 82 82 / var(--tw-bg-opacity)); background-color: rgb(240 82 82 / var(--tw-bg-opacity));
} }
.hover\:bg-violet-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(109 40 217 / var(--tw-bg-opacity));
}
.hover\:bg-white:hover { .hover\:bg-white:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@ -2613,6 +2599,10 @@ textarea:disabled:is(.dark *) {
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.hover\:decoration-red-600:hover {
text-decoration-color: #E02424;
}
.focus\:z-10:focus { .focus\:z-10:focus {
z-index: 10; z-index: 10;
} }
@ -2664,11 +2654,6 @@ textarea:disabled:is(.dark *) {
--tw-ring-color: rgb(4 108 78 / var(--tw-ring-opacity)); --tw-ring-color: rgb(4 108 78 / var(--tw-ring-opacity));
} }
.focus\:ring-violet-500:focus {
--tw-ring-opacity: 1;
--tw-ring-color: rgb(139 92 246 / var(--tw-ring-opacity));
}
.focus\:ring-offset-2:focus { .focus\:ring-offset-2:focus {
--tw-ring-offset-width: 2px; --tw-ring-offset-width: 2px;
} }
@ -2677,10 +2662,6 @@ textarea:disabled:is(.dark *) {
--tw-ring-offset-color: #C3DDFD; --tw-ring-offset-color: #C3DDFD;
} }
.focus\:ring-offset-violet-200:focus {
--tw-ring-offset-color: #ddd6fe;
}
.group:hover .group-hover\:absolute { .group:hover .group-hover\:absolute {
position: absolute; position: absolute;
} }
@ -2848,11 +2829,6 @@ textarea:disabled:is(.dark *) {
color: rgb(148 163 184 / var(--tw-text-opacity)); color: rgb(148 163 184 / var(--tw-text-opacity));
} }
.dark\:text-slate-500:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(100 116 139 / var(--tw-text-opacity));
}
.dark\:text-slate-600:is(.dark *) { .dark\:text-slate-600:is(.dark *) {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(71 85 105 / var(--tw-text-opacity)); color: rgb(71 85 105 / var(--tw-text-opacity));
@ -2979,14 +2955,6 @@ textarea:disabled:is(.dark *) {
padding-right: 1rem; padding-right: 1rem;
} }
.sm\:pl-2 {
padding-left: 0.5rem;
}
.sm\:pl-4 {
padding-left: 1rem;
}
.sm\:decoration-2 { .sm\:decoration-2 {
text-decoration-thickness: 2px; text-decoration-thickness: 2px;
} }
@ -3058,11 +3026,6 @@ textarea:disabled:is(.dark *) {
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
.md\:text-5xl {
font-size: 3rem;
line-height: 1;
}
.md\:text-blue-700 { .md\:text-blue-700 {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(26 86 219 / var(--tw-text-opacity)); color: rgb(26 86 219 / var(--tw-text-opacity));
@ -3113,11 +3076,6 @@ textarea:disabled:is(.dark *) {
.lg\:max-w-lg { .lg\:max-w-lg {
max-width: 32rem; max-width: 32rem;
} }
.lg\:text-6xl {
font-size: 3.75rem;
line-height: 1;
}
} }
@media (min-width: 1536px) { @media (min-width: 1536px) {
@ -3152,3 +3110,76 @@ textarea:disabled:is(.dark *) {
border-start-end-radius: 0.5rem; border-start-end-radius: 0.5rem;
border-end-end-radius: 0.5rem; border-end-end-radius: 0.5rem;
} }
.\[\&\>a\]\:underline>a {
text-decoration-line: underline;
}
.\[\&_a\]\:underline a {
text-decoration-line: underline;
}
.\[\&_a\]\:decoration-double a {
text-decoration-style: double;
}
.\[\&_a\]\:decoration-dashed a {
text-decoration-style: dashed;
}
.\[\&_a\]\:decoration-wavy a {
text-decoration-style: wavy;
}
.\[\&_a\]\:decoration-2 a {
text-decoration-thickness: 2px;
}
.\[\&_a\]\:underline-offset-4 a {
text-underline-offset: 4px;
}
.\[\&_a\]\:hover\:bg-purple-800:hover a {
--tw-bg-opacity: 1;
background-color: rgb(85 33 181 / var(--tw-bg-opacity));
}
.\[\&_a\]\:hover\:p-5:hover a {
padding: 1.25rem;
}
.\[\&_a\]\:hover\:p-1:hover a {
padding: 0.25rem;
}
.\[\&_a\]\:hover\:decoration-red-600:hover a {
text-decoration-color: #E02424;
}
.\[\&_a\]\:hover\:decoration-purple-600:hover a {
text-decoration-color: #7E3AF2;
}
.\[\&_a\]\:hover\:decoration-purple-300:hover a {
text-decoration-color: #CABFFD;
}
.\[\&_a\]\:hover\:decoration-purple-400:hover a {
text-decoration-color: #AC94FA;
}
.\[\&_a\]\:hover\:decoration-purple-500:hover a {
text-decoration-color: #9061F9;
}
.\[\&_a\]\:hover\:decoration-purple-900:hover a {
text-decoration-color: #4A1D96;
}
.\[\&_a\]\:hover\:decoration-gray-800:hover a {
text-decoration-color: #1F2937;
}
.\[\&_a\]\:hover\:underline-offset-8:hover a {
text-underline-offset: 8px;
}

View File

@ -1,4 +1,4 @@
<tr class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 border-b"> <tr class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 border-b [&_a]:underline [&_a]:underline-offset-4 [&_a]:decoration-2">
{% if slot %} {% if slot %}
{{ slot }} {{ slot }}
{% else %} {% else %}

View File

@ -7,7 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from common.utils import truncate_with_popover from common.utils import A, truncate_with_popover
from games.forms import EditionForm from games.forms import EditionForm
from games.models import Edition, Game from games.models import Edition, Game
from games.views.general import dateformat from games.views.general import dateformat
@ -48,7 +48,18 @@ def list_editions(request: HttpRequest) -> HttpResponse:
], ],
"rows": [ "rows": [
[ [
truncate_with_popover(edition.game.name), A(
[
(
"href",
reverse(
"view_game",
args=[edition.game.pk],
),
)
],
truncate_with_popover(edition.game.name),
),
truncate_with_popover( truncate_with_popover(
edition.name edition.name
if edition.game.name != edition.name if edition.game.name != edition.name

View File

@ -9,7 +9,7 @@ from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from common.time import format_duration from common.time import format_duration
from common.utils import safe_division, truncate_with_popover from common.utils import A, safe_division, truncate_with_popover
from games.forms import GameForm from games.forms import GameForm
from games.models import Edition, Game, Purchase, Session from games.models import Edition, Game, Purchase, Session
from games.views.general import ( from games.views.general import (
@ -55,7 +55,18 @@ def list_games(request: HttpRequest) -> HttpResponse:
], ],
"rows": [ "rows": [
[ [
truncate_with_popover(game.name), A(
[
(
"href",
reverse(
"view_game",
args=[game.pk],
),
)
],
truncate_with_popover(game.name),
),
truncate_with_popover( truncate_with_popover(
game.sort_name game.sort_name
if game.sort_name is not None and game.name != game.sort_name if game.sort_name is not None and game.name != game.sort_name

View File

@ -13,7 +13,7 @@ from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from common.utils import truncate_with_popover from common.utils import A, truncate_with_popover
from games.forms import PurchaseForm from games.forms import PurchaseForm
from games.models import Edition, Purchase from games.models import Edition, Purchase
from games.views.general import dateformat, use_custom_redirect from games.views.general import dateformat, use_custom_redirect
@ -57,7 +57,18 @@ def list_purchases(request: HttpRequest) -> HttpResponse:
], ],
"rows": [ "rows": [
[ [
truncate_with_popover(purchase.edition.name), A(
[
(
"href",
reverse(
"view_game",
args=[purchase.edition.game.pk],
),
),
],
truncate_with_popover(purchase.edition.game.name),
),
purchase.platform, purchase.platform,
purchase.price, purchase.price,
purchase.price_currency, purchase.price_currency,

View File

@ -9,7 +9,7 @@ from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from common.time import format_duration from common.time import format_duration
from common.utils import truncate_with_popover from common.utils import A, truncate_with_popover
from games.forms import SessionForm from games.forms import SessionForm
from games.models import Purchase, Session from games.models import Purchase, Session
from games.views.general import ( from games.views.general import (
@ -56,7 +56,18 @@ def list_sessions(request: HttpRequest) -> HttpResponse:
], ],
"rows": [ "rows": [
[ [
truncate_with_popover(session.purchase.edition.name), A(
[
(
"href",
reverse(
"view_game",
args=[session.purchase.edition.game.pk],
),
)
],
truncate_with_popover(session.purchase.edition.name),
),
f"{session.timestamp_start.strftime(datetimeformat)}{f"{session.timestamp_end.strftime(timeformat)}" if session.timestamp_end else ""}", f"{session.timestamp_start.strftime(datetimeformat)}{f"{session.timestamp_end.strftime(timeformat)}" if session.timestamp_end else ""}",
( (
format_duration(session.duration_calculated, durationformat) format_duration(session.duration_calculated, durationformat)