@plotdb/chart 0.0.2

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 (177) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +50 -0
  3. package/build +39 -0
  4. package/dist/chart.css +8 -0
  5. package/dist/chart.js +603 -0
  6. package/dist/chart.min.css +1 -0
  7. package/dist/chart.min.js +1 -0
  8. package/dist/data/index.js +73 -0
  9. package/dist/utils/axis.js +357 -0
  10. package/dist/utils/axis.min.js +1 -0
  11. package/dist/utils/config.js +33 -0
  12. package/dist/utils/config.min.js +1 -0
  13. package/dist/utils/data.js +238 -0
  14. package/dist/utils/data.min.js +1 -0
  15. package/dist/utils/format.js +70 -0
  16. package/dist/utils/format.min.js +1 -0
  17. package/dist/utils/legend.css +40 -0
  18. package/dist/utils/legend.js +263 -0
  19. package/dist/utils/legend.min.css +1 -0
  20. package/dist/utils/legend.min.js +1 -0
  21. package/dist/utils/tint.js +138 -0
  22. package/dist/utils/tint.min.js +1 -0
  23. package/dist/utils/tip.js +148 -0
  24. package/dist/utils/tip.min.js +1 -0
  25. package/doc/charts.md +58 -0
  26. package/doc/config.md +44 -0
  27. package/doc/draft/chart.md +146 -0
  28. package/doc/draft/data.md +8 -0
  29. package/doc/draft/definition.md +0 -0
  30. package/doc/draft/events.md +0 -0
  31. package/doc/draft/filter.md +0 -0
  32. package/doc/draft/lifecycle.md +89 -0
  33. package/doc/spec.md +89 -0
  34. package/doc/todo.md +27 -0
  35. package/doc/utils/tip.md +84 -0
  36. package/package.json +83 -0
  37. package/src/chart.ls +320 -0
  38. package/src/chart.styl +6 -0
  39. package/src/data/en-US/category +20 -0
  40. package/src/data/en-US/fruit +39 -0
  41. package/src/data/en-US/movie +125 -0
  42. package/src/data/en-US/name +58 -0
  43. package/src/data/index.ls +34 -0
  44. package/src/data/zh-TW/category +20 -0
  45. package/src/data/zh-TW/fruits +38 -0
  46. package/src/data/zh-TW/movie +125 -0
  47. package/src/data/zh-TW/names +105 -0
  48. package/src/utils/axis.ls +229 -0
  49. package/src/utils/config.ls +11 -0
  50. package/src/utils/data.ls +104 -0
  51. package/src/utils/format.ls +30 -0
  52. package/src/utils/legend.ls +134 -0
  53. package/src/utils/legend.styl +37 -0
  54. package/src/utils/tint.ls +66 -0
  55. package/src/utils/tip.ls +97 -0
  56. package/web/src/pug/base.pug +19 -0
  57. package/web/src/pug/block/axis/0.0.1/index.ls +38 -0
  58. package/web/src/pug/block/axis/0.0.1/index.pug +1 -0
  59. package/web/src/pug/block/axis-bubble/0.0.1/index.ls +141 -0
  60. package/web/src/pug/block/axis-bubble/0.0.1/index.pug +1 -0
  61. package/web/src/pug/block/bar/0.0.1/index.ls +274 -0
  62. package/web/src/pug/block/bar/0.0.1/index.pug +35 -0
  63. package/web/src/pug/block/bar-list/0.0.1/index.ls +85 -0
  64. package/web/src/pug/block/bar-list/0.0.1/index.pug +30 -0
  65. package/web/src/pug/block/bar-race/0.0.1/index.ls +83 -0
  66. package/web/src/pug/block/bar-race/0.0.1/index.pug +21 -0
  67. package/web/src/pug/block/base/0.0.1/index.ls +67 -0
  68. package/web/src/pug/block/base/0.0.1/index.pug +5 -0
  69. package/web/src/pug/block/blank/0.0.1/index.ls +25 -0
  70. package/web/src/pug/block/blank/0.0.1/index.pug +1 -0
  71. package/web/src/pug/block/bubble/0.0.1/index.ls +220 -0
  72. package/web/src/pug/block/bubble/0.0.1/index.pug +28 -0
  73. package/web/src/pug/block/curve-bar/0.0.1/index.ls +89 -0
  74. package/web/src/pug/block/curve-bar/0.0.1/index.pug +16 -0
  75. package/web/src/pug/block/curve-bar/0.0.1/sample.js +338 -0
  76. package/web/src/pug/block/dumbbell/0.0.1/index.ls +90 -0
  77. package/web/src/pug/block/dumbbell/0.0.1/index.pug +1 -0
  78. package/web/src/pug/block/empty/0.0.1/index.ls +27 -0
  79. package/web/src/pug/block/empty/0.0.1/index.pug +2 -0
  80. package/web/src/pug/block/gauge/0.0.1/index.ls +100 -0
  81. package/web/src/pug/block/gauge/0.0.1/index.pug +31 -0
  82. package/web/src/pug/block/gauge/0.0.1/sample.js +338 -0
  83. package/web/src/pug/block/line/0.0.1/index.ls +265 -0
  84. package/web/src/pug/block/line/0.0.1/index.pug +33 -0
  85. package/web/src/pug/block/number/0.0.1/index.ls +69 -0
  86. package/web/src/pug/block/number/0.0.1/index.pug +20 -0
  87. package/web/src/pug/block/percent-list/0.0.1/index.ls +167 -0
  88. package/web/src/pug/block/percent-list/0.0.1/index.pug +34 -0
  89. package/web/src/pug/block/pie/0.0.1/index.ls +125 -0
  90. package/web/src/pug/block/pie/0.0.1/index.pug +19 -0
  91. package/web/src/pug/block/pie/0.0.1/sample.js +338 -0
  92. package/web/src/pug/block/pie-bubble/0.0.1/index.ls +185 -0
  93. package/web/src/pug/block/pie-bubble/0.0.1/index.pug +40 -0
  94. package/web/src/pug/block/radar/0.0.1/index.ls +113 -0
  95. package/web/src/pug/block/radar/0.0.1/index.pug +1 -0
  96. package/web/src/pug/block/radar/0.0.1/xx +285 -0
  97. package/web/src/pug/block/scatter/0.0.1/index.ls +128 -0
  98. package/web/src/pug/block/scatter/0.0.1/index.pug +30 -0
  99. package/web/src/pug/block/sheet/0.0.1/index.ls +38 -0
  100. package/web/src/pug/block/sheet/0.0.1/index.pug +5 -0
  101. package/web/src/pug/block/table/0.0.1/index.ls +40 -0
  102. package/web/src/pug/block/table/0.0.1/index.pug +3 -0
  103. package/web/src/pug/block/taiwantown/0.0.1/index.ls +48 -0
  104. package/web/src/pug/block/taiwantown/0.0.1/index.pug +52 -0
  105. package/web/src/pug/block/taiwanvillage/0.0.1/index.ls +53 -0
  106. package/web/src/pug/block/taiwanvillage/0.0.1/index.pug +52 -0
  107. package/web/src/pug/block/treemap/0.0.1/index.ls +172 -0
  108. package/web/src/pug/block/treemap/0.0.1/index.pug +42 -0
  109. package/web/src/pug/block/voronoi-treemap/0.0.1/index.ls +174 -0
  110. package/web/src/pug/block/voronoi-treemap/0.0.1/index.pug +17 -0
  111. package/web/src/pug/block/voronoi-treemap/0.0.1/sample.js +241 -0
  112. package/web/src/pug/block/wordcloud/0.0.1/index.ls +61 -0
  113. package/web/src/pug/block/wordcloud/0.0.1/index.pug +1 -0
  114. package/web/src/pug/block/worldmap/0.0.1/index.ls +83 -0
  115. package/web/src/pug/block/worldmap/0.0.1/index.pug +12 -0
  116. package/web/src/pug/index.ls +72 -0
  117. package/web/src/pug/index.pug +52 -0
  118. package/web/src/styl/index.styl +0 -0
  119. package/web/static/assets/block/cache.js +4 -0
  120. package/web/static/assets/custom-lib/d3/d3-array.v2.min.js +2 -0
  121. package/web/static/assets/custom-lib/d3/d3-axis.v2.min.js +2 -0
  122. package/web/static/assets/custom-lib/d3/d3-brush.v2.min.js +2 -0
  123. package/web/static/assets/custom-lib/d3/d3-color.v2.min.js +2 -0
  124. package/web/static/assets/custom-lib/d3/d3-dispatch.v2.min.js +2 -0
  125. package/web/static/assets/custom-lib/d3/d3-drag.v2.min.js +2 -0
  126. package/web/static/assets/custom-lib/d3/d3-ease.v2.min.js +2 -0
  127. package/web/static/assets/custom-lib/d3/d3-force-boundary.v0.0.1.min.js +2 -0
  128. package/web/static/assets/custom-lib/d3/d3-force.v2.min.js +2 -0
  129. package/web/static/assets/custom-lib/d3/d3-format.v2.min.js +2 -0
  130. package/web/static/assets/custom-lib/d3/d3-hierarchy.v2.min.js +2 -0
  131. package/web/static/assets/custom-lib/d3/d3-interpolate.v2.min.js +2 -0
  132. package/web/static/assets/custom-lib/d3/d3-path.v2.min.js +2 -0
  133. package/web/static/assets/custom-lib/d3/d3-quadtree.v2.min.js +2 -0
  134. package/web/static/assets/custom-lib/d3/d3-random.v2.min.js +2 -0
  135. package/web/static/assets/custom-lib/d3/d3-scale-chromatic.v1.min.js +2 -0
  136. package/web/static/assets/custom-lib/d3/d3-selection.v2.min.js +2 -0
  137. package/web/static/assets/custom-lib/d3/d3-shape.v2.min.js +2 -0
  138. package/web/static/assets/custom-lib/d3/d3-timer.v2.min.js +2 -0
  139. package/web/static/assets/custom-lib/d3/d3-transition.v2.min.js +2 -0
  140. package/web/static/assets/custom-lib/d3/d3.v4.js +17178 -0
  141. package/web/static/assets/custom-lib/d3/d3.v4.min.js +2 -0
  142. package/web/static/assets/data/countrycode.json +1 -0
  143. package/web/static/assets/data/world-110m.json +1 -0
  144. package/web/static/assets/data/zh-to-alpha2.json +1 -0
  145. package/web/static/assets/img/fluid.svg +16 -0
  146. package/web/static/block/axis/0.0.1/index.html +79 -0
  147. package/web/static/block/axis-bubble/0.0.1/index.html +222 -0
  148. package/web/static/block/bar/0.0.1/index.html +581 -0
  149. package/web/static/block/bar-list/0.0.1/index.html +231 -0
  150. package/web/static/block/bar-race/0.0.1/index.html +184 -0
  151. package/web/static/block/base/0.0.1/index.html +197 -0
  152. package/web/static/block/blank/0.0.1/index.html +42 -0
  153. package/web/static/block/bubble/0.0.1/index.html +402 -0
  154. package/web/static/block/bubble/0.0.1/test.html +0 -0
  155. package/web/static/block/curve-bar/0.0.1/index.html +148 -0
  156. package/web/static/block/dumbbell/0.0.1/index.html +178 -0
  157. package/web/static/block/empty/0.0.1/index.html +76 -0
  158. package/web/static/block/gauge/0.0.1/index.html +150 -0
  159. package/web/static/block/line/0.0.1/index.html +540 -0
  160. package/web/static/block/number/0.0.1/index.html +152 -0
  161. package/web/static/block/percent-list/0.0.1/index.html +316 -0
  162. package/web/static/block/pie/0.0.1/index.html +281 -0
  163. package/web/static/block/pie-bubble/0.0.1/index.html +358 -0
  164. package/web/static/block/radar/0.0.1/index.html +149 -0
  165. package/web/static/block/scatter/0.0.1/index.html +251 -0
  166. package/web/static/block/sheet/0.0.1/index.html +103 -0
  167. package/web/static/block/table/0.0.1/index.html +73 -0
  168. package/web/static/block/taiwantown/0.0.1/index.html +210 -0
  169. package/web/static/block/taiwanvillage/0.0.1/index.html +220 -0
  170. package/web/static/block/treemap/0.0.1/index.html +360 -0
  171. package/web/static/block/voronoi-treemap/0.0.1/index.html +350 -0
  172. package/web/static/block/wordcloud/0.0.1/index.html +92 -0
  173. package/web/static/block/worldmap/0.0.1/index.html +207 -0
  174. package/web/static/css/index.css +0 -0
  175. package/web/static/css/index.min.css +0 -0
  176. package/web/static/favicon.ico +0 -0
  177. package/web/static/index.html +153 -0
