micro-sidebar 1.0.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.
- micro_sidebar-1.0.0/MANIFEST.in +9 -0
- micro_sidebar-1.0.0/PKG-INFO +40 -0
- micro_sidebar-1.0.0/README.md +12 -0
- micro_sidebar-1.0.0/micro_sidebar.egg-info/PKG-INFO +40 -0
- micro_sidebar-1.0.0/micro_sidebar.egg-info/SOURCES.txt +17 -0
- micro_sidebar-1.0.0/micro_sidebar.egg-info/dependency_links.txt +1 -0
- micro_sidebar-1.0.0/micro_sidebar.egg-info/requires.txt +1 -0
- micro_sidebar-1.0.0/micro_sidebar.egg-info/top_level.txt +1 -0
- micro_sidebar-1.0.0/pyproject.toml +38 -0
- micro_sidebar-1.0.0/setup.cfg +4 -0
- micro_sidebar-1.0.0/setup.py +33 -0
- micro_sidebar-1.0.0/sidebar/__init__.py +0 -0
- micro_sidebar-1.0.0/sidebar/apps.py +5 -0
- micro_sidebar-1.0.0/sidebar/migrations/__init__.py +0 -0
- micro_sidebar-1.0.0/sidebar/static/sidebar/js/sidebar.js +156 -0
- micro_sidebar-1.0.0/sidebar/static/sidebar/style.css +93 -0
- micro_sidebar-1.0.0/sidebar/templates/sidebar/content.html +31 -0
- micro_sidebar-1.0.0/sidebar/urls.py +6 -0
- micro_sidebar-1.0.0/sidebar/views.py +9 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: micro_sidebar
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A reusable Django sidebar for Web Apps
|
|
5
|
+
Home-page: https://github.com/debeski/micro-sidebar
|
|
6
|
+
Author: DeBeski
|
|
7
|
+
Author-email: DeBeski <debeski1@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/debeski/micro-sidebar
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 5
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: Django>=5.1
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
|
|
29
|
+
# Micro Sidebar
|
|
30
|
+
|
|
31
|
+
A reusable Django sidebar app.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
1. Add `sidebar` to your `INSTALLED_APPS` setting.
|
|
36
|
+
2. Include the URLconf in your project urls.py:
|
|
37
|
+
```python
|
|
38
|
+
path('sidebar/', include('sidebar.urls')),
|
|
39
|
+
```
|
|
40
|
+
3. Override the content by creating `templates/sidebar/content.html` in your project. See `sidebar/templates/sidebar/example_content.html` for a starting point.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Micro Sidebar
|
|
2
|
+
|
|
3
|
+
A reusable Django sidebar app.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
1. Add `sidebar` to your `INSTALLED_APPS` setting.
|
|
8
|
+
2. Include the URLconf in your project urls.py:
|
|
9
|
+
```python
|
|
10
|
+
path('sidebar/', include('sidebar.urls')),
|
|
11
|
+
```
|
|
12
|
+
3. Override the content by creating `templates/sidebar/content.html` in your project. See `sidebar/templates/sidebar/example_content.html` for a starting point.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: micro_sidebar
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A reusable Django sidebar for Web Apps
|
|
5
|
+
Home-page: https://github.com/debeski/micro-sidebar
|
|
6
|
+
Author: DeBeski
|
|
7
|
+
Author-email: DeBeski <debeski1@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/debeski/micro-sidebar
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 5
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: Django>=5.1
|
|
25
|
+
Dynamic: author
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
|
|
29
|
+
# Micro Sidebar
|
|
30
|
+
|
|
31
|
+
A reusable Django sidebar app.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
1. Add `sidebar` to your `INSTALLED_APPS` setting.
|
|
36
|
+
2. Include the URLconf in your project urls.py:
|
|
37
|
+
```python
|
|
38
|
+
path('sidebar/', include('sidebar.urls')),
|
|
39
|
+
```
|
|
40
|
+
3. Override the content by creating `templates/sidebar/content.html` in your project. See `sidebar/templates/sidebar/example_content.html` for a starting point.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
micro_sidebar.egg-info/PKG-INFO
|
|
6
|
+
micro_sidebar.egg-info/SOURCES.txt
|
|
7
|
+
micro_sidebar.egg-info/dependency_links.txt
|
|
8
|
+
micro_sidebar.egg-info/requires.txt
|
|
9
|
+
micro_sidebar.egg-info/top_level.txt
|
|
10
|
+
sidebar/__init__.py
|
|
11
|
+
sidebar/apps.py
|
|
12
|
+
sidebar/urls.py
|
|
13
|
+
sidebar/views.py
|
|
14
|
+
sidebar/migrations/__init__.py
|
|
15
|
+
sidebar/static/sidebar/style.css
|
|
16
|
+
sidebar/static/sidebar/js/sidebar.js
|
|
17
|
+
sidebar/templates/sidebar/content.html
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Django>=5.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sidebar
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "micro_sidebar"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A reusable Django sidebar for Web Apps"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "DeBeski", email = "debeski1@gmail.com"},
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Framework :: Django",
|
|
17
|
+
"Framework :: Django :: 5",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.8",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"Django>=5.1",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/debeski/micro-sidebar"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["."]
|
|
38
|
+
include = ["sidebar*"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="micro_sidebar",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
author="DeBeski",
|
|
7
|
+
author_email="debeski1@gmail.com",
|
|
8
|
+
description="A reusable Django sidebar for Web Apps",
|
|
9
|
+
long_description=open('README.md').read() if open('README.md') else '',
|
|
10
|
+
long_description_content_type="text/markdown",
|
|
11
|
+
url="https://github.com/debeski/micro-sidebar",
|
|
12
|
+
packages=["sidebar"],
|
|
13
|
+
include_package_data=True,
|
|
14
|
+
classifiers=[
|
|
15
|
+
"Framework :: Django",
|
|
16
|
+
"Framework :: Django :: 5",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.8",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
],
|
|
28
|
+
python_requires=">=3.9",
|
|
29
|
+
install_requires=[
|
|
30
|
+
"Django>=5.1",
|
|
31
|
+
],
|
|
32
|
+
license="MIT",
|
|
33
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
2
|
+
const sidebar = document.getElementById("sidebar");
|
|
3
|
+
const sidebarToggle = document.getElementById("sidebarToggle");
|
|
4
|
+
|
|
5
|
+
// Read config from data attribute on sidebar or a global config
|
|
6
|
+
// Assuming sidebar has data attributes, or we can look for a meta tag
|
|
7
|
+
const sidebarConfigStr = sidebar.getAttribute('data-sidebar-config');
|
|
8
|
+
let sidebarConfig = {};
|
|
9
|
+
if (sidebarConfigStr) {
|
|
10
|
+
try {
|
|
11
|
+
sidebarConfig = JSON.parse(sidebarConfigStr.replace(/'/g, '"')); // Simple parse attempt, better to use valid JSON in attribute
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.error("Error parsing sidebar config", e);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const toggleUrl = sidebar.dataset.toggleUrl;
|
|
18
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
19
|
+
let isSessionCollapsed = sidebar.dataset.sessionCollapsed === "true";
|
|
20
|
+
|
|
21
|
+
// Function to handle sidebar collapsing based on window size
|
|
22
|
+
function adjustSidebarForWindowSize() {
|
|
23
|
+
const screenWidth = window.innerWidth;
|
|
24
|
+
const titlebar = document.querySelector('.titlebar');
|
|
25
|
+
const titlebarHeight = titlebar ? titlebar.offsetHeight : 0;
|
|
26
|
+
|
|
27
|
+
if (screenWidth < 1100) {
|
|
28
|
+
// Always collapse sidebar on small screens
|
|
29
|
+
sidebar.classList.add("collapsed");
|
|
30
|
+
initializeTooltips();
|
|
31
|
+
|
|
32
|
+
// Dynamic positioning to anchor to titlebar
|
|
33
|
+
sidebar.style.top = titlebarHeight + 'px';
|
|
34
|
+
sidebar.style.height = (window.innerHeight - titlebarHeight) + 'px';
|
|
35
|
+
} else {
|
|
36
|
+
// Reset styles for large screens to let CSS take over (sticky)
|
|
37
|
+
sidebar.style.top = '';
|
|
38
|
+
sidebar.style.height = '';
|
|
39
|
+
|
|
40
|
+
// Use session state for larger screens
|
|
41
|
+
if (isSessionCollapsed) {
|
|
42
|
+
sidebar.classList.add("collapsed");
|
|
43
|
+
initializeTooltips();
|
|
44
|
+
} else {
|
|
45
|
+
sidebar.classList.remove("collapsed");
|
|
46
|
+
deinitializeTooltips();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Adjust sidebar state on load
|
|
52
|
+
adjustSidebarForWindowSize();
|
|
53
|
+
|
|
54
|
+
// Check if sidebar is collapsed on load and initialize tooltips if so
|
|
55
|
+
if (sidebar.classList.contains("collapsed")) {
|
|
56
|
+
initializeTooltips();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Listen for window resize
|
|
60
|
+
window.addEventListener("resize", adjustSidebarForWindowSize);
|
|
61
|
+
|
|
62
|
+
// Toggle sidebar and update session via AJAX
|
|
63
|
+
if (sidebarToggle) {
|
|
64
|
+
sidebarToggle.addEventListener("click", function () {
|
|
65
|
+
sidebar.classList.toggle("collapsed");
|
|
66
|
+
const isCollapsed = sidebar.classList.contains("collapsed");
|
|
67
|
+
|
|
68
|
+
// Update tooltips immediately for all screen sizes
|
|
69
|
+
if (isCollapsed) {
|
|
70
|
+
initializeTooltips();
|
|
71
|
+
} else {
|
|
72
|
+
deinitializeTooltips();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Only update session if screen width is >= 1100px
|
|
76
|
+
if (window.innerWidth >= 1100) {
|
|
77
|
+
fetch(toggleUrl, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: {
|
|
80
|
+
"X-CSRFToken": csrfToken,
|
|
81
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
82
|
+
},
|
|
83
|
+
body: `collapsed=${isCollapsed}`
|
|
84
|
+
}).then(response => response.json())
|
|
85
|
+
.then(data => {
|
|
86
|
+
if (data.status === "success") {
|
|
87
|
+
isSessionCollapsed = isCollapsed; // Update local state
|
|
88
|
+
}
|
|
89
|
+
}).catch(error => console.error("Error updating sidebar state:", error));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
setTimeout(triggerAutoscale, 250);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Close sidebar when clicking outside (only for small screens)
|
|
98
|
+
document.addEventListener("click", function (event) {
|
|
99
|
+
const sidebar = document.getElementById("sidebar");
|
|
100
|
+
const sidebarToggle = document.getElementById("sidebarToggle");
|
|
101
|
+
const screenWidth = window.innerWidth;
|
|
102
|
+
|
|
103
|
+
if (sidebar && sidebarToggle && screenWidth < 1100 && !sidebar.contains(event.target) && !sidebarToggle.contains(event.target)) {
|
|
104
|
+
if (!sidebar.classList.contains("collapsed")) {
|
|
105
|
+
sidebar.classList.add("collapsed");
|
|
106
|
+
|
|
107
|
+
const toggleUrl = sidebar.dataset.toggleUrl;
|
|
108
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
109
|
+
|
|
110
|
+
fetch(toggleUrl, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: {
|
|
113
|
+
"X-CSRFToken": csrfToken,
|
|
114
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
115
|
+
},
|
|
116
|
+
body: "collapsed=true"
|
|
117
|
+
}).then(response => response.json())
|
|
118
|
+
.catch(error => console.error("Error updating sidebar state:", error));
|
|
119
|
+
|
|
120
|
+
initializeTooltips();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
function initializeTooltips() {
|
|
126
|
+
const sidebarItems = document.querySelectorAll(".sidebar.collapsed .list-group-item, .sidebar.collapsed .accordion-button");
|
|
127
|
+
sidebarItems.forEach(item => {
|
|
128
|
+
if (item._tooltip) {
|
|
129
|
+
item._tooltip.dispose();
|
|
130
|
+
}
|
|
131
|
+
item._tooltip = new bootstrap.Tooltip(item, {
|
|
132
|
+
title: item.querySelector("span").textContent,
|
|
133
|
+
placement: "right",
|
|
134
|
+
customClass: "tooltip-custom"
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function deinitializeTooltips() {
|
|
140
|
+
const sidebarItems = document.querySelectorAll(".sidebar .list-group-item, .sidebar .accordion-button");
|
|
141
|
+
sidebarItems.forEach(item => {
|
|
142
|
+
if (item._tooltip) {
|
|
143
|
+
item._tooltip.dispose();
|
|
144
|
+
delete item._tooltip;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function triggerAutoscale() {
|
|
150
|
+
if (window.innerWidth > 1100) {
|
|
151
|
+
const autoscaleButton = document.querySelector('.modebar-btn[data-title="Reset axes"]');
|
|
152
|
+
if (autoscaleButton) {
|
|
153
|
+
autoscaleButton.click();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
|
|
2
|
+
.sidebar {
|
|
3
|
+
z-index: 900;
|
|
4
|
+
transition: width 0.15s ease-in-out;
|
|
5
|
+
background-color: white;
|
|
6
|
+
position: -webkit-sticky; /* For Safari */
|
|
7
|
+
position: sticky;
|
|
8
|
+
top: 0; /* Sticks the sidebar at the top of the parent container */
|
|
9
|
+
overflow-y: auto;
|
|
10
|
+
overflow-x: hidden;
|
|
11
|
+
height: calc(100vh - 45px); /* Fallback/Base height for sticky behavior */
|
|
12
|
+
/* Custom Scrollbar for Webkit */
|
|
13
|
+
scrollbar-width: thin;
|
|
14
|
+
scrollbar-color: rgba(0,0,0,0.2) transparent;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.sidebar::-webkit-scrollbar {
|
|
18
|
+
width: 6px;
|
|
19
|
+
}
|
|
20
|
+
.sidebar::-webkit-scrollbar-track {
|
|
21
|
+
background: transparent;
|
|
22
|
+
}
|
|
23
|
+
.sidebar::-webkit-scrollbar-thumb {
|
|
24
|
+
background-color: rgba(0,0,0,0.2);
|
|
25
|
+
border-radius: 20px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.sidebar.collapsed {
|
|
29
|
+
width: 52px !important;
|
|
30
|
+
}
|
|
31
|
+
.sidebar.collapsed .list-group-item span {
|
|
32
|
+
display: none !important;
|
|
33
|
+
}
|
|
34
|
+
.sidebar.collapsed .accordion-button span {
|
|
35
|
+
display: none !important;
|
|
36
|
+
}
|
|
37
|
+
.sidebar.collapsed .accordion-button::after {
|
|
38
|
+
background-image: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
.sidebar-ghost {
|
|
43
|
+
display: none;
|
|
44
|
+
width: 52px;
|
|
45
|
+
flex-shrink: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@media (max-width: 1100px) {
|
|
49
|
+
.sidebar-ghost {
|
|
50
|
+
display: block;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.sidebar {
|
|
54
|
+
position: fixed;
|
|
55
|
+
top: 45px; /* Fallback, JS calculates exact height */
|
|
56
|
+
right: 0;
|
|
57
|
+
width: 250px;
|
|
58
|
+
transition: width 0.15s ease-in-out;
|
|
59
|
+
height: calc(100vh - 45px); /* Fallback */
|
|
60
|
+
z-index: 1000;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.sidebar.collapsed {
|
|
64
|
+
width: 52px !important;
|
|
65
|
+
right: 0;
|
|
66
|
+
position: fixed;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
.sidebar .list-group-item {
|
|
70
|
+
border: none !important;
|
|
71
|
+
font-size: 18px !important;
|
|
72
|
+
font-weight: 600 !important;
|
|
73
|
+
height: 60px !important;
|
|
74
|
+
white-space: nowrap;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
padding: 12px;
|
|
77
|
+
align-items: center;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.sidebar .accordion-button {
|
|
81
|
+
font-size: 18px !important;
|
|
82
|
+
font-weight: 600 !important;
|
|
83
|
+
border: none !important;
|
|
84
|
+
height: 60px !important;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
padding: 12px;
|
|
88
|
+
align-items: center;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.sidebar .accordion-button:not(.collapsed) {
|
|
92
|
+
background-color: var(--body) !important;
|
|
93
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
<link rel="stylesheet" href="{% static 'sidebar/style.css' %}">
|
|
3
|
+
<script src="{% static 'sidebar/js/sidebar.js' %}" nonce="{{ request.csp_nonce }}" defer></script>
|
|
4
|
+
<!-- Ghost Sidebar for small screens layout stability -->
|
|
5
|
+
<div class="sidebar-ghost"></div>
|
|
6
|
+
|
|
7
|
+
<!-- sidebar.html -->
|
|
8
|
+
<div class="col-2 flex-column shadow-sm sidebar {% if request.session.sidebarCollapsed %}collapsed{% endif %} no-print"
|
|
9
|
+
id="sidebar"
|
|
10
|
+
data-toggle-url="{% url 'toggle_sidebar' %}"
|
|
11
|
+
data-session-collapsed="{{ request.session.sidebarCollapsed|yesno:'true,false' }}">
|
|
12
|
+
<div class="list-group flex-shrink-0">
|
|
13
|
+
|
|
14
|
+
<!-- DEFAULT CONTENT / INSTRUCTIONS -->
|
|
15
|
+
<div class="p-3 text-center text-muted">
|
|
16
|
+
<i class="bi bi-info-circle mb-2" style="font-size: 24px;"></i>
|
|
17
|
+
<p class="small">
|
|
18
|
+
<strong>Default Sidebar</strong><br>
|
|
19
|
+
To customize this menu, create a file at:<br>
|
|
20
|
+
<code>templates/sidebar/content.html</code><br>
|
|
21
|
+
in your Django project.
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<a href="#" class="list-group-item list-group-item-action">
|
|
26
|
+
<i class="bi bi-house me-2" style="font-size: 24px;"></i>
|
|
27
|
+
<span>Example Home</span>
|
|
28
|
+
</a>
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from django.http import JsonResponse
|
|
2
|
+
|
|
3
|
+
# Helper Function that handles the sidebar toggle and state
|
|
4
|
+
def toggle_sidebar(request):
|
|
5
|
+
if request.method == "POST" and request.user.is_authenticated:
|
|
6
|
+
collapsed = request.POST.get("collapsed") == "true"
|
|
7
|
+
request.session["sidebarCollapsed"] = collapsed
|
|
8
|
+
return JsonResponse({"status": "success"})
|
|
9
|
+
return JsonResponse({"status": "error"}, status=400)
|