django-spire 0.22.4__py3-none-any.whl → 0.23.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_spire/auth/group/forms.py +3 -4
- django_spire/auth/group/utils.py +1 -2
- django_spire/auth/group/views/form_views.py +22 -14
- django_spire/auth/group/views/page_views.py +1 -1
- django_spire/auth/mfa/utils.py +1 -1
- django_spire/auth/permissions/consts.py +2 -1
- django_spire/auth/permissions/decorators.py +1 -2
- django_spire/auth/permissions/permissions.py +1 -3
- django_spire/comment/factories.py +1 -1
- django_spire/comment/mixins.py +1 -1
- django_spire/comment/views.py +1 -1
- django_spire/consts.py +1 -1
- django_spire/contrib/breadcrumb/breadcrumbs.py +1 -1
- django_spire/contrib/form/confirmation_forms.py +1 -1
- django_spire/contrib/form/utils.py +26 -16
- django_spire/contrib/generic_views/modal_views.py +1 -1
- django_spire/contrib/generic_views/portal_views.py +18 -55
- django_spire/contrib/ordering/validators.py +4 -8
- django_spire/contrib/queryset/enums.py +2 -3
- django_spire/contrib/queryset/mixins.py +17 -6
- django_spire/contrib/session/controller.py +0 -1
- django_spire/core/context_processors.py +1 -1
- django_spire/core/management/commands/spire_startapp_pkg/user_input.py +1 -1
- django_spire/core/middleware/maintenance.py +2 -1
- django_spire/core/middleware.py +2 -1
- django_spire/core/redirect/generic_redirect.py +1 -1
- django_spire/core/redirect/safe_redirect.py +2 -1
- django_spire/core/shortcuts.py +4 -2
- django_spire/core/table/__init__.py +0 -0
- django_spire/core/table/enums.py +18 -0
- django_spire/core/templates/django_spire/card/infinite_scroll_card.html +3 -137
- django_spire/core/templates/django_spire/container/infinite_scroll_container.html +64 -0
- django_spire/core/templates/django_spire/infinite_scroll/base.html +348 -0
- django_spire/core/templates/django_spire/infinite_scroll/element/footer.html +11 -0
- django_spire/core/templates/django_spire/infinite_scroll/scroll.html +142 -0
- django_spire/core/templates/django_spire/item/infinite_scroll_item.html +33 -0
- django_spire/core/templates/django_spire/lazy_tab/element/lazy_tab_section_element.html +19 -0
- django_spire/core/templates/django_spire/lazy_tab/element/lazy_tab_trigger_element.html +15 -0
- django_spire/core/templates/django_spire/lazy_tab/lazy_tab.html +157 -0
- django_spire/core/templates/django_spire/page/infinite_scroll_list_page.html +7 -0
- django_spire/core/templates/django_spire/table/base.html +185 -373
- django_spire/core/templates/django_spire/table/element/footer.html +7 -15
- django_spire/core/templates/django_spire/table/element/header.html +1 -1
- django_spire/core/templates/django_spire/table/element/row.html +15 -7
- django_spire/core/templatetags/spire_core_tags.py +1 -2
- django_spire/file/fields.py +1 -1
- django_spire/file/interfaces.py +1 -1
- django_spire/file/views.py +1 -1
- django_spire/history/activity/utils.py +1 -1
- django_spire/history/mixins.py +0 -4
- django_spire/notification/app/context_data.py +3 -1
- django_spire/notification/app/views/json_views.py +1 -1
- django_spire/notification/app/views/page_views.py +2 -1
- django_spire/notification/app/views/template_views.py +2 -2
- django_spire/profiling/middleware/profiling.py +2 -2
- django_spire/profiling/panel.py +2 -2
- django_spire/testing/__init__.py +0 -0
- django_spire/testing/playwright/__init__.py +64 -0
- django_spire/testing/playwright/components/__init__.py +45 -0
- django_spire/testing/playwright/components/accordion.py +55 -0
- django_spire/testing/playwright/components/attribute_element.py +73 -0
- django_spire/testing/playwright/components/base_session_filter_form.py +57 -0
- django_spire/testing/playwright/components/breadcrumb_element.py +56 -0
- django_spire/testing/playwright/components/card.py +102 -0
- django_spire/testing/playwright/components/dropdown.py +87 -0
- django_spire/testing/playwright/components/infinite_scroll.py +158 -0
- django_spire/testing/playwright/components/lazy_tab.py +92 -0
- django_spire/testing/playwright/components/modal.py +101 -0
- django_spire/testing/playwright/components/navigation.py +119 -0
- django_spire/testing/playwright/components/notification_bell.py +59 -0
- django_spire/testing/playwright/components/theme_selector.py +46 -0
- django_spire/testing/playwright/components/toast.py +72 -0
- django_spire/testing/playwright/fixtures.py +54 -0
- django_spire/testing/playwright/pages/__init__.py +6 -0
- django_spire/testing/playwright/pages/base.py +24 -0
- django_spire/theme/models.py +1 -1
- django_spire/theme/tests/test_context_processor.py +0 -1
- django_spire/theme/views/json_views.py +1 -1
- django_spire/theme/views/page_views.py +1 -1
- {django_spire-0.22.4.dist-info → django_spire-0.23.2.dist-info}/METADATA +4 -1
- {django_spire-0.22.4.dist-info → django_spire-0.23.2.dist-info}/RECORD +84 -54
- {django_spire-0.22.4.dist-info → django_spire-0.23.2.dist-info}/WHEEL +0 -0
- {django_spire-0.22.4.dist-info → django_spire-0.23.2.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.22.4.dist-info → django_spire-0.23.2.dist-info}/top_level.txt +0 -0
django_spire/auth/group/forms.py
CHANGED
|
@@ -5,9 +5,6 @@ import json
|
|
|
5
5
|
from django import forms
|
|
6
6
|
from django.contrib.auth.models import Group, User
|
|
7
7
|
|
|
8
|
-
from crispy_forms.helper import FormHelper
|
|
9
|
-
from crispy_forms.layout import Column, Layout, Row, Submit
|
|
10
|
-
|
|
11
8
|
from django_spire.auth.group.factories import bulk_create_groups_from_names
|
|
12
9
|
|
|
13
10
|
|
|
@@ -28,8 +25,10 @@ class GroupNamesForm(forms.Form):
|
|
|
28
25
|
class GroupForm(forms.ModelForm):
|
|
29
26
|
def clean_name(self):
|
|
30
27
|
name = self.cleaned_data['name']
|
|
28
|
+
|
|
31
29
|
if name.lower() == 'all users':
|
|
32
|
-
|
|
30
|
+
message = '"All Users" is a reserved name. Please choose another name.'
|
|
31
|
+
raise forms.ValidationError(message)
|
|
33
32
|
|
|
34
33
|
return name
|
|
35
34
|
class Meta:
|
django_spire/auth/group/utils.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from django.core.handlers.wsgi import WSGIRequest
|
|
4
3
|
from django.http import HttpResponseRedirect
|
|
5
4
|
from django.shortcuts import get_object_or_404
|
|
6
|
-
from django.template.response import TemplateResponse
|
|
7
5
|
from django.urls import reverse
|
|
8
6
|
|
|
9
7
|
import django_glue as dg
|
|
8
|
+
|
|
10
9
|
from django_spire.auth.group import models, forms
|
|
11
10
|
from django_spire.auth.group.utils import set_group_users
|
|
12
11
|
from django_spire.auth.permissions.decorators import permission_required
|
|
@@ -16,12 +15,17 @@ from django_spire.contrib.form.utils import show_form_errors
|
|
|
16
15
|
from django_spire.contrib.generic_views import portal_views
|
|
17
16
|
from django_spire.core.shortcuts import get_object_or_null_obj
|
|
18
17
|
from django_spire.history.activity.utils import add_form_activity
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
22
|
+
from django.template.response import TemplateResponse
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
@permission_required('django_spire_auth_group.change_authgroup')
|
|
22
26
|
def form_view(
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
request: WSGIRequest,
|
|
28
|
+
pk: int = 0
|
|
25
29
|
) -> TemplateResponse | HttpResponseRedirect:
|
|
26
30
|
group = get_object_or_null_obj(models.AuthGroup, pk=pk)
|
|
27
31
|
|
|
@@ -36,8 +40,8 @@ def form_view(
|
|
|
36
40
|
|
|
37
41
|
return_url = reverse('django_spire:auth:group:page:list')
|
|
38
42
|
return HttpResponseRedirect(return_url)
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
|
|
44
|
+
show_form_errors(request, form)
|
|
41
45
|
|
|
42
46
|
form = forms.GroupForm(instance=group)
|
|
43
47
|
|
|
@@ -54,11 +58,12 @@ def form_view(
|
|
|
54
58
|
|
|
55
59
|
@permission_required('django_spire_auth_group.add_authgroup')
|
|
56
60
|
def user_form_view(
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
request: WSGIRequest,
|
|
62
|
+
pk: int
|
|
59
63
|
) -> TemplateResponse | HttpResponseRedirect:
|
|
60
64
|
group = get_object_or_404(models.AuthGroup, pk=pk)
|
|
61
65
|
user_choices = AuthUser.services.get_user_choices()
|
|
66
|
+
|
|
62
67
|
selected_user_ids = list(
|
|
63
68
|
AuthUser.objects
|
|
64
69
|
.filter(groups=group)
|
|
@@ -84,8 +89,8 @@ def user_form_view(
|
|
|
84
89
|
|
|
85
90
|
return_url = reverse('django_spire:auth:group:page:detail', kwargs={'pk': pk})
|
|
86
91
|
return HttpResponseRedirect(return_url)
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
|
|
93
|
+
show_form_errors(request, form)
|
|
89
94
|
|
|
90
95
|
form = forms.GroupUserForm()
|
|
91
96
|
|
|
@@ -124,9 +129,9 @@ def delete_form_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
|
124
129
|
|
|
125
130
|
@permission_required('django_spire_auth_group.delete_authgroup')
|
|
126
131
|
def group_remove_user_form_view(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
request: WSGIRequest,
|
|
133
|
+
group_pk: int,
|
|
134
|
+
pk: int
|
|
130
135
|
) -> HttpResponseRedirect | TemplateResponse:
|
|
131
136
|
group = get_object_or_404(models.AuthGroup, pk=group_pk)
|
|
132
137
|
user = get_object_or_404(AuthUser, pk=pk)
|
|
@@ -147,7 +152,10 @@ def group_remove_user_form_view(
|
|
|
147
152
|
)
|
|
148
153
|
|
|
149
154
|
return HttpResponseRedirect(
|
|
150
|
-
reverse(
|
|
155
|
+
reverse(
|
|
156
|
+
'django_spire:auth:group:page:detail',
|
|
157
|
+
kwargs={'pk': group_pk}
|
|
158
|
+
)
|
|
151
159
|
)
|
|
152
160
|
|
|
153
161
|
form = DeleteConfirmationForm(request.GET, obj=user)
|
django_spire/auth/mfa/utils.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import Literal
|
|
4
4
|
|
|
5
5
|
from django_spire.auth.group.models import AuthGroup
|
|
6
6
|
from django_spire.auth.user.models import AuthUser
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
PERMISSIONS_LEVEL_CHOICES = (
|
|
9
10
|
(0, 'None'),
|
|
10
11
|
(1, 'View'),
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import functools
|
|
4
|
-
from typing import Sequence
|
|
5
4
|
|
|
6
|
-
from
|
|
5
|
+
from typing import Sequence, TYPE_CHECKING
|
|
7
6
|
|
|
8
7
|
from django.core.exceptions import PermissionDenied
|
|
9
8
|
from django.http import HttpResponseRedirect
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from typing_extensions import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
6
4
|
|
|
7
5
|
from django.contrib.auth.models import Permission, Group, User
|
|
8
6
|
from django.contrib.contenttypes.models import ContentType
|
django_spire/comment/mixins.py
CHANGED
django_spire/comment/views.py
CHANGED
django_spire/consts.py
CHANGED
|
@@ -1,30 +1,40 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from typing_extensions import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
5
|
from django.contrib import messages
|
|
7
6
|
|
|
8
7
|
if TYPE_CHECKING:
|
|
9
8
|
from django.core.handlers.wsgi import WSGIRequest
|
|
9
|
+
from django.forms import Form
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
def form_errors_as_list(form: Form) -> list[str]:
|
|
13
|
+
form_errors = []
|
|
14
|
+
|
|
15
|
+
for field_name, error_list in form.errors.items():
|
|
16
|
+
for error in error_list.data:
|
|
17
|
+
error_message = ''
|
|
18
|
+
|
|
19
|
+
if field_name != '__all__':
|
|
20
|
+
error_message += f'{field_name.title()}: '
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
if hasattr(error, 'message_responses'):
|
|
23
|
+
error_message += f'{" ".join(error.message_responses)}'
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
elif hasattr(error, 'messages'):
|
|
26
|
+
error_message += f'{" ".join(error.messages)}'
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
else:
|
|
29
|
+
message = 'Error message not found.'
|
|
30
|
+
raise Exception(message)
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
raise Exception('Error message not found.')
|
|
32
|
+
form_errors.append(error_message)
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
return form_errors
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def show_form_errors(request: WSGIRequest, *forms: Form) -> None:
|
|
38
|
+
for form in forms:
|
|
39
|
+
for error in form_errors_as_list(form):
|
|
40
|
+
messages.error(request=request, message=error)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import Any, Callable, TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from django.db.models import QuerySet
|
|
6
6
|
from django.http import HttpResponseRedirect
|
|
@@ -121,34 +121,40 @@ def delete_form_view(
|
|
|
121
121
|
def infinite_scrolling_view(
|
|
122
122
|
request: WSGIRequest,
|
|
123
123
|
*,
|
|
124
|
-
context_data: dict[str, Any],
|
|
125
124
|
queryset: QuerySet | list,
|
|
126
125
|
queryset_name: str,
|
|
127
|
-
template: str
|
|
126
|
+
template: str,
|
|
127
|
+
context_data: dict[str, Any] | None = None
|
|
128
128
|
) -> TemplateResponse:
|
|
129
129
|
if context_data is None:
|
|
130
130
|
context_data = {}
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
page_size = int(request.GET.get('page_size', 10))
|
|
132
|
+
default_batch_size = 25
|
|
134
133
|
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
page = int(request.GET.get('page', 1))
|
|
135
|
+
|
|
136
|
+
batch_size = (
|
|
137
|
+
context_data.get('batch_size')
|
|
138
|
+
if 'batch_size' in context_data
|
|
139
|
+
else request.GET.get('batch_size', default_batch_size)
|
|
140
|
+
)
|
|
137
141
|
|
|
138
|
-
|
|
142
|
+
batch_size = int(batch_size)
|
|
143
|
+
offset = (page - 1) * batch_size
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
total_count = (
|
|
141
146
|
queryset.count()
|
|
142
147
|
if isinstance(queryset, QuerySet)
|
|
143
148
|
else len(queryset)
|
|
144
149
|
)
|
|
145
150
|
|
|
146
|
-
|
|
151
|
+
object_list = queryset[offset:offset + batch_size]
|
|
152
|
+
has_next = offset + batch_size < total_count
|
|
147
153
|
|
|
148
154
|
base_context_data = {
|
|
149
|
-
'
|
|
155
|
+
'batch_size': batch_size,
|
|
150
156
|
'has_next': has_next,
|
|
151
|
-
'
|
|
157
|
+
'total_count': total_count,
|
|
152
158
|
queryset_name: object_list
|
|
153
159
|
}
|
|
154
160
|
|
|
@@ -270,49 +276,6 @@ def model_form_view(
|
|
|
270
276
|
)
|
|
271
277
|
|
|
272
278
|
|
|
273
|
-
def table_view(
|
|
274
|
-
request: WSGIRequest,
|
|
275
|
-
*,
|
|
276
|
-
queryset: QuerySet,
|
|
277
|
-
queryset_name: str,
|
|
278
|
-
template: str,
|
|
279
|
-
context_data: dict[str, Any] | None = None
|
|
280
|
-
) -> TemplateResponse:
|
|
281
|
-
if context_data is None:
|
|
282
|
-
context_data = {}
|
|
283
|
-
|
|
284
|
-
default_batch_size = 25
|
|
285
|
-
|
|
286
|
-
page = int(request.GET.get('page', 1))
|
|
287
|
-
|
|
288
|
-
batch_size = (
|
|
289
|
-
context_data.get('batch_size')
|
|
290
|
-
if 'batch_size' in context_data
|
|
291
|
-
else request.GET.get('batch_size', default_batch_size)
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
batch_size = int(batch_size)
|
|
295
|
-
offset = (page - 1) * batch_size
|
|
296
|
-
|
|
297
|
-
total_count = queryset.count()
|
|
298
|
-
object_list = queryset[offset:offset + batch_size]
|
|
299
|
-
has_next = offset + batch_size < total_count
|
|
300
|
-
|
|
301
|
-
base_context_data = {
|
|
302
|
-
queryset_name: object_list,
|
|
303
|
-
'has_next': has_next,
|
|
304
|
-
'total_count': total_count,
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
context_data.update(base_context_data)
|
|
308
|
-
|
|
309
|
-
return TemplateResponse(
|
|
310
|
-
request,
|
|
311
|
-
context=context_data,
|
|
312
|
-
template=template
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
279
|
def template_view(
|
|
317
280
|
request: WSGIRequest,
|
|
318
281
|
page_title: str,
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
from typing_extensions import TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
from django.db.models import QuerySet
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
7
4
|
|
|
8
5
|
from django_spire.contrib.ordering.exceptions import OrderingMixinException
|
|
9
6
|
|
|
10
7
|
if TYPE_CHECKING:
|
|
11
|
-
from
|
|
12
|
-
from django.db.models import Model
|
|
8
|
+
from django.db.models import Model, QuerySet
|
|
13
9
|
|
|
14
10
|
|
|
15
11
|
class OrderingMixinValidator:
|
|
@@ -37,9 +33,9 @@ class OrderingMixinValidator:
|
|
|
37
33
|
Validates that the destination and origin and insertion objects and position are valid.
|
|
38
34
|
Returns tuple of validity and applicable error messages.
|
|
39
35
|
"""
|
|
40
|
-
self._validate_position()
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
self._validate_position()
|
|
38
|
+
return not self._errors
|
|
43
39
|
|
|
44
40
|
def _validate_position(self):
|
|
45
41
|
"""Ensure position is valid."""
|
|
@@ -22,24 +22,35 @@ class SessionFilterQuerySetMixin(QuerySet):
|
|
|
22
22
|
) -> QuerySet:
|
|
23
23
|
# Session keys must match to process new queryset data
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
try:
|
|
26
|
+
action = SessionFilterActionEnum(request.GET.get('action'))
|
|
27
|
+
except ValueError:
|
|
28
|
+
action = None
|
|
29
|
+
|
|
26
30
|
form = form_class(request.GET)
|
|
27
31
|
|
|
28
32
|
if form.is_valid():
|
|
29
33
|
session = SessionController(request=request, session_key=session_key)
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
if action == SessionFilterActionEnum.CLEAR.value:
|
|
35
|
+
if action == SessionFilterActionEnum.CLEAR:
|
|
33
36
|
session.purge()
|
|
34
37
|
return self
|
|
35
38
|
|
|
36
|
-
#
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
+
# Apply filters when the user submits the filter form
|
|
40
|
+
if (
|
|
41
|
+
action == SessionFilterActionEnum.FILTER
|
|
42
|
+
and session_key == request.GET.get('session_filter_key')
|
|
43
|
+
):
|
|
39
44
|
# Update session data
|
|
40
45
|
for key, value in form.cleaned_data.items():
|
|
41
46
|
session.add_data(key, value)
|
|
42
47
|
|
|
48
|
+
# If the session is expired, return the unfiltered queryset
|
|
49
|
+
if session.is_expired:
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
# When no new filter data is applied and session is NOT yet expired,
|
|
53
|
+
# return the original queryset
|
|
43
54
|
return self.bulk_filter(session.data)
|
|
44
55
|
else:
|
|
45
56
|
show_form_errors(request, form)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.conf import settings
|
|
4
6
|
from django.template.response import TemplateResponse
|
|
5
|
-
from typing_extensions import TYPE_CHECKING
|
|
6
7
|
|
|
7
8
|
from django_spire.consts import MAINTENANCE_MODE_SETTINGS_NAME
|
|
8
9
|
|
django_spire/core/middleware.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.conf import settings
|
|
4
6
|
from django.template.response import TemplateResponse
|
|
5
|
-
from typing_extensions import TYPE_CHECKING
|
|
6
7
|
|
|
7
8
|
from django_spire.consts import MAINTENANCE_MODE_SETTINGS_NAME
|
|
8
9
|
|
django_spire/core/shortcuts.py
CHANGED
|
@@ -2,14 +2,16 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
|
|
5
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
6
|
+
|
|
5
7
|
from django.contrib.contenttypes.models import ContentType
|
|
6
8
|
from django.db.models import Model
|
|
7
|
-
from typing_extensions import TYPE_CHECKING, TypeVar
|
|
8
9
|
|
|
9
10
|
if TYPE_CHECKING:
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
10
13
|
from django.db.models import QuerySet
|
|
11
14
|
from django.http import HttpRequest
|
|
12
|
-
from typing_extensions import Any
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
T = TypeVar('T', bound=Model)
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ResponsiveMode(StrEnum):
|
|
7
|
+
"""
|
|
8
|
+
It defines how tables handle responsive behavior at different viewport sizes.
|
|
9
|
+
|
|
10
|
+
Collapse: Any columns with breakpoint attributes are hidden at smaller viewports
|
|
11
|
+
and shown at their specified breakpoint.
|
|
12
|
+
|
|
13
|
+
Scroll: All columns remain visible regardless of viewport size. The table
|
|
14
|
+
container enables horizontal scrolling to accommodate the full width.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
COLLAPSE = 'collapse'
|
|
18
|
+
SCROLL = 'scroll'
|