@opendata-ai/openchart-engine 6.27.2 → 6.28.4
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/dist/index.d.ts +38 -6
- package/dist/index.js +1032 -521
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/compile-snapshot.test.ts.snap +31 -4
- package/src/__tests__/legend.test.ts +2 -2
- package/src/barlist/__tests__/compile-barlist.test.ts +200 -0
- package/src/barlist/compile-barlist.ts +380 -0
- package/src/barlist/types.ts +28 -0
- package/src/charts/bar/__tests__/compute.test.ts +120 -0
- package/src/charts/bar/compute.ts +77 -45
- package/src/charts/bar/index.ts +1 -0
- package/src/charts/bar/labels.ts +3 -2
- package/src/charts/column/compute.ts +60 -27
- package/src/charts/column/index.ts +1 -0
- package/src/charts/column/labels.ts +2 -1
- package/src/charts/line/__tests__/compute.test.ts +2 -2
- package/src/charts/line/area.ts +25 -4
- package/src/charts/line/compute.ts +15 -5
- package/src/compile.ts +26 -1
- package/src/compiler/normalize.ts +25 -1
- package/src/compiler/types.ts +5 -3
- package/src/compiler/validate.ts +120 -5
- package/src/index.ts +5 -0
- package/src/layout/axes/ticks.ts +6 -4
- package/src/layout/axes.ts +2 -2
- package/src/layout/dimensions.ts +10 -4
- package/src/layout/scales.ts +10 -0
- package/src/legend/wrap.ts +1 -1
- package/src/tables/__tests__/heatmap.test.ts +22 -0
- package/src/tables/heatmap.ts +28 -0
- package/src/tilemap/__tests__/compile-tilemap.test.ts +10 -5
- package/src/tilemap/compile-tilemap.ts +51 -30
- package/src/tooltips/compute.ts +4 -2
|
@@ -44,8 +44,8 @@ import type { NormalizedTileMapSpec } from './types';
|
|
|
44
44
|
// Constants
|
|
45
45
|
// ---------------------------------------------------------------------------
|
|
46
46
|
|
|
47
|
-
const TILE_CORNER_RADIUS =
|
|
48
|
-
const TILE_STROKE_WIDTH =
|
|
47
|
+
const TILE_CORNER_RADIUS = 6;
|
|
48
|
+
const TILE_STROKE_WIDTH = 1;
|
|
49
49
|
|
|
50
50
|
// ---------------------------------------------------------------------------
|
|
51
51
|
// Public API
|
|
@@ -143,23 +143,22 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
143
143
|
const min = values.length > 0 ? Math.min(...values) : 0;
|
|
144
144
|
const max = values.length > 0 ? Math.max(...values) : 100;
|
|
145
145
|
|
|
146
|
-
// 8. Build
|
|
146
|
+
// 8. Build opacity scale: single base color, opacity encodes value
|
|
147
147
|
const paletteStops = [...(SEQUENTIAL_PALETTES[tilemapSpec.palette] ?? SEQUENTIAL_PALETTES.blue)];
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const colorScale = scaleLinear<string>().domain(domain).range(paletteStops).clamp(true);
|
|
148
|
+
const baseColor = isDarkMode ? paletteStops[0] : paletteStops[paletteStops.length - 1];
|
|
149
|
+
const opacityRange: [number, number] = isDarkMode ? [0.15, 1] : [0.2, 1];
|
|
150
|
+
const opacityScale = scaleLinear<number>().domain([min, max]).range(opacityRange).clamp(true);
|
|
152
151
|
|
|
153
152
|
// 9. Reserve space for gradient legend at bottom (unless hidden)
|
|
154
153
|
const showLegend = tilemapSpec.legend?.show !== false;
|
|
155
|
-
const legendBarHeight =
|
|
156
|
-
const legendLabelGap =
|
|
154
|
+
const legendBarHeight = 6;
|
|
155
|
+
const legendLabelGap = 6;
|
|
157
156
|
const legendTotalHeight = showLegend ? legendBarHeight + legendLabelGap + 14 : 0;
|
|
158
157
|
|
|
159
158
|
// 10. Compute tile positions in the remaining area
|
|
160
159
|
const legendGap = showLegend ? 8 : 0;
|
|
161
160
|
const tileAreaHeight = fullArea.height - legendTotalHeight - legendGap;
|
|
162
|
-
const tilePositions = computeTilePositions(fullArea.width, tileAreaHeight,
|
|
161
|
+
const tilePositions = computeTilePositions(fullArea.width, tileAreaHeight, 5);
|
|
163
162
|
|
|
164
163
|
// Center tile grid horizontally
|
|
165
164
|
const tileGridOffsetX = fullArea.x + (fullArea.width - tilePositions.gridWidth) / 2;
|
|
@@ -173,9 +172,9 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
173
172
|
// 11. Build TileMapTileMark[]
|
|
174
173
|
const formatter = buildD3Formatter(tilemapSpec.valueFormat) ?? formatNumber;
|
|
175
174
|
const neutralFillLight = '#e0e0e0';
|
|
176
|
-
const neutralFillDark = '#
|
|
175
|
+
const neutralFillDark = '#1e2a30';
|
|
177
176
|
const neutralStrokeLight = '#d0d0d0';
|
|
178
|
-
const neutralStrokeDark = '
|
|
177
|
+
const neutralStrokeDark = 'rgba(255,255,255,0.08)';
|
|
179
178
|
|
|
180
179
|
const neutralFill = isDarkMode ? neutralFillDark : neutralFillLight;
|
|
181
180
|
const neutralStroke = isDarkMode ? neutralStrokeDark : neutralStrokeLight;
|
|
@@ -188,12 +187,18 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
188
187
|
|
|
189
188
|
const hasData = stateValueMap.has(stateCode);
|
|
190
189
|
const value = hasData ? stateValueMap.get(stateCode)! : null;
|
|
191
|
-
const
|
|
190
|
+
const opacity = hasData ? opacityScale(value!) : 0;
|
|
191
|
+
const fill = hasData ? baseColor : neutralFill;
|
|
192
|
+
const stroke = hasData
|
|
193
|
+
? isDarkMode
|
|
194
|
+
? 'rgba(255,255,255,0.1)'
|
|
195
|
+
: 'rgba(0,0,0,0.1)'
|
|
196
|
+
: neutralStroke;
|
|
192
197
|
const formattedValue = hasData ? formatter(value!) : '–';
|
|
193
198
|
|
|
194
199
|
const labelStyle: TextStyle = {
|
|
195
200
|
fontFamily: theme.fonts.family,
|
|
196
|
-
fontSize: tilePositions.tileSize > 24 ?
|
|
201
|
+
fontSize: tilePositions.tileSize > 24 ? 10 : 7,
|
|
197
202
|
fontWeight: 700,
|
|
198
203
|
fill: '#ffffff',
|
|
199
204
|
lineHeight: 1.2,
|
|
@@ -201,20 +206,24 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
201
206
|
|
|
202
207
|
const valueLabelStyle: TextStyle = {
|
|
203
208
|
fontFamily: theme.fonts.family,
|
|
204
|
-
fontSize: tilePositions.tileSize > 24 ?
|
|
205
|
-
fontWeight:
|
|
206
|
-
fill: '
|
|
209
|
+
fontSize: tilePositions.tileSize > 24 ? 10 : 7,
|
|
210
|
+
fontWeight: 300,
|
|
211
|
+
fill: 'rgba(255,255,255,0.6)',
|
|
207
212
|
lineHeight: 1.2,
|
|
208
213
|
};
|
|
209
214
|
|
|
215
|
+
const tileCenterX = tileGridOffsetX + pos.x + tilePositions.tileSize / 2;
|
|
216
|
+
const tileTopY = tileGridOffsetY + pos.y;
|
|
217
|
+
const sz = tilePositions.tileSize;
|
|
218
|
+
|
|
210
219
|
// Only show value label on larger tiles
|
|
211
220
|
const valueLabel =
|
|
212
|
-
|
|
221
|
+
sz < 24
|
|
213
222
|
? { text: '', x: 0, y: 0, style: valueLabelStyle, visible: false }
|
|
214
223
|
: {
|
|
215
224
|
text: formattedValue,
|
|
216
|
-
x:
|
|
217
|
-
y:
|
|
225
|
+
x: tileCenterX,
|
|
226
|
+
y: tileTopY + sz * 0.78,
|
|
218
227
|
style: valueLabelStyle,
|
|
219
228
|
visible: true,
|
|
220
229
|
};
|
|
@@ -223,10 +232,11 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
223
232
|
type: 'tile' as const,
|
|
224
233
|
stateCode,
|
|
225
234
|
x: tileGridOffsetX + pos.x,
|
|
226
|
-
y:
|
|
227
|
-
size:
|
|
235
|
+
y: tileTopY,
|
|
236
|
+
size: sz,
|
|
228
237
|
fill,
|
|
229
|
-
|
|
238
|
+
fillOpacity: hasData ? opacity : 1,
|
|
239
|
+
stroke,
|
|
230
240
|
strokeWidth: TILE_STROKE_WIDTH,
|
|
231
241
|
cornerRadius: TILE_CORNER_RADIUS,
|
|
232
242
|
value: value ?? null,
|
|
@@ -234,8 +244,8 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
234
244
|
hasData,
|
|
235
245
|
label: {
|
|
236
246
|
text: stateCode,
|
|
237
|
-
x:
|
|
238
|
-
y:
|
|
247
|
+
x: tileCenterX,
|
|
248
|
+
y: tileTopY + sz * 0.28,
|
|
239
249
|
style: labelStyle,
|
|
240
250
|
visible: true,
|
|
241
251
|
},
|
|
@@ -269,10 +279,12 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
269
279
|
let gradientLegend: GradientLegendLayout | null = null;
|
|
270
280
|
|
|
271
281
|
if (showLegend) {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
282
|
+
const numStops = 5;
|
|
283
|
+
const gradientColorStops: GradientColorStop[] = Array.from({ length: numStops }, (_, i) => {
|
|
284
|
+
const t = i / (numStops - 1);
|
|
285
|
+
const o = opacityRange[0] + t * (opacityRange[1] - opacityRange[0]);
|
|
286
|
+
return { offset: t, color: baseColor, opacity: o };
|
|
287
|
+
});
|
|
276
288
|
|
|
277
289
|
gradientLegend = {
|
|
278
290
|
type: 'gradient',
|
|
@@ -317,6 +329,15 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
317
329
|
// 15. Resolve animation
|
|
318
330
|
const resolvedAnimation: ResolvedAnimation | undefined = resolveAnimation(tilemapSpec.animation);
|
|
319
331
|
|
|
332
|
+
// Tight content height: tiles + legend + chrome + padding
|
|
333
|
+
const contentHeight =
|
|
334
|
+
tileGridOffsetY +
|
|
335
|
+
tilePositions.gridHeight +
|
|
336
|
+
legendGap +
|
|
337
|
+
legendTotalHeight +
|
|
338
|
+
chrome.bottomHeight +
|
|
339
|
+
padding;
|
|
340
|
+
|
|
320
341
|
return {
|
|
321
342
|
area: fullArea,
|
|
322
343
|
chrome,
|
|
@@ -326,7 +347,7 @@ export function compileTileMap(spec: unknown, options: CompileOptions): TileMapL
|
|
|
326
347
|
a11y,
|
|
327
348
|
theme,
|
|
328
349
|
width: options.width,
|
|
329
|
-
height:
|
|
350
|
+
height: contentHeight,
|
|
330
351
|
animation: resolvedAnimation,
|
|
331
352
|
watermark,
|
|
332
353
|
measureText:
|
package/src/tooltips/compute.ts
CHANGED
|
@@ -59,12 +59,14 @@ function formatValue(value: unknown, fieldType?: string, format?: string): strin
|
|
|
59
59
|
|
|
60
60
|
/** Resolve the display label for an encoding channel: title > axis.title > field name. */
|
|
61
61
|
function resolveLabel(ch: EncodingChannel): string {
|
|
62
|
-
|
|
62
|
+
const ax = ch.axis || undefined;
|
|
63
|
+
return ch.title ?? ax?.title ?? ch.field;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/** Resolve the format string for an encoding channel: format > axis.format. */
|
|
66
67
|
function resolveFormat(ch: EncodingChannel): string | undefined {
|
|
67
|
-
|
|
68
|
+
const ax = ch.axis || undefined;
|
|
69
|
+
return ch.format ?? ax?.format;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/** Build tooltip fields from explicit tooltip encoding channels. */
|