vanta-admin 0.6.0__tar.gz → 0.7.0__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.
Files changed (44) hide show
  1. {vanta_admin-0.6.0/vanta_admin.egg-info → vanta_admin-0.7.0}/PKG-INFO +5 -1
  2. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/README.md +4 -0
  3. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/pyproject.toml +1 -1
  4. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/theme.admin.css +159 -116
  5. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/theme.responsive.css +10 -12
  6. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/js/changelist.js +107 -0
  7. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/change_list.html +35 -12
  8. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templatetags/admin_theme.py +21 -1
  9. {vanta_admin-0.6.0 → vanta_admin-0.7.0/vanta_admin.egg-info}/PKG-INFO +5 -1
  10. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/LICENSE +0 -0
  11. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/MANIFEST.in +0 -0
  12. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/setup.cfg +0 -0
  13. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/__init__.py +0 -0
  14. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/apps.py +0 -0
  15. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/theme.base.css +0 -0
  16. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/theme.css +0 -0
  17. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/theme.login.css +0 -0
  18. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/theme.tokens.css +0 -0
  19. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/css/two_factor.css +0 -0
  20. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/img/favicon.png +0 -0
  21. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/js/forms.js +0 -0
  22. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/js/messages.js +0 -0
  23. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/js/sidebar.js +0 -0
  24. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/static/admin/js/vanta-theme.js +0 -0
  25. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/app_index.html +0 -0
  26. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/base.html +0 -0
  27. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/base_site.html +0 -0
  28. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/change_form.html +0 -0
  29. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/filter.html +0 -0
  30. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/includes/breadcrumbs.html +0 -0
  31. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/index.html +0 -0
  32. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/login.html +0 -0
  33. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/admin/nav_sidebar.html +0 -0
  34. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/two_factor/_base.html +0 -0
  35. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/two_factor/_base_focus.html +0 -0
  36. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/two_factor/_wizard_actions.html +0 -0
  37. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/two_factor/core/login.html +0 -0
  38. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/two_factor/core/setup_complete.html +0 -0
  39. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templates/two_factor/profile/profile.html +0 -0
  40. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin/templatetags/__init__.py +0 -0
  41. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin.egg-info/SOURCES.txt +0 -0
  42. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin.egg-info/dependency_links.txt +0 -0
  43. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin.egg-info/requires.txt +0 -0
  44. {vanta_admin-0.6.0 → vanta_admin-0.7.0}/vanta_admin.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vanta-admin
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A sharp, dark, and portable Django admin theme forged for builders who want the admin to feel less default.
5
5
  Author: oli-dev0
6
6
  License-Expression: MIT
@@ -50,6 +50,10 @@ feels more deliberate in everyday use.
50
50
  themselves after a short time.
51
51
  - Changelist actions that appear only when rows are selected, stay available
52
52
  while scrolling, and include a clear-selected control.
53
+ - Changelist filters that open from a compact toolbar button instead of taking a
54
+ permanent right-side column.
55
+ - Active-filter indicators in the toolbar, including a count when filters are
56
+ applied.
53
57
  - Styled select controls for common admin dropdowns, while Django's native
54
58
  multi-select fields stay unchanged.
55
59
  - Built-in icons for common admin areas, models, and recent actions.
@@ -25,6 +25,10 @@ feels more deliberate in everyday use.
25
25
  themselves after a short time.
26
26
  - Changelist actions that appear only when rows are selected, stay available
27
27
  while scrolling, and include a clear-selected control.
28
+ - Changelist filters that open from a compact toolbar button instead of taking a
29
+ permanent right-side column.
30
+ - Active-filter indicators in the toolbar, including a count when filters are
31
+ applied.
28
32
  - Styled select controls for common admin dropdowns, while Django's native
29
33
  multi-select fields stay unchanged.
30
34
  - Built-in icons for common admin areas, models, and recent actions.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vanta-admin"
7
- version = "0.6.0"
7
+ version = "0.7.0"
8
8
  description = "A sharp, dark, and portable Django admin theme forged for builders who want the admin to feel less default."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -896,6 +896,7 @@ body.change-list #content-main {
896
896
  }
897
897
 
898
898
  #changelist {
899
+ position: relative;
899
900
  margin: 0;
900
901
  border: 0;
901
902
  box-shadow: none;
@@ -903,14 +904,12 @@ body.change-list #content-main {
903
904
  }
904
905
 
905
906
  #changelist .changelist-form-container {
907
+ position: relative;
906
908
  display: block;
907
909
  }
908
910
 
