django-bootstrap6-admin 0.1.2__tar.gz → 0.2.1__tar.gz
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_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/CHANGELOG.md +33 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/CONTRIBUTING.md +2 -2
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/PKG-INFO +26 -8
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/README.md +25 -7
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/__init__.py +1 -1
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/css/admin.css +135 -54
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/js/admin.js +98 -6
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/base.html +52 -50
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/login.html +0 -1
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin.egg-info/PKG-INFO +26 -8
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/test_admin_ui.py +13 -1
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/test_package.py +18 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/LICENSE +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/MANIFEST.in +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/NOTICE +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/pyproject.toml +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/setup.cfg +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/apps.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/css/widgets-overrides.css +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/js/theme.js +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/vendor/bootstrap/LICENSE +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/vendor/bootstrap/css/bootstrap.min.css +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/vendor/bootstrap/js/bootstrap.bundle.min.js +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/404.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/500.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/actions.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/app_index.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/app_list.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/auth/user/add_form.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/auth/user/change_password.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/base_site.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/change_form.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/change_form_object_tools.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/change_list.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/change_list_object_tools.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/change_list_results.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/color_theme_toggle.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/date_hierarchy.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/delete_confirmation.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/delete_selected_confirmation.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/edit_inline/stacked.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/edit_inline/tabular.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/filter.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/includes/fieldset.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/includes/object_delete_summary.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/index.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/nav_sidebar.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/object_history.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/pagination.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/search_form.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/submit_line.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/widgets/foreign_key_raw_id.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/admin/widgets/related_widget_wrapper.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/registration/logged_out.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/registration/password_change_done.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templates/registration/password_change_form.html +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templatetags/__init__.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/templatetags/bootstrap6_admin.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin.egg-info/SOURCES.txt +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin.egg-info/dependency_links.txt +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin.egg-info/requires.txt +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin.egg-info/top_level.txt +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/__init__.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/js/theme_toggle.test.js +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/settings.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/smoke_wheel.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/test_templatetags.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/testapp/__init__.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/testapp/admin.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/testapp/apps.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/testapp/models.py +0 -0
- {django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/urls.py +0 -0
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1] - 2026-11-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- The "View site" link is now always visible in the admin top bar instead of
|
|
15
|
+
being hidden in the user menu.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Refreshed the palette and surface treatment to match the warm near-black and
|
|
20
|
+
yellow visual language of the Pardus Teknofest project.
|
|
21
|
+
- Updated the login screen brand mark and browser theme colour to the new
|
|
22
|
+
palette.
|
|
23
|
+
- Documented that the admin follows Django's active language instead of being
|
|
24
|
+
hard-coded to English.
|
|
25
|
+
|
|
26
|
+
## [0.2.0] - 2026-11-15
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- The desktop filter panel can now be collapsed and expanded, and the
|
|
31
|
+
collapsed state is remembered between changelist pages.
|
|
32
|
+
- The "Show counts" / "Hide counts" choice is remembered across changelist
|
|
33
|
+
pages instead of resetting on navigation.
|
|
34
|
+
- Subtle vertical rules separate columns in changelists, inline tables and the
|
|
35
|
+
object history table.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Removed the duplicate native dropdown arrow on the bulk-action select.
|
|
40
|
+
- Improved dark-theme text contrast for secondary labels, table headers,
|
|
41
|
+
filter links, help text and links.
|
|
42
|
+
|
|
10
43
|
## [0.1.2] - 2026-11-15
|
|
11
44
|
|
|
12
45
|
### Fixed
|
|
@@ -71,8 +71,8 @@ environment and compiles every bundled template as a smoke test.
|
|
|
71
71
|
5. Create and push an annotated tag:
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
git tag -a v0.1
|
|
75
|
-
git push origin v0.1
|
|
74
|
+
git tag -a v0.2.1 -m "django-bootstrap6-admin 0.2.1"
|
|
75
|
+
git push origin v0.2.1
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
6. The `Release` workflow builds the distributions, verifies that the tag
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-bootstrap6-admin
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A modern Bootstrap 6 skin for the Django admin.
|
|
5
5
|
Author-email: Aliberk Sandıkçı <git@asandikci.com>
|
|
6
6
|
Maintainer-email: Aliberk Sandıkçı <git@asandikci.com>
|
|
@@ -53,16 +53,23 @@ permissions and JavaScript remain in charge.
|
|
|
53
53
|
## Features
|
|
54
54
|
|
|
55
55
|
- Bootstrap 6 markup and design tokens throughout the admin.
|
|
56
|
+
- "View site" is always available in the top bar, not buried in the user menu.
|
|
56
57
|
- Sticky top bar with a collapsible sidebar (desktop) and drawer navigation
|
|
57
58
|
with a backdrop (mobile).
|
|
58
59
|
- Dashboard with application cards, model shortcuts and recent activity.
|
|
59
60
|
- Changelist with search, date hierarchy, collapsible filters, sortable
|
|
60
|
-
headers, bulk actions and Bootstrap pagination.
|
|
61
|
+
headers, bulk actions and Bootstrap pagination. The desktop filter panel can
|
|
62
|
+
be collapsed, and both its state and the "Show counts" choice are remembered
|
|
63
|
+
between pages.
|
|
64
|
+
- Subtle vertical rules separate columns in changelists, inline tables and the
|
|
65
|
+
object history.
|
|
61
66
|
- Change form with card-based fieldsets, collapsible sections, a clear save
|
|
62
67
|
bar, tabular/stacked inlines and related-object popups.
|
|
63
68
|
- Modern login, logout and password change pages.
|
|
64
69
|
- Light, dark and system themes. The existing Django `theme` localStorage
|
|
65
70
|
preference is synchronised with Bootstrap's `data-bs-theme` attribute.
|
|
71
|
+
- Dark-theme secondary text, table headers, filter links and help text use
|
|
72
|
+
higher-contrast tokens for better readability.
|
|
66
73
|
- Soft fade-in/fade-out transition when switching themes, skipped
|
|
67
74
|
automatically for users who prefer reduced motion.
|
|
68
75
|
- Every click changes the visible theme: if `auto` would resolve to the theme
|
|
@@ -75,8 +82,8 @@ permissions and JavaScript remain in charge.
|
|
|
75
82
|
- CSP-aware: bundled scripts and styles use Django 6.1's
|
|
76
83
|
`{% csp_nonce_attr %}` tag, so nonce-based policies work out of the box.
|
|
77
84
|
- Generic by design: no models, no migrations, no project-specific imports and
|
|
78
|
-
no assumptions about the host project's branding. The default
|
|
79
|
-
palette is only a set of overridable CSS variables.
|
|
85
|
+
no assumptions about the host project's branding. The default warm
|
|
86
|
+
near-black/yellow palette is only a set of overridable CSS variables.
|
|
80
87
|
|
|
81
88
|
## Requirements
|
|
82
89
|
|
|
@@ -188,14 +195,15 @@ custom admin CSS file:
|
|
|
188
195
|
|
|
189
196
|
```css
|
|
190
197
|
:root {
|
|
191
|
-
--ba-topbar-bg: #
|
|
192
|
-
--ba-gold: #
|
|
198
|
+
--ba-topbar-bg: #231f20;
|
|
199
|
+
--ba-gold: #ffcb08;
|
|
200
|
+
--ba-canvas: #f6f6f4;
|
|
193
201
|
--ba-sidebar-width: 300px;
|
|
194
202
|
}
|
|
195
203
|
```
|
|
196
204
|
|
|
197
|
-
The default primary
|
|
198
|
-
|
|
205
|
+
The default primary is the warm yellow accent with dark text, and the danger
|
|
206
|
+
colour remains a deep red for readable white labels.
|
|
199
207
|
|
|
200
208
|
The select controls use small `data:` SVG arrows. If you enable a strict CSP,
|
|
201
209
|
allow `img-src 'self' data:` (or replace the arrows in your own CSS).
|
|
@@ -223,6 +231,16 @@ There is no runtime state to clean up.
|
|
|
223
231
|
- `bootstrap.bundle.min.js`:
|
|
224
232
|
`47ea447f46e288dd9d3e83147b7ba539c6df5192c047b8f91739cd56a5721148`
|
|
225
233
|
|
|
234
|
+
### Language
|
|
235
|
+
|
|
236
|
+
Django's admin is translated using the active language; it is not fixed to
|
|
237
|
+
English. With `LANGUAGE_CODE = "tr"`, Django's own admin labels are Turkish,
|
|
238
|
+
and this theme follows the same project language. The package's source strings
|
|
239
|
+
are English and are translated when a matching locale is installed. To keep
|
|
240
|
+
the admin in English while the public site uses another language, activate
|
|
241
|
+
English for `/admin/` in your project (for example with a small middleware),
|
|
242
|
+
or set `LANGUAGE_CODE = "en-us"` if the whole site should be English.
|
|
243
|
+
|
|
226
244
|
## Development
|
|
227
245
|
|
|
228
246
|
The test suite uses an isolated settings module and a small demo app. Create a
|
|
@@ -16,16 +16,23 @@ permissions and JavaScript remain in charge.
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
18
|
- Bootstrap 6 markup and design tokens throughout the admin.
|
|
19
|
+
- "View site" is always available in the top bar, not buried in the user menu.
|
|
19
20
|
- Sticky top bar with a collapsible sidebar (desktop) and drawer navigation
|
|
20
21
|
with a backdrop (mobile).
|
|
21
22
|
- Dashboard with application cards, model shortcuts and recent activity.
|
|
22
23
|
- Changelist with search, date hierarchy, collapsible filters, sortable
|
|
23
|
-
headers, bulk actions and Bootstrap pagination.
|
|
24
|
+
headers, bulk actions and Bootstrap pagination. The desktop filter panel can
|
|
25
|
+
be collapsed, and both its state and the "Show counts" choice are remembered
|
|
26
|
+
between pages.
|
|
27
|
+
- Subtle vertical rules separate columns in changelists, inline tables and the
|
|
28
|
+
object history.
|
|
24
29
|
- Change form with card-based fieldsets, collapsible sections, a clear save
|
|
25
30
|
bar, tabular/stacked inlines and related-object popups.
|
|
26
31
|
- Modern login, logout and password change pages.
|
|
27
32
|
- Light, dark and system themes. The existing Django `theme` localStorage
|
|
28
33
|
preference is synchronised with Bootstrap's `data-bs-theme` attribute.
|
|
34
|
+
- Dark-theme secondary text, table headers, filter links and help text use
|
|
35
|
+
higher-contrast tokens for better readability.
|
|
29
36
|
- Soft fade-in/fade-out transition when switching themes, skipped
|
|
30
37
|
automatically for users who prefer reduced motion.
|
|
31
38
|
- Every click changes the visible theme: if `auto` would resolve to the theme
|
|
@@ -38,8 +45,8 @@ permissions and JavaScript remain in charge.
|
|
|
38
45
|
- CSP-aware: bundled scripts and styles use Django 6.1's
|
|
39
46
|
`{% csp_nonce_attr %}` tag, so nonce-based policies work out of the box.
|
|
40
47
|
- Generic by design: no models, no migrations, no project-specific imports and
|
|
41
|
-
no assumptions about the host project's branding. The default
|
|
42
|
-
palette is only a set of overridable CSS variables.
|
|
48
|
+
no assumptions about the host project's branding. The default warm
|
|
49
|
+
near-black/yellow palette is only a set of overridable CSS variables.
|
|
43
50
|
|
|
44
51
|
## Requirements
|
|
45
52
|
|
|
@@ -151,14 +158,15 @@ custom admin CSS file:
|
|
|
151
158
|
|
|
152
159
|
```css
|
|
153
160
|
:root {
|
|
154
|
-
--ba-topbar-bg: #
|
|
155
|
-
--ba-gold: #
|
|
161
|
+
--ba-topbar-bg: #231f20;
|
|
162
|
+
--ba-gold: #ffcb08;
|
|
163
|
+
--ba-canvas: #f6f6f4;
|
|
156
164
|
--ba-sidebar-width: 300px;
|
|
157
165
|
}
|
|
158
166
|
```
|
|
159
167
|
|
|
160
|
-
The default primary
|
|
161
|
-
|
|
168
|
+
The default primary is the warm yellow accent with dark text, and the danger
|
|
169
|
+
colour remains a deep red for readable white labels.
|
|
162
170
|
|
|
163
171
|
The select controls use small `data:` SVG arrows. If you enable a strict CSP,
|
|
164
172
|
allow `img-src 'self' data:` (or replace the arrows in your own CSS).
|
|
@@ -186,6 +194,16 @@ There is no runtime state to clean up.
|
|
|
186
194
|
- `bootstrap.bundle.min.js`:
|
|
187
195
|
`47ea447f46e288dd9d3e83147b7ba539c6df5192c047b8f91739cd56a5721148`
|
|
188
196
|
|
|
197
|
+
### Language
|
|
198
|
+
|
|
199
|
+
Django's admin is translated using the active language; it is not fixed to
|
|
200
|
+
English. With `LANGUAGE_CODE = "tr"`, Django's own admin labels are Turkish,
|
|
201
|
+
and this theme follows the same project language. The package's source strings
|
|
202
|
+
are English and are translated when a matching locale is installed. To keep
|
|
203
|
+
the admin in English while the public site uses another language, activate
|
|
204
|
+
English for `/admin/` in your project (for example with a small middleware),
|
|
205
|
+
or set `LANGUAGE_CODE = "en-us"` if the whole site should be English.
|
|
206
|
+
|
|
189
207
|
## Development
|
|
190
208
|
|
|
191
209
|
The test suite uses an isolated settings module and a small demo app. Create a
|
|
@@ -12,42 +12,45 @@
|
|
|
12
12
|
--ba-sidebar-width: 272px;
|
|
13
13
|
--ba-content-max: 1600px;
|
|
14
14
|
|
|
15
|
-
--ba-topbar-bg: light-dark(#
|
|
16
|
-
--ba-topbar-fg: light-dark(#
|
|
17
|
-
--ba-topbar-muted: light-dark(#
|
|
18
|
-
--ba-canvas: light-dark(#
|
|
19
|
-
--ba-surface: light-dark(#ffffff, #
|
|
20
|
-
--ba-surface-muted: light-dark(#
|
|
21
|
-
--ba-border: light-dark(#
|
|
22
|
-
--ba-border-strong: light-dark(#
|
|
23
|
-
--ba-gold: light-dark(#
|
|
24
|
-
--ba-shadow-sm: 0 1px 2px rgb(
|
|
25
|
-
--ba-shadow-md: 0 0.
|
|
26
|
-
--ba-shadow-lg: 0
|
|
27
|
-
--ba-radius:
|
|
28
|
-
--ba-radius-sm:
|
|
29
|
-
--ba-text-muted: light-dark(#
|
|
30
|
-
--ba-control-border: light-dark(#
|
|
15
|
+
--ba-topbar-bg: light-dark(#231f20, #242122);
|
|
16
|
+
--ba-topbar-fg: light-dark(#f6f4f4, #f2efef);
|
|
17
|
+
--ba-topbar-muted: light-dark(#d8d3d2, #c6c0c0);
|
|
18
|
+
--ba-canvas: light-dark(#f6f6f4, #191718);
|
|
19
|
+
--ba-surface: light-dark(#ffffff, #222021);
|
|
20
|
+
--ba-surface-muted: light-dark(#fafaf8, #292627);
|
|
21
|
+
--ba-border: light-dark(#e3e3df, #3a3637);
|
|
22
|
+
--ba-border-strong: light-dark(#cfcfc8, #4a4546);
|
|
23
|
+
--ba-gold: light-dark(#ffcb08, #d8ab32);
|
|
24
|
+
--ba-shadow-sm: 0 1px 2px rgb(35 31 32 / 0.05);
|
|
25
|
+
--ba-shadow-md: 0 0.35rem 1rem rgb(35 31 32 / 0.07);
|
|
26
|
+
--ba-shadow-lg: 0 0.75rem 2rem rgb(35 31 32 / 0.12);
|
|
27
|
+
--ba-radius: 6px;
|
|
28
|
+
--ba-radius-sm: 4px;
|
|
29
|
+
--ba-text-muted: light-dark(#65605f, #c6c0c0);
|
|
30
|
+
--ba-control-border: light-dark(#b8b3ad, #6b6464);
|
|
31
31
|
|
|
32
32
|
/*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* The Pardus Teknofest palette uses a warm near-black surface and a
|
|
34
|
+
* saturated yellow accent with dark text. Keep the primary role usable as
|
|
35
|
+
* an accent rather than a blue action colour.
|
|
36
36
|
*/
|
|
37
|
-
--bs-primary-base: light-dark(#
|
|
38
|
-
--bs-primary-bg: light-dark(#
|
|
39
|
-
--bs-primary-contrast: #
|
|
40
|
-
--bs-primary-fg: light-dark(#
|
|
41
|
-
--bs-primary-fg-emphasis: light-dark(#
|
|
42
|
-
--bs-primary-bg-subtle: light-dark(#
|
|
43
|
-
--bs-primary-bg-muted: light-dark(#
|
|
44
|
-
--bs-primary-border: light-dark(#
|
|
45
|
-
--bs-secondary-border: light-dark(#
|
|
37
|
+
--bs-primary-base: light-dark(#ffcb08, #d8ab32);
|
|
38
|
+
--bs-primary-bg: light-dark(#ffcb08, #d8ab32);
|
|
39
|
+
--bs-primary-contrast: #231f20;
|
|
40
|
+
--bs-primary-fg: light-dark(#7a5a00, #e8c66e);
|
|
41
|
+
--bs-primary-fg-emphasis: light-dark(#5f4600, #f3d98f);
|
|
42
|
+
--bs-primary-bg-subtle: light-dark(#fff6d6, #342c18);
|
|
43
|
+
--bs-primary-bg-muted: light-dark(#ffeaa1, #4a3d1e);
|
|
44
|
+
--bs-primary-border: light-dark(#d4a900, #8a6a19);
|
|
45
|
+
--bs-secondary-border: light-dark(#b8b3ad, #6b6464);
|
|
46
46
|
--bs-primary-focus-ring: color-mix(
|
|
47
47
|
in oklch,
|
|
48
|
-
var(--bs-primary-bg)
|
|
49
|
-
|
|
48
|
+
var(--bs-primary-bg) 45%,
|
|
49
|
+
transparent
|
|
50
50
|
);
|
|
51
|
+
--bs-link-color: light-dark(#8a6500, #e8c66e);
|
|
52
|
+
--bs-link-hover-color: light-dark(#6f5000, #f3d98f);
|
|
53
|
+
--bs-border-muted: light-dark(#e3e3df, #3a3637);
|
|
51
54
|
|
|
52
55
|
--bs-danger-base: light-dark(#b4233a, #c23a53);
|
|
53
56
|
--bs-danger-bg: light-dark(#b4233a, #c23a53);
|
|
@@ -62,8 +65,8 @@
|
|
|
62
65
|
var(--bs-danger-bg) 55%,
|
|
63
66
|
var(--bs-bg-body)
|
|
64
67
|
);
|
|
65
|
-
--ba-select-arrow: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%
|
|
66
|
-
--ba-select-arrow-dark: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%
|
|
68
|
+
--ba-select-arrow: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%237a7472' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
|
|
69
|
+
--ba-select-arrow-dark: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23c6c0c0' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
|
|
67
70
|
|
|
68
71
|
/* Django admin CSS variables used by widgets.css / autocomplete.css. */
|
|
69
72
|
--primary: var(--bs-primary-bg);
|
|
@@ -111,6 +114,23 @@
|
|
|
111
114
|
2. Base
|
|
112
115
|
-------------------------------------------------------------------------- */
|
|
113
116
|
|
|
117
|
+
/*
|
|
118
|
+
* Bootstrap's dark gray-500/600 secondary text is too dim on the deep navy
|
|
119
|
+
* admin surfaces, especially at the small sizes used by filters, table
|
|
120
|
+
* headers and action help. Lift those tokens for the effective dark theme.
|
|
121
|
+
*/
|
|
122
|
+
[data-bs-theme="dark"],
|
|
123
|
+
[data-effective-theme="dark"] {
|
|
124
|
+
--bs-fg-1: #f2efef;
|
|
125
|
+
--bs-fg-2: #dedada;
|
|
126
|
+
--bs-fg-3: #c6c0c0;
|
|
127
|
+
--bs-fg-4: #b0aaa9;
|
|
128
|
+
--ba-text-muted: #c6c0c0;
|
|
129
|
+
--bs-link-color: #e8c66e;
|
|
130
|
+
--bs-link-hover-color: #f3d98f;
|
|
131
|
+
--bs-border-muted: #3a3637;
|
|
132
|
+
}
|
|
133
|
+
|
|
114
134
|
html {
|
|
115
135
|
min-height: 100%;
|
|
116
136
|
}
|
|
@@ -353,14 +373,9 @@ samp {
|
|
|
353
373
|
inset-inline-start: 0;
|
|
354
374
|
width: 100%;
|
|
355
375
|
height: 3px;
|
|
356
|
-
background:
|
|
357
|
-
90deg,
|
|
358
|
-
var(--ba-gold) 0,
|
|
359
|
-
var(--ba-gold) 4.5rem,
|
|
360
|
-
transparent 4.5rem
|
|
361
|
-
);
|
|
376
|
+
background: var(--ba-gold);
|
|
362
377
|
content: "";
|
|
363
|
-
opacity:
|
|
378
|
+
opacity: 1;
|
|
364
379
|
pointer-events: none;
|
|
365
380
|
}
|
|
366
381
|
|
|
@@ -421,13 +436,12 @@ samp {
|
|
|
421
436
|
place-items: center;
|
|
422
437
|
width: 2.35rem;
|
|
423
438
|
height: 2.35rem;
|
|
424
|
-
border-radius:
|
|
425
|
-
background:
|
|
426
|
-
color:
|
|
439
|
+
border-radius: var(--ba-radius-sm);
|
|
440
|
+
background: var(--ba-gold);
|
|
441
|
+
color: #231f20;
|
|
427
442
|
font-size: 1.05rem;
|
|
428
443
|
font-weight: 800;
|
|
429
444
|
line-height: 1;
|
|
430
|
-
box-shadow: 0 2px 0 rgb(0 0 0 / 0.12);
|
|
431
445
|
}
|
|
432
446
|
|
|
433
447
|
.ba-brand-text {
|
|
@@ -462,6 +476,32 @@ samp {
|
|
|
462
476
|
margin-inline-start: auto;
|
|
463
477
|
}
|
|
464
478
|
|
|
479
|
+
.ba-view-site {
|
|
480
|
+
--bs-btn-color: var(--ba-topbar-fg);
|
|
481
|
+
--bs-btn-bg: transparent;
|
|
482
|
+
--bs-btn-border-color: rgb(255 255 255 / 0.22);
|
|
483
|
+
--bs-btn-hover-bg: rgb(255 255 255 / 0.1);
|
|
484
|
+
--bs-btn-hover-color: #fff;
|
|
485
|
+
--bs-btn-hover-border-color: rgb(255 255 255 / 0.38);
|
|
486
|
+
--bs-btn-active-bg: rgb(255 255 255 / 0.16);
|
|
487
|
+
--bs-btn-active-color: #fff;
|
|
488
|
+
display: inline-flex;
|
|
489
|
+
align-items: center;
|
|
490
|
+
justify-content: center;
|
|
491
|
+
gap: 0.35rem;
|
|
492
|
+
min-height: 2.35rem;
|
|
493
|
+
padding: 0.25rem 0.7rem;
|
|
494
|
+
border-radius: var(--ba-radius-sm);
|
|
495
|
+
font-size: 0.8rem;
|
|
496
|
+
font-weight: 600;
|
|
497
|
+
white-space: nowrap;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.ba-view-site .ba-icon {
|
|
501
|
+
width: 0.9rem;
|
|
502
|
+
height: 0.9rem;
|
|
503
|
+
}
|
|
504
|
+
|
|
465
505
|
.ba-theme-toggle {
|
|
466
506
|
--bs-btn-color: var(--ba-topbar-fg);
|
|
467
507
|
--bs-btn-bg: transparent;
|
|
@@ -473,7 +513,7 @@ samp {
|
|
|
473
513
|
--bs-btn-active-color: #fff;
|
|
474
514
|
width: 2.35rem;
|
|
475
515
|
height: 2.35rem;
|
|
476
|
-
border-radius:
|
|
516
|
+
border-radius: var(--ba-radius-sm);
|
|
477
517
|
}
|
|
478
518
|
|
|
479
519
|
#branding > .ba-theme-toggle {
|
|
@@ -522,7 +562,7 @@ html[data-effective-theme="dark"] .theme-icon-when-dark {
|
|
|
522
562
|
--bs-btn-active-color: #fff;
|
|
523
563
|
min-height: 2.35rem;
|
|
524
564
|
padding: 0.25rem 0.55rem 0.25rem 0.3rem;
|
|
525
|
-
border-radius:
|
|
565
|
+
border-radius: var(--ba-radius-sm);
|
|
526
566
|
}
|
|
527
567
|
|
|
528
568
|
.ba-user-avatar {
|
|
@@ -533,7 +573,7 @@ html[data-effective-theme="dark"] .theme-icon-when-dark {
|
|
|
533
573
|
height: 1.8rem;
|
|
534
574
|
border-radius: 50%;
|
|
535
575
|
background: var(--ba-gold);
|
|
536
|
-
color: #
|
|
576
|
+
color: #231f20;
|
|
537
577
|
font-size: 0.78rem;
|
|
538
578
|
font-weight: 800;
|
|
539
579
|
}
|
|
@@ -1365,7 +1405,7 @@ a.ba-timeline-object:focus {
|
|
|
1365
1405
|
}
|
|
1366
1406
|
|
|
1367
1407
|
.ba-filters-toggle-row {
|
|
1368
|
-
display:
|
|
1408
|
+
display: flex;
|
|
1369
1409
|
}
|
|
1370
1410
|
|
|
1371
1411
|
.ba-filters-toggle {
|
|
@@ -1551,6 +1591,20 @@ a.ba-date-chip:focus {
|
|
|
1551
1591
|
border-block-end: 0;
|
|
1552
1592
|
}
|
|
1553
1593
|
|
|
1594
|
+
/* Very light column rules keep dense admin tables readable without making
|
|
1595
|
+
* every cell look boxed in. */
|
|
1596
|
+
#result_list thead th:not(:last-child),
|
|
1597
|
+
#result_list tbody th:not(:last-child),
|
|
1598
|
+
#result_list tbody td:not(:last-child),
|
|
1599
|
+
.ba-inline-table thead th:not(:last-child),
|
|
1600
|
+
.ba-inline-table tbody th:not(:last-child),
|
|
1601
|
+
.ba-inline-table tbody td:not(:last-child),
|
|
1602
|
+
.ba-history-table thead th:not(:last-child),
|
|
1603
|
+
.ba-history-table tbody th:not(:last-child),
|
|
1604
|
+
.ba-history-table tbody td:not(:last-child) {
|
|
1605
|
+
border-inline-end: 1px solid var(--bs-border-muted);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1554
1608
|
#result_list tbody tr.selected > * {
|
|
1555
1609
|
background: var(--bs-primary-bg-subtle) !important;
|
|
1556
1610
|
}
|
|
@@ -1597,13 +1651,15 @@ a.ba-date-chip:focus {
|
|
|
1597
1651
|
|
|
1598
1652
|
#result_list select {
|
|
1599
1653
|
padding-inline-end: 1.6rem;
|
|
1654
|
+
appearance: none;
|
|
1600
1655
|
background-image: var(--ba-select-arrow);
|
|
1601
1656
|
background-position: right 0.45rem center;
|
|
1602
1657
|
background-repeat: no-repeat;
|
|
1603
1658
|
background-size: 0.65rem;
|
|
1604
1659
|
}
|
|
1605
1660
|
|
|
1606
|
-
[data-bs-theme="dark"] #result_list select
|
|
1661
|
+
[data-bs-theme="dark"] #result_list select,
|
|
1662
|
+
[data-effective-theme="dark"] #result_list select {
|
|
1607
1663
|
background-image: var(--ba-select-arrow-dark);
|
|
1608
1664
|
}
|
|
1609
1665
|
|
|
@@ -2177,6 +2233,7 @@ label.required::after {
|
|
|
2177
2233
|
.ba-form-group select,
|
|
2178
2234
|
.ba-actions select {
|
|
2179
2235
|
padding-inline-end: 2rem;
|
|
2236
|
+
appearance: none;
|
|
2180
2237
|
background-image: var(--ba-select-arrow);
|
|
2181
2238
|
background-position: right 0.7rem center;
|
|
2182
2239
|
background-repeat: no-repeat;
|
|
@@ -2185,7 +2242,10 @@ label.required::after {
|
|
|
2185
2242
|
|
|
2186
2243
|
[data-bs-theme="dark"] .ba-field select,
|
|
2187
2244
|
[data-bs-theme="dark"] .ba-form-group select,
|
|
2188
|
-
[data-bs-theme="dark"] .ba-actions select
|
|
2245
|
+
[data-bs-theme="dark"] .ba-actions select,
|
|
2246
|
+
[data-effective-theme="dark"] .ba-field select,
|
|
2247
|
+
[data-effective-theme="dark"] .ba-form-group select,
|
|
2248
|
+
[data-effective-theme="dark"] .ba-actions select {
|
|
2189
2249
|
background-image: var(--ba-select-arrow-dark);
|
|
2190
2250
|
}
|
|
2191
2251
|
|
|
@@ -2551,13 +2611,15 @@ div.help ul {
|
|
|
2551
2611
|
|
|
2552
2612
|
.ba-inline-table select {
|
|
2553
2613
|
padding-inline-end: 1.6rem;
|
|
2614
|
+
appearance: none;
|
|
2554
2615
|
background-image: var(--ba-select-arrow);
|
|
2555
2616
|
background-position: right 0.45rem center;
|
|
2556
2617
|
background-repeat: no-repeat;
|
|
2557
2618
|
background-size: 0.65rem;
|
|
2558
2619
|
}
|
|
2559
2620
|
|
|
2560
|
-
[data-bs-theme="dark"] .ba-inline-table select
|
|
2621
|
+
[data-bs-theme="dark"] .ba-inline-table select,
|
|
2622
|
+
[data-effective-theme="dark"] .ba-inline-table select {
|
|
2561
2623
|
background-image: var(--ba-select-arrow-dark);
|
|
2562
2624
|
}
|
|
2563
2625
|
|
|
@@ -2905,7 +2967,7 @@ body.login .ba-topbar-inner {
|
|
|
2905
2967
|
padding: 2.5rem;
|
|
2906
2968
|
border-radius: calc(var(--ba-radius) + 0.25rem);
|
|
2907
2969
|
background:
|
|
2908
|
-
radial-gradient(circle at 18% 18%, rgb(
|
|
2970
|
+
radial-gradient(circle at 18% 18%, rgb(255 203 8 / 0.18), transparent 42%),
|
|
2909
2971
|
var(--ba-topbar-bg);
|
|
2910
2972
|
color: var(--ba-topbar-fg);
|
|
2911
2973
|
box-shadow: var(--ba-shadow-lg);
|
|
@@ -2927,9 +2989,9 @@ body.login .ba-topbar-inner {
|
|
|
2927
2989
|
place-items: center;
|
|
2928
2990
|
width: 3rem;
|
|
2929
2991
|
height: 3rem;
|
|
2930
|
-
border-radius:
|
|
2931
|
-
background:
|
|
2932
|
-
color:
|
|
2992
|
+
border-radius: var(--ba-radius);
|
|
2993
|
+
background: var(--ba-gold);
|
|
2994
|
+
color: #231f20;
|
|
2933
2995
|
font-size: 1.3rem;
|
|
2934
2996
|
font-weight: 800;
|
|
2935
2997
|
}
|
|
@@ -3202,6 +3264,16 @@ body.popup .ba-submit-row {
|
|
|
3202
3264
|
13. Responsive
|
|
3203
3265
|
-------------------------------------------------------------------------- */
|
|
3204
3266
|
|
|
3267
|
+
@media (min-width: 1200px) {
|
|
3268
|
+
#changelist.ba-filters-collapsed .ba-changelist-layout {
|
|
3269
|
+
grid-template-columns: minmax(0, 1fr);
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
#changelist.ba-filters-collapsed .ba-filters {
|
|
3273
|
+
display: none;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3205
3277
|
@media (max-width: 1199.98px) {
|
|
3206
3278
|
.ba-dashboard {
|
|
3207
3279
|
grid-template-columns: minmax(0, 1fr);
|
|
@@ -3346,6 +3418,15 @@ body.popup .ba-submit-row {
|
|
|
3346
3418
|
padding-inline: 0.25rem;
|
|
3347
3419
|
}
|
|
3348
3420
|
|
|
3421
|
+
.ba-view-site {
|
|
3422
|
+
width: 2.35rem;
|
|
3423
|
+
padding: 0;
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
.ba-view-site-label {
|
|
3427
|
+
display: none;
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3349
3430
|
.ba-brand-title {
|
|
3350
3431
|
max-width: 11rem;
|
|
3351
3432
|
font-size: 0.9rem;
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
(() => {
|
|
12
12
|
const MOBILE = window.matchMedia("(max-width: 1023.98px)");
|
|
13
|
+
const DESKTOP = window.matchMedia("(min-width: 1200px)");
|
|
13
14
|
const STORAGE_KEY = "django.admin.navSidebarIsOpen";
|
|
15
|
+
const FILTERS_KEY = "django.admin.filtersCollapsed";
|
|
16
|
+
const FACETS_KEY = "django.admin.showFacets";
|
|
14
17
|
|
|
15
18
|
function initSidebar() {
|
|
16
19
|
const main = document.getElementById("main");
|
|
@@ -228,16 +231,50 @@
|
|
|
228
231
|
return;
|
|
229
232
|
}
|
|
230
233
|
|
|
234
|
+
const isCollapsed = () =>
|
|
235
|
+
changelist.classList.contains("ba-filters-collapsed");
|
|
236
|
+
|
|
231
237
|
const sync = () => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
changelist.classList.contains("ba-filters-open")
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
+
const expanded = DESKTOP.matches
|
|
239
|
+
? !isCollapsed()
|
|
240
|
+
: changelist.classList.contains("ba-filters-open");
|
|
241
|
+
toggle.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
242
|
+
if (panel) {
|
|
243
|
+
panel.toggleAttribute(
|
|
244
|
+
"inert",
|
|
245
|
+
DESKTOP.matches && isCollapsed(),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
238
248
|
};
|
|
239
249
|
|
|
250
|
+
const setCollapsed = (collapsed) => {
|
|
251
|
+
changelist.classList.toggle("ba-filters-collapsed", collapsed);
|
|
252
|
+
if (DESKTOP.matches) {
|
|
253
|
+
localStorage.setItem(
|
|
254
|
+
FILTERS_KEY,
|
|
255
|
+
collapsed ? "true" : "false",
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
sync();
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Restore the desktop preference. On mobile the panel is a drawer and
|
|
262
|
+
// the collapsed class has no visual effect.
|
|
263
|
+
if (DESKTOP.matches && localStorage.getItem(FILTERS_KEY) === "true") {
|
|
264
|
+
changelist.classList.add("ba-filters-collapsed");
|
|
265
|
+
}
|
|
266
|
+
|
|
240
267
|
toggle.addEventListener("click", () => {
|
|
268
|
+
if (DESKTOP.matches) {
|
|
269
|
+
const collapsed = !isCollapsed();
|
|
270
|
+
setCollapsed(collapsed);
|
|
271
|
+
if (!collapsed) {
|
|
272
|
+
const target = panel?.querySelector("summary, a, button");
|
|
273
|
+
window.requestAnimationFrame(() => target?.focus());
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
241
278
|
changelist.classList.toggle("ba-filters-open");
|
|
242
279
|
sync();
|
|
243
280
|
if (changelist.classList.contains("ba-filters-open")) {
|
|
@@ -249,15 +286,69 @@
|
|
|
249
286
|
|
|
250
287
|
if (close) {
|
|
251
288
|
close.addEventListener("click", () => {
|
|
289
|
+
setCollapsed(true);
|
|
252
290
|
changelist.classList.remove("ba-filters-open");
|
|
253
291
|
sync();
|
|
254
292
|
toggle.focus();
|
|
255
293
|
});
|
|
256
294
|
}
|
|
257
295
|
|
|
296
|
+
if (DESKTOP.addEventListener) {
|
|
297
|
+
DESKTOP.addEventListener("change", () => {
|
|
298
|
+
if (DESKTOP.matches) {
|
|
299
|
+
changelist.classList.toggle(
|
|
300
|
+
"ba-filters-collapsed",
|
|
301
|
+
localStorage.getItem(FILTERS_KEY) === "true",
|
|
302
|
+
);
|
|
303
|
+
changelist.classList.remove("ba-filters-open");
|
|
304
|
+
}
|
|
305
|
+
sync();
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
258
309
|
sync();
|
|
259
310
|
}
|
|
260
311
|
|
|
312
|
+
function initFacetPreference() {
|
|
313
|
+
const panel = document.getElementById("changelist-filter");
|
|
314
|
+
if (!panel) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const links = document.querySelectorAll(
|
|
319
|
+
".ba-filters-actions .hidelink, .ba-filters-actions .viewlink",
|
|
320
|
+
);
|
|
321
|
+
if (!links.length) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
links.forEach((link) => {
|
|
326
|
+
link.addEventListener("click", () => {
|
|
327
|
+
const show = link.classList.contains("viewlink");
|
|
328
|
+
localStorage.setItem(FACETS_KEY, show ? "true" : "false");
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const stored = localStorage.getItem(FACETS_KEY);
|
|
333
|
+
if (stored !== "true" && stored !== "false") {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const url = new URL(window.location.href);
|
|
338
|
+
const hasFacets = url.searchParams.has("_facets");
|
|
339
|
+
const showFacets = stored === "true";
|
|
340
|
+
if (hasFacets === showFacets) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (showFacets) {
|
|
345
|
+
url.searchParams.set("_facets", "1");
|
|
346
|
+
} else {
|
|
347
|
+
url.searchParams.delete("_facets");
|
|
348
|
+
}
|
|
349
|
+
window.location.replace(url.toString());
|
|
350
|
+
}
|
|
351
|
+
|
|
261
352
|
function initDirtyGuard() {
|
|
262
353
|
const form =
|
|
263
354
|
document.querySelector(".ba-change-form form") ||
|
|
@@ -327,6 +418,7 @@
|
|
|
327
418
|
function initAll() {
|
|
328
419
|
initSidebar();
|
|
329
420
|
initFilterDisclosure();
|
|
421
|
+
initFacetPreference();
|
|
330
422
|
initDirtyGuard();
|
|
331
423
|
initErrorFocus();
|
|
332
424
|
initAccessibility();
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<title>{% block title %}{% endblock %}</title>
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
8
|
<meta name="color-scheme" content="light dark">
|
|
9
|
-
<meta name="theme-color" content="#
|
|
10
|
-
<meta name="theme-color" content="#
|
|
9
|
+
<meta name="theme-color" content="#231f20" media="(prefers-color-scheme: light)">
|
|
10
|
+
<meta name="theme-color" content="#242122" media="(prefers-color-scheme: dark)">
|
|
11
11
|
<script src="{% static 'django_bootstrap6_admin/js/theme.js' %}" {% csp_nonce_attr %}></script>
|
|
12
12
|
<link rel="stylesheet" href="{% static 'django_bootstrap6_admin/vendor/bootstrap/css/bootstrap.min.css' %}" {% csp_nonce_attr %}>
|
|
13
13
|
<link rel="stylesheet" href="{% static 'admin/css/widgets.css' %}" {% csp_nonce_attr %}>
|
|
@@ -48,58 +48,60 @@
|
|
|
48
48
|
</div>
|
|
49
49
|
|
|
50
50
|
{% block usertools %}
|
|
51
|
-
{% if has_permission %}
|
|
51
|
+
{% if site_url or has_permission %}
|
|
52
52
|
<div id="user-tools" class="ba-user-tools">
|
|
53
|
-
{%
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
</
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{% translate '
|
|
75
|
-
</
|
|
76
|
-
{%
|
|
77
|
-
{%
|
|
78
|
-
{%
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
|
|
53
|
+
{% if site_url %}
|
|
54
|
+
<a class="btn btn-outline ba-view-site" href="{{ site_url }}">
|
|
55
|
+
<svg class="ba-icon" aria-hidden="true"><use href="#ba-external"></use></svg>
|
|
56
|
+
<span class="ba-view-site-label">{% translate 'View site' %}</span>
|
|
57
|
+
</a>
|
|
58
|
+
{% endif %}
|
|
59
|
+
{% if has_permission %}
|
|
60
|
+
{% include "admin/color_theme_toggle.html" %}
|
|
61
|
+
{% firstof user.get_short_name user.get_username as user_name %}
|
|
62
|
+
<div class="ba-user-menu-wrap">
|
|
63
|
+
<button type="button" class="btn btn-outline theme-secondary ba-user-menu-button"
|
|
64
|
+
data-bs-toggle="menu" data-bs-target="#ba-user-menu"
|
|
65
|
+
aria-expanded="false" aria-haspopup="true">
|
|
66
|
+
<span class="visually-hidden">{% translate 'User menu' %}</span>
|
|
67
|
+
<span class="ba-user-avatar" aria-hidden="true">{{ user_name|first|upper }}</span>
|
|
68
|
+
<span class="ba-user-name">{% firstof user.get_short_name user.get_username %}</span>
|
|
69
|
+
<svg class="ba-icon ba-chevron" aria-hidden="true"><use href="#ba-chevron-down"></use></svg>
|
|
70
|
+
</button>
|
|
71
|
+
<div class="menu ba-user-menu" id="ba-user-menu">
|
|
72
|
+
{% block welcome-msg %}
|
|
73
|
+
<div class="menu-header">
|
|
74
|
+
{% translate 'Welcome,' %} <strong>{% firstof user.get_short_name user.get_username %}</strong>.
|
|
75
|
+
</div>
|
|
76
|
+
{% endblock %}
|
|
77
|
+
{% block userlinks %}
|
|
78
|
+
{% if user.is_active and user.is_staff %}
|
|
79
|
+
{% url 'django-admindocs-docroot' as docsroot %}
|
|
80
|
+
{% if docsroot %}
|
|
81
|
+
<a class="menu-item" href="{{ docsroot }}">
|
|
82
|
+
<svg class="ba-icon" aria-hidden="true"><use href="#ba-document"></use></svg>
|
|
83
|
+
{% translate 'Documentation' %}
|
|
84
|
+
</a>
|
|
85
|
+
{% endif %}
|
|
86
|
+
{% endif %}
|
|
87
|
+
{% if user.has_usable_password %}
|
|
88
|
+
<a class="menu-item" href="{% url 'admin:password_change' %}">
|
|
89
|
+
<svg class="ba-icon" aria-hidden="true"><use href="#ba-key"></use></svg>
|
|
90
|
+
{% translate 'Change password' %}
|
|
83
91
|
</a>
|
|
84
92
|
{% endif %}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<button type="submit" class="menu-item">
|
|
96
|
-
<svg class="ba-icon" aria-hidden="true"><use href="#ba-logout"></use></svg>
|
|
97
|
-
{% translate 'Log out' %}
|
|
98
|
-
</button>
|
|
99
|
-
</form>
|
|
100
|
-
{% endblock %}
|
|
93
|
+
<div class="menu-divider"></div>
|
|
94
|
+
<form id="logout-form" method="post" action="{% url 'admin:logout' %}">
|
|
95
|
+
{% csrf_token %}
|
|
96
|
+
<button type="submit" class="menu-item">
|
|
97
|
+
<svg class="ba-icon" aria-hidden="true"><use href="#ba-logout"></use></svg>
|
|
98
|
+
{% translate 'Log out' %}
|
|
99
|
+
</button>
|
|
100
|
+
</form>
|
|
101
|
+
{% endblock %}
|
|
102
|
+
</div>
|
|
101
103
|
</div>
|
|
102
|
-
|
|
104
|
+
{% endif %}
|
|
103
105
|
</div>
|
|
104
106
|
{% endif %}
|
|
105
107
|
{% endblock %}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
{% block extrastyle %}{{ block.super }}{% csp_nonce_attr form.media %}{% endblock %}
|
|
6
6
|
{% block bodyclass %}{{ block.super }} login ba-login-page ba-no-sidebar{% endblock %}
|
|
7
7
|
|
|
8
|
-
{% block usertools %}{% endblock %}
|
|
9
8
|
{% block nav-global %}{% endblock %}
|
|
10
9
|
{% block nav-sidebar %}{% endblock %}
|
|
11
10
|
{% block nav-breadcrumbs %}{% endblock %}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-bootstrap6-admin
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A modern Bootstrap 6 skin for the Django admin.
|
|
5
5
|
Author-email: Aliberk Sandıkçı <git@asandikci.com>
|
|
6
6
|
Maintainer-email: Aliberk Sandıkçı <git@asandikci.com>
|
|
@@ -53,16 +53,23 @@ permissions and JavaScript remain in charge.
|
|
|
53
53
|
## Features
|
|
54
54
|
|
|
55
55
|
- Bootstrap 6 markup and design tokens throughout the admin.
|
|
56
|
+
- "View site" is always available in the top bar, not buried in the user menu.
|
|
56
57
|
- Sticky top bar with a collapsible sidebar (desktop) and drawer navigation
|
|
57
58
|
with a backdrop (mobile).
|
|
58
59
|
- Dashboard with application cards, model shortcuts and recent activity.
|
|
59
60
|
- Changelist with search, date hierarchy, collapsible filters, sortable
|
|
60
|
-
headers, bulk actions and Bootstrap pagination.
|
|
61
|
+
headers, bulk actions and Bootstrap pagination. The desktop filter panel can
|
|
62
|
+
be collapsed, and both its state and the "Show counts" choice are remembered
|
|
63
|
+
between pages.
|
|
64
|
+
- Subtle vertical rules separate columns in changelists, inline tables and the
|
|
65
|
+
object history.
|
|
61
66
|
- Change form with card-based fieldsets, collapsible sections, a clear save
|
|
62
67
|
bar, tabular/stacked inlines and related-object popups.
|
|
63
68
|
- Modern login, logout and password change pages.
|
|
64
69
|
- Light, dark and system themes. The existing Django `theme` localStorage
|
|
65
70
|
preference is synchronised with Bootstrap's `data-bs-theme` attribute.
|
|
71
|
+
- Dark-theme secondary text, table headers, filter links and help text use
|
|
72
|
+
higher-contrast tokens for better readability.
|
|
66
73
|
- Soft fade-in/fade-out transition when switching themes, skipped
|
|
67
74
|
automatically for users who prefer reduced motion.
|
|
68
75
|
- Every click changes the visible theme: if `auto` would resolve to the theme
|
|
@@ -75,8 +82,8 @@ permissions and JavaScript remain in charge.
|
|
|
75
82
|
- CSP-aware: bundled scripts and styles use Django 6.1's
|
|
76
83
|
`{% csp_nonce_attr %}` tag, so nonce-based policies work out of the box.
|
|
77
84
|
- Generic by design: no models, no migrations, no project-specific imports and
|
|
78
|
-
no assumptions about the host project's branding. The default
|
|
79
|
-
palette is only a set of overridable CSS variables.
|
|
85
|
+
no assumptions about the host project's branding. The default warm
|
|
86
|
+
near-black/yellow palette is only a set of overridable CSS variables.
|
|
80
87
|
|
|
81
88
|
## Requirements
|
|
82
89
|
|
|
@@ -188,14 +195,15 @@ custom admin CSS file:
|
|
|
188
195
|
|
|
189
196
|
```css
|
|
190
197
|
:root {
|
|
191
|
-
--ba-topbar-bg: #
|
|
192
|
-
--ba-gold: #
|
|
198
|
+
--ba-topbar-bg: #231f20;
|
|
199
|
+
--ba-gold: #ffcb08;
|
|
200
|
+
--ba-canvas: #f6f6f4;
|
|
193
201
|
--ba-sidebar-width: 300px;
|
|
194
202
|
}
|
|
195
203
|
```
|
|
196
204
|
|
|
197
|
-
The default primary
|
|
198
|
-
|
|
205
|
+
The default primary is the warm yellow accent with dark text, and the danger
|
|
206
|
+
colour remains a deep red for readable white labels.
|
|
199
207
|
|
|
200
208
|
The select controls use small `data:` SVG arrows. If you enable a strict CSP,
|
|
201
209
|
allow `img-src 'self' data:` (or replace the arrows in your own CSS).
|
|
@@ -223,6 +231,16 @@ There is no runtime state to clean up.
|
|
|
223
231
|
- `bootstrap.bundle.min.js`:
|
|
224
232
|
`47ea447f46e288dd9d3e83147b7ba539c6df5192c047b8f91739cd56a5721148`
|
|
225
233
|
|
|
234
|
+
### Language
|
|
235
|
+
|
|
236
|
+
Django's admin is translated using the active language; it is not fixed to
|
|
237
|
+
English. With `LANGUAGE_CODE = "tr"`, Django's own admin labels are Turkish,
|
|
238
|
+
and this theme follows the same project language. The package's source strings
|
|
239
|
+
are English and are translated when a matching locale is installed. To keep
|
|
240
|
+
the admin in English while the public site uses another language, activate
|
|
241
|
+
English for `/admin/` in your project (for example with a small middleware),
|
|
242
|
+
or set `LANGUAGE_CODE = "en-us"` if the whole site should be English.
|
|
243
|
+
|
|
226
244
|
## Development
|
|
227
245
|
|
|
228
246
|
The test suite uses an isolated settings module and a small demo app. Create a
|
|
@@ -49,6 +49,7 @@ class AdminUiTests(TestCase):
|
|
|
49
49
|
self.assertContains(response, "ba-app-grid")
|
|
50
50
|
self.assertContains(response, "ba-recent-card")
|
|
51
51
|
self.assertContains(response, "theme-toggle")
|
|
52
|
+
self.assertContains(response, "ba-view-site")
|
|
52
53
|
|
|
53
54
|
def test_default_django_admin_css_is_not_loaded(self):
|
|
54
55
|
response = self.client.get(reverse("admin:index"))
|
|
@@ -76,6 +77,8 @@ class AdminUiTests(TestCase):
|
|
|
76
77
|
'id="changelist-search"',
|
|
77
78
|
'id="searchbar"',
|
|
78
79
|
'id="changelist-filter"',
|
|
80
|
+
'id="ba-filters-toggle"',
|
|
81
|
+
'id="ba-filters-close"',
|
|
79
82
|
'id="action-toggle"',
|
|
80
83
|
'id="date-hierarchy-label"',
|
|
81
84
|
'class="actions ba-actions"',
|
|
@@ -119,7 +122,6 @@ class AdminUiTests(TestCase):
|
|
|
119
122
|
def test_changelist_search_and_pagination(self):
|
|
120
123
|
response = self.client.get(reverse("admin:testapp_book_changelist"), {"q": "Django"})
|
|
121
124
|
self.assertContains(response, "Django for Everyone")
|
|
122
|
-
|
|
123
125
|
for index in range(105):
|
|
124
126
|
Book.objects.create(
|
|
125
127
|
title=f"Extra book {index:03d}",
|
|
@@ -134,6 +136,15 @@ class AdminUiTests(TestCase):
|
|
|
134
136
|
self.assertContains(response, "?p=1")
|
|
135
137
|
self.assertContains(response, 'aria-current="page">2')
|
|
136
138
|
|
|
139
|
+
def test_facet_count_toggle_links(self):
|
|
140
|
+
url = reverse("admin:testapp_book_changelist")
|
|
141
|
+
|
|
142
|
+
response = self.client.get(url)
|
|
143
|
+
self.assertContains(response, "Show counts")
|
|
144
|
+
|
|
145
|
+
response = self.client.get(url, {"_facets": "1"})
|
|
146
|
+
self.assertContains(response, "Hide counts")
|
|
147
|
+
|
|
137
148
|
def test_change_form_keeps_widgets_and_submit_contract(self):
|
|
138
149
|
response = self.client.get(reverse("admin:testapp_book_change", args=[self.book.pk]))
|
|
139
150
|
|
|
@@ -212,6 +223,7 @@ class AdminUiTests(TestCase):
|
|
|
212
223
|
self.assertContains(response, 'type="password"')
|
|
213
224
|
self.assertContains(response, "theme-toggle")
|
|
214
225
|
self.assertContains(response, "ba-login")
|
|
226
|
+
self.assertContains(response, "ba-view-site")
|
|
215
227
|
|
|
216
228
|
def test_login_post_works(self):
|
|
217
229
|
self.client.logout()
|
|
@@ -46,3 +46,21 @@ class PackageAssetsTests(SimpleTestCase):
|
|
|
46
46
|
self.assertIn(".ba-theme-fade", admin_css)
|
|
47
47
|
self.assertIn(".ba-theme-fade.is-active", admin_css)
|
|
48
48
|
self.assertIn("transition: opacity 200ms ease", admin_css)
|
|
49
|
+
|
|
50
|
+
def test_changelist_ux_assets(self):
|
|
51
|
+
static_dir = Path(django_bootstrap6_admin.__file__).parent / "static"
|
|
52
|
+
admin_js = (static_dir / "django_bootstrap6_admin" / "js" / "admin.js").read_text(
|
|
53
|
+
encoding="utf-8"
|
|
54
|
+
)
|
|
55
|
+
admin_css = (static_dir / "django_bootstrap6_admin" / "css" / "admin.css").read_text(
|
|
56
|
+
encoding="utf-8"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
self.assertIn("django.admin.showFacets", admin_js)
|
|
60
|
+
self.assertIn("django.admin.filtersCollapsed", admin_js)
|
|
61
|
+
self.assertIn("ba-filters-collapsed", admin_css)
|
|
62
|
+
self.assertIn("border-inline-end: 1px solid var(--bs-border-muted)", admin_css)
|
|
63
|
+
self.assertIn("appearance: none", admin_css)
|
|
64
|
+
self.assertIn("--bs-fg-4: #b0aaa9", admin_css)
|
|
65
|
+
self.assertIn("--ba-topbar-bg: light-dark(#231f20, #242122)", admin_css)
|
|
66
|
+
self.assertIn("--bs-primary-bg: light-dark(#ffcb08, #d8ab32)", admin_css)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/src/django_bootstrap6_admin/apps.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_bootstrap6_admin-0.1.2 → django_bootstrap6_admin-0.2.1}/tests/js/theme_toggle.test.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|