@teamflojo/floimg-d3 0.2.0 → 0.2.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/LICENSE +21 -0
- package/README.md +175 -181
- package/package.json +12 -13
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Flojo, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
D3 data visualization generator for floimg using server-side rendering.
|
|
4
4
|
|
|
5
|
+
## Standing on the Shoulders of Giants
|
|
6
|
+
|
|
7
|
+
This plugin is a thin wrapper around [D3.js](https://d3js.org/). We don't abstract or limit the underlying library—your render function has full access to D3's API.
|
|
8
|
+
|
|
9
|
+
- **Full D3 power**: Complete access to all D3 capabilities
|
|
10
|
+
- **Native API**: Write real D3 code, not a FloImg abstraction
|
|
11
|
+
- **Their docs are your docs**: See [D3 documentation](https://d3js.org/) and [D3 gallery](https://observablehq.com/@d3/gallery)
|
|
12
|
+
|
|
13
|
+
FloImg orchestrates the workflow (generate → transform → save). D3 does what it does best.
|
|
14
|
+
|
|
5
15
|
## Installation
|
|
6
16
|
|
|
7
17
|
```bash
|
|
@@ -11,41 +21,36 @@ npm install @teamflojo/floimg @teamflojo/floimg-d3
|
|
|
11
21
|
## Usage
|
|
12
22
|
|
|
13
23
|
```typescript
|
|
14
|
-
import createClient from
|
|
15
|
-
import d3viz from
|
|
24
|
+
import createClient from "@teamflojo/floimg";
|
|
25
|
+
import d3viz from "@teamflojo/floimg-d3";
|
|
16
26
|
|
|
17
27
|
const floimg = createClient();
|
|
18
28
|
floimg.registerGenerator(d3viz());
|
|
19
29
|
|
|
20
30
|
// Create a bar chart
|
|
21
31
|
const chart = await floimg.generate({
|
|
22
|
-
generator:
|
|
32
|
+
generator: "d3",
|
|
23
33
|
params: {
|
|
24
34
|
width: 600,
|
|
25
35
|
height: 400,
|
|
26
36
|
render: (svg, d3, data) => {
|
|
27
37
|
// Use D3 directly - full API available
|
|
28
|
-
svg
|
|
38
|
+
svg
|
|
39
|
+
.selectAll("rect")
|
|
29
40
|
.data(data)
|
|
30
|
-
.join(
|
|
31
|
-
.attr(
|
|
32
|
-
.attr(
|
|
33
|
-
.attr(
|
|
34
|
-
.attr(
|
|
35
|
-
.attr(
|
|
41
|
+
.join("rect")
|
|
42
|
+
.attr("x", (d, i) => i * 60)
|
|
43
|
+
.attr("y", (d) => 400 - d.value * 3)
|
|
44
|
+
.attr("width", 50)
|
|
45
|
+
.attr("height", (d) => d.value * 3)
|
|
46
|
+
.attr("fill", "steelblue");
|
|
36
47
|
},
|
|
37
|
-
data: [
|
|
38
|
-
|
|
39
|
-
{ value: 80 },
|
|
40
|
-
{ value: 45 },
|
|
41
|
-
{ value: 60 },
|
|
42
|
-
{ value: 20 }
|
|
43
|
-
]
|
|
44
|
-
}
|
|
48
|
+
data: [{ value: 30 }, { value: 80 }, { value: 45 }, { value: 60 }, { value: 20 }],
|
|
49
|
+
},
|
|
45
50
|
});
|
|
46
51
|
|
|
47
52
|
// Upload to S3
|
|
48
|
-
await floimg.save(chart,
|
|
53
|
+
await floimg.save(chart, "./output/bar.svg");
|
|
49
54
|
```
|
|
50
55
|
|
|
51
56
|
## Examples
|
|
@@ -54,7 +59,7 @@ await floimg.save(chart, './output/bar.svg');
|
|
|
54
59
|
|
|
55
60
|
```typescript
|
|
56
61
|
const barChart = await floimg.generate({
|
|
57
|
-
generator:
|
|
62
|
+
generator: "d3",
|
|
58
63
|
params: {
|
|
59
64
|
width: 800,
|
|
60
65
|
height: 400,
|
|
@@ -63,41 +68,39 @@ const barChart = await floimg.generate({
|
|
|
63
68
|
const width = 800 - margin.left - margin.right;
|
|
64
69
|
const height = 400 - margin.top - margin.bottom;
|
|
65
70
|
|
|
66
|
-
const g = svg.append(
|
|
67
|
-
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
71
|
+
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
68
72
|
|
|
69
|
-
const x = d3
|
|
70
|
-
.
|
|
73
|
+
const x = d3
|
|
74
|
+
.scaleBand()
|
|
75
|
+
.domain(data.map((d) => d.name))
|
|
71
76
|
.range([0, width])
|
|
72
77
|
.padding(0.1);
|
|
73
78
|
|
|
74
|
-
const y = d3
|
|
75
|
-
.
|
|
79
|
+
const y = d3
|
|
80
|
+
.scaleLinear()
|
|
81
|
+
.domain([0, d3.max(data, (d) => d.value)])
|
|
76
82
|
.range([height, 0]);
|
|
77
83
|
|
|
78
|
-
g.selectAll(
|
|
84
|
+
g.selectAll("rect")
|
|
79
85
|
.data(data)
|
|
80
|
-
.join(
|
|
81
|
-
.attr(
|
|
82
|
-
.attr(
|
|
83
|
-
.attr(
|
|
84
|
-
.attr(
|
|
85
|
-
.attr(
|
|
86
|
-
|
|
87
|
-
g.append(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
g.append('g')
|
|
92
|
-
.call(d3.axisLeft(y));
|
|
86
|
+
.join("rect")
|
|
87
|
+
.attr("x", (d) => x(d.name))
|
|
88
|
+
.attr("y", (d) => y(d.value))
|
|
89
|
+
.attr("width", x.bandwidth())
|
|
90
|
+
.attr("height", (d) => height - y(d.value))
|
|
91
|
+
.attr("fill", "steelblue");
|
|
92
|
+
|
|
93
|
+
g.append("g").attr("transform", `translate(0,${height})`).call(d3.axisBottom(x));
|
|
94
|
+
|
|
95
|
+
g.append("g").call(d3.axisLeft(y));
|
|
93
96
|
},
|
|
94
97
|
data: [
|
|
95
|
-
{ name:
|
|
96
|
-
{ name:
|
|
97
|
-
{ name:
|
|
98
|
-
{ name:
|
|
99
|
-
]
|
|
100
|
-
}
|
|
98
|
+
{ name: "Q1", value: 120 },
|
|
99
|
+
{ name: "Q2", value: 200 },
|
|
100
|
+
{ name: "Q3", value: 150 },
|
|
101
|
+
{ name: "Q4", value: 170 },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
101
104
|
});
|
|
102
105
|
```
|
|
103
106
|
|
|
@@ -105,7 +108,7 @@ const barChart = await floimg.generate({
|
|
|
105
108
|
|
|
106
109
|
```typescript
|
|
107
110
|
const lineChart = await floimg.generate({
|
|
108
|
-
generator:
|
|
111
|
+
generator: "d3",
|
|
109
112
|
params: {
|
|
110
113
|
width: 800,
|
|
111
114
|
height: 400,
|
|
@@ -114,34 +117,33 @@ const lineChart = await floimg.generate({
|
|
|
114
117
|
const width = 800 - margin.left - margin.right;
|
|
115
118
|
const height = 400 - margin.top - margin.bottom;
|
|
116
119
|
|
|
117
|
-
const g = svg.append(
|
|
118
|
-
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
120
|
+
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
119
121
|
|
|
120
|
-
const x = d3
|
|
122
|
+
const x = d3
|
|
123
|
+
.scaleLinear()
|
|
121
124
|
.domain([0, data.length - 1])
|
|
122
125
|
.range([0, width]);
|
|
123
126
|
|
|
124
|
-
const y = d3
|
|
125
|
-
.
|
|
127
|
+
const y = d3
|
|
128
|
+
.scaleLinear()
|
|
129
|
+
.domain([0, d3.max(data, (d) => d.value)])
|
|
126
130
|
.range([height, 0]);
|
|
127
131
|
|
|
128
|
-
const line = d3
|
|
132
|
+
const line = d3
|
|
133
|
+
.line()
|
|
129
134
|
.x((d, i) => x(i))
|
|
130
|
-
.y(d => y(d.value));
|
|
135
|
+
.y((d) => y(d.value));
|
|
131
136
|
|
|
132
|
-
g.append(
|
|
137
|
+
g.append("path")
|
|
133
138
|
.datum(data)
|
|
134
|
-
.attr(
|
|
135
|
-
.attr(
|
|
136
|
-
.attr(
|
|
137
|
-
.attr(
|
|
139
|
+
.attr("fill", "none")
|
|
140
|
+
.attr("stroke", "steelblue")
|
|
141
|
+
.attr("stroke-width", 2)
|
|
142
|
+
.attr("d", line);
|
|
138
143
|
|
|
139
|
-
g.append(
|
|
140
|
-
.attr('transform', `translate(0,${height})`)
|
|
141
|
-
.call(d3.axisBottom(x));
|
|
144
|
+
g.append("g").attr("transform", `translate(0,${height})`).call(d3.axisBottom(x));
|
|
142
145
|
|
|
143
|
-
g.append(
|
|
144
|
-
.call(d3.axisLeft(y));
|
|
146
|
+
g.append("g").call(d3.axisLeft(y));
|
|
145
147
|
},
|
|
146
148
|
data: [
|
|
147
149
|
{ value: 30 },
|
|
@@ -149,9 +151,9 @@ const lineChart = await floimg.generate({
|
|
|
149
151
|
{ value: 45 },
|
|
150
152
|
{ value: 80 },
|
|
151
153
|
{ value: 70 },
|
|
152
|
-
{ value: 90 }
|
|
153
|
-
]
|
|
154
|
-
}
|
|
154
|
+
{ value: 90 },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
155
157
|
});
|
|
156
158
|
```
|
|
157
159
|
|
|
@@ -159,7 +161,7 @@ const lineChart = await floimg.generate({
|
|
|
159
161
|
|
|
160
162
|
```typescript
|
|
161
163
|
const pieChart = await floimg.generate({
|
|
162
|
-
generator:
|
|
164
|
+
generator: "d3",
|
|
163
165
|
params: {
|
|
164
166
|
width: 500,
|
|
165
167
|
height: 500,
|
|
@@ -168,39 +170,34 @@ const pieChart = await floimg.generate({
|
|
|
168
170
|
const height = 500;
|
|
169
171
|
const radius = Math.min(width, height) / 2;
|
|
170
172
|
|
|
171
|
-
const g = svg.append(
|
|
172
|
-
.attr('transform', `translate(${width / 2},${height / 2})`);
|
|
173
|
+
const g = svg.append("g").attr("transform", `translate(${width / 2},${height / 2})`);
|
|
173
174
|
|
|
174
175
|
const color = d3.scaleOrdinal(d3.schemeCategory10);
|
|
175
176
|
|
|
176
|
-
const pie = d3.pie()
|
|
177
|
-
.value(d => d.value);
|
|
177
|
+
const pie = d3.pie().value((d) => d.value);
|
|
178
178
|
|
|
179
|
-
const arc = d3.arc()
|
|
180
|
-
.innerRadius(0)
|
|
181
|
-
.outerRadius(radius);
|
|
179
|
+
const arc = d3.arc().innerRadius(0).outerRadius(radius);
|
|
182
180
|
|
|
183
|
-
const arcs = g.selectAll(
|
|
184
|
-
.data(pie(data))
|
|
185
|
-
.join('g')
|
|
186
|
-
.attr('class', 'arc');
|
|
181
|
+
const arcs = g.selectAll("arc").data(pie(data)).join("g").attr("class", "arc");
|
|
187
182
|
|
|
188
|
-
arcs
|
|
189
|
-
.
|
|
190
|
-
.attr(
|
|
183
|
+
arcs
|
|
184
|
+
.append("path")
|
|
185
|
+
.attr("d", arc)
|
|
186
|
+
.attr("fill", (d, i) => color(i));
|
|
191
187
|
|
|
192
|
-
arcs
|
|
193
|
-
.
|
|
194
|
-
.attr(
|
|
195
|
-
.text
|
|
188
|
+
arcs
|
|
189
|
+
.append("text")
|
|
190
|
+
.attr("transform", (d) => `translate(${arc.centroid(d)})`)
|
|
191
|
+
.attr("text-anchor", "middle")
|
|
192
|
+
.text((d) => d.data.label);
|
|
196
193
|
},
|
|
197
194
|
data: [
|
|
198
|
-
{ label:
|
|
199
|
-
{ label:
|
|
200
|
-
{ label:
|
|
201
|
-
{ label:
|
|
202
|
-
]
|
|
203
|
-
}
|
|
195
|
+
{ label: "A", value: 30 },
|
|
196
|
+
{ label: "B", value: 70 },
|
|
197
|
+
{ label: "C", value: 45 },
|
|
198
|
+
{ label: "D", value: 60 },
|
|
199
|
+
],
|
|
200
|
+
},
|
|
204
201
|
});
|
|
205
202
|
```
|
|
206
203
|
|
|
@@ -208,7 +205,7 @@ const pieChart = await floimg.generate({
|
|
|
208
205
|
|
|
209
206
|
```typescript
|
|
210
207
|
const scatterPlot = await floimg.generate({
|
|
211
|
-
generator:
|
|
208
|
+
generator: "d3",
|
|
212
209
|
params: {
|
|
213
210
|
width: 600,
|
|
214
211
|
height: 400,
|
|
@@ -217,41 +214,39 @@ const scatterPlot = await floimg.generate({
|
|
|
217
214
|
const width = 600 - margin.left - margin.right;
|
|
218
215
|
const height = 400 - margin.top - margin.bottom;
|
|
219
216
|
|
|
220
|
-
const g = svg.append(
|
|
221
|
-
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
217
|
+
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
222
218
|
|
|
223
|
-
const x = d3
|
|
224
|
-
.
|
|
219
|
+
const x = d3
|
|
220
|
+
.scaleLinear()
|
|
221
|
+
.domain([0, d3.max(data, (d) => d.x)])
|
|
225
222
|
.range([0, width]);
|
|
226
223
|
|
|
227
|
-
const y = d3
|
|
228
|
-
.
|
|
224
|
+
const y = d3
|
|
225
|
+
.scaleLinear()
|
|
226
|
+
.domain([0, d3.max(data, (d) => d.y)])
|
|
229
227
|
.range([height, 0]);
|
|
230
228
|
|
|
231
|
-
g.selectAll(
|
|
229
|
+
g.selectAll("circle")
|
|
232
230
|
.data(data)
|
|
233
|
-
.join(
|
|
234
|
-
.attr(
|
|
235
|
-
.attr(
|
|
236
|
-
.attr(
|
|
237
|
-
.attr(
|
|
238
|
-
.attr(
|
|
239
|
-
|
|
240
|
-
g.append(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
g.append('g')
|
|
245
|
-
.call(d3.axisLeft(y));
|
|
231
|
+
.join("circle")
|
|
232
|
+
.attr("cx", (d) => x(d.x))
|
|
233
|
+
.attr("cy", (d) => y(d.y))
|
|
234
|
+
.attr("r", 5)
|
|
235
|
+
.attr("fill", "steelblue")
|
|
236
|
+
.attr("opacity", 0.7);
|
|
237
|
+
|
|
238
|
+
g.append("g").attr("transform", `translate(0,${height})`).call(d3.axisBottom(x));
|
|
239
|
+
|
|
240
|
+
g.append("g").call(d3.axisLeft(y));
|
|
246
241
|
},
|
|
247
242
|
data: [
|
|
248
243
|
{ x: 10, y: 20 },
|
|
249
244
|
{ x: 20, y: 40 },
|
|
250
245
|
{ x: 30, y: 25 },
|
|
251
246
|
{ x: 40, y: 60 },
|
|
252
|
-
{ x: 50, y: 45 }
|
|
253
|
-
]
|
|
254
|
-
}
|
|
247
|
+
{ x: 50, y: 45 },
|
|
248
|
+
],
|
|
249
|
+
},
|
|
255
250
|
});
|
|
256
251
|
```
|
|
257
252
|
|
|
@@ -259,7 +254,7 @@ const scatterPlot = await floimg.generate({
|
|
|
259
254
|
|
|
260
255
|
```typescript
|
|
261
256
|
const areaChart = await floimg.generate({
|
|
262
|
-
generator:
|
|
257
|
+
generator: "d3",
|
|
263
258
|
params: {
|
|
264
259
|
width: 800,
|
|
265
260
|
height: 400,
|
|
@@ -268,34 +263,29 @@ const areaChart = await floimg.generate({
|
|
|
268
263
|
const width = 800 - margin.left - margin.right;
|
|
269
264
|
const height = 400 - margin.top - margin.bottom;
|
|
270
265
|
|
|
271
|
-
const g = svg.append(
|
|
272
|
-
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
266
|
+
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
273
267
|
|
|
274
|
-
const x = d3
|
|
268
|
+
const x = d3
|
|
269
|
+
.scaleLinear()
|
|
275
270
|
.domain([0, data.length - 1])
|
|
276
271
|
.range([0, width]);
|
|
277
272
|
|
|
278
|
-
const y = d3
|
|
279
|
-
.
|
|
273
|
+
const y = d3
|
|
274
|
+
.scaleLinear()
|
|
275
|
+
.domain([0, d3.max(data, (d) => d.value)])
|
|
280
276
|
.range([height, 0]);
|
|
281
277
|
|
|
282
|
-
const area = d3
|
|
278
|
+
const area = d3
|
|
279
|
+
.area()
|
|
283
280
|
.x((d, i) => x(i))
|
|
284
281
|
.y0(height)
|
|
285
|
-
.y1(d => y(d.value));
|
|
282
|
+
.y1((d) => y(d.value));
|
|
286
283
|
|
|
287
|
-
g.append(
|
|
288
|
-
.datum(data)
|
|
289
|
-
.attr('fill', 'steelblue')
|
|
290
|
-
.attr('opacity', 0.7)
|
|
291
|
-
.attr('d', area);
|
|
284
|
+
g.append("path").datum(data).attr("fill", "steelblue").attr("opacity", 0.7).attr("d", area);
|
|
292
285
|
|
|
293
|
-
g.append(
|
|
294
|
-
.attr('transform', `translate(0,${height})`)
|
|
295
|
-
.call(d3.axisBottom(x));
|
|
286
|
+
g.append("g").attr("transform", `translate(0,${height})`).call(d3.axisBottom(x));
|
|
296
287
|
|
|
297
|
-
g.append(
|
|
298
|
-
.call(d3.axisLeft(y));
|
|
288
|
+
g.append("g").call(d3.axisLeft(y));
|
|
299
289
|
},
|
|
300
290
|
data: [
|
|
301
291
|
{ value: 30 },
|
|
@@ -303,9 +293,9 @@ const areaChart = await floimg.generate({
|
|
|
303
293
|
{ value: 45 },
|
|
304
294
|
{ value: 80 },
|
|
305
295
|
{ value: 70 },
|
|
306
|
-
{ value: 90 }
|
|
307
|
-
]
|
|
308
|
-
}
|
|
296
|
+
{ value: 90 },
|
|
297
|
+
],
|
|
298
|
+
},
|
|
309
299
|
});
|
|
310
300
|
```
|
|
311
301
|
|
|
@@ -313,7 +303,7 @@ const areaChart = await floimg.generate({
|
|
|
313
303
|
|
|
314
304
|
```typescript
|
|
315
305
|
const heatmap = await floimg.generate({
|
|
316
|
-
generator:
|
|
306
|
+
generator: "d3",
|
|
317
307
|
params: {
|
|
318
308
|
width: 600,
|
|
319
309
|
height: 400,
|
|
@@ -322,25 +312,26 @@ const heatmap = await floimg.generate({
|
|
|
322
312
|
const rows = data.length;
|
|
323
313
|
const cols = data[0].length;
|
|
324
314
|
|
|
325
|
-
const color = d3.scaleSequential(d3.interpolateBlues)
|
|
326
|
-
.domain([0, d3.max(data.flat())]);
|
|
315
|
+
const color = d3.scaleSequential(d3.interpolateBlues).domain([0, d3.max(data.flat())]);
|
|
327
316
|
|
|
328
317
|
for (let i = 0; i < rows; i++) {
|
|
329
318
|
for (let j = 0; j < cols; j++) {
|
|
330
|
-
svg
|
|
331
|
-
.
|
|
332
|
-
.attr(
|
|
333
|
-
.attr(
|
|
334
|
-
.attr(
|
|
335
|
-
.attr(
|
|
336
|
-
.attr(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
.
|
|
341
|
-
.attr(
|
|
342
|
-
.attr(
|
|
343
|
-
.attr(
|
|
319
|
+
svg
|
|
320
|
+
.append("rect")
|
|
321
|
+
.attr("x", j * cellSize)
|
|
322
|
+
.attr("y", i * cellSize)
|
|
323
|
+
.attr("width", cellSize)
|
|
324
|
+
.attr("height", cellSize)
|
|
325
|
+
.attr("fill", color(data[i][j]))
|
|
326
|
+
.attr("stroke", "white");
|
|
327
|
+
|
|
328
|
+
svg
|
|
329
|
+
.append("text")
|
|
330
|
+
.attr("x", j * cellSize + cellSize / 2)
|
|
331
|
+
.attr("y", i * cellSize + cellSize / 2)
|
|
332
|
+
.attr("text-anchor", "middle")
|
|
333
|
+
.attr("dominant-baseline", "middle")
|
|
334
|
+
.attr("fill", data[i][j] > 5 ? "white" : "black")
|
|
344
335
|
.text(data[i][j]);
|
|
345
336
|
}
|
|
346
337
|
}
|
|
@@ -349,31 +340,33 @@ const heatmap = await floimg.generate({
|
|
|
349
340
|
[1, 3, 5, 7],
|
|
350
341
|
[2, 4, 6, 8],
|
|
351
342
|
[9, 7, 5, 3],
|
|
352
|
-
[8, 6, 4, 2]
|
|
353
|
-
]
|
|
354
|
-
}
|
|
343
|
+
[8, 6, 4, 2],
|
|
344
|
+
],
|
|
345
|
+
},
|
|
355
346
|
});
|
|
356
347
|
```
|
|
357
348
|
|
|
358
349
|
## Parameters
|
|
359
350
|
|
|
360
|
-
| Parameter
|
|
361
|
-
|
|
362
|
-
| `width`
|
|
363
|
-
| `height`
|
|
364
|
-
| `backgroundColor` | string
|
|
365
|
-
| `render`
|
|
366
|
-
| `renderString`
|
|
367
|
-
| `data`
|
|
351
|
+
| Parameter | Type | Default | Description |
|
|
352
|
+
| ----------------- | -------- | ---------- | ----------------------------------------------------- |
|
|
353
|
+
| `width` | number | 800 | Output width in pixels |
|
|
354
|
+
| `height` | number | 600 | Output height in pixels |
|
|
355
|
+
| `backgroundColor` | string | 'white' | Background color |
|
|
356
|
+
| `render` | function | _required_ | Function that receives (svg, d3, data) |
|
|
357
|
+
| `renderString` | string | - | String version of render function (for serialization) |
|
|
358
|
+
| `data` | any | [] | Data to pass to render function |
|
|
368
359
|
|
|
369
360
|
## Configuration
|
|
370
361
|
|
|
371
362
|
```typescript
|
|
372
|
-
floimg.registerGenerator(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
363
|
+
floimg.registerGenerator(
|
|
364
|
+
d3viz({
|
|
365
|
+
width: 1000, // Default width
|
|
366
|
+
height: 600, // Default height
|
|
367
|
+
backgroundColor: "#f0f0f0", // Default background
|
|
368
|
+
})
|
|
369
|
+
);
|
|
377
370
|
```
|
|
378
371
|
|
|
379
372
|
## Features
|
|
@@ -387,6 +380,7 @@ floimg.registerGenerator(d3viz({
|
|
|
387
380
|
## D3 Documentation
|
|
388
381
|
|
|
389
382
|
For full D3 capabilities and examples:
|
|
383
|
+
|
|
390
384
|
- https://d3js.org/
|
|
391
385
|
- https://observablehq.com/@d3/gallery
|
|
392
386
|
|
|
@@ -403,9 +397,9 @@ For full D3 capabilities and examples:
|
|
|
403
397
|
```typescript
|
|
404
398
|
// Generate multiple charts for a report
|
|
405
399
|
const charts = await Promise.all([
|
|
406
|
-
floimg.generate({ generator:
|
|
407
|
-
floimg.generate({ generator:
|
|
408
|
-
floimg.generate({ generator:
|
|
400
|
+
floimg.generate({ generator: "d3", params: salesChartConfig }),
|
|
401
|
+
floimg.generate({ generator: "d3", params: revenueChartConfig }),
|
|
402
|
+
floimg.generate({ generator: "d3", params: growthChartConfig }),
|
|
409
403
|
]);
|
|
410
404
|
```
|
|
411
405
|
|
|
@@ -416,26 +410,26 @@ const charts = await Promise.all([
|
|
|
416
410
|
setInterval(async () => {
|
|
417
411
|
const liveData = await fetchLiveData();
|
|
418
412
|
const chart = await floimg.generate({
|
|
419
|
-
generator:
|
|
413
|
+
generator: "d3",
|
|
420
414
|
params: {
|
|
421
415
|
render: createChartRenderer(),
|
|
422
|
-
data: liveData
|
|
423
|
-
}
|
|
416
|
+
data: liveData,
|
|
417
|
+
},
|
|
424
418
|
});
|
|
425
|
-
await floimg.save(chart,
|
|
419
|
+
await floimg.save(chart, "./output/dashboard-live.svg");
|
|
426
420
|
}, 60000);
|
|
427
421
|
```
|
|
428
422
|
|
|
429
423
|
### API Endpoints
|
|
430
424
|
|
|
431
425
|
```typescript
|
|
432
|
-
app.get(
|
|
426
|
+
app.get("/api/chart/:type", async (req, res) => {
|
|
433
427
|
const data = await fetchData(req.params.type);
|
|
434
428
|
const chart = await floimg.generate({
|
|
435
|
-
generator:
|
|
436
|
-
params: { render: getRenderer(req.params.type), data }
|
|
429
|
+
generator: "d3",
|
|
430
|
+
params: { render: getRenderer(req.params.type), data },
|
|
437
431
|
});
|
|
438
|
-
res.type(
|
|
432
|
+
res.type("image/svg+xml").send(chart.bytes);
|
|
439
433
|
});
|
|
440
434
|
```
|
|
441
435
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamflojo/floimg-d3",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "D3 data visualization generator for floimg using server-side rendering",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,14 +16,6 @@
|
|
|
16
16
|
"README.md",
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsc",
|
|
21
|
-
"dev": "tsc --watch",
|
|
22
|
-
"test": "vitest",
|
|
23
|
-
"typecheck": "tsc --noEmit",
|
|
24
|
-
"clean": "rm -rf dist",
|
|
25
|
-
"prepublishOnly": "npm run build"
|
|
26
|
-
},
|
|
27
19
|
"keywords": [
|
|
28
20
|
"floimg",
|
|
29
21
|
"d3",
|
|
@@ -40,7 +32,7 @@
|
|
|
40
32
|
"directory": "packages/floimg-d3"
|
|
41
33
|
},
|
|
42
34
|
"peerDependencies": {
|
|
43
|
-
"@teamflojo/floimg": "^0.
|
|
35
|
+
"@teamflojo/floimg": "^0.8.0"
|
|
44
36
|
},
|
|
45
37
|
"dependencies": {
|
|
46
38
|
"d3": "^7.9.0",
|
|
@@ -50,11 +42,18 @@
|
|
|
50
42
|
"@types/d3": "^7.4.3",
|
|
51
43
|
"@types/jsdom": "^21.1.7",
|
|
52
44
|
"@types/node": "^22.10.2",
|
|
53
|
-
"@teamflojo/floimg": "workspace:*",
|
|
54
45
|
"typescript": "^5.7.2",
|
|
55
|
-
"vitest": "^2.1.8"
|
|
46
|
+
"vitest": "^2.1.8",
|
|
47
|
+
"@teamflojo/floimg": "0.9.0"
|
|
56
48
|
},
|
|
57
49
|
"engines": {
|
|
58
50
|
"node": ">=18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc",
|
|
54
|
+
"dev": "tsc --watch",
|
|
55
|
+
"test": "vitest --run",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"clean": "rm -rf dist"
|
|
59
58
|
}
|
|
60
|
-
}
|
|
59
|
+
}
|