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.
Files changed (176) hide show
  1. {d3blocks-2.2.1 → d3blocks-2.3.0}/MANIFEST.in +3 -1
  2. {d3blocks-2.2.1/d3blocks.egg-info → d3blocks-2.3.0}/PKG-INFO +34 -12
  3. {d3blocks-2.2.1 → d3blocks-2.3.0}/README.md +31 -9
  4. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/__init__.py +1 -1
  5. d3blocks-2.3.0/d3blocks/_shared/__init__.py +208 -0
  6. d3blocks-2.3.0/d3blocks/_shared/d3js/theme.js +63 -0
  7. d3blocks-2.3.0/d3blocks/_shared/d3js/walkthrough.css +56 -0
  8. d3blocks-2.3.0/d3blocks/_shared/d3js/walkthrough.js +145 -0
  9. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/d3blocks.py +409 -11
  10. d3blocks-2.3.0/d3blocks/erd/Erd.py +477 -0
  11. d3blocks-2.3.0/d3blocks/erd/__init__.py +19 -0
  12. d3blocks-2.3.0/d3blocks/erd/d3js/d3.erd.js +739 -0
  13. d3blocks-2.3.0/d3blocks/erd/d3js/erd.css +329 -0
  14. d3blocks-2.3.0/d3blocks/erd/d3js/erd.html.j2 +122 -0
  15. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/examples.py +129 -0
  16. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/Flowgraph.py +524 -118
  17. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/d3.flowgraph.js +224 -48
  18. d3blocks-2.3.0/d3blocks/flowgraph/d3js/d3.v7.min.js +2 -0
  19. d3blocks-2.3.0/d3blocks/flowgraph/d3js/dagre.LICENSE +19 -0
  20. d3blocks-2.3.0/d3blocks/flowgraph/d3js/dagre.min.js +815 -0
  21. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/flowgraph.css +4 -0
  22. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/flowgraph.html.j2 +4 -1
  23. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/scatter.html.j2 +3168 -2948
  24. d3blocks-2.3.0/d3blocks/sequence/Sequence.py +475 -0
  25. d3blocks-2.3.0/d3blocks/sequence/__init__.py +19 -0
  26. d3blocks-2.3.0/d3blocks/sequence/d3js/d3.sequence.js +676 -0
  27. d3blocks-2.3.0/d3blocks/sequence/d3js/sequence.css +332 -0
  28. d3blocks-2.3.0/d3blocks/sequence/d3js/sequence.html.j2 +106 -0
  29. d3blocks-2.3.0/d3blocks/tests/test_erd.py +385 -0
  30. d3blocks-2.3.0/d3blocks/tests/test_flowgraph.py +820 -0
  31. d3blocks-2.3.0/d3blocks/tests/test_sequence.py +338 -0
  32. d3blocks-2.3.0/d3blocks/treemap/d3js/__init__.py +0 -0
  33. d3blocks-2.3.0/d3blocks/violin/__init__.py +0 -0
  34. d3blocks-2.3.0/d3blocks/violin/d3js/__init__.py +0 -0
  35. {d3blocks-2.2.1 → d3blocks-2.3.0/d3blocks.egg-info}/PKG-INFO +34 -12
  36. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/SOURCES.txt +22 -4
  37. {d3blocks-2.2.1 → d3blocks-2.3.0}/pyproject.toml +13 -7
  38. d3blocks-2.2.1/d3blocks/skills/d3blocks/SKILL.md +0 -78
  39. d3blocks-2.2.1/d3blocks/skills/d3blocks/references/scatter.md +0 -273
  40. d3blocks-2.2.1/d3blocks/skills_install.py +0 -263
  41. d3blocks-2.2.1/d3blocks/tests/test_flowgraph.py +0 -331
  42. d3blocks-2.2.1/d3blocks.egg-info/entry_points.txt +0 -2
  43. {d3blocks-2.2.1 → d3blocks-2.3.0}/LICENSE +0 -0
  44. {d3blocks-2.2.1/d3blocks/chord → d3blocks-2.3.0/d3blocks/_shared/d3js}/__init__.py +0 -0
  45. {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/_shared}/d3js/d3.v7.min.js +0 -0
  46. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/Chord.py +0 -0
  47. {d3blocks-2.2.1/d3blocks/chord/d3js → d3blocks-2.3.0/d3blocks/chord}/__init__.py +0 -0
  48. {d3blocks-2.2.1/d3blocks/circlepacking → d3blocks-2.3.0/d3blocks/chord/d3js}/__init__.py +0 -0
  49. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/d3js/chord.css +0 -0
  50. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/d3js/chord.html.j2 +0 -0
  51. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/chord/d3js/chord.js +0 -0
  52. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/Circlepacking.py +0 -0
  53. {d3blocks-2.2.1/d3blocks/circlepacking/d3js → d3blocks-2.3.0/d3blocks/circlepacking}/__init__.py +0 -0
  54. {d3blocks-2.2.1/d3blocks/elasticgraph → d3blocks-2.3.0/d3blocks/circlepacking}/d3js/__init__.py +0 -0
  55. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/circlepacking.css +0 -0
  56. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/circlepacking.html.j2 +0 -0
  57. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/d3.v6.min.js +0 -0
  58. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/circlepacking/d3js/lodash.min.js +0 -0
  59. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/__init__.py +0 -0
  60. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/.gitkeep +0 -0
  61. {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/elasticgraph}/d3js/__init__.py +0 -0
  62. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/d3.v2.js +0 -0
  63. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/elasticgraph.css +0 -0
  64. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/elasticgraph.html.j2 +0 -0
  65. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/d3js/elasticgraph.js +0 -0
  66. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/elasticgraph/elasticgraph.py +0 -0
  67. {d3blocks-2.2.1/d3blocks/heatmap → d3blocks-2.3.0/d3blocks/erd/d3js}/__init__.py +0 -0
  68. {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/erd}/d3js/dagre.LICENSE +0 -0
  69. {d3blocks-2.2.1/d3blocks/flowgraph → d3blocks-2.3.0/d3blocks/erd}/d3js/dagre.min.js +0 -0
  70. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/examples_docs.py +0 -0
  71. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/__init__.py +0 -0
  72. {d3blocks-2.2.1/d3blocks/heatmap → d3blocks-2.3.0/d3blocks/flowgraph}/d3js/__init__.py +0 -0
  73. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/elk.LICENSE.md +0 -0
  74. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/flowgraph/d3js/elk.bundled.js +0 -0
  75. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/Heatmap.py +0 -0
  76. {d3blocks-2.2.1/d3blocks/imageslider → d3blocks-2.3.0/d3blocks/heatmap}/__init__.py +0 -0
  77. {d3blocks-2.2.1/d3blocks/imageslider → d3blocks-2.3.0/d3blocks/heatmap}/d3js/__init__.py +0 -0
  78. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/d3.scale.chromatic.v1.min.js +0 -0
  79. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/d3.v4.js +0 -0
  80. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/heatmap.css +0 -0
  81. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/heatmap.html.j2 +0 -0
  82. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/heatmap/d3js/matrix.html.j2 +0 -0
  83. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/Imageslider.py +0 -0
  84. {d3blocks-2.2.1/d3blocks/maps → d3blocks-2.3.0/d3blocks/imageslider}/__init__.py +0 -0
  85. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/.keep +0 -0
  86. {d3blocks-2.2.1/d3blocks/maps → d3blocks-2.3.0/d3blocks/imageslider}/d3js/__init__.py +0 -0
  87. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/imageslider.css +0 -0
  88. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/imageslider.html.j2 +0 -0
  89. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/imageslider.js +0 -0
  90. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/jquery-2.1.1.js +0 -0
  91. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/modernizr.js +0 -0
  92. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/imageslider/d3js/reset.css +0 -0
  93. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/logo.txt +0 -0
  94. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/Maps.py +0 -0
  95. {d3blocks-2.2.1/d3blocks/matrix → d3blocks-2.3.0/d3blocks/maps}/__init__.py +0 -0
  96. {d3blocks-2.2.1/d3blocks/matrix → d3blocks-2.3.0/d3blocks/maps}/d3js/__init__.py +0 -0
  97. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/d3.v6.min.js +0 -0
  98. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/geojson.zip +0 -0
  99. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/maps.css +0 -0
  100. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/maps.html.j2 +0 -0
  101. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/maps/d3js/world.geojson +0 -0
  102. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/Matrix.py +0 -0
  103. {d3blocks-2.2.1/d3blocks/movingbubbles → d3blocks-2.3.0/d3blocks/matrix}/__init__.py +0 -0
  104. {d3blocks-2.2.1/d3blocks/movingbubbles → d3blocks-2.3.0/d3blocks/matrix}/d3js/__init__.py +0 -0
  105. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/d3js/d3.scale.chromatic.v1.min.js +0 -0
  106. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/d3js/d3.v4.js +0 -0
  107. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/matrix/d3js/matrix.html.j2 +0 -0
  108. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/Movingbubbles.py +0 -0
  109. {d3blocks-2.2.1/d3blocks/particles → d3blocks-2.3.0/d3blocks/movingbubbles}/__init__.py +0 -0
  110. {d3blocks-2.2.1/d3blocks/particles → d3blocks-2.3.0/d3blocks/movingbubbles}/d3js/__init__.py +0 -0
  111. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/d3js/d3-3-5-5.min.js +0 -0
  112. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/d3js/movingbubbles.css +0 -0
  113. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/movingbubbles/d3js/movingbubbles.html.j2 +0 -0
  114. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/Particles.py +0 -0
  115. {d3blocks-2.2.1/d3blocks/sankey → d3blocks-2.3.0/d3blocks/particles}/__init__.py +0 -0
  116. {d3blocks-2.2.1/d3blocks/sankey → d3blocks-2.3.0/d3blocks/particles}/d3js/__init__.py +0 -0
  117. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/d3js/d3-scale-chromatic.v1.min.js +0 -0
  118. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/d3js/d3.v4.min.js +0 -0
  119. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/particles/d3js/particles.html.j2 +0 -0
  120. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/Radialgraph.py +0 -0
  121. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/__init__.py +0 -0
  122. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/__init__ .py +0 -0
  123. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/d3.radialgraph.js +0 -0
  124. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/d3.v7.min.js +0 -0
  125. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/radialgraph.css +0 -0
  126. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/radialgraph/d3js/radialgraph.html.j2 +0 -0
  127. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/Sankey.py +0 -0
  128. {d3blocks-2.2.1/d3blocks/scatter → d3blocks-2.3.0/d3blocks/sankey}/__init__.py +0 -0
  129. {d3blocks-2.2.1/d3blocks/scatter → d3blocks-2.3.0/d3blocks/sankey}/d3js/__init__.py +0 -0
  130. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/d3js/sankey.css +0 -0
  131. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/d3js/sankey.html.j2 +0 -0
  132. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/sankey/d3js/sankey.js +0 -0
  133. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/Scatter.py +0 -0
  134. {d3blocks-2.2.1/d3blocks/tests → d3blocks-2.3.0/d3blocks/scatter}/__init__.py +0 -0
  135. {d3blocks-2.2.1/d3blocks/timeseries → d3blocks-2.3.0/d3blocks/scatter/d3js}/__init__.py +0 -0
  136. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/associations.js +0 -0
  137. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/d3.v4.min.js +0 -0
  138. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/scatter/d3js/scatter.css +0 -0
  139. {d3blocks-2.2.1/d3blocks/timeseries → d3blocks-2.3.0/d3blocks/sequence}/d3js/__init__.py +0 -0
  140. {d3blocks-2.2.1/d3blocks/tree → d3blocks-2.3.0/d3blocks/tests}/__init__.py +0 -0
  141. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_all_examples.py +0 -0
  142. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_chord_margins.py +0 -0
  143. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_d3blocks.py +0 -0
  144. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_medium_charts.py +0 -0
  145. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_movingbubbles_timedelta.py +0 -0
  146. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tests/test_radialgraph.py +0 -0
  147. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/Timeseries.py +0 -0
  148. {d3blocks-2.2.1/d3blocks/tree/d3js → d3blocks-2.3.0/d3blocks/timeseries}/__init__.py +0 -0
  149. {d3blocks-2.2.1/d3blocks/treemap → d3blocks-2.3.0/d3blocks/timeseries/d3js}/__init__.py +0 -0
  150. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/d3.v3.js +0 -0
  151. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/timeseries.css +0 -0
  152. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/timeseries.html.j2 +0 -0
  153. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/timeseries/d3js/timeseries.js +0 -0
  154. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/Tree.py +0 -0
  155. {d3blocks-2.2.1/d3blocks/treemap/d3js → d3blocks-2.3.0/d3blocks/tree}/__init__.py +0 -0
  156. {d3blocks-2.2.1/d3blocks/violin → d3blocks-2.3.0/d3blocks/tree/d3js}/__init__.py +0 -0
  157. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/collapsibleTree.js +0 -0
  158. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/d3.tree.js +0 -0
  159. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/d3.v4.min.js +0 -0
  160. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/tree.css +0 -0
  161. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/tree/d3js/tree.html.j2 +0 -0
  162. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/Treemap.py +0 -0
  163. {d3blocks-2.2.1/d3blocks/violin/d3js → d3blocks-2.3.0/d3blocks/treemap}/__init__.py +0 -0
  164. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/d3js/d3.v4.min.js +0 -0
  165. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/d3js/treemap.css +0 -0
  166. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/treemap/d3js/treemap.html.j2 +0 -0
  167. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/utils.py +0 -0
  168. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/Violin.py +0 -0
  169. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/d3-scale-chromatic.v1.min.js +0 -0
  170. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/d3.v4.min.js +0 -0
  171. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/violin.css +0 -0
  172. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks/violin/d3js/violin.html.j2 +0 -0
  173. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/dependency_links.txt +0 -0
  174. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/requires.txt +0 -0
  175. {d3blocks-2.2.1 → d3blocks-2.3.0}/d3blocks.egg-info/top_level.txt +0 -0
  176. {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.2.1
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://erdogant.github.io/d3blocks
8
- Project-URL: Download, https://github.com/erdogant/d3blocks/archive/{version}.tar.gz
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
  ![GitHub repo size](https://img.shields.io/github/repo-size/d3blocks/d3blocks)
45
45
  [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/d3blocks/d3blocks/blob/main/notebooks/d3blocks.ipynb)
46
46
  [![Donate](https://img.shields.io/badge/Support%20this%20project-grey.svg?logo=github%20sponsors)](https://d3blocks.github.io/d3blocks/Documentation.html#)
47
+ [![skills.sh](https://skills.sh/b/d3blocks/d3blocks)](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
- #### Installation (clone)
112
+ #### Clone
110
113
  ```bash
111
- git clone https://github.com/d3blocks/d3blocks.git
112
- cd d3blocks
113
- pip install -U .
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
  ![GitHub repo size](https://img.shields.io/github/repo-size/d3blocks/d3blocks)
14
14
  [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://github.com/d3blocks/d3blocks/blob/main/notebooks/d3blocks.ipynb)
15
15
  [![Donate](https://img.shields.io/badge/Support%20this%20project-grey.svg?logo=github%20sponsors)](https://d3blocks.github.io/d3blocks/Documentation.html#)
16
+ [![skills.sh](https://skills.sh/b/d3blocks/d3blocks)](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
- #### Installation (clone)
81
+ #### Clone
79
82
  ```bash
80
- git clone https://github.com/d3blocks/d3blocks.git
81
- cd d3blocks
82
- pip install -U .
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
@@ -10,7 +10,7 @@ from d3blocks.utils import (
10
10
 
11
11
  __author__ = 'Erdogan Taskesen'
12
12
  __email__ = 'erdogant@gmail.com'
13
- __version__ = '2.2.1'
13
+ __version__ = '2.3.0'
14
14
 
15
15
  # Setup root logger
16
16
  import logging
@@ -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
+ };