@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.
- package/CHANGELOG.md +11 -0
- package/README.md +50 -0
- package/build +39 -0
- package/dist/chart.css +8 -0
- package/dist/chart.js +603 -0
- package/dist/chart.min.css +1 -0
- package/dist/chart.min.js +1 -0
- package/dist/data/index.js +73 -0
- package/dist/utils/axis.js +357 -0
- package/dist/utils/axis.min.js +1 -0
- package/dist/utils/config.js +33 -0
- package/dist/utils/config.min.js +1 -0
- package/dist/utils/data.js +238 -0
- package/dist/utils/data.min.js +1 -0
- package/dist/utils/format.js +70 -0
- package/dist/utils/format.min.js +1 -0
- package/dist/utils/legend.css +40 -0
- package/dist/utils/legend.js +263 -0
- package/dist/utils/legend.min.css +1 -0
- package/dist/utils/legend.min.js +1 -0
- package/dist/utils/tint.js +138 -0
- package/dist/utils/tint.min.js +1 -0
- package/dist/utils/tip.js +148 -0
- package/dist/utils/tip.min.js +1 -0
- package/doc/charts.md +58 -0
- package/doc/config.md +44 -0
- package/doc/draft/chart.md +146 -0
- package/doc/draft/data.md +8 -0
- package/doc/draft/definition.md +0 -0
- package/doc/draft/events.md +0 -0
- package/doc/draft/filter.md +0 -0
- package/doc/draft/lifecycle.md +89 -0
- package/doc/spec.md +89 -0
- package/doc/todo.md +27 -0
- package/doc/utils/tip.md +84 -0
- package/package.json +83 -0
- package/src/chart.ls +320 -0
- package/src/chart.styl +6 -0
- package/src/data/en-US/category +20 -0
- package/src/data/en-US/fruit +39 -0
- package/src/data/en-US/movie +125 -0
- package/src/data/en-US/name +58 -0
- package/src/data/index.ls +34 -0
- package/src/data/zh-TW/category +20 -0
- package/src/data/zh-TW/fruits +38 -0
- package/src/data/zh-TW/movie +125 -0
- package/src/data/zh-TW/names +105 -0
- package/src/utils/axis.ls +229 -0
- package/src/utils/config.ls +11 -0
- package/src/utils/data.ls +104 -0
- package/src/utils/format.ls +30 -0
- package/src/utils/legend.ls +134 -0
- package/src/utils/legend.styl +37 -0
- package/src/utils/tint.ls +66 -0
- package/src/utils/tip.ls +97 -0
- package/web/src/pug/base.pug +19 -0
- package/web/src/pug/block/axis/0.0.1/index.ls +38 -0
- package/web/src/pug/block/axis/0.0.1/index.pug +1 -0
- package/web/src/pug/block/axis-bubble/0.0.1/index.ls +141 -0
- package/web/src/pug/block/axis-bubble/0.0.1/index.pug +1 -0
- package/web/src/pug/block/bar/0.0.1/index.ls +274 -0
- package/web/src/pug/block/bar/0.0.1/index.pug +35 -0
- package/web/src/pug/block/bar-list/0.0.1/index.ls +85 -0
- package/web/src/pug/block/bar-list/0.0.1/index.pug +30 -0
- package/web/src/pug/block/bar-race/0.0.1/index.ls +83 -0
- package/web/src/pug/block/bar-race/0.0.1/index.pug +21 -0
- package/web/src/pug/block/base/0.0.1/index.ls +67 -0
- package/web/src/pug/block/base/0.0.1/index.pug +5 -0
- package/web/src/pug/block/blank/0.0.1/index.ls +25 -0
- package/web/src/pug/block/blank/0.0.1/index.pug +1 -0
- package/web/src/pug/block/bubble/0.0.1/index.ls +220 -0
- package/web/src/pug/block/bubble/0.0.1/index.pug +28 -0
- package/web/src/pug/block/curve-bar/0.0.1/index.ls +89 -0
- package/web/src/pug/block/curve-bar/0.0.1/index.pug +16 -0
- package/web/src/pug/block/curve-bar/0.0.1/sample.js +338 -0
- package/web/src/pug/block/dumbbell/0.0.1/index.ls +90 -0
- package/web/src/pug/block/dumbbell/0.0.1/index.pug +1 -0
- package/web/src/pug/block/empty/0.0.1/index.ls +27 -0
- package/web/src/pug/block/empty/0.0.1/index.pug +2 -0
- package/web/src/pug/block/gauge/0.0.1/index.ls +100 -0
- package/web/src/pug/block/gauge/0.0.1/index.pug +31 -0
- package/web/src/pug/block/gauge/0.0.1/sample.js +338 -0
- package/web/src/pug/block/line/0.0.1/index.ls +265 -0
- package/web/src/pug/block/line/0.0.1/index.pug +33 -0
- package/web/src/pug/block/number/0.0.1/index.ls +69 -0
- package/web/src/pug/block/number/0.0.1/index.pug +20 -0
- package/web/src/pug/block/percent-list/0.0.1/index.ls +167 -0
- package/web/src/pug/block/percent-list/0.0.1/index.pug +34 -0
- package/web/src/pug/block/pie/0.0.1/index.ls +125 -0
- package/web/src/pug/block/pie/0.0.1/index.pug +19 -0
- package/web/src/pug/block/pie/0.0.1/sample.js +338 -0
- package/web/src/pug/block/pie-bubble/0.0.1/index.ls +185 -0
- package/web/src/pug/block/pie-bubble/0.0.1/index.pug +40 -0
- package/web/src/pug/block/radar/0.0.1/index.ls +113 -0
- package/web/src/pug/block/radar/0.0.1/index.pug +1 -0
- package/web/src/pug/block/radar/0.0.1/xx +285 -0
- package/web/src/pug/block/scatter/0.0.1/index.ls +128 -0
- package/web/src/pug/block/scatter/0.0.1/index.pug +30 -0
- package/web/src/pug/block/sheet/0.0.1/index.ls +38 -0
- package/web/src/pug/block/sheet/0.0.1/index.pug +5 -0
- package/web/src/pug/block/table/0.0.1/index.ls +40 -0
- package/web/src/pug/block/table/0.0.1/index.pug +3 -0
- package/web/src/pug/block/taiwantown/0.0.1/index.ls +48 -0
- package/web/src/pug/block/taiwantown/0.0.1/index.pug +52 -0
- package/web/src/pug/block/taiwanvillage/0.0.1/index.ls +53 -0
- package/web/src/pug/block/taiwanvillage/0.0.1/index.pug +52 -0
- package/web/src/pug/block/treemap/0.0.1/index.ls +172 -0
- package/web/src/pug/block/treemap/0.0.1/index.pug +42 -0
- package/web/src/pug/block/voronoi-treemap/0.0.1/index.ls +174 -0
- package/web/src/pug/block/voronoi-treemap/0.0.1/index.pug +17 -0
- package/web/src/pug/block/voronoi-treemap/0.0.1/sample.js +241 -0
- package/web/src/pug/block/wordcloud/0.0.1/index.ls +61 -0
- package/web/src/pug/block/wordcloud/0.0.1/index.pug +1 -0
- package/web/src/pug/block/worldmap/0.0.1/index.ls +83 -0
- package/web/src/pug/block/worldmap/0.0.1/index.pug +12 -0
- package/web/src/pug/index.ls +72 -0
- package/web/src/pug/index.pug +52 -0
- package/web/src/styl/index.styl +0 -0
- package/web/static/assets/block/cache.js +4 -0
- package/web/static/assets/custom-lib/d3/d3-array.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-axis.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-brush.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-color.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-dispatch.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-drag.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-ease.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-force-boundary.v0.0.1.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-force.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-format.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-hierarchy.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-interpolate.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-path.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-quadtree.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-random.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-scale-chromatic.v1.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-selection.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-shape.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-timer.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3-transition.v2.min.js +2 -0
- package/web/static/assets/custom-lib/d3/d3.v4.js +17178 -0
- package/web/static/assets/custom-lib/d3/d3.v4.min.js +2 -0
- package/web/static/assets/data/countrycode.json +1 -0
- package/web/static/assets/data/world-110m.json +1 -0
- package/web/static/assets/data/zh-to-alpha2.json +1 -0
- package/web/static/assets/img/fluid.svg +16 -0
- package/web/static/block/axis/0.0.1/index.html +79 -0
- package/web/static/block/axis-bubble/0.0.1/index.html +222 -0
- package/web/static/block/bar/0.0.1/index.html +581 -0
- package/web/static/block/bar-list/0.0.1/index.html +231 -0
- package/web/static/block/bar-race/0.0.1/index.html +184 -0
- package/web/static/block/base/0.0.1/index.html +197 -0
- package/web/static/block/blank/0.0.1/index.html +42 -0
- package/web/static/block/bubble/0.0.1/index.html +402 -0
- package/web/static/block/bubble/0.0.1/test.html +0 -0
- package/web/static/block/curve-bar/0.0.1/index.html +148 -0
- package/web/static/block/dumbbell/0.0.1/index.html +178 -0
- package/web/static/block/empty/0.0.1/index.html +76 -0
- package/web/static/block/gauge/0.0.1/index.html +150 -0
- package/web/static/block/line/0.0.1/index.html +540 -0
- package/web/static/block/number/0.0.1/index.html +152 -0
- package/web/static/block/percent-list/0.0.1/index.html +316 -0
- package/web/static/block/pie/0.0.1/index.html +281 -0
- package/web/static/block/pie-bubble/0.0.1/index.html +358 -0
- package/web/static/block/radar/0.0.1/index.html +149 -0
- package/web/static/block/scatter/0.0.1/index.html +251 -0
- package/web/static/block/sheet/0.0.1/index.html +103 -0
- package/web/static/block/table/0.0.1/index.html +73 -0
- package/web/static/block/taiwantown/0.0.1/index.html +210 -0
- package/web/static/block/taiwanvillage/0.0.1/index.html +220 -0
- package/web/static/block/treemap/0.0.1/index.html +360 -0
- package/web/static/block/voronoi-treemap/0.0.1/index.html +350 -0
- package/web/static/block/wordcloud/0.0.1/index.html +92 -0
- package/web/static/block/worldmap/0.0.1/index.html +207 -0
- package/web/static/css/index.css +0 -0
- package/web/static/css/index.min.css +0 -0
- package/web/static/favicon.ico +0 -0
- package/web/static/index.html +153 -0
package/doc/spec.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Chart Spec.
|
|
2
|
+
|
|
3
|
+
PlotDB.js 2.0?
|
|
4
|
+
|
|
5
|
+
- consider `plotdb` structure
|
|
6
|
+
- abstract certain functionality ( popup? )
|
|
7
|
+
- wrapped in `@plotdb/block` for:
|
|
8
|
+
- libraries / css isolation so it's possible to run d3v3, v4, v5, v6 at the same time.
|
|
9
|
+
- well-defined version / module name
|
|
10
|
+
- centralized registry
|
|
11
|
+
- `@plotdb/datadom` aware for easily creating webpage with charts.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Data Object
|
|
15
|
+
|
|
16
|
+
Except chart object, we still need additional data for chart to render, including:
|
|
17
|
+
|
|
18
|
+
- `raw` - the real data. maybe from database, or from csv, etc.
|
|
19
|
+
- `binding` - mapping between data dimension and chart dimension.
|
|
20
|
+
- `config` - how chart is rendered.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Raw
|
|
24
|
+
|
|
25
|
+
Raw data. Compiles to array of object ( store in `data` member variable ) according to the given `binding`, with additional fields below in each element:
|
|
26
|
+
|
|
27
|
+
- `_idx`: order of this element in `data`.
|
|
28
|
+
- `_raw`: raw data corresponding to this object
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Binding
|
|
33
|
+
|
|
34
|
+
binding is an object containing mapping from data fields to map dimensions:
|
|
35
|
+
|
|
36
|
+
`dimension-name`: {
|
|
37
|
+
key: `field-key`,
|
|
38
|
+
name: `field-name`,
|
|
39
|
+
description: `field-description`,
|
|
40
|
+
filter: <filter-object>
|
|
41
|
+
},
|
|
42
|
+
`dimension-name`: [
|
|
43
|
+
{key: `field-key`}, ...
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
detail of the object describing data fields:
|
|
47
|
+
|
|
48
|
+
- `key`: unique identifier of the corresponding field. used to look up field values in `raw`.
|
|
49
|
+
- `name`: field name. optional. fall back to `key` if not provided.
|
|
50
|
+
- `description`: field description. optional.
|
|
51
|
+
- `types`: optional. hash of probability for each types. e.g., following types hash suggests a `Category` type:
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
R: 0.1
|
|
55
|
+
O: 0.1
|
|
56
|
+
N: 0.3
|
|
57
|
+
C: 1.0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
- `filter`: filter information about this field. it can be either:
|
|
61
|
+
- object with following fields:
|
|
62
|
+
- e.g., `{type: "range", value: [1992, 2002]}`
|
|
63
|
+
- list of values. e.g., ['Apple', 'Orange']
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
#### Filter
|
|
67
|
+
|
|
68
|
+
possible filter format:
|
|
69
|
+
|
|
70
|
+
- plain array. indicate chosen values of the specific dimension. e.g., `["Apple", "Orange"]`.
|
|
71
|
+
- object in `{type, value}` format, where
|
|
72
|
+
- `type`: indicate the type of this filter.
|
|
73
|
+
- `value`: value according to the specific type.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
With following possible types and the meaning of their corresponding value:
|
|
77
|
+
|
|
78
|
+
- `index`: an array of position of chosen elements.
|
|
79
|
+
- `exclude`: an array of items to be excluded.
|
|
80
|
+
- `range`: a 2 elements array indicating the range of chosen values.
|
|
81
|
+
- `enum`: an array of chosen elements. identical to the `plain array` format.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## Events
|
|
85
|
+
|
|
86
|
+
- `filter`
|
|
87
|
+
- `select` / `hover`: fired when some elements with data selected / hovered.A
|
|
88
|
+
- n array of `{data, node}` pair should be sent along.
|
|
89
|
+
|
package/doc/todo.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# todo
|
|
2
|
+
|
|
3
|
+
- 如果省略 bind, 則可以直接用 dimension 對應到 data 中的欄位?
|
|
4
|
+
- 混合文字的數值也有可能是 type O. 如 104年, 105年, etc
|
|
5
|
+
- 在有可能做 unpivot 的情況下, 因為 column 難免要寫上單位(年), 這個問題避不掉, 勢必要處理.
|
|
6
|
+
- support zmgr in tip
|
|
7
|
+
- performance
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## layout
|
|
11
|
+
|
|
12
|
+
explicitly define `layout.update`, `layout.render` and consider following scenario:
|
|
13
|
+
|
|
14
|
+
chart.resize -> layout.update
|
|
15
|
+
|
|
16
|
+
by adding additional action / api such as:
|
|
17
|
+
|
|
18
|
+
- before-update
|
|
19
|
+
- after-update
|
|
20
|
+
- before-render
|
|
21
|
+
- after-render
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## config
|
|
25
|
+
|
|
26
|
+
consider adding additional `config` function for correctly updating config. ( we have `config` now but it's used to set cfg object only. )
|
|
27
|
+
|
package/doc/utils/tip.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# chart.utils.tip
|
|
2
|
+
|
|
3
|
+
tooltip utility. use ldview to render content
|
|
4
|
+
|
|
5
|
+
## usage
|
|
6
|
+
|
|
7
|
+
new chart.utils.tip({ ... });
|
|
8
|
+
|
|
9
|
+
constructor options:
|
|
10
|
+
|
|
11
|
+
- `root`: root node to store tip element.
|
|
12
|
+
- one of `relative`, `absolute` or `fixed` position is needed for this element
|
|
13
|
+
- `ndoe`: tip node to use. A default one will be created if omitted.
|
|
14
|
+
- `margin`: margin between tip and cursor. default 10px if omitted.
|
|
15
|
+
- `type`: trigger type of tip. either `over` or `move`. default `move`.
|
|
16
|
+
- `accessor`: accessor function for retrieving data from active event.
|
|
17
|
+
- default null if omitted.
|
|
18
|
+
- if provided, function takes {evt: eventObject} as input, and should return
|
|
19
|
+
- null, if no data is bound to this event
|
|
20
|
+
- a data object if there is any data bound to this event.
|
|
21
|
+
- when provided, `chart.utils.tip` automatically handle mouse events for tip popping.
|
|
22
|
+
- `range`: function returning bounding box of tip ( {x,y,width,height} ). optional.
|
|
23
|
+
- this box shares the same coordinate origin with `root`'s `getBoundingClientRect`.
|
|
24
|
+
- `view`: text handler for ldview. if omitted, handler with following names will be use:
|
|
25
|
+
- `name`
|
|
26
|
+
- `value`
|
|
27
|
+
- `zmgr`: `zmgr` module, use this to get z-index if provided. use `10` instead
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## API
|
|
31
|
+
|
|
32
|
+
- `render(opt)`: show tip based on the given options:
|
|
33
|
+
- `evt`: mouse event. `clientX` and `clientY` members will be used.
|
|
34
|
+
- `node`: target node. optional. used to get corresponding bounding box.
|
|
35
|
+
- bounding box hints `chart.util.tip` the position a tip should be placed.
|
|
36
|
+
- `box`: if `node` is omitted, `render` checks `box` for bounding box information. a box should contains:
|
|
37
|
+
- `x` / `y`: left-top coordinate of the box, or centroid of the box if width / height are omitted.
|
|
38
|
+
- `width` / `height`: box size, optional.
|
|
39
|
+
- `data`: data to be shown. will be set as context to internal ldview object.
|
|
40
|
+
- `toggle(v)`: set `v` to true to enable tip, otherwise to disable tip.
|
|
41
|
+
- tip is by default enabled after constructed.
|
|
42
|
+
- `hide()`: hide tip. this is a debounced function ( with @loadingio/debounce.js ) so you can also `hide().now()`.
|
|
43
|
+
- `destroy()`: destroy function, release unused resources.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Styling
|
|
47
|
+
|
|
48
|
+
With d3js, this is the minimal tip construct:
|
|
49
|
+
|
|
50
|
+
new chart.utils.tip({
|
|
51
|
+
root: myRoot,
|
|
52
|
+
accessor: function({evt}) { return (evt.target? d3.select(evt.target).datum() : null); }
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
To style your own tip, make a tip element first ( with HTML, yet we write this example in Pug for convenience ):
|
|
56
|
+
|
|
57
|
+
.my-tip
|
|
58
|
+
# ... some fancy design ...
|
|
59
|
+
div(ld="name")
|
|
60
|
+
div(ld="value")
|
|
61
|
+
div(ld="my-custom-field")
|
|
62
|
+
|
|
63
|
+
then pass it to tip cosntructor. To support `my-custom-field`, you need to also add `view` option:
|
|
64
|
+
|
|
65
|
+
new chart.utils.tip({
|
|
66
|
+
node: '.my-tip', ...
|
|
67
|
+
view:
|
|
68
|
+
name: ({ctx}) -> ctx.name
|
|
69
|
+
value: ({ctx}) -> ctx.name
|
|
70
|
+
"my-custom-field": ({ctx}) -> some-function(ctx, ctx.some-value);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Custom Behavior
|
|
75
|
+
|
|
76
|
+
With `accessor`, `chart.utils.tip` bind action automatically for you. However, you can also decide how tip should interact with your design by calling `render` manually.
|
|
77
|
+
|
|
78
|
+
tip = new chart.utils.tip({
|
|
79
|
+
/* ... construct without `accessor` option ... */
|
|
80
|
+
});
|
|
81
|
+
/* only trigger tip when clicking something */
|
|
82
|
+
myRoot.addEventListener("click", function(evt) {
|
|
83
|
+
tip.render({evt: evt, data: myData});
|
|
84
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "zbryikt",
|
|
3
|
+
"name": "@plotdb/chart",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "plotdb charts",
|
|
6
|
+
"version": "0.0.2",
|
|
7
|
+
"homepage": "https://github.com/plotdb/chart",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/plotdb/chart"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=10.14.1",
|
|
14
|
+
"npm": ">=6.4.1"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"start": "npx server -r web -o true",
|
|
18
|
+
"postinstall": "npx fedep"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@loadingio/bootstrap.ext": "^0.0.1",
|
|
22
|
+
"@loadingio/ldquery": "^2.2.0",
|
|
23
|
+
"@loadingio/loading.css": "^2.0.1",
|
|
24
|
+
"@plotdb/layout": "^0.0.10",
|
|
25
|
+
"@plotdb/pdmap-world": "^0.0.3",
|
|
26
|
+
"@plotdb/sheet": "^0.1.7",
|
|
27
|
+
"@zbryikt/template": "^2.3.22",
|
|
28
|
+
"bootstrap": "^4.5.2",
|
|
29
|
+
"fedep": "^0.0.16",
|
|
30
|
+
"ldloader": "^2.0.1",
|
|
31
|
+
"ldview": "^0.2.3",
|
|
32
|
+
"livescript": "^1.6.0",
|
|
33
|
+
"uglify-js": "^3.2.0",
|
|
34
|
+
"uglifycss": "^0.0.29"
|
|
35
|
+
},
|
|
36
|
+
"frontendDependencies": {
|
|
37
|
+
"root": "web/static/assets/lib",
|
|
38
|
+
"modules": [
|
|
39
|
+
"bootstrap",
|
|
40
|
+
"@loadingio/bootstrap.ext",
|
|
41
|
+
"@loadingio/ldquery",
|
|
42
|
+
"@loadingio/loading.css",
|
|
43
|
+
"@loadingio/ldcolorpicker",
|
|
44
|
+
"@loadingio/debounce.js",
|
|
45
|
+
"@plotdb/pdmap-world",
|
|
46
|
+
"@plotdb/block",
|
|
47
|
+
"@plotdb/csscope",
|
|
48
|
+
"@plotdb/rescope",
|
|
49
|
+
"@plotdb/layout",
|
|
50
|
+
"@plotdb/sheet",
|
|
51
|
+
"@plotdb/konfig",
|
|
52
|
+
"@zbryikt/voronoijs",
|
|
53
|
+
"choosefont.js",
|
|
54
|
+
"proxise",
|
|
55
|
+
"ldloader",
|
|
56
|
+
"ldcover",
|
|
57
|
+
"ldcolor",
|
|
58
|
+
"ldslider",
|
|
59
|
+
"wrap-svg-text",
|
|
60
|
+
"ldpalettepicker",
|
|
61
|
+
"ldview"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@loadingio/debounce.js": "^0.0.1",
|
|
66
|
+
"@loadingio/ldcolorpicker": "^2.0.4",
|
|
67
|
+
"@plotdb/block": "^2.1.0",
|
|
68
|
+
"@plotdb/csscope": "^2.0.0",
|
|
69
|
+
"@plotdb/konfig": "^1.2.5",
|
|
70
|
+
"@plotdb/rescope": "^3.0.0",
|
|
71
|
+
"@zbryikt/voronoijs": "^0.0.1",
|
|
72
|
+
"choosefont.js": "^0.0.1",
|
|
73
|
+
"ldcolor": "^0.0.4",
|
|
74
|
+
"ldcover": "^2.0.0",
|
|
75
|
+
"ldpalettepicker": "^3.1.3",
|
|
76
|
+
"ldslider": "^2.0.6",
|
|
77
|
+
"proxise": "^0.1.3",
|
|
78
|
+
"wrap-svg-text": "^0.0.6"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"proxise": "^0.1.2"
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/chart.ls
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
svgns = "http://www.w3.org/2000/svg"
|
|
2
|
+
|
|
3
|
+
parent = (n, s, e) ->
|
|
4
|
+
m = n
|
|
5
|
+
while n and n != e => n = n.parentNode # must under e
|
|
6
|
+
if n != e => return null
|
|
7
|
+
# if no selector - we are testing if s is under e.
|
|
8
|
+
if !s => return n
|
|
9
|
+
# must match s selector
|
|
10
|
+
n = m
|
|
11
|
+
while n and n != e and (!n.matches or (n.matches and !n.matches(s))) => n = n.parentNode
|
|
12
|
+
if n == e and (!e.matches or !e.matches(s)) => return null
|
|
13
|
+
return n
|
|
14
|
+
|
|
15
|
+
aniloop = ->
|
|
16
|
+
@running = false
|
|
17
|
+
@time = { now: 0, last: 0 }
|
|
18
|
+
@fps 60
|
|
19
|
+
@evt-handler = {}
|
|
20
|
+
@
|
|
21
|
+
|
|
22
|
+
aniloop.prototype = Object.create(Object.prototype) <<< do
|
|
23
|
+
on: (n, cb) -> (if Array.isArray(n) => n else [n]).map (n) ~> @evt-handler.[][n].push cb
|
|
24
|
+
fire: (n, ...v) -> for cb in (@evt-handler[n] or []) => cb.apply @, v
|
|
25
|
+
reset: -> @time = {now: 0}
|
|
26
|
+
set-time: (t) -> @time.now = t
|
|
27
|
+
get-time: -> @time.now
|
|
28
|
+
is-running: -> @running
|
|
29
|
+
toggle: ->
|
|
30
|
+
if it? => @running = !it
|
|
31
|
+
if @running => @pause! else @run!
|
|
32
|
+
pause: -> @running = false
|
|
33
|
+
render: -> @fire \tick, @time.now
|
|
34
|
+
run: (fps) ->
|
|
35
|
+
if fps? => @fps fps
|
|
36
|
+
if @running => return
|
|
37
|
+
@running = true
|
|
38
|
+
requestAnimationFrame (t) ~> @handler(@time.last = t, true)
|
|
39
|
+
throttle: (v = true) -> @is-throttled = v
|
|
40
|
+
fps: ->
|
|
41
|
+
if !(it?) => return @_fps_val
|
|
42
|
+
@_fps_val = it >? 0.01
|
|
43
|
+
@_fps_delay = 1000 / @_fps_val
|
|
44
|
+
handler: (t, force = false) ->
|
|
45
|
+
if !@running => return
|
|
46
|
+
@time.now = t
|
|
47
|
+
delay = if @is-throttled => (@_fps_delay >? 1000) else @_fps_delay
|
|
48
|
+
if force or (@time.now - @time.last) >= delay =>
|
|
49
|
+
@time.last = t
|
|
50
|
+
@render!
|
|
51
|
+
requestAnimationFrame (t) ~> @handler t, false
|
|
52
|
+
|
|
53
|
+
visibilityObserver = do
|
|
54
|
+
list: []
|
|
55
|
+
init: ->
|
|
56
|
+
@inited = true
|
|
57
|
+
[h,vc] = if document.hidden? => <[hidden visibilitychange]>
|
|
58
|
+
else if document.msHidden? => <[hidden visibilitychange]>
|
|
59
|
+
else if document.webkiHidden? => <[webkitHidden webkitvisibilitychange]>
|
|
60
|
+
else <[hidden visibilitychange]>
|
|
61
|
+
document.addEventListener vc, debounce 150, (~>
|
|
62
|
+
th = !!document[h]
|
|
63
|
+
#console.log "visibility changed, #{if th => 'enable' else 'disable'} chart aniloop throttling."
|
|
64
|
+
@list.map -> it.throttle th
|
|
65
|
+
), false
|
|
66
|
+
add: (aniloop) ->
|
|
67
|
+
if !@inited => @init!
|
|
68
|
+
@list.push aniloop
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
resizeObserver = do
|
|
72
|
+
wm: new WeakMap!
|
|
73
|
+
ro: new ResizeObserver (list) ->
|
|
74
|
+
list.map (n) ->
|
|
75
|
+
ret = resizeObserver.wm.get(n.target)
|
|
76
|
+
ret._resize!
|
|
77
|
+
add: (node, obj) ->
|
|
78
|
+
@wm.set node, obj
|
|
79
|
+
@ro.observe node
|
|
80
|
+
# observer won't fire the init event if node is not visible.
|
|
81
|
+
# - nonfixed - offsetParent will be null if it's hidden via the display style property.
|
|
82
|
+
# - fixed - check display property.
|
|
83
|
+
# - reference:
|
|
84
|
+
# - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
|
|
85
|
+
# - https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom
|
|
86
|
+
# thus, if we want to always fire right after node added, we then need:
|
|
87
|
+
# if !node.offsetParent or window.getComputedStyle(node).display == \none => obj._resize!
|
|
88
|
+
|
|
89
|
+
delete: ->
|
|
90
|
+
@ro.unobserve it
|
|
91
|
+
@wm.delete it
|
|
92
|
+
|
|
93
|
+
with-default = (cfg = {}, defcfg = {}) ->
|
|
94
|
+
defcfg = JSON.parse JSON.stringify defcfg
|
|
95
|
+
_ = (cfg, defcfg) ->
|
|
96
|
+
for k,v of defcfg =>
|
|
97
|
+
if !(cfg[k]?) => cfg[k] = v
|
|
98
|
+
else if typeof(cfg[k]) == \object and typeof(v) == \object => with-default(cfg[k], defcfg[k])
|
|
99
|
+
return cfg
|
|
100
|
+
return _ cfg, defcfg
|
|
101
|
+
|
|
102
|
+
# use this for now since we don't want to strictly depends on konfig. ( TBD )
|
|
103
|
+
default-config = (cfg) ->
|
|
104
|
+
_ = (meta, ret = {}) ->
|
|
105
|
+
ctrls = if meta.child => that else meta
|
|
106
|
+
for id,v of meta =>
|
|
107
|
+
if !v.type => _(v, ret{}[id])
|
|
108
|
+
else if v.default => ret[id] = v.default
|
|
109
|
+
return ret
|
|
110
|
+
return _ cfg
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
chart = (opt={}) ->
|
|
114
|
+
@root = if typeof(opt.root) == \string => document.querySelector(opt.root) else opt.root
|
|
115
|
+
@use-sample = if opt.use-sample? => opt.use-sample else true
|
|
116
|
+
@auto-select = if opt.auto-select? => opt.auto-select else true
|
|
117
|
+
@force-init = if opt.force-init => opt.force-init else false
|
|
118
|
+
@prepare-svg = if opt.prepare-svg? => opt.prepare-svg else true
|
|
119
|
+
@layout-opt = if opt.layout? => opt.layout else true
|
|
120
|
+
@delay-render = opt.delay-render or false
|
|
121
|
+
@data-accessor = if opt.data-accessor? => opt.data-accessor else (->if it => it.data._raw else null)
|
|
122
|
+
@raw = opt.raw or null
|
|
123
|
+
@mod = opt.mod or {}
|
|
124
|
+
@cfg = opt.config or {}
|
|
125
|
+
@seed = opt.seed or Math.random!
|
|
126
|
+
@binding = opt.binding or {}
|
|
127
|
+
@evt-handler = {}
|
|
128
|
+
@init = proxise.once ~> @_init!
|
|
129
|
+
# _resize is for and (should only be) used by `ResizeObserver`
|
|
130
|
+
@_resize = null
|
|
131
|
+
|
|
132
|
+
if @auto-select =>
|
|
133
|
+
@root.addEventListener \click, @_select = (e) ~>
|
|
134
|
+
if !(p = parent e.target, '.data', @root) => return
|
|
135
|
+
@fire \select, [{data: @data-accessor(p), node: p}]
|
|
136
|
+
@root.addEventListener \mouseover, @_hover = (e) ~>
|
|
137
|
+
if !(p = ld$.parent e.target, '.data', @root) => return
|
|
138
|
+
@fire \hover, [{data: @data-accessor(p), node: p}]
|
|
139
|
+
|
|
140
|
+
@
|
|
141
|
+
|
|
142
|
+
chart.prototype = Object.create(Object.prototype) <<< do
|
|
143
|
+
# we use `init` to ensure called once, and put the actual code in _init.
|
|
144
|
+
_init: ->
|
|
145
|
+
if window.getComputedStyle(@root).position == \static => @root.style.position = \relative
|
|
146
|
+
layout-root = if @layout-opt =>
|
|
147
|
+
if typeof(@layout-opt) != \object => @root.querySelector(\.pdl-layout)
|
|
148
|
+
else if typeof(@layout-opt.root) == \string => @root.querySelector(@layout-opt.root)
|
|
149
|
+
else if @layout-opt.root => @layout-opt.root
|
|
150
|
+
else null
|
|
151
|
+
else null
|
|
152
|
+
if @prepare-svg and !(@svg = @root.querySelector \svg) =>
|
|
153
|
+
@svg = document.createElementNS(svgns, "svg")
|
|
154
|
+
@svg.style <<< width: "100%", height: "100%"
|
|
155
|
+
if @layout-opt =>
|
|
156
|
+
if !layout-root =>
|
|
157
|
+
console.error "[@plotdb/chart] layout enabled yet layout root isn't configured properly. "
|
|
158
|
+
r = layout-root.querySelector('[data-type=render]')
|
|
159
|
+
if !r => console.warn """
|
|
160
|
+
[@plotdb/chart] prepareSvg and layout is configured at the same time,
|
|
161
|
+
yet render container for layout is not found. svg is thus appended in the root.
|
|
162
|
+
"""
|
|
163
|
+
(r or @root).appendChild @svg
|
|
164
|
+
|
|
165
|
+
# this can be implemented with singleton pattern
|
|
166
|
+
@_aniloop = new aniloop!
|
|
167
|
+
visibilityObserver.add @_aniloop
|
|
168
|
+
@_aniloop.on \tick, ~> @tick!
|
|
169
|
+
p = Promise.resolve!
|
|
170
|
+
.then ~>
|
|
171
|
+
if @raw and Array.isArray(@raw) => return
|
|
172
|
+
if @use-sample => @ <<< (if @mod.sample => @mod.sample! or {} else {}){raw, binding}
|
|
173
|
+
if !@raw => @raw = []
|
|
174
|
+
.then ~>
|
|
175
|
+
defcfg = default-config(@mod.config or {})
|
|
176
|
+
with-default @cfg, defcfg
|
|
177
|
+
if @layout-opt =>
|
|
178
|
+
if !(layout?) => console.warn "[@plotdb/chart] layout-opt is set yet @plotdb/layout is not found."
|
|
179
|
+
else
|
|
180
|
+
if !layout-root =>
|
|
181
|
+
console.error "[@plotdb/chart] cannot determine root node for @plotdb/layout."
|
|
182
|
+
console.error "[@plotdb/chart] config layout properly or disable layout by setting layout to false"
|
|
183
|
+
@layout = new layout(
|
|
184
|
+
if typeof(@layout-opt) == \object => ({} <<< @layout-opt <<< {watch-resize: false})
|
|
185
|
+
else {root: layout-root, watch-resize: false}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# we don't know when resizeObserver will call _resize but we need it to be called.
|
|
189
|
+
# thus, we lock execution here until resizeObserver calls _resize
|
|
190
|
+
_resize-lock = proxise ~> resizeObserver.add(@root, @); return null
|
|
191
|
+
# however, if somewhat resizeObserver won't be called soon ( due to invisibility of node )
|
|
192
|
+
# we then may want to get around it.
|
|
193
|
+
# see also: `resizeObserver.add` above.
|
|
194
|
+
if @force-init and !@root.offsetParent or window.getComputedStyle(@root).display == \none => return
|
|
195
|
+
# - unlocker
|
|
196
|
+
@_resize = -> _resize-lock.resolve!
|
|
197
|
+
# - locker
|
|
198
|
+
_resize-lock!
|
|
199
|
+
.then ~>
|
|
200
|
+
# once resizeObserver called _resize, we temporarily disable it until init is finished.
|
|
201
|
+
@_resize = ~>
|
|
202
|
+
.then ~>
|
|
203
|
+
# if no layout used, we init directly. otherwise we will call mod.init in layout.init
|
|
204
|
+
# we have to do this because:
|
|
205
|
+
# - layout prepare necessary dom which may be used by init
|
|
206
|
+
# - but init may generate necessary dom for layout.update
|
|
207
|
+
init = ~> if @mod.init => @mod.init.apply(@)
|
|
208
|
+
if !@layout => return init!
|
|
209
|
+
layout-lock = proxise ~> Promise.resolve(@layout.init init); return null
|
|
210
|
+
@_layout = ~> layout-lock.resolve!
|
|
211
|
+
@layout.on \render, ~> @_layout!
|
|
212
|
+
layout-lock!
|
|
213
|
+
.then ~>
|
|
214
|
+
# once layout prepared, we temporarily disable layout update event until init is finished.
|
|
215
|
+
@_layout = ~>
|
|
216
|
+
Promise.resolve!
|
|
217
|
+
.then ~>
|
|
218
|
+
# if for any reason user doesn't want to render right after init
|
|
219
|
+
# then we just lock it until set-raw.
|
|
220
|
+
if !@delay-render => return
|
|
221
|
+
@render-lock = proxise ~>
|
|
222
|
+
@render-lock!
|
|
223
|
+
.then ~>
|
|
224
|
+
# init is finished and we are prepared to render.
|
|
225
|
+
@_resize = ~> @resize!; @render!
|
|
226
|
+
@_layout = ~> @parse!; @bind!; @resize!; @render!
|
|
227
|
+
@parse!
|
|
228
|
+
@bind!
|
|
229
|
+
@resize!
|
|
230
|
+
@render!
|
|
231
|
+
# explicit return non-promise so init is declared as finished
|
|
232
|
+
# and won't block following codes.
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
parse: ->
|
|
236
|
+
@data = @raw.map (r,i) ~>
|
|
237
|
+
ret = {}
|
|
238
|
+
[{k,v} for k,v of @binding].map ({k,v}) ~>
|
|
239
|
+
if !v => return
|
|
240
|
+
ret[k] = if @mod.{}dimension{}[k].multiple => v.map -> r[it.key]
|
|
241
|
+
else r[v.key]
|
|
242
|
+
t = @mod.{}dimension{}[k].type
|
|
243
|
+
# TODO Since 'RC' is also possible, we should add and check the matched type instead of supported type.
|
|
244
|
+
if /[RIT]/.exec(t) and !/[CN]/.exec(t) =>
|
|
245
|
+
ret[k] = if Array.isArray(ret[k]) => ret[k].map(->parseFloat(it)) else parseFloat(ret[k])
|
|
246
|
+
ret._raw = r
|
|
247
|
+
ret._idx = i
|
|
248
|
+
ret
|
|
249
|
+
if @mod.parse => @mod.parse.apply @
|
|
250
|
+
bind: -> if @mod.bind => @mod.bind.apply @
|
|
251
|
+
start: ->
|
|
252
|
+
@_aniloop.run!
|
|
253
|
+
if @mod.start => @mod.start.apply @
|
|
254
|
+
stop: ->
|
|
255
|
+
@_aniloop.pause!
|
|
256
|
+
if @mod.stop => @mod.stop.apply @
|
|
257
|
+
resize: ->
|
|
258
|
+
# layout resize is disabled to prevent unnecessary rendering.
|
|
259
|
+
# however, we still need layout to be update ( without firing render event )
|
|
260
|
+
if @layout => @layout.update false
|
|
261
|
+
@box = @root.getBoundingClientRect!
|
|
262
|
+
if @mod.resize => @mod.resize.apply @
|
|
263
|
+
tick: -> if @mod.tick => @mod.tick.apply @
|
|
264
|
+
render: -> if @mod.render => @mod.render.apply @
|
|
265
|
+
destroy: ->
|
|
266
|
+
Promise.resolve!then ~>
|
|
267
|
+
if @_select => @root.removeEventListener \click, @_select
|
|
268
|
+
if @_hover => @root.removeEventListener \mouseover, @_hover
|
|
269
|
+
resizeObserver.delete @root
|
|
270
|
+
if @layout => @layout.destroy!
|
|
271
|
+
@stop!
|
|
272
|
+
if @mod.destroy => return @mod.destroy.apply @
|
|
273
|
+
|
|
274
|
+
config: (opt) ->
|
|
275
|
+
if !(opt? and opt) => return @cfg
|
|
276
|
+
@cfg <<< opt
|
|
277
|
+
|
|
278
|
+
set-bind: (n,b) ->
|
|
279
|
+
@binding[n] = b
|
|
280
|
+
@parse!
|
|
281
|
+
@bind!
|
|
282
|
+
@resize!
|
|
283
|
+
@render!
|
|
284
|
+
|
|
285
|
+
set-raw: (raw, binding, reinit = false) ->
|
|
286
|
+
if !Array.isArray(raw) => {raw,binding,reinit} = raw
|
|
287
|
+
if raw? => @raw = raw
|
|
288
|
+
if binding? => @binding = binding
|
|
289
|
+
# if there is render-lock, then we are still in initing process and no need to reinit.
|
|
290
|
+
# reinit is only needed if there is no render-lock
|
|
291
|
+
if @render-lock =>
|
|
292
|
+
@render-lock.resolve!
|
|
293
|
+
@render-lock = null
|
|
294
|
+
else
|
|
295
|
+
if reinit and @mod.init => @mod.init.apply(@)
|
|
296
|
+
@parse!
|
|
297
|
+
@bind!
|
|
298
|
+
@resize!
|
|
299
|
+
@render!
|
|
300
|
+
on: (n, cb) -> (if Array.isArray(n) => n else [n]).map (n) ~> @evt-handler.[][n].push cb
|
|
301
|
+
fire: (n, ...v) -> for cb in (@evt-handler[n] or []) => cb.apply @, v
|
|
302
|
+
filter: (filters = {}, internal = false) ->
|
|
303
|
+
bs = []
|
|
304
|
+
for k,v of @binding =>
|
|
305
|
+
u = if Array.isArray(v) => v else [v]
|
|
306
|
+
u = u.map (v) -> {k,v}
|
|
307
|
+
bs ++= u
|
|
308
|
+
for k,v of filters =>
|
|
309
|
+
bs
|
|
310
|
+
.filter(-> it.k == k)
|
|
311
|
+
.map -> it.v.filter = v
|
|
312
|
+
if internal => @fire \filter, filters
|
|
313
|
+
if @mod.filter => @mod.filter.apply @, [filters, internal]
|
|
314
|
+
@parse!
|
|
315
|
+
@bind!
|
|
316
|
+
@resize!
|
|
317
|
+
@render!
|
|
318
|
+
|
|
319
|
+
if module? => module.exports = chart
|
|
320
|
+
else if window? => window.chart = chart
|
package/src/chart.styl
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
books
|
|
2
|
+
business
|
|
3
|
+
education
|
|
4
|
+
entertainment
|
|
5
|
+
finance
|
|
6
|
+
food & drink
|
|
7
|
+
games
|
|
8
|
+
health & fitness
|
|
9
|
+
lifestyle
|
|
10
|
+
medical
|
|
11
|
+
music
|
|
12
|
+
navigation
|
|
13
|
+
news
|
|
14
|
+
photography
|
|
15
|
+
productivity
|
|
16
|
+
social network
|
|
17
|
+
sports
|
|
18
|
+
travel
|
|
19
|
+
utilities
|
|
20
|
+
weather
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
apricot
|
|
2
|
+
pomegranate
|
|
3
|
+
kiwi
|
|
4
|
+
mango
|
|
5
|
+
pear
|
|
6
|
+
watermelon
|
|
7
|
+
strawberry
|
|
8
|
+
nectarine
|
|
9
|
+
blackberry
|
|
10
|
+
raspberry
|
|
11
|
+
boysenberry
|
|
12
|
+
dragonfruit
|
|
13
|
+
lemon
|
|
14
|
+
cherry
|
|
15
|
+
satsuma
|
|
16
|
+
guava
|
|
17
|
+
orange
|
|
18
|
+
apple
|
|
19
|
+
kumquat
|
|
20
|
+
cantaloupe
|
|
21
|
+
grape
|
|
22
|
+
clementine
|
|
23
|
+
grapefruit
|
|
24
|
+
lime
|
|
25
|
+
tangerine
|
|
26
|
+
fig
|
|
27
|
+
peach
|
|
28
|
+
coconut
|
|
29
|
+
pineapple
|
|
30
|
+
plum
|
|
31
|
+
honeydew
|
|
32
|
+
papaya
|
|
33
|
+
starfruit
|
|
34
|
+
jujube
|
|
35
|
+
blueberry
|
|
36
|
+
banana
|
|
37
|
+
passon fruit
|
|
38
|
+
avocado
|
|
39
|
+
huckleberry
|