909
911
  #changelist.filtered .changelist-form-container {
910
- display: grid;
911
- grid-template-columns: minmax(0, 1fr) fit-content(13rem);
912
- gap: 0.72rem;
913
- align-items: start;
912
+ display: block;
914
913
  }
915
914
 
916
915
  #changelist .changelist-form-container > div {
@@ -938,7 +937,7 @@ body.change-list #content-main {
938
937
 
939
938
  .admin-changelist-card-header {
940
939
  display: flex;
941
- gap: 0.85rem;
940
+ gap: 0.65rem;
942
941
  justify-content: space-between;
943
942
  align-items: center;
944
943
  padding: 0.72rem;
@@ -1332,6 +1331,79 @@ html[data-theme="light"] #changelist .actions {
1332
1331
  width: auto;
1333
1332
  }
1334
1333
 
1334
+ #changelist .admin-filter-panel-toggle {
1335
+ position: relative;
1336
+ display: inline-grid;
1337
+ place-items: center;
1338
+ flex: 0 0 auto;
1339
+ width: 2.05rem;
1340
+ height: 1.8rem;
1341
+ min-width: 2.05rem;
1342
+ min-height: 1.8rem;
1343
+ margin-top: 2px;
1344
+ padding: 0;
1345
+ border: 0;
1346
+ border-radius: 0.27rem;
1347
+ background: var(--surface-muted);
1348
+ color: var(--admin-accent);
1349
+ }
1350
+
1351
+ #changelist .admin-filter-panel-toggle svg {
1352
+ display: block;
1353
+ width: 1.18rem;
1354
+ height: 1.18rem;
1355
+ }
1356
+
1357
+ #changelist .admin-filter-panel-toggle.has-active-filters,
1358
+ #changelist .admin-filter-panel-toggle[aria-expanded="true"] {
1359
+ background: var(--accent-soft);
1360
+ color: var(--accent-strong);
1361
+ }
1362
+
1363
+ #changelist .admin-filter-panel-toggle.has-active-filters::after {
1364
+ content: "";
1365
+ position: absolute;
1366
+ top: 0.28rem;
1367
+ right: 0.28rem;
1368
+ width: 0.38rem;
1369
+ height: 0.38rem;
1370
+ border-radius: 999px;
1371
+ background: var(--admin-accent);
1372
+ animation: admin-filter-active-pulse 1.8s ease-in-out infinite;
1373
+ }
1374
+
1375
+ #changelist .admin-filter-panel-toggle__count {
1376
+ position: absolute;
1377
+ right: 0.18rem;
1378
+ bottom: 0.12rem;
1379
+ display: block;
1380
+ min-width: 0;
1381
+ height: auto;
1382
+ padding: 0;
1383
+ background: transparent;
1384
+ color: var(--admin-accent-strong);
1385
+ font-size: 0.5rem;
1386
+ font-weight: 860;
1387
+ line-height: 1;
1388
+ pointer-events: none;
1389
+ }
1390
+
1391
+ #changelist .admin-filter-panel-toggle:focus-visible {
1392
+ outline: 2px solid var(--admin-accent-soft);
1393
+ outline-offset: 2px;
1394
+ }
1395
+
1396
+ @keyframes admin-filter-active-pulse {
1397
+ 0%,
1398
+ 100% {
1399
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--admin-accent) 34%, transparent);
1400
+ }
1401
+
1402
+ 50% {
1403
+ box-shadow: 0 0 0 0.22rem color-mix(in srgb, var(--admin-accent) 0%, transparent);
1404
+ }
1405
+ }
1406
+
1335
1407
  #changelist-search label {
1336
1408
  display: inline-flex;
1337
1409
  align-items: center;
@@ -2222,31 +2294,49 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2222
2294
  /* Filters */
2223
2295
 
2224
2296
  #changelist-filter {
2225
- position: sticky;
2226
- top: 1.25rem;
2227
- grid-column: 2;
2228
- order: 2;
2297
+ position: absolute;
2298
+ top: 3.35rem;
2299
+ right: 0.72rem;
2300
+ z-index: 20;
2229
2301
  display: flex;
2230
2302
  flex-direction: column;
2231
2303
  align-items: center;
2232
- width: auto;
2304
+ width: fit-content;
2305
+ max-width: min(14.5rem, calc(100% - 1.44rem));
2306
+ max-height: min(42rem, calc(100vh - 6rem));
2233
2307
  min-width: 0;
2234
2308
  margin: 0;
2235
2309
  flex: initial;
