staticdash 2026.4__py3-none-any.whl → 2026.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
staticdash/dashboard.py CHANGED
@@ -54,13 +54,15 @@ class AbstractPage:
54
54
  def add_text(self, text, width=None):
55
55
  self.elements.append(("text", text, width))
56
56
 
57
- def add_plot(self, plot, width=None, height=None, width_px=None, align="center"):
58
- # Keep backward-compatible: `width` is a fraction (0..1) of page width.
59
- # `height` is pixels (existing behavior). New optional `width_px`
60
- # (pixels) can be supplied to control plot width. `align` controls
61
- # horizontal alignment: 'left', 'center' (default), or 'right'.
62
- # We store a tuple (plot, height_px, width_px, align) for forward-compatibility.
63
- self.elements.append(("plot", (plot, height, width_px, align), width))
57
+ def add_plot(self, plot, el_width=None, height=None, width=None, align="center"):
58
+ # `el_width` is the fractional width (0..1) used for layout columns.
59
+ # `height` and `width` are pixel dimensions for the rendered figure/image.
60
+ # `align` controls horizontal alignment: 'left', 'center' (default), or 'right'.
61
+ # We store a tuple (plot, height, width, align) and keep `el_width` as
62
+ # the element-level fractional width for compatibility with page layout.
63
+ specified_height = height
64
+ specified_width = width
65
+ self.elements.append(("plot", (plot, specified_height, specified_width, align), el_width))
64
66
 
65
67
  def add_table(self, df, table_id=None, sortable=True, width=None):
66
68
  self.elements.append(("table", df, width))
@@ -156,16 +158,16 @@ class Page(AbstractPage):
156
158
  header_tag = {1: h1, 2: h2, 3: h3, 4: h4}[level]
157
159
  elem = header_tag(text)
158
160
  elif kind == "plot":
159
- # content may be stored as (figure, height), (figure, height, width_px)
160
- # or (figure, height, width_px, align)
161
+ # content may be stored as (figure, height), (figure, height, width)
162
+ # or (figure, height, width, align)
161
163
  specified_height = None
162
- specified_width_px = None
164
+ specified_width = None
163
165
  specified_align = "center"
164
166
  if isinstance(content, (list, tuple)):
165
167
  if len(content) == 4:
166
- fig, specified_height, specified_width_px, specified_align = content
168
+ fig, specified_height, specified_width, specified_align = content
167
169
  elif len(content) == 3:
168
- fig, specified_height, specified_width_px = content
170
+ fig, specified_height, specified_width = content
169
171
  elif len(content) == 2:
170
172
  fig, specified_height = content
171
173
  else:
@@ -203,23 +205,22 @@ class Page(AbstractPage):
203
205
  try:
204
206
  if specified_height is not None:
205
207
  fig.update_layout(height=specified_height)
206
- if specified_width_px is not None:
207
- fig.update_layout(width=specified_width_px)
208
+ if specified_width is not None:
209
+ fig.update_layout(width=specified_width)
208
210
  except Exception:
209
211
  pass
210
212
 
211
213
  # If a local vendored Plotly exists, rely on the head script.
212
214
  # Otherwise include Plotly from CDN so the inline newPlot call works.
213
- vendor_plotly = os.path.join(os.path.dirname(__file__), "assets", "vendor", "plotly", "plotly.min.js")
214
- include_plotly = False
215
- if not os.path.exists(vendor_plotly):
216
- include_plotly = "cdn"
217
- plotly_html = fig.to_html(full_html=False, include_plotlyjs=include_plotly, config={'responsive': True})
215
+ # Always rely on the page-level Plotly include (head). Avoid
216
+ # embedding another Plotly bundle inside each fragment which
217
+ # can lead to multiple conflicting versions in the same page.
218
+ plotly_html = fig.to_html(full_html=False, include_plotlyjs=False, config={'responsive': True})
218
219
 
219
220
  # Wrap the Plotly HTML in a container with explicit pixel sizing
220
221
  container_style = "width:100%;"
221
- if specified_width_px is not None:
222
- container_style = f"width:{specified_width_px}px;"
222
+ if specified_width is not None:
223
+ container_style = f"width:{specified_width}px;"
223
224
  if specified_height is not None:
