staticdash 2025.15__tar.gz → 2025.17__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: staticdash
3
- Version: 2025.15
3
+ Version: 2025.17
4
4
  Summary: A lightweight static HTML dashboard generator with Plotly and pandas support.
5
5
  Author-email: Brian Day <brian.day1@gmail.com>
6
6
  License: CC0-1.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "staticdash"
7
- version = "2025.15"
7
+ version = "2025.17"
8
8
  description = "A lightweight static HTML dashboard generator with Plotly and pandas support."
9
9
  authors = [
10
10
  { name = "Brian Day", email = "brian.day1@gmail.com" }
@@ -85,6 +85,25 @@ document.addEventListener("DOMContentLoaded", () => {
85
85
  return link ? link.getAttribute('href') : null;
86
86
  }
87
87
 
88
+ function saveSidebarState() {
89
+ const openGroups = Array.from(document.querySelectorAll('.sidebar-group.open'))
90
+ .map(getGroupSlug)
91
+ .filter(Boolean);
92
+ localStorage.setItem("sidebar-open-groups", JSON.stringify(openGroups));
93
+ }
94
+
95
+ function restoreSidebarState() {
96
+ const openGroups = JSON.parse(localStorage.getItem("sidebar-open-groups") || "[]");
97
+ document.querySelectorAll('.sidebar-group').forEach(group => {
98
+ const slug = getGroupSlug(group);
99
+ if (slug && openGroups.includes(slug)) {
100
+ group.classList.add('open');
101
+ } else {
102
+ group.classList.remove('open');
103
+ }
104
+ });
105
+ }
106
+
88
107
  // Restore open groups from localStorage
89
108
  const openGroups = JSON.parse(localStorage.getItem("sidebar-open-groups") || "[]");
90
109
  document.querySelectorAll('.sidebar-group').forEach(group => {
@@ -102,13 +121,7 @@ document.addEventListener("DOMContentLoaded", () => {
102
121
  const group = parent.closest('.sidebar-group');
103
122
  if (group) {
104
123
  group.classList.toggle('open');
105
- // Save open groups to localStorage
106
- const allGroups = Array.from(document.querySelectorAll('.sidebar-group'));
107
- const open = allGroups
108
- .filter(g => g.classList.contains('open'))
109
- .map(getGroupSlug)
110
- .filter(Boolean);
111
- localStorage.setItem("sidebar-open-groups", JSON.stringify(open));
124
+ saveSidebarState();
112
125
  }
113
126
  }
114
127
  });
@@ -131,4 +144,56 @@ document.addEventListener("DOMContentLoaded", () => {
131
144
  setTimeout(() => {
132
145
  document.body.classList.add("sidebar-animate");
133
146
  }, 0);
134
- });
147
+
148
+ // --- Sidebar collapse/expand memory ---
149
+ function saveSidebarState() {
150
+ const expanded = [];
151
+ document.querySelectorAll('.sidebar-group .sidebar-children').forEach(el => {
152
+ if (el.classList.contains('expanded')) {
153
+ // Use parent page slug as key
154
+ const parent = el.parentElement.querySelector('.sidebar-parent, .nav-link');
155
+ if (parent && parent.getAttribute('href')) {
156
+ expanded.push(parent.getAttribute('href'));
157
+ }
158
+ }
159
+ });
160
+ localStorage.setItem('sidebarExpanded', JSON.stringify(expanded));
161
+ }
162
+
163
+ function restoreSidebarState() {
164
+ const expanded = JSON.parse(localStorage.getItem('sidebarExpanded') || '[]');
165
+ document.querySelectorAll('.sidebar-group').forEach(group => {
166
+ const parent = group.querySelector('.sidebar-parent, .nav-link');
167
+ const children = group.querySelector('.sidebar-children');
168
+ if (parent && children) {
169
+ if (expanded.includes(parent.getAttribute('href'))) {
170
+ children.classList.add('expanded');
171
+ } else {
172
+ children.classList.remove('expanded');
173
+ }
174
+ }
175
+ });
176
+ }
177
+
178
+ restoreSidebarState();
179
+
180
+ document.querySelectorAll('.sidebar-group .sidebar-parent, .sidebar-group .nav-link').forEach(parent => {
181
+ parent.addEventListener('click', function(e) {
182
+ // Only toggle if arrow or parent link is clicked
183
+ if (e.target.classList.contains('sidebar-arrow') || e.currentTarget === e.target) {
184
+ const group = parent.closest('.sidebar-group');
185
+ const children = group.querySelector('.sidebar-children');
186
+ if (children) {
187
+ children.classList.toggle('expanded');
188
+ saveSidebarState();
189
+ }
190
+ }
191
+ });
192
+ });
193
+ });
194
+
195
+ /* CSS */
196
+ /* Add this CSS to handle the rotation of the arrow icon */
197
+ body.sidebar-animate .sidebar-group.open .sidebar-arrow {
198
+ transform: rotate(90deg);
199
+ }
@@ -201,6 +201,7 @@ class Dashboard:
201
201
 
202
202
  def write_page(page):
203
203
  doc = document(title=page.title)
204
+ effective_width = page.page_width or self.page_width or 900
204
205
  with doc.head:
205
206
  doc.head.add(link(rel="stylesheet", href="../assets/css/style.css"))
206
207
  doc.head.add(script(type="text/javascript", src="../assets/js/script.js"))
@@ -209,6 +210,8 @@ class Dashboard:
209
210
  doc.head.add(script(src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"))
210
211
  doc.head.add(script(src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-python.min.js"))
211
212
  doc.head.add(script(src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-javascript.min.js"))
213
+ # Inject dynamic page width
214
+ doc.head.add(raw_util(f"<style>.content-inner {{ max-width: {effective_width}px !important; }}</style>"))
212
215
  with doc:
213
216
  with div(id="sidebar"):
214
217
  a(self.title, href="../index.html", cls="sidebar-title")
@@ -227,7 +230,9 @@ class Dashboard:
227
230
  for page in self.pages:
228
231
  write_page(page)
229
232
 
233
+ # Index page
230
234
  index_doc = document(title=self.title)
235
+ effective_width = self.pages[0].page_width or self.page_width or 900
231
236
  with index_doc.head:
232
237
  index_doc.head.add(link(rel="stylesheet", href="assets/css/style.css"))
233
238
  index_doc.head.add(script(type="text/javascript", src="assets/js/script.js"))
@@ -241,7 +246,8 @@ class Dashboard:
241
246
  index_doc.head.add(script(src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-bash.min.js"))
242
247
  index_doc.head.add(script(src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-json.min.js"))
243
248
  index_doc.head.add(script(src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-c.min.js"))
244
-
249
+ # Inject dynamic page width
250
+ index_doc.head.add(raw_util(f"<style>.content-inner {{ max-width: {effective_width}px !important; }}</style>"))
245
251
  with index_doc:
246
252
  with div(id="sidebar"):
247
253
  a(self.title, href="index.html", cls="sidebar-title")
@@ -392,12 +398,19 @@ class Dashboard:
392
398
  story.append(PageBreak())
393
399
 
394
400
  if include_title_page:
395
- story.append(Paragraph(self.title, styles['Heading1']))
401
+ story.append(Spacer(1, 120))
402
+ # Title, centered and bold
403
+ story.append(Paragraph(f"<b>{self.title}</b>", styles['Title']))
404
+ story.append(Spacer(1, 48))
405
+ # Centered info block (no labels)
406
+ info_lines = []
396
407
  if author:
397
- story.append(Paragraph(f"Author: {author}", normal_style))
408
+ info_lines.append(str(author))
398
409
  if affiliation:
399
- story.append(Paragraph(f"Affiliation: {affiliation}", normal_style))
400
- story.append(Paragraph(f"Date: {pd.Timestamp.now().strftime('%B %d, %Y')}", normal_style))
410
+ info_lines.append(str(affiliation))
411
+ info_lines.append(pd.Timestamp.now().strftime('%B %d, %Y'))
412
+ info_html = "<br/>".join(info_lines)
413
+ story.append(Paragraph(f'<para align="center">{info_html}</para>', styles['Normal']))
401
414
  story.append(PageBreak())
402
415
 
403
416
  for page in self.pages:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: staticdash
3
- Version: 2025.15
3
+ Version: 2025.17
4
4
  Summary: A lightweight static HTML dashboard generator with Plotly and pandas support.
5
5
  Author-email: Brian Day <brian.day1@gmail.com>
6
6
  License: CC0-1.0
File without changes
File without changes