@cnrs/hecate-views-charts 0.2.0 → 0.5.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.
@@ -0,0 +1,39 @@
1
+ import { StackedAreaChart } from './stackedareachart.js';
2
+ import * as d3 from 'd3';
3
+
4
+ class Streamgraph extends StackedAreaChart {
5
+
6
+ createYDomain(parsers) {
7
+ return d3.extent(this._stacked_series.flat(2))
8
+ }
9
+
10
+ createStackOffset() { return d3.stackOffsetWiggle; }
11
+ createStackOrder() { return d3.stackOrderInsideOut; }
12
+
13
+ renderVAxis(svg, plot, axis) {
14
+ const xOffset = plot.padding.left;
15
+ const yOffset = plot.padding.top;
16
+ // append the y-axis, while extending 0% & 100% ticks to become top
17
+ // and bottom ticker "main" lines. Add its label, too.
18
+ axis.attr('transform', `translate(${xOffset},${yOffset})`)
19
+ .call(
20
+ d3.axisLeft(plot.vy)
21
+ .ticks(plot.height / 80)
22
+ .tickFormat((d) => Math.abs(d).toLocaleString('en-US'))
23
+ )
24
+ .call(g => g.select('.domain').remove())
25
+ .call(
26
+ g => g.selectAll('.tick line')
27
+ .clone()
28
+ .attr('x2', plot.width)
29
+ .attr('stroke-opacity', 0.1)
30
+ );
31
+ svg.append(() => axis.node());
32
+ }
33
+ }
34
+
35
+
36
+
37
+ customElements.define('hecate-streamgraph', Streamgraph);
38
+
39
+ export { Streamgraph };
@@ -1 +0,0 @@
1
- export { Sample } from './sample.js';
@@ -1,37 +0,0 @@
1
- /**
2
- * Sample web component.
3
- * It just displays a string.
4
- */
5
- class Sample extends HTMLElement {
6
-
7
- constructor() {
8
- super();
9
- this.attachShadow({ mode: 'open' });
10
- this.data = "placeholder";
11
- }
12
- /** Static property returning an array of attributes observed by the browser
13
- */
14
- static get observedAttributes() {
15
- return ['data'];
16
- }
17
- /**
18
- * This is called when an observed attribute is
19
- * defined in the HTML or changed using Javascript.
20
- */
21
- attributeChangedCallback(property, old, now) {
22
- if (old === now) return; // don't bother to render if there is nothing new
23
- this[property] = now;
24
- }
25
-
26
- /**
27
- * Called when the element is connected to a DOM.
28
- * Runs any required rendering.
29
- */
30
- connectedCallback() {
31
- this.shadowRoot.innerHTML += `<p>${this.data}</p>`;
32
- }
33
- }
34
-
35
-
36
-
37
- export { Sample };
@@ -1,193 +0,0 @@
1
- import * as d3 from 'd3';
2
-
3
-
4
- /**
5
- * Scatterplot web component.
6
- * @see https://en.wikipedia.org/wiki/Scatter_plot
7
- * @see https://scottmurray.org/tutorials/d3/
8
- */
9
- class ScatterPlot extends HTMLElement {
10
-
11
- constructor() {
12
- super();
13
- this.attachShadow({ mode: 'open' });
14
- this.hera = {};
15
- this.settings = {};
16
- }
17
- /**
18
- * Static property returning the array of attributes
19
- * observed by the browser.
20
- * @returns {Array<string>} Attribute names
21
- */
22
- static get observedAttributes() {
23
- return ['data-hera', 'data-settings'];
24
- }
25
-
26
- get items() { return this.hera.items || []; }
27
- get entities() { return this.hera.entities || []; }
28
- get properties() { return this.hera.properties || []; }
29
-
30
- get pidx() { return this.settings.x || 'x'; }
31
- get pidy() { return this.settings.y || 'y'; }
32
- get label() { return this.settings.label || 'label'; }
33
- get color() { return this.settings.color || '#7b4c98'; }
34
-
35
- get padding() {
36
- return this.settings.padding
37
- || { top: 3, right: 0, bottom: 10, left: 30 };
38
- }
39
-
40
- /**
41
- * This is called when an observed attribute is
42
- * defined in the HTML or changed using Javascript.
43
- * @param {!string} attribute - Attribute name
44
- * @param old - Previous value for `attribute`
45
- * @param now - Current value for `attribute`
46
- */
47
- attributeChangedCallback(attribute, old, now) {
48
- if (old === now) return; // don't bother if there is nothing new
49
- this[attribute] = now;
50
- // data-* attributes can only be strings, so we cannot deserialize JSON
51
- // in them ; so we use plain object fields to store deserialized values
52
- // we trim the prefix to get the field name, so 'data-x' becomes 'x'
53
- const fieldName = attribute.split('-').slice(1).join('-');
54
- this[fieldName] = JSON.parse(now);
55
- // note that this does not defeat the purpose of using data-* attributes
56
- // in the first place, as if a name clash should happen in the future,
57
- // we could edit this class with no impact on the users code
58
- }
59
-
60
- /**
61
- * Called when the element is connected to a DOM.
62
- * Runs any required rendering.
63
- */
64
- connectedCallback() {
65
- this.width = this.parentElement.clientWidth;
66
- this.height = this.parentElement.clientHeight;
67
- const svgWidth = this.width + this.padding.right + this.padding.left;
68
- const svgHeight = this.height + this.padding.top + this.padding.bottom;
69
-
70
- const container = document.createElement('div');
71
- this.shadowRoot.appendChild(container);
72
- const svg = d3.select(container)
73
- .append('svg')
74
- .attr('viewBox', `0 0 ${svgWidth} ${svgHeight}`)
75
- ;
76
- const {vx, vy} = this.viewport;
77
-
78
- // create a dot for each item in this.items
79
- svg.selectAll('circle')
80
- .data(this.items)
81
- .enter()
82
- .append('circle')
83
- .attr('cx', (item) => vx(this.getItemX(item)))
84
- .attr('cy', (item) => vy(this.getItemY(item)))
85
- .attr('r', (item) => this.getItemRadius(item))
86
- .attr('fill', (item) => this.getItemColor(item))
87
- .append('title')
88
- .text((item) => this.getItemLabel(item))
89
- ;
90
- svg.selectAll('circle')
91
- .on('click', (event) => console.log(event));
92
-
93
- var xAxis = d3.axisBottom(vx).ticks(11);
94
- var yAxis = d3.axisLeft(vy).ticks(2);
95
- const labels = this.getAxesLabels();
96
- svg.append('g')
97
- .attr('transform', `translate(0,${this.height-this.padding.top})`)
98
- .call(xAxis)
99
- .call((g) => g.append('text')
100
- .attr('x', this.width)
101
- .attr('y', 0)
102
- .attr('fill', 'black')
103
- .attr('text-anchor', 'end')
104
- .text(labels.y))
105
- ;
106
- svg.append('g')
107
- .attr('transform', `translate(${this.padding.left},0)`)
108
- .call(yAxis)
109
- .call((g) => g.append('text')
110
- .attr('x', 0)
111
- .attr('y', '1em')
112
- .attr('fill', 'currentColor')
113
- .attr('text-anchor', 'start')
114
- .text(labels.x))
115
- ;
116
- }
117
-
118
- /**
119
- * @callback scale
120
- * @property {number} n - A number inside the scale's domain
121
- * @see https://d3js.org/d3-scale
122
- */
123
-
124
- /**
125
- * This object is a wrapper for two d3.scale objects, which can be used to
126
- * convert coordinates from the graph space to this viewer viewport space.
127
- * @typedef {Object} Viewport
128
- * @property {scale} vx - A scale returning the x (horizontal) coordinate
129
- * @property {scale} vy - A scale returning the y (vertical) coordinate
130
- * @see https://d3js.org/d3-scale
131
- */
132
-
133
- /**
134
- * Property returning this element viewport.
135
- * The viewport is composed of two scale functions, which can be used to
136
- * convert x (resp. y) to a number between 0 and the width (resp. height)
137
- * of the viewer.
138
- * @returns {Viewport} This viewer current viewport
139
- */
140
- get viewport() {
141
- const xMax = d3.max(this.items, (item) => this.getItemX(item));
142
- const yMax = d3.max(this.items, (item) => this.getItemY(item));
143
- // a scale is a function, domain is its input and range is its output
144
- const vx = d3.scaleLinear()
145
- .domain([0, xMax])
146
- .range([this.padding.left, this.width-this.padding.right]);
147
- const vy = d3.scaleLinear()
148
- .domain([0, yMax])
149
- .range([this.height-this.padding.top, this.padding.bottom]);
150
- // y is inverted because having the origin (0;0) of the scatter graph
151
- // in the bottom left corner feels more natural than the top left origin
152
- // which is standard in SVG.
153
- return { vx, vy };
154
- }
155
-
156
- getItemX(item) {
157
- return this.getItemMetadata(item, this.pidx);
158
- }
159
- getItemY(item) {
160
- return this.getItemMetadata(item, this.pidy);
161
- }
162
- getItemRadius(item) {
163
- return 5;
164
- }
165
- getItemColor(item) {
166
- return this.color;
167
- }
168
- getItemLabel(item) {
169
- const name = this.getItemMetadata(item, this.label);
170
- return name ? name : `Missing property '${this.label}'`;
171
- }
172
- getAxesLabels() {
173
- const px = this.getProperty(this.pidx);
174
- const py = this.getProperty(this.pidy);
175
- return {
176
- x: px ? px.name : `Missing property '${this.pidx}'`,
177
- y: py ? py.name : `Missing property '${this.pidy}'`,
178
- };
179
- }
180
-
181
- getItemMetadata(item, pid) {
182
- const found = item.metadata.find((m) => m.pid === pid);
183
- return found ? found.value : null;
184
- }
185
- getProperty(pid) {
186
- const found = this.properties.find((p) => p['@id'] === pid);
187
- return found ? found : null;
188
- }
189
- }
190
-
191
-
192
-
193
- export { ScatterPlot };