224
225
  container_style = container_style + f" height:{specified_height}px;"
225
226
 
@@ -241,7 +242,7 @@ class Page(AbstractPage):
241
242
  try:
242
243
  if specified_height is not None:
243
244
  fig.update_layout(height=orig_height)
244
- if specified_width_px is not None:
245
+ if specified_width is not None:
245
246
  fig.update_layout(width=orig_width)
246
247
  except Exception:
247
248
  pass
@@ -258,12 +259,12 @@ class Page(AbstractPage):
258
259
  except Exception:
259
260
  dpi = None
260
261
  try:
261
- if dpi is not None and (specified_height is not None or specified_width_px is not None):
262
+ if dpi is not None and (specified_height is not None or specified_width is not None):
262
263
  orig_size = fig.get_size_inches()
263
264
  new_w = orig_size[0]
264
265
  new_h = orig_size[1]
265
- if specified_width_px is not None:
266
- new_w = specified_width_px / dpi
266
+ if specified_width is not None:
267
+ new_w = specified_width / dpi
267
268
  if specified_height is not None:
268
269
  new_h = specified_height / dpi
269
270
  fig.set_size_inches(new_w, new_h)
@@ -285,8 +286,8 @@ class Page(AbstractPage):
285
286
  img_style = "max-width:100%;"
286
287
  if specified_height is not None:
287
288
  img_style = img_style + f" height:{specified_height}px;"
288
- if specified_width_px is not None:
289
- img_style = img_style + f" width:{specified_width_px}px;"
289
+ if specified_width is not None:
290
+ img_style = img_style + f" width:{specified_width}px;"
290
291
 
291
292
  if specified_align not in ("left", "right", "center"):
292
293
  specified_align = "center"
@@ -378,12 +379,15 @@ class MiniPage(AbstractPage):
378
379
  header_tag = {1: h1, 2: h2, 3: h3, 4: h4}[level]
379
380
  elem = header_tag(text)
380
381
  elif kind == "plot":
381
- # content may be stored as (figure, height) or (figure, height, width_px)
382
+ # content may be stored as (figure, height, width, align)
382
383
  specified_height = None
383
- specified_width_px = None
384
+ specified_width = None
385
+ specified_align = "center"
384
386
  if isinstance(content, (list, tuple)):
385
- if len(content) == 3:
386
- fig, specified_height, specified_width_px = content
387
+ if len(content) == 4:
388
+ fig, specified_height, specified_width, specified_align = content
389
+ elif len(content) == 3:
390
+ fig, specified_height, specified_width = content
387
391
  elif len(content) == 2:
388
392
  fig, specified_height = content
389
393
  else:
@@ -418,19 +422,18 @@ class MiniPage(AbstractPage):
418
422
  try:
419
423
  if specified_height is not None:
420
424
  fig.update_layout(height=specified_height)
421
- if specified_width_px is not None:
422
- fig.update_layout(width=specified_width_px)
425
+ if specified_width is not None:
426
+ fig.update_layout(width=specified_width)
423
427
  except Exception:
424
428
  pass
425
429
 
426
- vendor_plotly = os.path.join(os.path.dirname(__file__), "assets", "vendor", "plotly", "plotly.min.js")
427
- include_plotly = False
428
- if not os.path.exists(vendor_plotly):
429
- include_plotly = "cdn"
430
- plotly_html = fig.to_html(full_html=False, include_plotlyjs=include_plotly, config={'responsive': True})
430
+ # Always rely on the page-level Plotly include (head). Avoid
431
+ # embedding another Plotly bundle inside each fragment which
432
+ # can lead to multiple conflicting versions in the same page.
433
+ plotly_html = fig.to_html(full_html=False, include_plotlyjs=False, config={'responsive': True})
431
434
  container_style = "width:100%;"
432
- if specified_width_px is not None:
433
- container_style = f"width:{specified_width_px}px;"
435
+ if specified_width is not None:
436
+ container_style = f"width:{specified_width}px;"
434
437
  if specified_height is not None:
435
438
  container_style = container_style + f" height:{specified_height}px;"
436
439
  plot_wrapped = f'<div style="{container_style}">{plotly_html}</div>'