2236
- padding: 0.9rem 0.9rem 0.45rem;
2310
+ padding: 0.82rem 0.62rem 0.45rem;
2237
2311
  border: 1px solid var(--border-strong);
2238
2312
  border-radius: var(--radius-md);
2239
- background: var(--surface-strong);
2313
+ background: color-mix(in srgb, var(--admin-surface-raised) 92%, var(--admin-bg));
2240
2314
  box-shadow: none;
2241
2315
  color: var(--admin-text);
2242
- text-align: center;
2316
+ opacity: 0;
2317
+ overflow: auto;
2318
+ text-align: left;
2319
+ transform: translateY(-0.28rem);
2320
+ transition:
2321
+ opacity var(--admin-transition),
2322
+ transform var(--admin-transition);
2323
+ }
2324
+
2325
+ #changelist-filter.is-open {
2326
+ opacity: 1;
2327
+ transform: translateY(0);
2328
+ }
2329
+
2330
+ html[data-theme="light"] #changelist-filter {
2331
+ background: #eef6ff;
2243
2332
  }
2244
2333
 
2245
2334
  #changelist-filter .admin-filter-header {
2246
2335
  display: flex;
2247
2336
  align-items: center;
2248
- justify-content: center;
2337
+ justify-content: flex-start;
2249
2338
  gap: 0.42rem;
2339
+ width: 100%;
2250
2340
  margin: 0 0 0.22rem;
2251
2341
  }
2252
2342
 
@@ -2336,13 +2426,15 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2336
2426
  margin: 0.45rem 0 0.18rem;
2337
2427
  padding: 0;
2338
2428
  font-size: 0.74rem;
2429
+ font-weight: 800;
2339
2430
  line-height: 1.2;
2340
2431
  text-align: left;
2432
+ width: 100%;
2341
2433
  }
2342
2434
 
2343
2435
  #changelist-filter details {
2344
2436
  display: grid;
2345
- justify-items: center;
2437
+ justify-items: stretch;
2346
2438
  width: 100%;
2347
2439
  }
2348
2440
 
@@ -2375,50 +2467,44 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2375
2467
  color: var(--admin-link);
2376
2468
  }
2377
2469
 
2378
- .admin-filter-footer {
2379
- display: flex;
2380
- justify-content: center;
2381
- margin-top: auto;
2382
- padding-top: 0.45rem;
2383
- padding-bottom: 0;
2384
- }
2385
-
2386
2470
  .vanta-filter-actions {
2387
2471
  display: inline-flex;
2388
- overflow: hidden;
2389
- border: 1px solid var(--admin-border);
2390
- border-radius: var(--admin-radius-md);
2391
- background: var(--admin-surface-muted);
2472
+ align-items: center;
2473
+ gap: 0.18rem;
2474
+ margin-left: 0.12rem;
2475
+ overflow: visible;
2476
+ border: 0;
2477
+ border-radius: 0;
2478
+ background: transparent;
2392
2479
  box-shadow: none;
2393
2480
  }
2394
2481
 
2395
2482
  .vanta-filter-action {
2396
- display: grid;
2397
- place-items: center;
2398
- width: 48px;
2399
- height: 22px;
2483
+ display: inline-flex;
2484
+ align-items: center;
2485
+ justify-content: center;
2486
+ width: 1.32rem;
2487
+ height: 1.32rem;
2400
2488
  border: 0;
2401
- color: var(--admin-muted);
2489
+ border-radius: var(--admin-radius-sm);
2490
+ color: var(--admin-link);
2402
2491
  background: transparent;
2403
- font-size: 18px;
2404
- line-height: 1;
2492
+ font-size: 0.86rem;
2493
+ font-weight: 800;
2494
+ line-height: 1.32rem;
2405
2495
  cursor: pointer;
2406
2496
  }
2407
2497
 
2408
2498
  .vanta-filter-action svg {
2409
2499
  display: block;
2410
- width: 16px;
2411
- height: 16px;
2412
- transform: translateY(2.5px);
2413
- }
2414
-
2415
- .vanta-filter-action + .vanta-filter-action {
2416
- border-left: 1px solid var(--admin-border);
2500
+ width: 0.96rem;
2501
+ height: 0.96rem;
2502
+ transform: translate(0.06rem, 0.12rem);
2417
2503
  }
2418
2504
 
2419
2505
  .vanta-filter-action:hover,
2420
2506
  .vanta-filter-action:focus-visible {
2421
- color: var(--admin-link-strong);
2507
+ color: var(--admin-accent-strong);
2422
2508
  background: var(--admin-accent-softer);
2423
2509
  }
