@preschen/gen-vis 0.4.0 → 0.6.0

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/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # gen-vis
2
+
3
+ A declarative d3 visualisation library: a chart is described by a JSON
4
+ definition (the *def*) and a CSV or JSON data file. Line, point, bar, stacked
5
+ bar and area charts with legends, hover, facets and form elements are
6
+ supported.
7
+
8
+ ## Usage
9
+
10
+ ### Standalone script
11
+
12
+ `npm run lib` builds `dist-lib/gen-vis-<version>.js`. It includes Vue, d3 and
13
+ the styles, and provides two global functions:
14
+
15
+ ```html
16
+ <div class="genVis" data-def-file="/data/bev/def.json"></div>
17
+
18
+ <script src="gen-vis-0.6.0.js"></script>
19
+ <script>mountGenVisByClass('genVis')</script>
20
+ ```
21
+
22
+ `mountGenVisByClass(className)` mounts all elements with the class which are
23
+ not mounted yet, `mountGenVisElement(element)` mounts a single element. The
24
+ props are taken from the `data-` attributes, see below.
25
+
26
+ ### Vue component
27
+
28
+ ```js
29
+ import GenVis from '@preschen/gen-vis';
30
+
31
+ app.use(GenVis);
32
+ ```
33
+
34
+ ```html
35
+ <GenVis def-file="/data/bev/def.json"/>
36
+ <GenVis :def="def" :data="rows"/>
37
+ ```
38
+
39
+ `import { GenVis } from '@preschen/gen-vis'` registers the component
40
+ locally instead. `vue` is a peer dependency, the package uses the Vue of the
41
+ application. `mountGenVisElement` and `mountGenVisByClass` are exported as
42
+ well.
43
+
44
+ ### Props
45
+
46
+ | Prop | Attribute | Description |
47
+ |------------|-----------------|-------------|
48
+ | `defFile` | `data-def-file` | url of the definition |
49
+ | `def` | `data-def` | the definition, an object or a JSON string |
50
+ | `data` | `data-data` | the data as rows or a CSV/JSON string, if not given it is loaded from the `data` url of the definition |
51
+ | `debug` | `data-debug` | shows the prepared definition |
52
+
53
+ Every visualisation has its own state, several of them can be used on a page.
54
+ If the props change, the visualisation is loaded again. Errors are shown in
55
+ place of the visualisation.
56
+
57
+ ### Styles
58
+
59
+ The styles of the package are in the cascade layer `gen-vis`, so every style
60
+ of the page overrides them, regardless of its specificity and of the order of
61
+ the styles, e.g.:
62
+
63
+ ```css
64
+ .formElement { display: block; margin: 6px 4px; }
65
+ .vis-header .subtitle { font-size: 15px; }
66
+ ```
67
+
68
+ The main classes are `vis`, `vis-header` (with `title` and `subtitle`),
69
+ `vis-form-elements` (with a `formElement` for every form element),
70
+ `vis-legends`, `facet-title` and `vis-footer`. Styles of the page which are in
71
+ a cascade layer themselves only override the package if their layer is
72
+ declared after it, e.g. `@layer gen-vis, page;`.
73
+
74
+ ## Definition
75
+
76
+ ```json
77
+ {
78
+ "parent": "../shared/def.json",
79
+ "data": "data.csv",
80
+ "options": { ... },
81
+ "globals": { ... },
82
+ "mapping": { ... },
83
+ "plot": [ ... ],
84
+ "facets": { ... },
85
+ "formElements": [ ... ]
86
+ }
87
+ ```
88
+
89
+ Relative urls are resolved against the definition referencing them, e.g.
90
+ `data.csv` is next to the definition file. A definition is deep merged into
91
+ its `parent` (and its parent into its own parent), arrays are replaced, not
92
+ merged. The merged definition is checked for common mistakes, e.g. unknown
93
+ plot types, the findings are logged as warnings in the console.
94
+
95
+ The package contains a JSON Schema of the definitions, `schema.json`. With
96
+ `"$schema"` in a definition, editors like VS Code complete and check it:
97
+
98
+ ```json
99
+ {
100
+ "$schema": "https://unpkg.com/@preschen/gen-vis/schema.json",
101
+ ...
102
+ }
103
+ ```
104
+
105
+ or with a local path, e.g. `"./node_modules/@preschen/gen-vis/schema.json"`.
106
+ The schema also allows parts of definitions, e.g. definitions with a `parent`.
107
+
108
+ ### `options`
109
+
110
+ `title`, `subtitle`, `footer` (HTML), `width` (the width of the container if
111
+ not given), `height` and `margins` (`{"top", "right", "bottom", "left"}` in
112
+ pixels). `height` can be a prop based on `totalWidth`, see [props](#props-1).
113
+
114
+ `locale` sets the number and date formats of the axes and the hover, also of
115
+ axes without `format`: `de` (the default) or `en`, or an object with a `base`
116
+ locale and the parts which are changed, see d3's
117
+ [formatLocale](https://d3js.org/d3-format#formatLocale) and
118
+ [timeFormatLocale](https://d3js.org/d3-time-format#timeFormatLocale):
119
+
120
+ ```json
121
+ "locale": { "base": "de", "number": { "currency": ["", " EUR"] } }
122
+ ```
123
+
124
+ `timeTicks` of the object are the tick formats of time axes without `format`,
125
+ by the interval of the date (`millisecond`, `second`, `minute`, `hour`, `day`,
126
+ `week`, `month`, `year`).
127
+
128
+ `fontFamily` sets the font, by default the css variable
129
+ `--gen-vis-font-family` or Century Gothic, so the font of all visualisations of
130
+ a page can be set with css:
131
+
132
+ ```css
133
+ .vis { --gen-vis-font-family: Arial, sans-serif; }
134
+ ```
135
+
136
+ ### `mapping`
137
+
138
+ Maps columns of the data to named dimensions, the plots and all other parts of
139
+ the definition refer to these names.
140
+
141
+ ```json
142
+ "y": {
143
+ "column": "value",
144
+ "type": "numeric",
145
+ "stacked": false,
146
+ "scale": { "type": "linear", "orientation": "vertical", "domain": [0, null] },
147
+ "axis": { "position": "left", "ticks": 8, "format": ".1f", "grid": true, "title": { "name": "€/l", "offset": 50 } },
148
+ "hover": { "format": ".2f" }
149
+ }
150
+ ```
151
+
152
+ - `type`: `numeric`, `date` or `categorical`. Numeric and date values which
153
+ are missing or invalid are `null`: they are gaps in lines and areas, points
154
+ and bars are not drawn, and they are not shown in the hover. Dates are
155
+ parsed with `Date.parse` or are timestamps.
156
+ - `scale`: `type` is a d3 scale (`linear`, `time`, `log`, `point`, `band`,
157
+ ...), continuous scales need a `numeric` or `date` type, `point` and `band`
158
+ a `categorical` one. `orientation` (`horizontal` or `vertical`) places the
159
+ scale on the plot. `domain` fixes the domain, `null` entries are taken from
160
+ the data. `domainRel` (relative to the domain) and `domainAbs` (absolute)
161
+ extend it. `padding` for categorical scales.
162
+ - `axis`: `position` (`top`, `bottom`, `left`, `right`), `ticks`, `values`
163
+ (fixed ticks), `format` (d3 number or time format), `rotate` (the angle of
164
+ the labels in degrees, positive counterclockwise, negative clockwise), `grid`
165
+ (lines at the ticks), `title` (`{"name", "offset"}`) and `padding`.
166
+ - `hover`: the hover shows the values of the vertical axis at the position of
167
+ the mouse. On touch devices it is shown by a tap and stays until a tap
168
+ outside of the plot, horizontal swipes move it, vertical ones scroll the
169
+ page. `format` of the horizontal and vertical axis, it defaults to the
170
+ axis format. For categorical mappings `props` are the columns of the entries,
171
+ `name` by default.
172
+ - `props`: the categories and their props, e.g. colors. `common` props are
173
+ used for all `manual` entries, `name` and `visible` are set by default. Only
174
+ visible categories are shown. The order of the `manual` entries is the order
175
+ of the legend, the facets and the stacks, not the order of the rows. Keys
176
+ which are integers, e.g. years, are ordered ascending by JavaScript.
177
+ - `legend`: a toggle for every category, `symbol` draws svg `elements` (with
178
+ props) of the given `size` before the name.
179
+ - `stacked`: stacks the values of a vertical axis, see `stackedBar`, the first
180
+ category is at the bottom.
181
+
182
+ ### `plot`
183
+
184
+ A plot or a list of plots, drawn in order:
185
+
186
+ ```json
187
+ {
188
+ "type": "svg:path",
189
+ "categories": ["nuts"],
190
+ "props": {
191
+ "stroke": "@color",
192
+ "fill": "none",
193
+ "d": { "x": "@x:scaled", "y": "@y:scaled" },
194
+ "highlight-stroke-width": "@highlight-stroke-width"
195
+ }
196
+ }
197
+ ```
198
+
199
+ - `type`: `svg:path` (a line per group, `d` with `x` and `y`), `base:area`
200
+ (an area per group, `d` with `x`, `y0` and `y1`), `svg:circle`, `svg:rect`,
201
+ `svg:line`, `svg:text` (an element per row), `bar` (props `cx` and
202
+ `height`, `width` defaults to the step of a categorical scale) and
203
+ `stackedBar` (props `x`, `y` and `width`).
204
+ - `categories`: the rows are grouped by these mappings, the props of their
205
+ categories are available in the plot props.
206
+ - `props`: svg attributes (and `text`). Props starting with `highlight-` are
207
+ used for the elements of the category under the mouse or the legend entry.
208
+
209
+ ### Props
210
+
211
+ Most values of a definition can be props:
212
+
213
+ - fixed values, e.g. `3` or `"none"`
214
+ - references `"@name"`: in a plot a column of the row (e.g. `@x`), the scaled
215
+ value (`@x:scaled`, `@x:scaled:0` for the position of 0, `@y:st:e:scaled`
216
+ for the end of a stacked value), a prop of the categories (e.g. `@color`) or
217
+ the size of the plot (`@width`, `@innerWidth`, `@height`, `@innerHeight`)
218
+ - `{"prop": "relative", "ref": "innerWidth", "ratio": 0.01}`: a ratio of a
219
+ reference
220
+ - `{"prop": "steps", "ref": "totalWidth", "steps": [{"cut": 0, "value": 220}, {"cut": 550, "value": 250}]}`:
221
+ the value of the last step with a cut below the reference
222
+
223
+ Objects without `prop` are nested props, e.g. `d` of a path.
224
+
225
+ ### `facets`
226
+
227
+ A plot for every category of `dim` (the name of a mapping with `props`), in
228
+ `cols` columns. The mappings listed in `scales` share their scale across all facets.
229
+ `cols` can be a prop based on `totalWidth`, `scales` a reference to `globals`.
230
+
231
+ ### `formElements` and `globals`
232
+
233
+ Form elements change `globals`, e.g. the shared scales of the facets. An entry
234
+ with a `mapping` also patches the mappings while it is selected, e.g. to switch
235
+ the column of an axis:
236
+
237
+ ```json
238
+ "globals": { "column": "value" },
239
+ "formElements": [{
240
+ "id": "column", "name": "Wert", "ref": "column", "type": "switch",
241
+ "values": [
242
+ { "id": "value", "name": "Wert", "value": "value" },
243
+ { "id": "share", "name": "Anteil", "value": "share", "mapping": { "y": { "column": "share" } } }
244
+ ]
245
+ }]
246
+ ```
247
+
248
+ If the column depends on several form elements, it can be a template of the
249
+ globals, e.g. the values and their shares in the columns `twh`, `co2`,
250
+ `twh.share` and `co2.share`:
251
+
252
+ ```json
253
+ "globals": { "values": "twh", "share": "" },
254
+ "formElements": [{
255
+ "id": "values", "name": "Werte", "ref": "values", "type": "switch",
256
+ "values": [
257
+ { "id": "twh", "name": "TWh", "value": "twh" },
258
+ { "id": "co2", "name": "CO₂", "value": "co2" }
259
+ ]
260
+ }, {
261
+ "id": "share", "name": "Darstellung", "ref": "share", "type": "switch",
262
+ "values": [
263
+ { "id": "abs", "name": "Absolut", "value": "" },
264
+ { "id": "rel", "name": "Anteil", "value": ".share", "mapping": { "y": { "axis": { "format": ".0%" } } } }
265
+ ]
266
+ }],
267
+ "mapping": { "y": { "column": "{values}{share}" } }
268
+ ```
269
+
270
+ `{name}` is replaced by the value of the global, unknown globals are kept and
271
+ reported as warnings.
272
+
273
+ ## Development
274
+
275
+ ```sh
276
+ npm install
277
+ npm run dev # dev server, the definition shown by dev.html is set in src/globals.js
278
+ npm test # unit tests, rendering and schema check of all definitions in data/
279
+ npm run build # es module for bundlers in dist/
280
+ npm run watch # rebuilds dist/ on changes, e.g. for `npm link`
281
+ npm run lib # standalone script in dist-lib/
282
+ npm run deploy # builds and uploads the standalone script
283
+ ```
284
+
285
+ `lib.html` shows the embedding with the dev server. The examples in `data/`
286
+ are rendered by the tests, so they have to stay valid.