@@ -448,7 +451,7 @@ class MiniPage(AbstractPage):
448
451
  try:
449
452
  if specified_height is not None:
450
453
  fig.update_layout(height=orig_height)
451
- if specified_width_px is not None:
454
+ if specified_width is not None:
452
455
  fig.update_layout(width=orig_width)
453
456
  except Exception:
454
457
  pass
@@ -465,12 +468,12 @@ class MiniPage(AbstractPage):
465
468
  except Exception:
466
469
  dpi = None
467
470
  try:
468
- if dpi is not None and (specified_height is not None or specified_width_px is not None):
471
+ if dpi is not None and (specified_height is not None or specified_width is not None):
469
472
  orig_size = fig.get_size_inches()
470
473
  new_w = orig_size[0]
471
474
  new_h = orig_size[1]
472
- if specified_width_px is not None:
473
- new_w = specified_width_px / dpi
475
+ if specified_width is not None:
476
+ new_w = specified_width / dpi
474
477
  if specified_height is not None:
475
478
  new_h = specified_height / dpi
476
479
  fig.set_size_inches(new_w, new_h)
@@ -492,8 +495,8 @@ class MiniPage(AbstractPage):
492
495
  img_style = "max-width:100%;"
493
496
  if specified_height is not None:
494
497
  img_style = img_style + f" height:{specified_height}px;"
495
- if specified_width_px is not None:
496
- img_style = img_style + f" width:{specified_width_px}px;"
498
+ if specified_width is not None:
499
+ img_style = img_style + f" width:{specified_width}px;"
497
500
  if specified_align not in ("left", "right", "center"):
498
501
  specified_align = "center"
499
502
  if specified_align == "center":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: staticdash
3
- Version: 2026.4
3
+ Version: 2026.5
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
@@ -1,5 +1,5 @@
1
1
  staticdash/__init__.py,sha256=MQGR6LAqx2aFEA64MZz1ADxwpXLPn3VYNVIyjt9qx4Q,268
2
- staticdash/dashboard.py,sha256=pR1AmmisyBAiPTUHlijPwF2E6bQJjU-9ZhWGTcVSeC0,42833
2
+ staticdash/dashboard.py,sha256=GJ5P1GC8h7xLDzB65K5nfHCao9Uc1tge5R1mk0al4iA,42938
3
3
  staticdash/assets/css/style.css,sha256=AOYdkw-nK_WvV6im_Y34gz4rJZWifk5o-mRmCKwMP60,7014
4
4
  staticdash/assets/js/script.js,sha256=7xBRlz_19wybbNVwAcfuKNXtDEojGB4EB0Yj4klsoTA,6998
5
5
  staticdash/assets/vendor/mathjax/tex-mml-chtml.js,sha256=MASABpB4tYktI2Oitl4t-78w_lyA-D7b_s9GEP0JOGI,1173007
@@ -13,7 +13,7 @@ staticdash/assets/vendor/prism/components/prism-json.min.js,sha256=lW2GuqWufsQQZ
13
13
  staticdash/assets/vendor/prism/components/prism-markup.min.js,sha256=h5_J0lbDUtmA4FOFf6cHMwhTuL-2fOKE6mYaJN7FdW4,2850
14
14
  staticdash/assets/vendor/prism/components/prism-python.min.js,sha256=7UOFaFvPLUk1yNu6tL3hZgPaEyngktK_NsPa3WfpqFw,2113
15
15
  staticdash/assets/vendor/prism/components/prism-sql.min.js,sha256=P8X4zmmVDsc63JcvBh30Kq6nj6pIZHCRNOoq3Ag_OjM,3261
16
- staticdash-2026.4.dist-info/METADATA,sha256=FhGqUb1OWQuzZ_Jjtip3xV5gQjzJRHBV2pDuOTe2Y6A,2521
17
- staticdash-2026.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- staticdash-2026.4.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
19
- staticdash-2026.4.dist-info/RECORD,,
16
+ staticdash-2026.5.dist-info/METADATA,sha256=Wj1xgiF9vHTJb8zV_g5c0rb34x20dS9dOYn3BUVuBpo,2521
17
+ staticdash-2026.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ staticdash-2026.5.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
19
+ staticdash-2026.5.dist-info/RECORD,,