staticdash 2026.4__py3-none-any.whl → 2026.6__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 +110 -58
- {staticdash-2026.4.dist-info → staticdash-2026.6.dist-info}/METADATA +1 -1
- {staticdash-2026.4.dist-info → staticdash-2026.6.dist-info}/RECORD +5 -5
- {staticdash-2026.4.dist-info → staticdash-2026.6.dist-info}/WHEEL +0 -0
- {staticdash-2026.4.dist-info → staticdash-2026.6.dist-info}/top_level.txt +0 -0
staticdash/dashboard.py
CHANGED
|
@@ -12,6 +12,7 @@ import io
|
|
|
12
12
|
import base64
|
|
13
13
|
import matplotlib
|
|
14
14
|
from matplotlib import rc_context
|
|
15
|
+
import json
|
|
15
16
|
|
|
16
17
|
def split_paragraphs_preserving_math(text):
|
|
17
18
|
"""
|
|
@@ -54,13 +55,15 @@ class AbstractPage:
|
|
|
54
55
|
def add_text(self, text, width=None):
|
|
55
56
|
self.elements.append(("text", text, width))
|
|
56
57
|
|
|
57
|
-
def add_plot(self, plot,
|
|
58
|
-
#
|
|
59
|
-
# `height`
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
|
|
58
|
+
def add_plot(self, plot, el_width=None, height=None, width=None, align="center"):
|
|
59
|
+
# `el_width` is the fractional width (0..1) used for layout columns.
|
|
60
|
+
# `height` and `width` are pixel dimensions for the rendered figure/image.
|
|
61
|
+
# `align` controls horizontal alignment: 'left', 'center' (default), or 'right'.
|
|
62
|
+
# We store a tuple (plot, height, width, align) and keep `el_width` as
|
|
63
|
+
# the element-level fractional width for compatibility with page layout.
|
|
64
|
+
specified_height = height
|
|
65
|
+
specified_width = width
|
|
66
|
+
self.elements.append(("plot", (plot, specified_height, specified_width, align), el_width))
|
|
64
67
|
|
|
65
68
|
def add_table(self, df, table_id=None, sortable=True, width=None):
|
|
66
69
|
self.elements.append(("table", df, width))
|
|
@@ -156,16 +159,16 @@ class Page(AbstractPage):
|
|
|
156
159
|
header_tag = {1: h1, 2: h2, 3: h3, 4: h4}[level]
|
|
157
160
|
elem = header_tag(text)
|
|
158
161
|
elif kind == "plot":
|
|
159
|
-
# content may be stored as (figure, height), (figure, height,
|
|
160
|
-
# or (figure, height,
|
|
162
|
+
# content may be stored as (figure, height), (figure, height, width)
|
|
163
|
+
# or (figure, height, width, align)
|
|
161
164
|
specified_height = None
|
|
162
|
-
|
|
165
|
+
specified_width = None
|
|
163
166
|
specified_align = "center"
|
|
164
167
|
if isinstance(content, (list, tuple)):
|
|
165
168
|
if len(content) == 4:
|
|
166
|
-
fig, specified_height,
|
|
169
|
+
fig, specified_height, specified_width, specified_align = content
|
|
167
170
|
elif len(content) == 3:
|
|
168
|
-
fig, specified_height,
|
|
171
|
+
fig, specified_height, specified_width = content
|
|
169
172
|
elif len(content) == 2:
|
|
170
173
|
fig, specified_height = content
|
|
171
174
|
else:
|
|
@@ -203,27 +206,48 @@ class Page(AbstractPage):
|
|
|
203
206
|
try:
|
|
204
207
|
if specified_height is not None:
|
|
205
208
|
fig.update_layout(height=specified_height)
|
|
206
|
-
if
|
|
207
|
-
fig.update_layout(width=
|
|
209
|
+
if specified_width is not None:
|
|
210
|
+
fig.update_layout(width=specified_width)
|
|
208
211
|
except Exception:
|
|
209
212
|
pass
|
|
210
213
|
|
|
211
|
-
#
|
|
212
|
-
#
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
214
|
+
# Defer Plotly.newPlot until the Plotly bundle is available
|
|
215
|
+
# by embedding the figure JSON and calling newPlot from a
|
|
216
|
+
# small loader that polls for `window.Plotly`.
|
|
217
|
+
try:
|
|
218
|
+
fig_json = fig.to_json()
|
|
219
|
+
except Exception:
|
|
220
|
+
# Fallback: use the html fragment (older path)
|
|
221
|
+
plotly_html = fig.to_html(full_html=False, include_plotlyjs=False, config={'responsive': True})
|
|
222
|
+
container_style = "width:100%;"
|
|
223
|
+
if specified_width is not None:
|
|
224
|
+
container_style = f"width:{specified_width}px;"
|
|
225
|
+
if specified_height is not None:
|
|
226
|
+
container_style = container_style + f" height:{specified_height}px;"
|
|
227
|
+
plot_wrapped = f'<div style="{container_style}">{plotly_html}</div>'
|
|
228
|
+
else:
|
|
229
|
+
fig_json = fig_json.replace('</script>', '<\\/script>')
|
|
230
|
+
div_id = f'plot-{uuid.uuid4()}'
|
|
231
|
+
container_style = "width:100%;"
|
|
232
|
+
if specified_width is not None:
|
|
233
|
+
container_style = f"width:{specified_width}px;"
|
|
234
|
+
if specified_height is not None:
|
|
235
|
+
container_style = container_style + f" height:{specified_height}px;"
|
|
236
|
+
|
|
237
|
+
plot_div = f'<div id="{div_id}" class="plotly-graph-div" style="{container_style}"></div>'
|
|
238
|
+
loader = (
|
|
239
|
+
'<script type="text/javascript">(function(){' \
|
|
240
|
+
f'var fig = {fig_json};' \
|
|
241
|
+
'function tryPlot(){' \
|
|
242
|
+
'if(window.Plotly && typeof window.Plotly.newPlot === "function"){' \
|
|
243
|
+
f'Plotly.newPlot("{div_id}", fig.data, fig.layout, {json.dumps({"responsive": True})});' \
|
|
244
|
+
'} else { setTimeout(tryPlot, 50); }' \
|
|
245
|
+
'}' \
|
|
246
|
+
'if(document.readyState === "complete"){ tryPlot(); } else { window.addEventListener("load", tryPlot); }' \
|
|
247
|
+
'})();</script>'
|
|
248
|
+
)
|
|
249
|
+
plot_wrapped = plot_div + loader
|
|
225
250
|
|
|
226
|
-
plot_wrapped = f'<div style="{container_style}">{plotly_html}</div>'
|
|
227
251
|
# Apply alignment wrapper
|
|
228
252
|
if specified_align not in ("left", "right", "center"):
|
|
229
253
|
specified_align = "center"
|
|
@@ -241,7 +265,7 @@ class Page(AbstractPage):
|
|
|
241
265
|
try:
|
|
242
266
|
if specified_height is not None:
|
|
243
267
|
fig.update_layout(height=orig_height)
|
|
244
|
-
if
|
|
268
|
+
if specified_width is not None:
|
|
245
269
|
fig.update_layout(width=orig_width)
|
|
246
270
|
except Exception:
|
|
247
271
|
pass
|
|
@@ -258,12 +282,12 @@ class Page(AbstractPage):
|
|
|
258
282
|
except Exception:
|
|
259
283
|
dpi = None
|
|
260
284
|
try:
|
|
261
|
-
if dpi is not None and (specified_height is not None or
|
|
285
|
+
if dpi is not None and (specified_height is not None or specified_width is not None):
|
|
262
286
|
orig_size = fig.get_size_inches()
|
|
263
287
|
new_w = orig_size[0]
|
|
264
288
|
new_h = orig_size[1]
|
|
265
|
-
if
|
|
266
|
-
new_w =
|
|
289
|
+
if specified_width is not None:
|
|
290
|
+
new_w = specified_width / dpi
|
|
267
291
|
if specified_height is not None:
|
|
268
292
|
new_h = specified_height / dpi
|
|
269
293
|
fig.set_size_inches(new_w, new_h)
|
|
@@ -285,8 +309,8 @@ class Page(AbstractPage):
|
|
|
285
309
|
img_style = "max-width:100%;"
|
|
286
310
|
if specified_height is not None:
|
|
287
311
|
img_style = img_style + f" height:{specified_height}px;"
|
|
288
|
-
if
|
|
289
|
-
img_style = img_style + f" width:{
|
|
312
|
+
if specified_width is not None:
|
|
313
|
+
img_style = img_style + f" width:{specified_width}px;"
|
|
290
314
|
|
|
291
315
|
if specified_align not in ("left", "right", "center"):
|
|
292
316
|
specified_align = "center"
|
|
@@ -378,12 +402,15 @@ class MiniPage(AbstractPage):
|
|
|
378
402
|
header_tag = {1: h1, 2: h2, 3: h3, 4: h4}[level]
|
|
379
403
|
elem = header_tag(text)
|
|
380
404
|
elif kind == "plot":
|
|
381
|
-
# content may be stored as (figure, height
|
|
405
|
+
# content may be stored as (figure, height, width, align)
|
|
382
406
|
specified_height = None
|
|
383
|
-
|
|
407
|
+
specified_width = None
|
|
408
|
+
specified_align = "center"
|
|
384
409
|
if isinstance(content, (list, tuple)):
|
|
385
|
-
if len(content) ==
|
|
386
|
-
fig, specified_height,
|
|
410
|
+
if len(content) == 4:
|
|
411
|
+
fig, specified_height, specified_width, specified_align = content
|
|
412
|
+
elif len(content) == 3:
|
|
413
|
+
fig, specified_height, specified_width = content
|
|
387
414
|
elif len(content) == 2:
|
|
388
415
|
fig, specified_height = content
|
|
389
416
|
else:
|
|
@@ -418,22 +445,47 @@ class MiniPage(AbstractPage):
|
|
|
418
445
|
try:
|
|
419
446
|
if specified_height is not None:
|
|
420
447
|
fig.update_layout(height=specified_height)
|
|
421
|
-
if
|
|
422
|
-
fig.update_layout(width=
|
|
448
|
+
if specified_width is not None:
|
|
449
|
+
fig.update_layout(width=specified_width)
|
|
423
450
|
except Exception:
|
|
424
451
|
pass
|
|
425
452
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
453
|
+
# Defer Plotly.newPlot until the Plotly bundle is available
|
|
454
|
+
# by embedding the figure JSON and calling newPlot from a
|
|
455
|
+
# small loader that polls for `window.Plotly`.
|
|
456
|
+
try:
|
|
457
|
+
fig_json = fig.to_json()
|
|
458
|
+
except Exception:
|
|
459
|
+
# Fallback: use the html fragment (older path)
|
|
460
|
+
plotly_html = fig.to_html(full_html=False, include_plotlyjs=False, config={'responsive': True})
|
|
461
|
+
container_style = "width:100%;"
|
|
462
|
+
if specified_width is not None:
|
|
463
|
+
container_style = f"width:{specified_width}px;"
|
|
464
|
+
if specified_height is not None:
|
|
465
|
+
container_style = container_style + f" height:{specified_height}px;"
|
|
466
|
+
plot_wrapped = f'<div style="{container_style}">{plotly_html}</div>'
|
|
467
|
+
else:
|
|
468
|
+
fig_json = fig_json.replace('</script>', '<\\/script>')
|
|
469
|
+
div_id = f'plot-{uuid.uuid4()}'
|
|
470
|
+
container_style = "width:100%;"
|
|
471
|
+
if specified_width is not None:
|
|
472
|
+
container_style = f"width:{specified_width}px;"
|
|
473
|
+
if specified_height is not None:
|
|
474
|
+
container_style = container_style + f" height:{specified_height}px;"
|
|
475
|
+
|
|
476
|
+
plot_div = f'<div id="{div_id}" class="plotly-graph-div" style="{container_style}"></div>'
|
|
477
|
+
loader = (
|
|
478
|
+
'<script type="text/javascript">(function(){' \
|
|
479
|
+
f'var fig = {fig_json};' \
|
|
480
|
+
'function tryPlot(){' \
|
|
481
|
+
'if(window.Plotly && typeof window.Plotly.newPlot === "function"){' \
|
|
482
|
+
f'Plotly.newPlot("{div_id}", fig.data, fig.layout, {json.dumps({"responsive": True})});' \
|
|
483
|
+
'} else { setTimeout(tryPlot, 50); }' \
|
|
484
|
+
'}' \
|
|
485
|
+
'if(document.readyState === "complete"){ tryPlot(); } else { window.addEventListener("load", tryPlot); }' \
|
|
486
|
+
'})();</script>'
|
|
487
|
+
)
|
|
488
|
+
plot_wrapped = plot_div + loader
|
|
437
489
|
if specified_align not in ("left", "right", "center"):
|
|
438
490
|
specified_align = "center"
|
|
439
491
|
if specified_align == "center":
|
|
@@ -448,7 +500,7 @@ class MiniPage(AbstractPage):
|
|
|
448
500
|
try:
|
|
449
501
|
if specified_height is not None:
|
|
450
502
|
fig.update_layout(height=orig_height)
|
|
451
|
-
if
|
|
503
|
+
if specified_width is not None:
|
|
452
504
|
fig.update_layout(width=orig_width)
|
|
453
505
|
except Exception:
|
|
454
506
|
pass
|
|
@@ -465,12 +517,12 @@ class MiniPage(AbstractPage):
|
|
|
465
517
|
except Exception:
|
|
466
518
|
dpi = None
|
|
467
519
|
try:
|
|
468
|
-
if dpi is not None and (specified_height is not None or
|
|
520
|
+
if dpi is not None and (specified_height is not None or specified_width is not None):
|
|
469
521
|
orig_size = fig.get_size_inches()
|
|
470
522
|
new_w = orig_size[0]
|
|
471
523
|
new_h = orig_size[1]
|
|
472
|
-
if
|
|
473
|
-
new_w =
|
|
524
|
+
if specified_width is not None:
|
|
525
|
+
new_w = specified_width / dpi
|
|
474
526
|
if specified_height is not None:
|
|
475
527
|
new_h = specified_height / dpi
|
|
476
528
|
fig.set_size_inches(new_w, new_h)
|
|
@@ -492,8 +544,8 @@ class MiniPage(AbstractPage):
|
|
|
492
544
|
img_style = "max-width:100%;"
|
|
493
545
|
if specified_height is not None:
|
|
494
546
|
img_style = img_style + f" height:{specified_height}px;"
|
|
495
|
-
if
|
|
496
|
-
img_style = img_style + f" width:{
|
|
547
|
+
if specified_width is not None:
|
|
548
|
+
img_style = img_style + f" width:{specified_width}px;"
|
|
497
549
|
if specified_align not in ("left", "right", "center"):
|
|
498
550
|
specified_align = "center"
|
|
499
551
|
if specified_align == "center":
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
staticdash/__init__.py,sha256=MQGR6LAqx2aFEA64MZz1ADxwpXLPn3VYNVIyjt9qx4Q,268
|
|
2
|
-
staticdash/dashboard.py,sha256=
|
|
2
|
+
staticdash/dashboard.py,sha256=7XCwSKzmY9K-DwSikzykQGtbsxzxEfqAaTJlUP-KuGE,46106
|
|
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.
|
|
17
|
-
staticdash-2026.
|
|
18
|
-
staticdash-2026.
|
|
19
|
-
staticdash-2026.
|
|
16
|
+
staticdash-2026.6.dist-info/METADATA,sha256=gYrNTyhXOQWigyRspfiGBRzPJ7qfV17nooCMRFUdH-o,2521
|
|
17
|
+
staticdash-2026.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
staticdash-2026.6.dist-info/top_level.txt,sha256=3MzZU6SptkUkjcHV1cvPji0H4aRzPphLHnpStgGEcxM,11
|
|
19
|
+
staticdash-2026.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|