d3blocks 2.2.1__tar.gz → 2.3.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.
- {d3blocks-2.2.1 → d3blocks-2.3.0}/MANIFEST.in +3 -1
- {d3blocks-2.2.1/d3blocks.egg-info → d3blocks-2.3.0}/PKG-INFO +34 -12
- {d3blocks-2.2.1 → d3blocks-2.3.0}/README.md +31 -9
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/__init__.py +1 -1
- d3blocks-2.3.0/d3blocks/_shared/__init__.py +208 -0
- d3blocks-2.3.0/d3blocks/_shared/d3js/theme.js +63 -0
- d3blocks-2.3.0/d3blocks/_shared/d3js/walkthrough.css +56 -0
- d3blocks-2.3.0/d3blocks/_shared/d3js/walkthrough.js +145 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/d3blocks.py +409 -11
- d3blocks-2.3.0/d3blocks/erd/Erd.py +477 -0
- d3blocks-2.3.0/d3blocks/erd/__init__.py +19 -0
- d3blocks-2.3.0/d3blocks/erd/d3js/d3.erd.js +739 -0
- d3blocks-2.3.0/d3blocks/erd/d3js/erd.css +329 -0
- d3blocks-2.3.0/d3blocks/erd/d3js/erd.html.j2 +122 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/examples.py +129 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/Flowgraph.py +524 -118
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/d3.flowgraph.js +224 -48
- d3blocks-2.3.0/d3blocks/flowgraph/d3js/d3.v7.min.js +2 -0
- d3blocks-2.3.0/d3blocks/flowgraph/d3js/dagre.LICENSE +19 -0
- d3blocks-2.3.0/d3blocks/flowgraph/d3js/dagre.min.js +815 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/flowgraph.css +4 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/flowgraph.html.j2 +4 -1
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/scatter.html.j2 +3168 -2948
- d3blocks-2.3.0/d3blocks/sequence/Sequence.py +475 -0
- d3blocks-2.3.0/d3blocks/sequence/__init__.py +19 -0
- d3blocks-2.3.0/d3blocks/sequence/d3js/d3.sequence.js +676 -0
- d3blocks-2.3.0/d3blocks/sequence/d3js/sequence.css +332 -0
- d3blocks-2.3.0/d3blocks/sequence/d3js/sequence.html.j2 +106 -0
- d3blocks-2.3.0/d3blocks/tests/test_erd.py +385 -0
- d3blocks-2.3.0/d3blocks/tests/test_flowgraph.py +820 -0
- d3blocks-2.3.0/d3blocks/tests/test_sequence.py +338 -0
- d3blocks-2.3.0/d3blocks/treemap/d3js/__init__.py +0 -0
- d3blocks-2.3.0/d3blocks/violin/__init__.py +0 -0
- d3blocks-2.3.0/d3blocks/violin/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0/d3blocks.egg-info}/PKG-INFO +34 -12
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/SOURCES.txt +22 -4
- {d3blocks-2.2.1 → d3blocks-2.3.0}/pyproject.toml +13 -7
- d3blocks-2.2.1/d3blocks/skills/d3blocks/SKILL.md +0 -78
- d3blocks-2.2.1/d3blocks/skills/d3blocks/references/scatter.md +0 -273
- d3blocks-2.2.1/d3blocks/skills_install.py +0 -263
- d3blocks-2.2.1/d3blocks/tests/test_flowgraph.py +0 -331
- d3blocks-2.2.1/d3blocks.egg-info/entry_points.txt +0 -2
- {d3blocks-2.2.1 → d3blocks-2.3.0}/LICENSE +0 -0
- {d3blocks-2.2.1/d3blocks/chord → d3blocks-2.3.0/d3blocks/_shared/d3js}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/_shared}/d3js/d3.v7.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/Chord.py +0 -0
- {d3blocks-2.2.1/d3blocks/chord/d3js → d3blocks-2.3.0/d3blocks/chord}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/circlepacking → d3blocks-2.3.0/d3blocks/chord/d3js}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/d3js/chord.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/d3js/chord.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/d3js/chord.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/Circlepacking.py +0 -0
- {d3blocks-2.2.1/d3blocks/circlepacking/d3js → d3blocks-2.3.0/d3blocks/circlepacking}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/elasticgraph → d3blocks-2.3.0/d3blocks/circlepacking}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/circlepacking.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/circlepacking.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/d3.v6.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/lodash.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/.gitkeep +0 -0
- {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/elasticgraph}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/d3.v2.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/elasticgraph.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/elasticgraph.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/elasticgraph.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/elasticgraph.py +0 -0
- {d3blocks-2.2.1/d3blocks/heatmap → d3blocks-2.3.0/d3blocks/erd/d3js}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/erd}/d3js/dagre.LICENSE +0 -0
- {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/erd}/d3js/dagre.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/examples_docs.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/heatmap → d3blocks-2.3.0/d3blocks/flowgraph}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/elk.LICENSE.md +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/elk.bundled.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/Heatmap.py +0 -0
- {d3blocks-2.2.1/d3blocks/imageslider → d3blocks-2.3.0/d3blocks/heatmap}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/imageslider → d3blocks-2.3.0/d3blocks/heatmap}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/d3.scale.chromatic.v1.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/d3.v4.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/heatmap.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/heatmap.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/matrix.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/Imageslider.py +0 -0
- {d3blocks-2.2.1/d3blocks/maps → d3blocks-2.3.0/d3blocks/imageslider}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/.keep +0 -0
- {d3blocks-2.2.1/d3blocks/maps → d3blocks-2.3.0/d3blocks/imageslider}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/imageslider.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/imageslider.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/imageslider.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/jquery-2.1.1.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/modernizr.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/reset.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/logo.txt +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/Maps.py +0 -0
- {d3blocks-2.2.1/d3blocks/matrix → d3blocks-2.3.0/d3blocks/maps}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/matrix → d3blocks-2.3.0/d3blocks/maps}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/d3.v6.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/geojson.zip +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/maps.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/maps.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/world.geojson +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/Matrix.py +0 -0
- {d3blocks-2.2.1/d3blocks/movingbubbles → d3blocks-2.3.0/d3blocks/matrix}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/movingbubbles → d3blocks-2.3.0/d3blocks/matrix}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/d3js/d3.scale.chromatic.v1.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/d3js/d3.v4.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/d3js/matrix.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/Movingbubbles.py +0 -0
- {d3blocks-2.2.1/d3blocks/particles → d3blocks-2.3.0/d3blocks/movingbubbles}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/particles → d3blocks-2.3.0/d3blocks/movingbubbles}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/d3js/d3-3-5-5.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/d3js/movingbubbles.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/d3js/movingbubbles.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/Particles.py +0 -0
- {d3blocks-2.2.1/d3blocks/sankey → d3blocks-2.3.0/d3blocks/particles}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/sankey → d3blocks-2.3.0/d3blocks/particles}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/d3js/d3-scale-chromatic.v1.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/d3js/d3.v4.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/d3js/particles.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/Radialgraph.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/__init__ .py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/d3.radialgraph.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/d3.v7.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/radialgraph.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/radialgraph.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/Sankey.py +0 -0
- {d3blocks-2.2.1/d3blocks/scatter → d3blocks-2.3.0/d3blocks/sankey}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/scatter → d3blocks-2.3.0/d3blocks/sankey}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/d3js/sankey.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/d3js/sankey.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/d3js/sankey.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/Scatter.py +0 -0
- {d3blocks-2.2.1/d3blocks/tests → d3blocks-2.3.0/d3blocks/scatter}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/timeseries → d3blocks-2.3.0/d3blocks/scatter/d3js}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/associations.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/d3.v4.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/scatter.css +0 -0
- {d3blocks-2.2.1/d3blocks/timeseries → d3blocks-2.3.0/d3blocks/sequence}/d3js/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/tree → d3blocks-2.3.0/d3blocks/tests}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_all_examples.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_chord_margins.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_d3blocks.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_medium_charts.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_movingbubbles_timedelta.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_radialgraph.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/Timeseries.py +0 -0
- {d3blocks-2.2.1/d3blocks/tree/d3js → d3blocks-2.3.0/d3blocks/timeseries}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/treemap → d3blocks-2.3.0/d3blocks/timeseries/d3js}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/d3.v3.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/timeseries.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/timeseries.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/timeseries.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/Tree.py +0 -0
- {d3blocks-2.2.1/d3blocks/treemap/d3js → d3blocks-2.3.0/d3blocks/tree}/__init__.py +0 -0
- {d3blocks-2.2.1/d3blocks/violin → d3blocks-2.3.0/d3blocks/tree/d3js}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/collapsibleTree.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/d3.tree.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/d3.v4.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/tree.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/tree.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/Treemap.py +0 -0
- {d3blocks-2.2.1/d3blocks/violin/d3js → d3blocks-2.3.0/d3blocks/treemap}/__init__.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/d3js/d3.v4.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/d3js/treemap.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/d3js/treemap.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/utils.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/Violin.py +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/d3-scale-chromatic.v1.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/d3.v4.min.js +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/violin.css +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/violin.html.j2 +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/dependency_links.txt +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/requires.txt +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/top_level.txt +0 -0
- {d3blocks-2.2.1 → d3blocks-2.3.0}/setup.cfg +0 -0
|
@@ -15,5 +15,7 @@ include d3blocks/maps/d3js/*
|
|
|
15
15
|
include d3blocks/elasticgraph/d3js/*
|
|
16
16
|
include d3blocks/radialgraph/d3js/*
|
|
17
17
|
include d3blocks/flowgraph/d3js/*
|
|
18
|
+
include d3blocks/sequence/d3js/*
|
|
19
|
+
include d3blocks/erd/d3js/*
|
|
20
|
+
include d3blocks/_shared/d3js/*
|
|
18
21
|
include d3blocks/*
|
|
19
|
-
recursive-include d3blocks/skills *
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: d3blocks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Python package d3blocks.
|
|
5
5
|
Author-email: Erdogan Taskesen <erdogant@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
7
|
-
Project-URL: Homepage, https://
|
|
8
|
-
Project-URL: Download, https://github.com/
|
|
7
|
+
Project-URL: Homepage, https://d3blocks.github.io/d3blocks
|
|
8
|
+
Project-URL: Download, https://github.com/d3blocks/d3blocks/archive/{version}.tar.gz
|
|
9
9
|
Keywords: Python,visualizations,interactive,javascript,d3js,d3blocks,network,graph,data visualization,web,extendable,extension,plugin,modular,customizable
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
@@ -44,8 +44,9 @@ Dynamic: license-file
|
|
|
44
44
|

|
|
45
45
|
[](https://github.com/d3blocks/d3blocks/blob/main/notebooks/d3blocks.ipynb)
|
|
46
46
|
[](https://d3blocks.github.io/d3blocks/Documentation.html#)
|
|
47
|
+
[](https://skills.sh/d3blocks/d3blocks)
|
|
47
48
|
|
|
48
|
-
###
|
|
49
|
+
###
|
|
49
50
|
|
|
50
51
|
<div>
|
|
51
52
|
|
|
@@ -97,6 +98,8 @@ The [documentation pages](https://d3blocks.github.io/d3blocks/) contain detailed
|
|
|
97
98
|
| 15 | [Maps](https://d3blocks.github.io/d3blocks/Maps.html) | ``` d3.maps() ``` | [D3Blocks](https://medium.com/data-science-collective/d3blocks-the-sweet-spot-for-data-scientists-interactive-charts-without-deployment-friction-c7f576025510) |
|
|
98
99
|
| 16 | [Circlepacking](https://d3blocks.github.io/d3blocks/Circlepacking.html) | ``` d3.circlepacking() ``` | [D3Blocks](https://medium.com/data-science-collective/d3blocks-the-sweet-spot-for-data-scientists-interactive-charts-without-deployment-friction-c7f576025510) |
|
|
99
100
|
| 17 | [Flowgraph](https://d3blocks.github.io/d3blocks/Flowgraph.html) | ``` d3.flowgraph() ``` | [Dagre](https://github.com/dagrejs/dagre) |
|
|
101
|
+
| 18 | [Sequence](https://d3blocks.github.io/d3blocks/Sequence.html) | ``` d3.sequence() ``` | [Mermaid sequenceDiagram](https://mermaid.js.org/syntax/sequenceDiagram.html) |
|
|
102
|
+
| 19 | [ERD](https://d3blocks.github.io/d3blocks/ERD.html) | ``` d3.erd() ``` | [Mermaid erDiagram](https://mermaid.js.org/syntax/entityRelationshipDiagram.html) |
|
|
100
103
|
|
|
101
104
|
-------------------------------------------------------------------------
|
|
102
105
|
|
|
@@ -106,12 +109,31 @@ pip install d3blocks # Normal installation
|
|
|
106
109
|
pip install -U d3blocks # Force update
|
|
107
110
|
```
|
|
108
111
|
|
|
109
|
-
####
|
|
112
|
+
#### Clone
|
|
110
113
|
```bash
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
# Clone only the latest branch to avoid downloading large history.
|
|
115
|
+
git config --global clone.depth 1
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
#### Installation (from GitHub source)
|
|
121
|
+
```bash
|
|
122
|
+
pip install git+https://github.com/d3blocks/d3blocks.git
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
##### Install skill:
|
|
126
|
+
```bash
|
|
127
|
+
# for visualizations
|
|
128
|
+
npx skills add d3blocks/d3blocks --skill d3blocks
|
|
129
|
+
|
|
130
|
+
# for chart builder
|
|
131
|
+
npx skills add d3blocks/d3blocks --skill d3blocks-chart-builder
|
|
132
|
+
|
|
133
|
+
# for docs diagrams (convert a repo's Mermaid diagrams)
|
|
134
|
+
npx skills add d3blocks/d3blocks --skill d3blocks-docs-diagrams
|
|
135
|
+
```
|
|
136
|
+
|
|
115
137
|
|
|
116
138
|
#### Import d3blocks package
|
|
117
139
|
```python
|
|
@@ -124,16 +146,16 @@ d3 = D3Blocks()
|
|
|
124
146
|
---
|
|
125
147
|
|
|
126
148
|
### D3Blocks💙Streamlit
|
|
127
|
-
Every chart from D3Block can easily be incorporated into a Streamlit Dashboard. Because every D3Blocks chart is just HTML and JavaScript, you can generate the HTML in memory and embed it
|
|
149
|
+
Every chart from D3Block can easily be incorporated into a Streamlit Dashboard. Because every D3Blocks chart is just HTML and JavaScript, you can generate the HTML in memory and embed it
|
|
128
150
|
A few practical rules of thumb: prefer `st.iframe` over the older `components.html` for full interactive charts, always pass an explicit pixel height, and set `show_side_panel=False` when Streamlit already owns the controls so the UI doesn't duplicate. `color_background='streamlit'` picks backgrounds that match a typical Streamlit light or dark page.
|
|
129
151
|
|
|
130
|
-
* [Minimal examples for each chart can be found in the Streamlit directory in the repo](https://github.com/d3blocks/d3blocks/tree/main/streamlit).
|
|
152
|
+
* [Minimal examples for each chart can be found in the Streamlit directory in the repo](https://github.com/d3blocks/d3blocks/tree/main/streamlit).
|
|
131
153
|
* [More Streamlit documentation can be found here.](https://d3blocks.github.io/d3blocks/Streamlit.html)
|
|
132
154
|
|
|
133
155
|
---
|
|
134
156
|
|
|
135
157
|
### Ethical Ads
|
|
136
|
-
When HTMLs are generated, it automatically includes ethical ads as a small way to support development.
|
|
158
|
+
When HTMLs are generated, it automatically includes ethical ads as a small way to support development.
|
|
137
159
|
I maintain this entire project thoughtfully and in my free time. Ads can be disabled via an input parameter, and you can also support the project with a coffee donation instead.
|
|
138
160
|
|
|
139
161
|
### Star history
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|

|
|
14
14
|
[](https://github.com/d3blocks/d3blocks/blob/main/notebooks/d3blocks.ipynb)
|
|
15
15
|
[](https://d3blocks.github.io/d3blocks/Documentation.html#)
|
|
16
|
+
[](https://skills.sh/d3blocks/d3blocks)
|
|
16
17
|
|
|
17
|
-
###
|
|
18
|
+
###
|
|
18
19
|
|
|
19
20
|
<div>
|
|
20
21
|
|
|
@@ -66,6 +67,8 @@ The [documentation pages](https://d3blocks.github.io/d3blocks/) contain detailed
|
|
|
66
67
|
| 15 | [Maps](https://d3blocks.github.io/d3blocks/Maps.html) | ``` d3.maps() ``` | [D3Blocks](https://medium.com/data-science-collective/d3blocks-the-sweet-spot-for-data-scientists-interactive-charts-without-deployment-friction-c7f576025510) |
|
|
67
68
|
| 16 | [Circlepacking](https://d3blocks.github.io/d3blocks/Circlepacking.html) | ``` d3.circlepacking() ``` | [D3Blocks](https://medium.com/data-science-collective/d3blocks-the-sweet-spot-for-data-scientists-interactive-charts-without-deployment-friction-c7f576025510) |
|
|
68
69
|
| 17 | [Flowgraph](https://d3blocks.github.io/d3blocks/Flowgraph.html) | ``` d3.flowgraph() ``` | [Dagre](https://github.com/dagrejs/dagre) |
|
|
70
|
+
| 18 | [Sequence](https://d3blocks.github.io/d3blocks/Sequence.html) | ``` d3.sequence() ``` | [Mermaid sequenceDiagram](https://mermaid.js.org/syntax/sequenceDiagram.html) |
|
|
71
|
+
| 19 | [ERD](https://d3blocks.github.io/d3blocks/ERD.html) | ``` d3.erd() ``` | [Mermaid erDiagram](https://mermaid.js.org/syntax/entityRelationshipDiagram.html) |
|
|
69
72
|
|
|
70
73
|
-------------------------------------------------------------------------
|
|
71
74
|
|
|
@@ -75,12 +78,31 @@ pip install d3blocks # Normal installation
|
|
|
75
78
|
pip install -U d3blocks # Force update
|
|
76
79
|
```
|
|
77
80
|
|
|
78
|
-
####
|
|
81
|
+
#### Clone
|
|
79
82
|
```bash
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
# Clone only the latest branch to avoid downloading large history.
|
|
84
|
+
git config --global clone.depth 1
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
#### Installation (from GitHub source)
|
|
90
|
+
```bash
|
|
91
|
+
pip install git+https://github.com/d3blocks/d3blocks.git
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
##### Install skill:
|
|
95
|
+
```bash
|
|
96
|
+
# for visualizations
|
|
97
|
+
npx skills add d3blocks/d3blocks --skill d3blocks
|
|
98
|
+
|
|
99
|
+
# for chart builder
|
|
100
|
+
npx skills add d3blocks/d3blocks --skill d3blocks-chart-builder
|
|
101
|
+
|
|
102
|
+
# for docs diagrams (convert a repo's Mermaid diagrams)
|
|
103
|
+
npx skills add d3blocks/d3blocks --skill d3blocks-docs-diagrams
|
|
104
|
+
```
|
|
105
|
+
|
|
84
106
|
|
|
85
107
|
#### Import d3blocks package
|
|
86
108
|
```python
|
|
@@ -93,16 +115,16 @@ d3 = D3Blocks()
|
|
|
93
115
|
---
|
|
94
116
|
|
|
95
117
|
### D3Blocks💙Streamlit
|
|
96
|
-
Every chart from D3Block can easily be incorporated into a Streamlit Dashboard. Because every D3Blocks chart is just HTML and JavaScript, you can generate the HTML in memory and embed it
|
|
118
|
+
Every chart from D3Block can easily be incorporated into a Streamlit Dashboard. Because every D3Blocks chart is just HTML and JavaScript, you can generate the HTML in memory and embed it
|
|
97
119
|
A few practical rules of thumb: prefer `st.iframe` over the older `components.html` for full interactive charts, always pass an explicit pixel height, and set `show_side_panel=False` when Streamlit already owns the controls so the UI doesn't duplicate. `color_background='streamlit'` picks backgrounds that match a typical Streamlit light or dark page.
|
|
98
120
|
|
|
99
|
-
* [Minimal examples for each chart can be found in the Streamlit directory in the repo](https://github.com/d3blocks/d3blocks/tree/main/streamlit).
|
|
121
|
+
* [Minimal examples for each chart can be found in the Streamlit directory in the repo](https://github.com/d3blocks/d3blocks/tree/main/streamlit).
|
|
100
122
|
* [More Streamlit documentation can be found here.](https://d3blocks.github.io/d3blocks/Streamlit.html)
|
|
101
123
|
|
|
102
124
|
---
|
|
103
125
|
|
|
104
126
|
### Ethical Ads
|
|
105
|
-
When HTMLs are generated, it automatically includes ethical ads as a small way to support development.
|
|
127
|
+
When HTMLs are generated, it automatically includes ethical ads as a small way to support development.
|
|
106
128
|
I maintain this entire project thoughtfully and in my free time. Ads can be disabled via an input parameter, and you can also support the project with a coffee donation instead.
|
|
107
129
|
|
|
108
130
|
### Star history
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Code shared by the flowgraph, sequence and erd blocks.
|
|
2
|
+
|
|
3
|
+
* template_env(): a Jinja environment over a block's own d3js/ folder and
|
|
4
|
+
d3blocks/_shared/d3js/ (walkthrough.js/.css, theme.js and the d3.v7.min.js
|
|
5
|
+
used by sequence and erd), so a template can {% include %} both and the
|
|
6
|
+
output stays self-contained.
|
|
7
|
+
* notebook_iframe(): the Jupyter iframe wrapper.
|
|
8
|
+
* dark_mode_flag(): validates dark_mode (True, False or 'auto').
|
|
9
|
+
* normalize_steps(): validates the captioned walkthrough steps.
|
|
10
|
+
|
|
11
|
+
Library : d3blocks
|
|
12
|
+
Github : https://github.com/d3blocks/d3blocks
|
|
13
|
+
License : GPL3
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from html import escape as html_escape
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader
|
|
21
|
+
|
|
22
|
+
DARK_MODES = (True, False, "auto")
|
|
23
|
+
STEP_KEYS = ("title", "caption", "highlight")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def template_env(package_name, block_file):
|
|
27
|
+
"""Jinja environment over the block's d3js/ folder, then _shared/d3js/.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
package_name : str
|
|
32
|
+
__name__ of the block module, e.g. 'd3blocks.sequence.Sequence'.
|
|
33
|
+
block_file : str
|
|
34
|
+
__file__ of the block module (used when the package loader is unavailable).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def loader(package, folder):
|
|
38
|
+
try:
|
|
39
|
+
return PackageLoader(package_name=package, package_path="d3js")
|
|
40
|
+
except Exception:
|
|
41
|
+
return FileSystemLoader(str(Path(folder).resolve().parent / "d3js"))
|
|
42
|
+
|
|
43
|
+
return Environment(
|
|
44
|
+
loader=ChoiceLoader(
|
|
45
|
+
[loader(package_name, block_file), loader(__name__, __file__)]
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def notebook_iframe(html, config, block):
|
|
51
|
+
"""Wrap a chart page in an iframe for Jupyter.
|
|
52
|
+
|
|
53
|
+
The page fills its window (fixed positioning, own body background) and uses
|
|
54
|
+
fixed element ids, so it is isolated in an iframe instead of being injected
|
|
55
|
+
into the notebook page. Size: figsize, else full width x 650 px.
|
|
56
|
+
"""
|
|
57
|
+
width, height = (list(config.get("figsize") or [None, None]) + [None, None])[:2]
|
|
58
|
+
width = "%dpx" % width if width else "100%"
|
|
59
|
+
height = "%dpx" % height if height else "650px"
|
|
60
|
+
# Wrapped in a div: IPython warns about HTML that starts with '<iframe'.
|
|
61
|
+
return (
|
|
62
|
+
'<div class="d3blocks-%s"><iframe srcdoc="%s" title="%s" '
|
|
63
|
+
'style="width:%s;height:%s;border:0;border-radius:6px" '
|
|
64
|
+
'sandbox="allow-scripts allow-downloads allow-popups"></iframe></div>'
|
|
65
|
+
% (
|
|
66
|
+
block,
|
|
67
|
+
html_escape(html, quote=True),
|
|
68
|
+
html_escape(str(config.get("title", block))),
|
|
69
|
+
width,
|
|
70
|
+
height,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def dark_mode_flag(dark_mode):
|
|
76
|
+
"""Template flag for dark_mode: 'true', 'false' or 'auto'.
|
|
77
|
+
|
|
78
|
+
* True: dark theme.
|
|
79
|
+
* False: light theme.
|
|
80
|
+
* 'auto': follow the operating system theme until the viewer uses the theme toggle.
|
|
81
|
+
"""
|
|
82
|
+
if isinstance(dark_mode, (bool, np.bool_)):
|
|
83
|
+
return "true" if dark_mode else "false"
|
|
84
|
+
if isinstance(dark_mode, str) and dark_mode.lower() == "auto":
|
|
85
|
+
return "auto"
|
|
86
|
+
raise ValueError("dark_mode must be True, False or 'auto'; got %r." % (dark_mode,))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def check_dark_mode(dark_mode):
|
|
90
|
+
"""dark_mode as True, False or 'auto'; raises ValueError for anything else."""
|
|
91
|
+
flag = dark_mode_flag(dark_mode)
|
|
92
|
+
return "auto" if flag == "auto" else flag == "true"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def normalize_steps(steps, refs, block):
|
|
96
|
+
"""Validate walkthrough steps and return them as JSON-ready dicts.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
steps : list of dict or None
|
|
101
|
+
[{'title': str, 'caption': str, 'highlight': {key: [ref, ...]}}]
|
|
102
|
+
refs : dict
|
|
103
|
+
{highlight key: check(ref, step_index) -> resolved ref}. Only these
|
|
104
|
+
highlight keys are accepted; a check raises ValueError for an unknown ref.
|
|
105
|
+
block : str
|
|
106
|
+
Block name, used in error messages.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
list of dict or None
|
|
111
|
+
[{'title', 'caption', 'highlight'}], or None when steps is None.
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
if steps is None:
|
|
115
|
+
return None
|
|
116
|
+
if isinstance(steps, (str, dict)) or not hasattr(steps, "__iter__"):
|
|
117
|
+
raise ValueError(
|
|
118
|
+
"steps must be a list of dicts or None; got %s." % type(steps).__name__
|
|
119
|
+
)
|
|
120
|
+
out = []
|
|
121
|
+
for i, step in enumerate(steps):
|
|
122
|
+
if not isinstance(step, dict):
|
|
123
|
+
raise ValueError(
|
|
124
|
+
"steps[%d]: expected a dict with %s; got %s."
|
|
125
|
+
% (i, STEP_KEYS, type(step).__name__)
|
|
126
|
+
)
|
|
127
|
+
unknown = sorted(set(step) - set(STEP_KEYS))
|
|
128
|
+
if unknown:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"steps[%d]: unknown key(s) %s; use %s." % (i, unknown, STEP_KEYS)
|
|
131
|
+
)
|
|
132
|
+
highlight = step.get("highlight") or {}
|
|
133
|
+
if not isinstance(highlight, dict):
|
|
134
|
+
raise ValueError(
|
|
135
|
+
"steps[%d]: 'highlight' must be a dict; got %s."
|
|
136
|
+
% (i, type(highlight).__name__)
|
|
137
|
+
)
|
|
138
|
+
unsupported = sorted(set(highlight) - set(refs))
|
|
139
|
+
if unsupported:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
"steps[%d]: highlight key(s) %s are not supported by %s; use %s."
|
|
142
|
+
% (i, unsupported, block, tuple(refs))
|
|
143
|
+
)
|
|
144
|
+
resolved = {}
|
|
145
|
+
for key, values in highlight.items():
|
|
146
|
+
if values is None:
|
|
147
|
+
continue
|
|
148
|
+
if isinstance(values, (str, int, np.integer)):
|
|
149
|
+
values = [values]
|
|
150
|
+
resolved[key] = [refs[key](v, i) for v in values]
|
|
151
|
+
out.append(
|
|
152
|
+
{
|
|
153
|
+
"title": str(step.get("title") or "Step %d" % (i + 1)),
|
|
154
|
+
"caption": str(step.get("caption") or ""),
|
|
155
|
+
"highlight": resolved,
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
return out
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def ref_in(names, what):
|
|
162
|
+
"""Check for a reference that must be one of `names`."""
|
|
163
|
+
names = set(names)
|
|
164
|
+
|
|
165
|
+
def check(ref, i):
|
|
166
|
+
key = str(ref)
|
|
167
|
+
if key not in names:
|
|
168
|
+
raise ValueError("steps[%d]: unknown %s %r." % (i, what, ref))
|
|
169
|
+
return key
|
|
170
|
+
|
|
171
|
+
return check
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def pair_in(pairs, what, undirected=False):
|
|
175
|
+
"""Check for a (source, target) reference that must be in `pairs`."""
|
|
176
|
+
pairs = {(str(s), str(t)) for s, t in pairs}
|
|
177
|
+
|
|
178
|
+
def check(ref, i):
|
|
179
|
+
if isinstance(ref, str) or not hasattr(ref, "__len__") or len(ref) != 2:
|
|
180
|
+
raise ValueError(
|
|
181
|
+
"steps[%d]: %s must be (source, target) pairs; got %r." % (i, what, ref)
|
|
182
|
+
)
|
|
183
|
+
s, t = str(ref[0]), str(ref[1])
|
|
184
|
+
if (s, t) in pairs:
|
|
185
|
+
return [s, t]
|
|
186
|
+
if undirected and (t, s) in pairs:
|
|
187
|
+
return [t, s]
|
|
188
|
+
raise ValueError("steps[%d]: unknown %s %r." % (i, what, tuple(ref)))
|
|
189
|
+
|
|
190
|
+
return check
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def index_in(count, what):
|
|
194
|
+
"""Check for an integer index in range(count)."""
|
|
195
|
+
|
|
196
|
+
def check(ref, i):
|
|
197
|
+
if (
|
|
198
|
+
isinstance(ref, (bool, np.bool_))
|
|
199
|
+
or not isinstance(ref, (int, np.integer))
|
|
200
|
+
or not 0 <= ref < count
|
|
201
|
+
):
|
|
202
|
+
raise ValueError(
|
|
203
|
+
"steps[%d]: unknown %s %r; use an index from 0 to %d."
|
|
204
|
+
% (i, what, ref, count - 1)
|
|
205
|
+
)
|
|
206
|
+
return int(ref)
|
|
207
|
+
|
|
208
|
+
return check
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* theme.js - dark/light theme for d3blocks diagrams (flowgraph, sequence, erd).
|
|
3
|
+
* Included right after <body data-dark-mode="true|false|auto">, so the first
|
|
4
|
+
* paint already has the right theme. 'auto' follows the operating system
|
|
5
|
+
* (prefers-color-scheme) until the viewer presses the theme toggle; a
|
|
6
|
+
* ?theme=light|dark URL parameter overrides 'auto', as in archify.
|
|
7
|
+
*/
|
|
8
|
+
window.D3BlocksTheme = (function () {
|
|
9
|
+
'use strict';
|
|
10
|
+
var body = document.body;
|
|
11
|
+
var mode = body.getAttribute('data-dark-mode') || 'true';
|
|
12
|
+
var followOs = mode === 'auto';
|
|
13
|
+
var media = null;
|
|
14
|
+
|
|
15
|
+
function isLight() { return body.classList.contains('light'); }
|
|
16
|
+
|
|
17
|
+
function syncButton(btn) {
|
|
18
|
+
if (!btn) return;
|
|
19
|
+
var light = isLight();
|
|
20
|
+
btn.textContent = light ? '☀' : '🌙';
|
|
21
|
+
btn.title = light ? 'Theme: Light' : 'Theme: Dark';
|
|
22
|
+
btn.setAttribute('aria-pressed', light ? 'true' : 'false');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var buttons = [];
|
|
26
|
+
function set(light) {
|
|
27
|
+
body.classList.toggle('light', !!light);
|
|
28
|
+
buttons.forEach(syncButton);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (followOs) {
|
|
32
|
+
var param = null;
|
|
33
|
+
try { param = new URLSearchParams(window.location.search).get('theme'); } catch (e) { /* no URL */ }
|
|
34
|
+
if (param === 'light' || param === 'dark') {
|
|
35
|
+
followOs = false;
|
|
36
|
+
set(param === 'light');
|
|
37
|
+
} else if (window.matchMedia) {
|
|
38
|
+
// No OS preference (or no matchMedia) falls back to dark.
|
|
39
|
+
media = window.matchMedia('(prefers-color-scheme: light)');
|
|
40
|
+
set(media.matches);
|
|
41
|
+
var onChange = function (e) { if (followOs) set(e.matches); };
|
|
42
|
+
if (media.addEventListener) media.addEventListener('change', onChange);
|
|
43
|
+
else if (media.addListener) media.addListener(onChange);
|
|
44
|
+
} else {
|
|
45
|
+
set(false);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
isLight: isLight,
|
|
51
|
+
// Wire a toggle button; after the first press the toggle wins over the OS theme.
|
|
52
|
+
bind: function (id) {
|
|
53
|
+
var btn = document.getElementById(id);
|
|
54
|
+
if (!btn) return;
|
|
55
|
+
buttons.push(btn);
|
|
56
|
+
syncButton(btn);
|
|
57
|
+
btn.addEventListener('click', function () {
|
|
58
|
+
followOs = false;
|
|
59
|
+
set(!isLight());
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
})();
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* Captioned walkthrough (walkthrough.js). Colours come from the block's theme
|
|
2
|
+
variables, so the bar follows the dark/light theme. */
|
|
3
|
+
.d3b-walk {
|
|
4
|
+
position: fixed;
|
|
5
|
+
left: 50%;
|
|
6
|
+
bottom: 16px;
|
|
7
|
+
transform: translateX(-50%);
|
|
8
|
+
width: min(760px, calc(100vw - 32px));
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
z-index: 1300;
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-columns: 1fr auto;
|
|
13
|
+
grid-template-areas: "text controls" "rail rail";
|
|
14
|
+
gap: 8px 14px;
|
|
15
|
+
align-items: center;
|
|
16
|
+
padding: 12px 16px;
|
|
17
|
+
border-radius: 10px;
|
|
18
|
+
background: var(--panel-bg);
|
|
19
|
+
color: var(--text);
|
|
20
|
+
border: 1px solid var(--panel-border);
|
|
21
|
+
box-shadow: var(--panel-shadow);
|
|
22
|
+
font-family: "Helvetica", Arial, sans-serif;
|
|
23
|
+
transition: var(--transition);
|
|
24
|
+
}
|
|
25
|
+
.d3b-walk[hidden] { display: none; }
|
|
26
|
+
.d3b-walk-text { grid-area: text; min-width: 0; }
|
|
27
|
+
.d3b-walk-count {
|
|
28
|
+
font-size: 11px;
|
|
29
|
+
color: var(--text-muted);
|
|
30
|
+
text-transform: uppercase;
|
|
31
|
+
letter-spacing: 0.06em;
|
|
32
|
+
}
|
|
33
|
+
.d3b-walk-title { font-size: 15px; font-weight: bold; margin: 2px 0; }
|
|
34
|
+
.d3b-walk-caption { font-size: 13px; line-height: 1.45; color: var(--text); opacity: 0.9; }
|
|
35
|
+
.d3b-walk-controls { grid-area: controls; display: flex; gap: 6px; }
|
|
36
|
+
.d3b-walk-controls .button-switch { margin: 0; padding: 5px 10px; font-size: 13px; white-space: nowrap; }
|
|
37
|
+
.d3b-walk-rail { grid-area: rail; display: flex; flex-wrap: wrap; gap: 5px; }
|
|
38
|
+
.d3b-walk-stop {
|
|
39
|
+
width: 18px;
|
|
40
|
+
height: 6px;
|
|
41
|
+
padding: 0;
|
|
42
|
+
border: 0;
|
|
43
|
+
border-radius: 3px;
|
|
44
|
+
background: var(--btn-border);
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
transition: background 0.15s;
|
|
47
|
+
}
|
|
48
|
+
.d3b-walk-stop.done { background: var(--text-muted); }
|
|
49
|
+
.d3b-walk-stop.active { background: var(--accent); }
|
|
50
|
+
.d3b-walk-stop:focus-visible, .d3b-walk-controls .button-switch:focus-visible {
|
|
51
|
+
outline: 2px solid var(--accent);
|
|
52
|
+
outline-offset: 2px;
|
|
53
|
+
}
|
|
54
|
+
@media (max-width: 640px) {
|
|
55
|
+
.d3b-walk { grid-template-columns: 1fr; grid-template-areas: "text" "rail" "controls"; }
|
|
56
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* walkthrough.js - captioned walkthrough shared by flowgraph, sequence and erd.
|
|
3
|
+
* A caption bar at the bottom of the chart shows the step title, caption and
|
|
4
|
+
* "Step i of n", a rail of step stops and Prev / Play-Pause / Next. The block
|
|
5
|
+
* highlights the step's elements in onStep(step) and restores in onClose().
|
|
6
|
+
* Keyboard while open: <- and -> move, Space plays or pauses, Esc closes.
|
|
7
|
+
* Playback is reader-started and bounded: it stops on the last step.
|
|
8
|
+
*/
|
|
9
|
+
window.D3BlocksWalkthrough = function (opts) {
|
|
10
|
+
'use strict';
|
|
11
|
+
var steps = opts.steps || [];
|
|
12
|
+
var PLAY_MS = 3000;
|
|
13
|
+
var index = -1, timer = null, isOpen = false;
|
|
14
|
+
|
|
15
|
+
function el(tag, cls, text) {
|
|
16
|
+
var e = document.createElement(tag);
|
|
17
|
+
if (cls) e.className = cls;
|
|
18
|
+
if (text !== undefined) e.textContent = text;
|
|
19
|
+
return e;
|
|
20
|
+
}
|
|
21
|
+
function button(cls, text, title) {
|
|
22
|
+
var b = el('button', 'button-switch ' + cls, text);
|
|
23
|
+
b.type = 'button';
|
|
24
|
+
b.title = title;
|
|
25
|
+
b.setAttribute('aria-label', title);
|
|
26
|
+
return b;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var bar = el('div', 'd3b-walk');
|
|
30
|
+
bar.setAttribute('role', 'region');
|
|
31
|
+
bar.setAttribute('aria-label', 'Walkthrough');
|
|
32
|
+
bar.hidden = true;
|
|
33
|
+
var text = el('div', 'd3b-walk-text');
|
|
34
|
+
var count = el('div', 'd3b-walk-count');
|
|
35
|
+
var title = el('div', 'd3b-walk-title');
|
|
36
|
+
var caption = el('div', 'd3b-walk-caption');
|
|
37
|
+
caption.setAttribute('aria-live', 'polite');
|
|
38
|
+
text.appendChild(count); text.appendChild(title); text.appendChild(caption);
|
|
39
|
+
var rail = el('div', 'd3b-walk-rail');
|
|
40
|
+
rail.setAttribute('aria-label', 'Steps');
|
|
41
|
+
var stops = steps.map(function (s, i) {
|
|
42
|
+
var b = el('button', 'd3b-walk-stop');
|
|
43
|
+
b.type = 'button';
|
|
44
|
+
b.title = (i + 1) + '. ' + s.title;
|
|
45
|
+
b.setAttribute('aria-label', 'Step ' + (i + 1) + ': ' + s.title);
|
|
46
|
+
b.addEventListener('click', function () { pause(); go(i); });
|
|
47
|
+
rail.appendChild(b);
|
|
48
|
+
return b;
|
|
49
|
+
});
|
|
50
|
+
if (steps.length > 40) rail.style.display = 'none';
|
|
51
|
+
var controls = el('div', 'd3b-walk-controls');
|
|
52
|
+
var prevBtn = button('d3b-walk-prev', '‹ Prev', 'Previous step');
|
|
53
|
+
var playBtn = button('d3b-walk-play', '▶ Play', 'Play the walkthrough');
|
|
54
|
+
var nextBtn = button('d3b-walk-next', 'Next ›', 'Next step');
|
|
55
|
+
var closeBtn = button('d3b-walk-close', '✕', 'Close the walkthrough');
|
|
56
|
+
[prevBtn, playBtn, nextBtn, closeBtn].forEach(function (b) { controls.appendChild(b); });
|
|
57
|
+
bar.appendChild(text); bar.appendChild(rail); bar.appendChild(controls);
|
|
58
|
+
document.body.appendChild(bar);
|
|
59
|
+
|
|
60
|
+
prevBtn.addEventListener('click', function () { pause(); go(index - 1); });
|
|
61
|
+
nextBtn.addEventListener('click', function () { pause(); go(index + 1); });
|
|
62
|
+
playBtn.addEventListener('click', function () { if (timer) pause(); else play(); });
|
|
63
|
+
closeBtn.addEventListener('click', close);
|
|
64
|
+
|
|
65
|
+
var triggers = (opts.triggers || []).map(function (id) { return document.getElementById(id); }).filter(Boolean);
|
|
66
|
+
triggers.forEach(function (b) {
|
|
67
|
+
b.addEventListener('click', function () { if (isOpen) close(); else open(); });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
function go(i) {
|
|
71
|
+
if (!steps.length) return;
|
|
72
|
+
index = Math.max(0, Math.min(steps.length - 1, i));
|
|
73
|
+
var s = steps[index];
|
|
74
|
+
count.textContent = 'Step ' + (index + 1) + ' of ' + steps.length;
|
|
75
|
+
title.textContent = s.title;
|
|
76
|
+
caption.textContent = s.caption;
|
|
77
|
+
caption.style.display = s.caption ? '' : 'none';
|
|
78
|
+
stops.forEach(function (b, k) {
|
|
79
|
+
b.classList.toggle('done', k < index);
|
|
80
|
+
b.classList.toggle('active', k === index);
|
|
81
|
+
if (k === index) b.setAttribute('aria-current', 'step'); else b.removeAttribute('aria-current');
|
|
82
|
+
});
|
|
83
|
+
prevBtn.disabled = index === 0;
|
|
84
|
+
nextBtn.disabled = index === steps.length - 1;
|
|
85
|
+
if (opts.onStep) opts.onStep(s, index);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function play() {
|
|
89
|
+
if (index >= steps.length - 1) go(0);
|
|
90
|
+
playBtn.textContent = '❚❚ Pause';
|
|
91
|
+
playBtn.title = 'Pause the walkthrough';
|
|
92
|
+
timer = setInterval(function () {
|
|
93
|
+
if (index >= steps.length - 1) { pause(); return; }
|
|
94
|
+
go(index + 1);
|
|
95
|
+
}, PLAY_MS);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function pause() {
|
|
99
|
+
clearInterval(timer);
|
|
100
|
+
timer = null;
|
|
101
|
+
playBtn.textContent = '▶ Play';
|
|
102
|
+
playBtn.title = 'Play the walkthrough';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function open(i) {
|
|
106
|
+
if (!steps.length) return;
|
|
107
|
+
isOpen = true;
|
|
108
|
+
bar.hidden = false;
|
|
109
|
+
document.body.classList.add('d3b-walking');
|
|
110
|
+
triggers.forEach(function (b) { b.classList.add('active'); });
|
|
111
|
+
if (opts.onOpen) opts.onOpen();
|
|
112
|
+
go(i === undefined ? 0 : i);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function close() {
|
|
116
|
+
if (!isOpen) return;
|
|
117
|
+
pause();
|
|
118
|
+
isOpen = false;
|
|
119
|
+
bar.hidden = true;
|
|
120
|
+
index = -1;
|
|
121
|
+
document.body.classList.remove('d3b-walking');
|
|
122
|
+
triggers.forEach(function (b) { b.classList.remove('active'); });
|
|
123
|
+
if (opts.onClose) opts.onClose();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
document.addEventListener('keydown', function (e) {
|
|
127
|
+
if (!isOpen) return;
|
|
128
|
+
var tag = document.activeElement && document.activeElement.tagName;
|
|
129
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
|
|
130
|
+
if (e.key === 'ArrowRight') { e.preventDefault(); pause(); go(index + 1); }
|
|
131
|
+
else if (e.key === 'ArrowLeft') { e.preventDefault(); pause(); go(index - 1); }
|
|
132
|
+
else if (e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); if (timer) pause(); else play(); }
|
|
133
|
+
else if (e.key === 'Escape') { close(); }
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
open: open,
|
|
138
|
+
close: close,
|
|
139
|
+
go: go,
|
|
140
|
+
isOpen: function () { return isOpen; },
|
|
141
|
+
index: function () { return index; },
|
|
142
|
+
// Height of the caption bar (plus its margin) while open; 0 when closed.
|
|
143
|
+
height: function () { return isOpen ? bar.getBoundingClientRect().height + 16 : 0; }
|
|
144
|
+
};
|
|
145
|
+
};
|