2424
2510
 
@@ -2428,12 +2514,12 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2428
2514
  }
2429
2515
 
2430
2516
  .vanta-filter-actions.has-active-action {
2431
- border-color: var(--admin-accent-border);
2517
+ color: var(--admin-accent-strong);
2432
2518
  }
2433
2519
 
2434
2520
  .vanta-filter-action.is-active {
2435
- color: var(--admin-link-strong);
2436
- background: var(--accent-soft);
2521
+ color: var(--admin-accent-strong);
2522
+ background: transparent;
2437
2523
  }
2438
2524
 
2439
2525
  .vanta-filter-action.is-disabled {
@@ -2442,25 +2528,18 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2442
2528
  pointer-events: none;
2443
2529
  }
2444
2530
 
2445
- .vanta-filter-action.is-disabled:last-child {
2446
- padding-top: 1px;
2447
- }
2448
-
2449
- .vanta-filter-action.is-active:last-child {
2450
- padding-top: 3px;
2451
- }
2452
-
2453
2531
  #changelist-filter details ul {
2454
- display: inline-flex;
2455
- flex-wrap: nowrap;
2456
- gap: 0;
2457
- width: auto;
2532
+ display: grid;
2533
+ grid-template-columns: 1fr;
2534
+ gap: 0.02rem;
2535
+ justify-self: stretch;
2536
+ width: 100%;
2458
2537
  padding: 0;
2459
2538
  border: 0;
2460
2539
  border-radius: 0;
2461
2540
  background: transparent;
2462
2541
  box-shadow: none;
2463
- overflow: hidden;
2542
+ overflow: visible;
2464
2543
  }
2465
2544
 
2466
2545
  #changelist-filter details .admin-filter-options--stacked {
@@ -2477,6 +2556,7 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2477
2556
 
2478
2557
  #changelist-filter details li {
2479
2558
  display: flex;
2559
+ width: 100%;
2480
2560
  min-width: 0;
2481
2561
  margin: 0;
2482
2562
  padding: 0;
@@ -2494,22 +2574,23 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2494
2574
 
2495
2575
  #changelist-filter details li a,
2496
2576
  #changelist-filter details li.selected a {
2497
- min-height: 1.45rem;
2577
+ min-height: 1rem;
2498
2578
  position: relative;
2499
2579
  z-index: 0;
2500
- display: inline-flex;
2580
+ display: flex;
2501
2581
  align-items: center;
2502
- justify-content: center;
2582
+ justify-content: flex-start;
2583
+ width: fit-content;
2503
2584
  border: 0;
2504
- border-radius: 0;
2585
+ border-radius: var(--admin-radius-sm);
2505
2586
  background: transparent;
2506
- border: 1px solid var(--admin-border);
2507
2587
  color: var(--admin-muted);
2508
2588
  margin: 0;
2509
- padding: 0.12rem 0.42rem;
2510
- font-size: 0.72rem;
2589
+ padding: 0.01rem 0.24rem;
2590
+ font-size: 0.73rem;
2511
2591
  font-weight: 720;
2512
2592
  letter-spacing: 0.01em;
2593
+ line-height: 1.08;
2513
2594
  max-width: 100%;
2514
2595
  overflow: hidden;
2515
2596
  text-overflow: ellipsis;
@@ -2522,7 +2603,7 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2522
2603
 
2523
2604
  #changelist-filter details .admin-filter-options--inline li a,
2524
2605
  #changelist-filter details .admin-filter-options--inline li.selected a {
2525
- min-width: 2.65rem;
2606
+ min-width: 0;
2526
2607
  }
2527
2608
 
2528
2609
  #changelist-filter .admin-facet-count {
@@ -2530,84 +2611,46 @@ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
2530
2611
  font-variant-numeric: tabular-nums;
2531
2612
  }
2532
2613
 