@@ -0,0 +1,53 @@
1
+ <-(->it!) _
2
+
3
+ block-factory =
4
+ pkg:
5
+ name: 'taiwanvillage', version: '0.0.1'
6
+ extend: {name: "base", version: "0.0.1"}
7
+ dependencies: [
8
+ {url: "https://d3js.org/topojson.v2.min.js", async: false}
9
+ {url: "/js/work/map.list.js", async: false}
10
+ {url: "https://cdn.jsdelivr.net/npm/pdmaptw@0.0.2/dist/pdmaptw.min.js"}
11
+ ]
12
+
13
+ init: ({root, context, pubsub}) ->
14
+ pubsub.fire \init, mod: mod {context} .then ~> it.0
15
+
16
+ mod = ({context}) ->
17
+ {d3,forceBoundary,ldColor,repeatString$, pdmaptw, counties, towns, villages} = context
18
+ sample: ->
19
+ val =
20
+ counties: counties.map (d,i) -> {val: Math.random!, county: d}
21
+ towns: towns.map (d,i) -> {val: Math.random!, town: d}
22
+ villages: villages.map (d,i) -> {val: Math.random!, village: d}
23
+ raw: val.villages
24
+ binding: do
25
+ color: {key: \val}
26
+ name: {key: \village}
27
+ config: {}
28
+ dimension:
29
+ name: {type: \N, name: "國家"}
30
+ color: {type: \R, name: "顏色"}
31
+ init: ->
32
+ @obj = pdmaptw.create do
33
+ root: @svg
34
+ type: \village
35
+ baseurl: 'https://cdn.jsdelivr.net/npm/pdmaptw@0.0.2/dist'
36
+ @obj.init!then ~> @obj.fit!
37
+ parse: ->
38
+ d3.select @svg .selectAll \path .each (d,i) ~>
39
+ d.data = @data.filter(-> it.name == d.properties.name).0 or {}
40
+ d3.select @svg .selectAll \path
41
+ .filter (d,i) -> !/彰化縣/.exec(d.properties.name)
42
+ .remove!
43
+ @obj.fit!
44
+ resize: -> #@obj.fit!
45
+ render: ->
46
+ @color = d3.interpolateTurbo
47
+ if @cfg? and @cfg.palette =>
48
+ @color = d3.interpolateDiscrete @cfg.palette.colors.map -> ldColor.web(it.value or it)
49
+ d3.select @svg .selectAll \path
50
+ .transition!duration 350
51
+ .attr \fill, (d,i) ~> @color d.data.color
52
+
53
+ return block-factory
@@ -0,0 +1,52 @@
1
+ div: script(type="@plotdb/block"): include:lsc index.ls
2
+
3
+ div: script(type="@plotdb/block"): :lsc
4
+ {
5
+ pkg:
6
+ name: 'taiwantown', version: '0.0.1'
7
+ dependencies: [
8
+ {url: "https://d3js.org/d3.v4.js", async: false}
9
+ "https://d3js.org/d3-format.v2.min.js"
10
+ "https://d3js.org/d3-array.v2.min.js"
11
+ "https://d3js.org/topojson.v2.min.js"
12
+ {url: "https://d3js.org/d3-color.v1.min.js", async: false}
13
+ {url: "https://d3js.org/d3-interpolate.v1.min.js", async: false}
14
+ {url: "https://d3js.org/d3-scale-chromatic.v1.min.js", async: false}
15
+ "/js/map.list.js"
16
+ "https://cdn.jsdelivr.net/npm/pdmaptw@0.0.2/dist/pdmaptw.min.js"
17
+ ]
18
+ init: ({root, context}) ->
19
+ counties = context.counties.map (d,i) -> {val: Math.random!, county: d}
20
+ towns = context.towns.map (d,i) -> {val: Math.random!, town: d}
21
+ c = new chart do
22
+ root: root
23
+ raw: towns
24
+ mod: @mod({context})
25
+ binding: do
26
+ color: {name: \val}
27
+ name: {name: \town}
28
+ c.init!then -> c.render!
29
+ @c = c
30
+
31
+ interface: -> @c
32
+
33
+ mod: ({context}) ->
34
+ return do
35
+ init: ->
36
+ @obj = context.pdmaptw.create do
37
+ root: @svg
38
+ type: \town
39
+ baseurl: 'https://cdn.jsdelivr.net/npm/pdmaptw@0.0.2/dist'
40
+ @obj.init!then ~> @obj.fit!
41
+ parse: ->
42
+ d3.select @svg .selectAll \path .each (d,i) ~>
43
+ d.data = @data.filter(-> it.name == d.properties.name).0 or {}
44
+ resize: -> #@obj.fit!
45
+ render: ->
46
+ @color = d3.interpolateTurbo
47
+ if config.pal => @color = d3.interpolateDiscrete config.pal
48
+ d3.select @svg .selectAll \path
49
+ .transition!duration 350
50
+ .attr \fill, (d,i) ~> @color d.data.color
51
+
52
+ }
@@ -0,0 +1,172 @@
1
+ <-(->it!) _
2
+
3
+ block-factory =
4
+ pkg:
5
+ name: 'treemap', version: '0.0.1'
6
+ extend: {name: "base", version: "0.0.1"}
7
+ dependencies: []
8
+ i18n:
9
+ "zh-TW":
10
+ "other": "其它"
11
+ "unit": "單位"
12
+ init: ({root, context, pubsub, t}) ->
13
+ pubsub.fire \init, mod: mod {root, context, t}
14
+
15
+ mod = ({root, context, t}) ->
16
+ {chart,d3,ldcolor,wrap-svg-text} = context
17
+ sample: ->
18
+ raw: [0 to 50].map (val) ~> {val: (Math.random!)toFixed(2), cat: Math.floor(1 + 6 * Math.random!)}
19
+ binding:
20
+ color: {key: \cat}
21
+ area: {key: \val, unit: 'MB'}
22
+ name: {key: \cat}
23
+ category: {key: \cat}
24
+ config:
25
+ unit-position: type: \choice, values: <[inner outside none]>
26
+ dimension:
27
+ color: {type: \RC, name: "顏色", priority: 4}
28
+ area: {type: \R, name: "面積", priority: 1}
29
+ name: {type: \NC, name: "名稱", priority: 2}
30
+ category: {type: \C, name: "分類", priority: 3}
31
+ init: ->
32
+ @tint = tint = new chart.utils.tint!
33
+ @g = Object.fromEntries <[view unit legend]>.map ~> [it, d3.select(@layout.get-group it)]
34
+ @text-renderer = text-renderer = document.createElement \div
35
+ text-renderer.style <<< do
36
+ textAlign: \right
37
+ fontSize: \0.9em
38
+ padding: ".5em .5em"
39
+ line-height: \1.1em
40
+ white-space: "pre-line"
41
+ display: 'inline-block'
42
+ position: \absolute
43
+ top: 0
44
+ left: 0
45
+ opacity: 0
46
+ pointer-events: \none
47
+ user-select: \none
48
+ zIndex: 0
49
+ root.appendChild text-renderer
50
+ @legend = new chart.utils.legend do
51
+ layout: @layout
52
+ name: \legend
53
+ root: @root
54
+ shape: (d) -> d3.select(@).attr \fill, tint.get d.key
55
+ direction: if @cfg.{}legend.position == \bottom => \horizontal else \vertical
56
+ cfg: selectable: true
57
+ @legend.on \select, ~> @bind!; @resize!; @render!
58
+ @tip = new chart.utils.tip {
59
+ root: @root
60
+ accessor: ({evt}) ~>
61
+ if !(evt.target and data = d3.select(evt.target).datum!) => return null
62
+ v = if isNaN(data.data.area) => '-' else "#{(data.data.area).toFixed(2)}#{@binding.area.unit or ''}"
63
+ return {name: data.data.name, value: v}
64
+ range: ~> @layout.get-node \view .getBoundingClientRect!
65
+ }
66
+
67
+ destroy: -> @tip.destroy!
68
+
69
+ parse: ->
70
+ @tint.reset!
71
+ hash = {}
72
+ @data.map (d,i) ->
73
+ d.id = "name-#i"
74
+ if !d.category or d.category == "" => d.category = t("other")
75
+ if !@binding.category => @data.map (d) -> d.category = 'root-1'
76
+ @data.map -> hash[it.category] = {id: it.category, category: 'root'}
77
+ @cats = Array.from(new Set @data.map -> it.category).filter -> it != 'root-1'
78
+ @data = @data ++ [v for k,v of hash] ++ [{id: 'root'}]
79
+ @legend.data @cats.map -> {key: it, text: it}
80
+ bind: ->
81
+ data = @data.filter ~>
82
+ it.id == 'root' or (it.category in <[root root-1]>) or @legend.is-selected(it.category)
83
+ s = d3.stratify!
84
+ .id -> it.id
85
+ .parentId -> it.category
86
+ @ret = s(data)
87
+ resize: ->
88
+ @root.querySelector('.pdl-layout').classList.toggle \legend-bottom, @cfg.{}legend.position == \bottom
89
+ @tip.toggle(if @cfg.{}tip.enabled? => @cfg.tip.enabled else true)
90
+ @unit =
91
+ inner: if @cfg.{}unit.position == \inner => (@binding.area.unit or '') else ''
92
+ outer: (
93
+ if @cfg.{}unit.position in <[inner none]> => ''
94
+ else if @binding.area.unit => "#{t('unit')}: #{@binding.area.unit or ''}" else ''
95
+ )
96
+ @layout.get-node \unit
97
+ ..style.display = if !@unit.outer => 'none' else ''
98
+ ..textContent = @unit.outer
99
+ @legend.config @cfg.legend
100
+ @legend.update!
101
+ @layout.update false
102
+ @ret.sum -> it.area
103
+ box = @layout.get-box \view
104
+ tree = d3.treemap!padding(1).size([box.width, box.height]) @ret
105
+ @nodes = @ret.leaves!filter -> it.data._raw
106
+ @nodes.map -> it._raw = it.data._raw
107
+ @extent = d3.extent @nodes.map -> it.value
108
+ render: ->
109
+ {cfg, unit, binding, layout, text-renderer, tint} = @
110
+ tint.set cfg.palette
111
+ #else tint.set @cats.map (d,i) ~> ldcolor.web(d3.interpolateTurbo(i/((@cats.length - 1) or 1)))
112
+ @g.unit.call ~>
113
+ node = layout.get-node \unit
114
+ ret = wrap-svg-text {node, useRange: true}
115
+ @g.unit.node!textContent = ''
116
+ @g.unit.node!appendChild ret
117
+ @g.view.selectAll \rect.data .data(@nodes, -> it._raw._idx)
118
+ ..exit!remove!
119
+ ..enter!append \rect
120
+ .attr \class, \data
121
+ .attr \x, -> (it.x1 + it.x0)/2 - (it.x1 - it.x0)/4
122
+ .attr \y, -> (it.y1 + it.y0)/2 - (it.y1 - it.y0)/4
123
+ .attr \width, -> (it.x1 - it.x0) / 2
124
+ .attr \height, -> (it.y1 - it.y0) / 2
125
+ .attr \fill, ~> tint.get(it.data.color or it.data.category)
126
+ .style \opacity, 0
127
+
128
+ @g.view.selectAll \rect
129
+ .transition!duration 350
130
+ .attr \x, -> it.x0
131
+ .attr \y, -> it.y0
132
+ .attr \width, -> it.x1 - it.x0
133
+ .attr \height, -> it.y1 - it.y0
134
+ .attr \fill, ~>
135
+ v = (((it.value - @extent.0) / (@extent.1 - @extent.0) ) - 0.5)
136
+ tint.get(it.data.color or it.data.category)
137
+ .style \opacity, 1
138
+
139
+ @g.view.selectAll \g.label .data(@nodes, -> it._raw._idx)
140
+ ..exit!remove!
141
+ ..enter!append \g
142
+ .attr \class, 'label data'
143
+ .attr \transform, (d,i) -> "translate(#{d.x0},#{d.y0})"
144
+ .attr \opacity, 0
145
+ .style \pointer-events, \none
146
+ .style \cursor, \pointer
147
+ @g.view.selectAll \g.label
148
+ .attr \font-size, \0.9em
149
+ .each (d,i) ->
150
+ text-renderer.style.width = "#{d.x1 - d.x0}px"
151
+ area = d.data.area
152
+ area = d3.format('.3s') area
153
+ #area = if area > 1000000 => (area/1000000).toFixed(2) + t(\M)
154
+ #else if area > 1000 => (area/1000).toFixed(2) + t(\K)
155
+ #else if area? and area.toFixed => (area).toFixed(2)
156
+ #else area
157
+ text-renderer.innerHTML = """
158
+ <div>#{d.data.name or ''}</div>
159
+ <div>#{area}#{unit.inner}</div>
160
+ """
161
+ ret = wrap-svg-text node: text-renderer, useRange: true
162
+ @textContent = ''
163
+ @appendChild ret
164
+ .transition!duration 350
165
+ .attr \transform, (d,i) -> "translate(#{d.x0},#{d.y0})"
166
+ .attr \fill, ~> tint.text(it.data.color or it.data.category)
167
+ .attr \opacity, (d,i) ->
168
+ box = @getBBox!
169
+ return if box.width > d.x1 - d.x0 or box.height > d.y1 - d.y0 => 0 else 1
170
+ @legend.render!
171
+
172
+ return block-factory
@@ -0,0 +1,42 @@
1
+ div
2
+ div(plug="layout")
3
+ .pdl-layout
4
+ div(data-type="layout")
5
+ .pdl-cell(data-name="view")
6
+ .pdl-cell(data-name="unit")
7
+ .pdl-cell(data-name="legend")
8
+ div(data-type="render")
9
+ style(type="text/css"): :stylus
10
+ .pdl-layout > div[data-type=layout]
11
+ display: grid
12
+ grid-template-columns: 1fr fit-content(10em)
13
+ grid-template-rows: 1fr fit-content(1em)
14
+ & > .pdl-cell[data-name=view]
15
+ grid-area: 1 / 1
16
+ & > .pdl-cell[data-name=legend]
17
+ grid-column: 2
18
+ grid-row: 1 / span 2
19
+ padding: 0 .5em 0 .5em
20
+ & > .pdl-cell[data-name=unit]
21
+ grid-column: 1
22
+ grid-row: 2
23
+ line-height: 1em
24
+ text-align: right
25
+ padding: .25em 0
26
+ .pdl-layout.legend-bottom > div[data-type=layout]
27
+ grid-template-columns: 1fr fit-content(1em)
28
+ grid-template-rows: 1fr fit-content(3em)
29
+ .pdl-cell[data-name=view]
30
+ grid-column: 1 / span 2
31
+ grid-row: 1
32
+ .pdl-cell[data-name=legend]
33
+ padding-top: 1em
34
+ grid-column: 1
35
+ grid-row: 2
36
+ .pdl-cell[data-name=unit]
37
+ padding-top: 1em
38
+ grid-column: 2
39
+ grid-row: 2
40
+
41
+
42
+ script(type="@plotdb/block"): include:lsc index.ls
@@ -0,0 +1,174 @@
1
+ <-(->it!) _
2
+
3
+ block-factory =
4
+ pkg:
5
+ name: 'voronoi-treemap', version: '0.0.1'
6
+ extend: {name: "base", version: "0.0.1"}
7
+ dependencies: [
8
+ {name: "@zbryikt/voronoijs", version: "main", path: "voronoi.min.js"}
9
+ ]
10
+ init: ({root, context, pubsub}) ->
11
+ pubsub.fire \init, mod: mod {context} .then ~> it.0
12
+
13
+ mod = ({context}) ->
14
+ {d3,ldcolor,voronoi,chart} = context
15
+ sample: ->
16
+ raw: [0 to 100].map (val) ~> {val: Math.random! * 10, cat: Math.floor(1 + 10 * Math.random!), name: val}
17
+ binding:
18
+ name: {key: \name}
19
+ area: {key: \val}
20
+ category: {key: \cat}
21
+ config: {}
22
+ dimension:
23
+ area: { type: \R, name: "area"},
24
+ name: { type: \N, desc: "name" },
25
+ category: { type: \N, desc: "category"},
26
+
27
+ init: ->
28
+ @tint = tint = new chart.utils.tint!
29
+ @g = Object.fromEntries <[view legend]>.map ~> [it, d3.select(@layout.get-group it)]
30
+ @layout.get-group(\view).appendChild g = (document.createElementNS "http://www.w3.org/2000/svg", "g")
31
+ @g.shape = d3.select g
32
+ @site-boxes = []
33
+ @legend = new chart.utils.legend do
34
+ layout: @layout
35
+ name: \legend
36
+ root: @root
37
+ shape: (d) -> d3.select(@).attr \fill, tint.get d.key
38
+ cfg: selectable: true
39
+ @legend.on \select, ~> @bind!; @resize!; @render!
40
+ @tip = new chart.utils.tip do
41
+ root: @root
42
+ accessor: ({evt}) ~>
43
+ if !(evt.target and data = d3.select(evt.target).datum!) => return null
44
+ if Array.isArray(data) =>
45
+ if !@_sites => return
46
+ if !(data.idx?) => return
47
+ if !(data = @_sites[data.idx]) => return
48
+ return {name: data.name, value: "#{(data.area).toFixed 2}#{@binding.area.unit or ''}"}
49
+ range: ~> @layout.get-node \view .getBoundingClientRect!
50
+
51
+ @start!
52
+ parse: ->
53
+ @data.map -> it <<< {key: it.name, value: it.area}
54
+ @total = @data.reduce(((a,b) -> a + b.area), 0)
55
+ @cats = Array.from(new Set(@data.map (d) -> d.category))
56
+ @legend.data @cats.map -> {key: it, text: it}
57
+ bind: ->
58
+ data = @data.filter ~> @legend.is-selected(it.category )
59
+ @parsed = children: d3.nest!.key(->it.category).entries(data).map -> it <<< {children: it.values}
60
+ box = @box or @svg.getBoundingClientRect!
61
+ [w,h] = [box.width, box.height]
62
+ w = h = Math.min(w,h)
63
+ @treemap = new voronoi.Treemap(@parsed, voronoi.Polygon.create(w, h, 60), w, h)
64
+ sites = @treemap.getSites!
65
+ @g.shape.selectAll \path.data .data @treemap.getPolygons!
66
+ ..exit!remove!
67
+ ..enter!append \path .attr \class, \data
68
+ .each (d,i) -> d.idx = i
69
+ @g.shape.selectAll \path.data .attr \class, (d,i) -> if sites[i].lv <= 0 => 'data group' else 'data'
70
+ @polygons = @g.shape.selectAll \path.data
71
+
72
+ @g.shape.selectAll \g.label .data sites
73
+ ..exit!remove!
74
+ ..enter!append \g .attr \class, \label
75
+ .each (d,i) ->
76
+ d3.select @
77
+ ..append \text .attr \class, 'name' .attr \font-size, '.8em'
78
+ ..append \text .attr \class, 'value'
79
+ @sites = @g.shape.selectAll \g.label
80
+ @sites.each (d,i) ->
81
+ d3.select @ .selectAll \text .data [d,d]
82
+ .attr \text-anchor, \middle
83
+ .attr \dominant-baseline, \center
84
+ resize: ->
85
+ box = @layout.get-box \view
86
+ [w,h] = [box.width, box.height]
87
+ s = Math.min(w,h)
88
+ @legend.config @cfg.legend
89
+ @legend.update!
90
+ @layout.update false
91
+ @g.shape.attr \transform, "translate(#{w/2 - s/2},#{h/2 - s/2})"
92
+ @scale =
93
+ x: d3.scaleLinear!domain [0,w] .range [0, w]
94
+ y: d3.scaleLinear!domain [0,h] .range [0, h]
95
+ color: d3.interpolateTurbo
96
+ if @cfg? and @cfg.palette =>
97
+ @scale.color = d3.interpolateDiscrete @cfg.palette.colors.map -> ldcolor.web(it.value or it)
98
+ @tint.set @cfg.palette
99
+ sites = @treemap.getSites!
100
+ @count = 0
101
+ @gbox = @svg.getBoundingClientRect!
102
+
103
+ render: (tick = false)->
104
+ @_sites = sites = @treemap.getSites!
105
+ polygons = @treemap.getPolygons!
106
+ [w,h] = [@box.width, @box.height]
107
+
108
+ if !tick =>
109
+
110
+ @legend.render!
111
+ @sites.selectAll \.name
112
+ .attr \dy, \0.88em
113
+ .text ->
114
+ n = ("#{it.name or ''}")
115
+ return n
116
+ if it.lv > 0 => n.substring(0,7) + (if n.length > 7 => '...' else '') else ''
117
+ .attr \fill, (d,i) ~> if ldcolor.hcl(@tint.get d.category).l > 60 => \#000 else \#eee
118
+ .style \pointer-events, \none
119
+ @sites.selectAll \.value
120
+ .attr \dy, \-0.28em
121
+ .text -> if it.lv > 0 => (it.value or 0).toFixed(2) else ''
122
+ .attr \fill, (d,i) ~> if ldcolor.hcl(@tint.get d.category).l > 60 => \#000 else \#eee
123
+ .style \pointer-events, \none
124
+ @site-boxes = site-boxes = []
125
+ @sites.each (d,i) -> site-boxes.push @getBoundingClientRect!
126
+ @polygons
127
+ .attr \fill, (d,i) ~> @tint.get sites[i].category
128
+ .attr \stroke, \#fff
129
+ .attr \stroke-width, 1
130
+
131
+ site-boxes = @site-boxes
132
+ boxes = []
133
+
134
+ @polygons.data polygons
135
+ .attr \d, (d,i) ->
136
+ d.idx = i
137
+ if !(d and d.length) =>
138
+ boxes.push {}
139
+ return ""
140
+ ret = [[\M, d.0.x, d.0.y]]
141
+ [min,max] = [{x: d.0.x, y: d.0.y}, {d.0.x, d.0.y}]
142
+ for idx from 0 til d.length =>
143
+ [x,y] = [d[idx].x, d[idx].y]
144
+ if min.x > x => min.x = x
145
+ if max.x < x => max.x = x
146
+ if min.y > y => min.y = y
147
+ if max.y < y => max.y = y
148
+ ret.push [\L, x, y]
149
+ ret.push [\L, d.0.x, d.0.y]
150
+ boxes.push {x: min.x, y: min.y, width: max.x - min.x, height: max.y - min.y}
151
+ return ret.map(->it.join(' ')).join(' ')
152
+
153
+ @sites
154
+ .each (d,i) ->
155
+ box = boxes[i]
156
+ mybox = site-boxes[i]
157
+ if !mybox => mybox = @getBoundingClientRect!
158
+ d3.select @
159
+ .attr \transform, ->
160
+ x = box.x + box.width / 2
161
+ y = box.y + box.height / 2
162
+ if isNaN(x) => x = -10000
163
+ if isNaN(y) => y = -10000
164
+ return "translate(#x, #y)"
165
+ .attr \opacity, -> (1 - 6 * ((mybox.width / (box.width >? 0) - 1) >? 0)) >? 0
166
+
167
+
168
+ tick: ->
169
+ @count = (@count or 0) + 1
170
+ if @count < 100 =>
171
+ @treemap.compute!
172
+ @render true
173
+
174
+ return block-factory
@@ -0,0 +1,17 @@
1
+ div
2
+ div(plug="layout")
3
+ .pdl-layout
4
+ div(data-type="layout")
5
+ .pdl-cell(data-name="view")
6
+ .pdl-cell(data-name="legend")
7
+ div(data-type="render")
8
+ style(type="text/css"): :stylus
9
+ .pdl-layout > div[data-type=layout]
10
+ display: grid
11
+ grid-template-columns: 1fr fit-content(10em)
12
+ & > .pdl-cell[data-name=view]
13
+ grid-column: 1
14
+ & > .pdl-cell[data-name=legend]
15
+ grid-column: 2
16
+ padding: 0 .5em 0 .5em
17
+ script(type="@plotdb/block"): include:lsc index.ls