2533
- #changelist-filter details li:not(:first-child) a {
2534
- margin-left: -1px;
2535
- }
2536
-
2537
- #changelist-filter details .admin-filter-options--stacked li:not(:first-child) a {
2538
- margin-top: -1px;
2539
- margin-left: 0;
2540
- }
2541
-
2542
- #changelist-filter details li:first-child a,
2543
- #changelist-filter details li.selected:first-child a {
2544
- border-radius: 0.28rem 0 0 0.28rem;
2545
- }
2546
-
2547
- #changelist-filter details .admin-filter-options--stacked li:first-child a,
2548
- #changelist-filter details .admin-filter-options--stacked li.selected:first-child a {
2549
- border-radius: 0.28rem 0.28rem 0 0;
2550
- }
2551
-
2552
- #changelist-filter details li:last-child a,
2553
- #changelist-filter details li.selected:last-child a {
2554
- border-radius: 0 0.28rem 0.28rem 0;
2555
- }
2556
-
2557
- #changelist-filter details .admin-filter-options--stacked li:last-child a,
2558
- #changelist-filter details .admin-filter-options--stacked li.selected:last-child a {
2559
- border-radius: 0 0 0.28rem 0.28rem;
2560
- }
2561
-
2562
2614
  #changelist-filter details .admin-filter-options--stacked li a,
2563
2615
  #changelist-filter details .admin-filter-options--stacked li.selected a {
2564
- display: block;
2565
- min-width: 7.5rem;
2566
- width: 100%;
2567
- padding-inline: 0.52rem;
2616
+ display: flex;
2617
+ min-width: 0;
2618
+ width: fit-content;
2619
+ padding-inline: 0.24rem;
2568
2620
  text-align: left;
2569
2621
  }
2570
2622
 
2571
2623
  #changelist-filter details li a:hover {
2572
2624
  z-index: 1;
2573
- color: var(--admin-text);
2574
- background: var(--admin-accent-softer);
2625
+ color: var(--admin-accent-strong);
2626
+ transform: translateY(-2px);
2575
2627
  }
2576
2628
 
2577
2629
  #changelist-filter details li a:active {
2578
2630
  z-index: 2;
2579
2631
  outline: none;
2580
- color: var(--admin-text);
2581
- background: var(--admin-accent-softer);
2632
+ color: var(--admin-accent-strong);
2582
2633
  }
2583
2634
 
2584
2635
  #changelist-filter details li a:focus-visible {
2585
2636
  z-index: 1;
2586
- color: var(--admin-text);
2587
- background: var(--admin-accent-softer);
2588
- outline: 2px solid var(--admin-accent-soft);
2637
+ color: var(--admin-accent-strong);
2638
+ outline: 2px solid var(--admin-accent);
2589
2639
  outline-offset: 2px;
2590
- border-color: var(--admin-accent-border);
2591
2640
  }
2592
2641
 
2593
2642
  #changelist-filter details li.selected a {
2594
2643
  z-index: 1;
2595
- background: var(--admin-nav-item-active-bg);
2596
- color: var(--admin-link-strong);
2597
- border-color: var(--admin-accent-border);
2644
+ color: var(--admin-accent-strong);
2598
2645
  }
2599
2646
 
2600
2647
  #changelist-filter details li.selected a:active {
2601
2648
  z-index: 2;
2602
- color: var(--admin-link-strong);
2603
- background: var(--admin-nav-item-active-bg);
2604
- border-color: var(--admin-accent-border);
2649
+ color: var(--admin-accent-strong);
2605
2650
  }
2606
2651
 
2607
2652
  #changelist-filter details li.selected a:focus-visible {
2608
- color: var(--admin-link-strong);
2609
- background: var(--admin-nav-item-active-bg);
2610
- border-color: var(--admin-accent-border);
2653
+ color: var(--admin-accent-strong);
2611
2654
  }
2612
2655
 
2613
2656
  #changelist-filter details summary:focus-visible {
@@ -194,21 +194,16 @@
194
194
  justify-content: flex-start;
195
195
  }
196
196
 
197
- #changelist.filtered .changelist-form-container {
198
- grid-template-columns: 1fr;
199
- }
200
-
201
- #changelist .changelist-form-container > div,
202
- #changelist-filter {
203
- grid-column: 1;
204
- }
205
-
206
197
  #changelist .changelist-form-container > div {
207
198
  min-width: 0;
208
199
  }
209
200
 
210
201
  #changelist-filter {
211
- position: static;
202
+ top: 3.15rem;
203
+ right: 0.6rem;
204
+ left: 0.6rem;
205
+ width: auto;
206
+ max-height: min(38rem, calc(100vh - 5.25rem));
212
207
  }
213
208
  }
214
209
 
@@ -259,13 +254,16 @@
259
254
  .admin-sidebar__model a,
260
255
  .admin-sidebar__account-links a,
261
256
  .admin-sidebar__account-links button,
262
- .admin-action-link {
257
+ .admin-action-link,
258
+ .admin-filter-panel-toggle,
259
+ #changelist-filter {
263
260
  transition: none;
264
261
  }
265
262
 
266
263
  .admin-sidebar__donation-link .donation-button__steam,
267
264
  .admin-sidebar__donation-link .donation-button__steam::before,
268
- .admin-sidebar__donation-link .donation-button__steam::after {
265
+ .admin-sidebar__donation-link .donation-button__steam::after,
266
+ #changelist .admin-filter-panel-toggle.has-active-filters::after {
269
267
  animation: none;
270
268
  }
271
269
  }
@@ -1,8 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  {
4
+ const changelist = document.getElementById('changelist');
5
+ const filterPanel = document.getElementById('changelist-filter');
6
+ const filterPanelToggle = document.getElementById('admin-filter-panel-toggle');
4
7
  const filterSectionsToggle = document.getElementById('admin-filter-sections-toggle');
5
8
  const filterSections = Array.from(document.querySelectorAll('#changelist-filter details'));
9
+ const filterPanelReopenKey = `django.admin.theme.changelistFilter.reopen:${window.location.pathname}`;
10
+ let filterPanelCloseTimer = null;
6
11
 
7
12
  function isDetailsOpen(section) {
8
13
  return section.open;
@@ -25,6 +30,61 @@
25
30
  section.open = isOpen;
26
31
  }
27
32
 
33
+ function setFilterPanelOpen(isOpen) {
34
+ if (!changelist || !filterPanel || !filterPanelToggle) {
35
+ return;
36
+ }
37
+
38
+ window.clearTimeout(filterPanelCloseTimer);
39
+ const hasActiveFilters = filterPanelToggle.classList.contains('has-active-filters');
40
+ const openLabel = hasActiveFilters
41
+ ? filterPanelToggle.dataset.openActiveLabel || 'Open filters, active filters applied'
42
+ : filterPanelToggle.dataset.openLabel || 'Open filters';
43
+ const label = isOpen
44
+ ? filterPanelToggle.dataset.closeLabel || 'Close filters'
45
+ : openLabel;
46
+
47
+ changelist.classList.toggle('has-open-filters', isOpen);
48
+ filterPanelToggle.setAttribute('aria-expanded', String(isOpen));
49
+ filterPanelToggle.setAttribute('aria-label', label);
50
+ filterPanelToggle.setAttribute('title', label);
51
+
52
+ if (isOpen) {
53
+ filterPanel.hidden = false;
54
+ window.requestAnimationFrame(() => {
55
+ filterPanel.classList.add('is-open');
56
+ });
57
+ return;
58
+ }
59
+
60
+ filterPanel.classList.remove('is-open');
61
+ filterPanelCloseTimer = window.setTimeout(() => {
62
+ if (!filterPanel.classList.contains('is-open')) {
63
+ filterPanel.hidden = true;
64
+ }
65
+ }, 180);
66
+ }
67
+
68
+ function shouldReopenFilterPanel() {
69
+ try {
70
+ return window.sessionStorage.getItem(filterPanelReopenKey) === 'true';
71
+ } catch (error) {
72
+ return false;
73
+ }
74
+ }
75
+
76
+ function clearFilterPanelReopen() {
77
+ try {
78
+ window.sessionStorage.removeItem(filterPanelReopenKey);
79
+ } catch (error) {}
80
+ }
81
+
82
+ function rememberFilterPanelReopen() {
83
+ try {
84
+ window.sessionStorage.setItem(filterPanelReopenKey, 'true');
85
+ } catch (error) {}
86
+ }
87
+
28
88
  function syncChangelistActions() {
29
89
  const form = document.getElementById('changelist-form');
30
90
  const actions = Array.from(document.querySelectorAll('#changelist .actions'));
@@ -104,6 +164,53 @@
104
164
 
105
165
  syncChangelistActions();
106
166
 
167
+ if (filterPanelToggle && filterPanel) {
168
+ setFilterPanelOpen(shouldReopenFilterPanel());
169
+ clearFilterPanelReopen();
170
+
171
+ filterPanelToggle.addEventListener('click', () => {
172
+ setFilterPanelOpen(filterPanelToggle.getAttribute('aria-expanded') !== 'true');
173
+ });
174
+
175
+ document.addEventListener('click', (event) => {
176
+ if (
177
+ filterPanelToggle.getAttribute('aria-expanded') !== 'true'
178
+ || filterPanel.contains(event.target)
179
+ || filterPanelToggle.contains(event.target)
180
+ ) {
181
+ return;
182
+ }
183
+
184
+ setFilterPanelOpen(false);
185
+ });
186
+
187
+ filterPanel.addEventListener('click', (event) => {
188
+ const link = event.target.closest('a[href]');
189
+ if (!link) {
190
+ return;
191
+ }
192
+
193
+ const url = new URL(link.href, window.location.href);
194
+ if (url.pathname === window.location.pathname) {
195
+ rememberFilterPanelReopen();
196
+ }
197
+ });
198
+
199
+ document.addEventListener('keydown', (event) => {
200
+ if (
201
+ event.key === 'Escape'
202
+ && filterPanelToggle.getAttribute('aria-expanded') === 'true'
203
+ && (
204
+ filterPanel.contains(document.activeElement)
205
+ || filterPanelToggle === document.activeElement
206
+ )
207
+ ) {
208
+ setFilterPanelOpen(false);
209
+ filterPanelToggle.focus();
210
+ }
211
+ });
212
+ }
213
+
107
214
  if (filterSectionsToggle) {
108
215
  filterSectionsToggle.addEventListener('click', () => {
109
216
  const allOpen = filterSections.length > 0 && filterSections.every(isDetailsOpen);
@@ -1,5 +1,5 @@
1
1
  {% extends "admin/base_site.html" %}
2
- {% load i18n admin_urls static admin_list %}
2
+ {% load i18n admin_urls static admin_list admin_theme %}
3
3
 
4
4
  {% block title %}{% if cl.formset and cl.formset.errors %}{% translate "Error:" %} {% endif %}{{ block.super }}{% endblock %}
5
5
  {% block extrastyle %}
@@ -48,7 +48,7 @@
48
48
  <div class="changelist-form-container">
49
49
  {% block filters %}
50
50
  {% if cl.has_filters %}
51
- <search id="changelist-filter" aria-labelledby="changelist-filter-header">
51
+ <search id="changelist-filter" aria-labelledby="changelist-filter-header" hidden>
52
52
  <div class="admin-filter-header">
53
53
  <button class="admin-filter-sections-toggle" id="admin-filter-sections-toggle" type="button" aria-label="{% translate 'Collapse all filter sections' %}" title="{% translate 'Collapse all filter sections' %}">
54
54
  <svg aria-hidden="true" viewBox="0 0 24 24">
@@ -58,31 +58,28 @@
58
58
  <h2 id="changelist-filter-header">
59
59
  <span>{% translate 'Filter' %}</span>
60
60
  </h2>
61
- </div>
62
- {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
63
- <div class="admin-filter-footer">
64
61
  <div class="vanta-filter-actions{% if cl.add_facets or cl.has_active_filters %} has-active-action{% endif %}">
65
62
  {% if cl.is_facets_optional %}
66
63
  {% if cl.add_facets %}
67
64
  <a class="vanta-filter-action is-active" href="{{ cl.remove_facet_link }}" aria-label="{% translate 'Hide counts' %}" title="{% translate 'Hide counts' %}">
68
65
  <svg aria-hidden="true" viewBox="0 0 24 24">
69
- <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
70
- <circle cx="12" cy="12" r="2.8" fill="none" stroke="currentColor" stroke-width="1.8"></circle>
66
+ <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
67
+ <circle cx="12" cy="12" r="2.8" fill="none" stroke="currentColor" stroke-width="2.2"></circle>
71
68
  </svg>
72
69
  </a>
73
70
  {% else %}
74
71
  <a class="vanta-filter-action" href="{{ cl.add_facet_link }}" aria-label="{% translate 'Show counts' %}" title="{% translate 'Show counts' %}">
75
72
  <svg aria-hidden="true" viewBox="0 0 24 24">
76
- <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
77
- <path d="M4 4l16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"></path>
73
+ <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
74
+ <path d="M4 4l16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"></path>
78
75
  </svg>
79
76
  </a>
80
77
  {% endif %}
81
78
  {% else %}
82
79
  <span class="vanta-filter-action is-disabled" aria-hidden="true">
83
80
  <svg aria-hidden="true" viewBox="0 0 24 24">
84
- <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
85
- <path d="M4 4l16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"></path>
81
+ <path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"></path>
82
+ <path d="M4 4l16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"></path>
86
83
  </svg>
87
84
  </span>
88
85
  {% endif %}
@@ -93,11 +90,12 @@
93
90
  {% endif %}
94
91
  </div>
95
92
  </div>
93
+ {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
96
94
  </search>
97
95
  {% endif %}
98
96
  {% endblock %}
99
97
  <div>
100
- {% if cl.search_fields or has_add_permission %}
98
+ {% if cl.search_fields or has_add_permission or cl.has_filters %}
101
99
  <div id="toolbar" class="admin-changelist-card-header">
102
100
  {% block search %}
103
101
  {% if cl.search_fields %}
@@ -130,6 +128,31 @@
130
128
  {% endblock %}
131
129
  </ul>
132
130
  {% endblock %}
131
+ {% if cl.has_filters %}
132
+ {% active_admin_filter_count cl as active_filter_count %}
133
+ <button
134
+ class="admin-filter-panel-toggle{% if cl.has_active_filters %} has-active-filters{% endif %}"
135
+ id="admin-filter-panel-toggle"
136
+ type="button"
137
+ aria-controls="changelist-filter"
138
+ aria-expanded="false"
139
+ aria-label="{% if cl.has_active_filters %}{% translate 'Open filters, active filters applied' %}{% else %}{% translate 'Open filters' %}{% endif %}"
140
+ title="{% if cl.has_active_filters %}{% translate 'Open filters, active filters applied' %}{% else %}{% translate 'Open filters' %}{% endif %}"
141
+ data-open-label="{% translate 'Open filters' %}"
142
+ data-open-active-label="{% translate 'Open filters, active filters applied' %}"
143
+ data-close-label="{% translate 'Close filters' %}"
144
+ >
145
+ <svg aria-hidden="true" viewBox="0 0 24 24">
146
+ <path d="M3 5h18l-7 8v5l-4 2v-7Z" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"></path>
147
+ </svg>
148
+ {% if active_filter_count %}
149
+ <span class="admin-filter-panel-toggle__count" aria-hidden="true">{{ active_filter_count }}</span>
150
+ {% endif %}
151
+ {% if cl.has_active_filters %}
152
+ <span class="visually-hidden">{% translate 'Active filters applied' %}</span>
153
+ {% endif %}
154
+ </button>
155
+ {% endif %}
133
156
  </div>
134
157
  {% endif %}
135
158
  {% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
@@ -11,6 +11,16 @@ FACET_COUNT_PATTERN = re.compile(r"^(?P<label>.*) \((?P<count>\d+)\)$")
11
11
  COMPACT_SUFFIXES = ("", "k", "m", "b", "t")
12
12
  MAX_INLINE_FILTER_LABEL_LENGTH = 3
13
13
  DEFAULT_ADMIN_ICON = "admin-icon-file"
14
+ NON_FILTER_CHANGE_LIST_PARAMS = {
15
+ "all",
16
+ "e",
17
+ "o",
18
+ "p",
19
+ "q",
20
+ "_facets",
21
+ "_popup",
22
+ "is_facets",
23
+ }
14
24
 
15
25
  APP_ICON_MAP = {
16
26
  "auth": "admin-icon-shield",
@@ -221,6 +231,16 @@ def _facet_label_text(value):
221
231
  return match.group("label") if match else text
222
232
 
223
233
 
234
+ @register.simple_tag
235
+ def active_admin_filter_count(cl):
236
+ params = getattr(cl, "params", {}) or {}
237
+ return sum(
238
+ 1
239
+ for key, value in params.items()
240
+ if key not in NON_FILTER_CHANGE_LIST_PARAMS and value not in (None, "")
241
+ )
242
+
243
+
224
244
 
225
245
 
226
246
  def _clone_admin_item(item):
@@ -304,7 +324,7 @@ def format_admin_facet_label(value, autoescape=True):
304
324
  label = escaper(match.group("label"))
305
325
  compact_count = _format_compact_count(match.group("count"))
306
326
  return format_html(
307
- '{}&nbsp;<span class="admin-facet-count">{}</span>',
327
+ '{}&nbsp;-&nbsp;<span class="admin-facet-count">{}</span>',
308
328
  label,
309
329
  compact_count,
310
330
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vanta-admin
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A sharp, dark, and portable Django admin theme forged for builders who want the admin to feel less default.
5
5
  Author: oli-dev0
6
6
  License-Expression: MIT
@@ -50,6 +50,10 @@ feels more deliberate in everyday use.
50
50
  themselves after a short time.
51
51
  - Changelist actions that appear only when rows are selected, stay available
52
52
  while scrolling, and include a clear-selected control.
53
+ - Changelist filters that open from a compact toolbar button instead of taking a
54
+ permanent right-side column.
55
+ - Active-filter indicators in the toolbar, including a count when filters are
56
+ applied.
53
57
  - Styled select controls for common admin dropdowns, while Django's native
54
58
  multi-select fields stay unchanged.
55
59
  - Built-in icons for common admin areas, models, and recent actions.
File without changes
File without changes
File without changes