@grafloria/element 0.4.14 → 0.4.15
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/package.json
CHANGED
|
@@ -344,6 +344,14 @@ const CSS = `
|
|
|
344
344
|
flex: 0 0 auto; width: auto; height: 100%; max-height: 260px; max-width: 60%; aspect-ratio: 1 / 1;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/* READABILITY TIERS (widgets.ts chartTier): the text is always in the DOM;
|
|
348
|
+
what a short body cannot afford is hidden, not dropped. */
|
|
349
|
+
.axdb-tier-1 .axdb-yt--q, .axdb-tier-1 .axdb-yl--q,
|
|
350
|
+
.axdb-tier-2 .axdb-yt--q, .axdb-tier-2 .axdb-yl--q,
|
|
351
|
+
.axdb-tier-2 .axdb-yt--h, .axdb-tier-2 .axdb-yl--h,
|
|
352
|
+
.axdb-tier-2 .axdb-xt, .axdb-tier-2 .axdb-vt,
|
|
353
|
+
.axdb-lg--off { display: none; }
|
|
354
|
+
|
|
347
355
|
/* legend chips, shared by line and donut */
|
|
348
356
|
.axdb-lg { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 9px; }
|
|
349
357
|
.axdb-lg--col { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 0; }
|
|
@@ -97,6 +97,21 @@ export declare const BUILT_IN_WIDGET_KINDS: readonly ["kpi", "line", "bar", "don
|
|
|
97
97
|
* the classic 640×250 stands in, and the size watcher below repaints once the
|
|
98
98
|
* real box exists.
|
|
99
99
|
*/
|
|
100
|
+
/**
|
|
101
|
+
* READABILITY TIERS. A squeezed chart — a one-row line on a fit board, a
|
|
102
|
+
* 60-px row after a pull — piled its five y labels onto each other and, under
|
|
103
|
+
* 45 px, gave its whole body to the legend (kit lab L03/L05/L07, scenario
|
|
104
|
+
* s11/s23). The text set never changes with the box (the save/load gate's
|
|
105
|
+
* contract: a board reloaded at another size paints the same text), so the
|
|
106
|
+
* SVG carries a tier class and the stylesheet HIDES what does not fit:
|
|
107
|
+
* tier 1 drops the quarter ticks, tier 2 keeps only min and max, drops the x
|
|
108
|
+
* labels and the bar values; a legend on a body under 64 px is hidden and the
|
|
109
|
+
* chart takes the room. 0 = unmeasured (jsdom, a detached host): full tier.
|
|
110
|
+
*/
|
|
111
|
+
export declare function chartTier(bodyH: number, hasLegend: boolean): {
|
|
112
|
+
tier: 0 | 1 | 2;
|
|
113
|
+
legendShown: boolean;
|
|
114
|
+
};
|
|
100
115
|
export declare function chartBox(body: {
|
|
101
116
|
clientWidth: number;
|
|
102
117
|
clientHeight: number;
|
|
@@ -101,6 +101,24 @@ const data = (widget) => { var _a; return ((_a = widget.data) !== null && _a !==
|
|
|
101
101
|
* the classic 640×250 stands in, and the size watcher below repaints once the
|
|
102
102
|
* real box exists.
|
|
103
103
|
*/
|
|
104
|
+
/**
|
|
105
|
+
* READABILITY TIERS. A squeezed chart — a one-row line on a fit board, a
|
|
106
|
+
* 60-px row after a pull — piled its five y labels onto each other and, under
|
|
107
|
+
* 45 px, gave its whole body to the legend (kit lab L03/L05/L07, scenario
|
|
108
|
+
* s11/s23). The text set never changes with the box (the save/load gate's
|
|
109
|
+
* contract: a board reloaded at another size paints the same text), so the
|
|
110
|
+
* SVG carries a tier class and the stylesheet HIDES what does not fit:
|
|
111
|
+
* tier 1 drops the quarter ticks, tier 2 keeps only min and max, drops the x
|
|
112
|
+
* labels and the bar values; a legend on a body under 64 px is hidden and the
|
|
113
|
+
* chart takes the room. 0 = unmeasured (jsdom, a detached host): full tier.
|
|
114
|
+
*/
|
|
115
|
+
export function chartTier(bodyH, hasLegend) {
|
|
116
|
+
if (!bodyH)
|
|
117
|
+
return { tier: 0, legendShown: hasLegend };
|
|
118
|
+
const legendShown = hasLegend && bodyH >= 64;
|
|
119
|
+
const h = bodyH - (legendShown ? 26 : 0);
|
|
120
|
+
return { tier: h < 60 ? 2 : h < 120 ? 1 : 0, legendShown };
|
|
121
|
+
}
|
|
104
122
|
export function chartBox(body, legend = false) {
|
|
105
123
|
const w = body.clientWidth || 0;
|
|
106
124
|
const h = (body.clientHeight || 0) - (legend ? 26 : 0);
|
|
@@ -154,7 +172,7 @@ const srTable = (caption, columns, rows) => `<table class="axdb-sr"><caption>${e
|
|
|
154
172
|
.join('')}</tr></thead><tbody>${rows
|
|
155
173
|
.map((r) => `<tr>${r.map((v) => `<td>${esc(v)}</td>`).join('')}</tr>`)
|
|
156
174
|
.join('')}</tbody></table>`;
|
|
157
|
-
const legend = (items, column = false) => `<div class="axdb-lg${column ? ' axdb-lg--col' : ''}">` +
|
|
175
|
+
const legend = (items, column = false, off = false) => `<div class="axdb-lg${column ? ' axdb-lg--col' : ''}${off ? ' axdb-lg--off' : ''}">` +
|
|
158
176
|
items.map((i) => `<i><b style="background:${esc(i.color)}"></b>${esc(i.label)}</i>`).join('') +
|
|
159
177
|
'</div>';
|
|
160
178
|
// -- kpi ----------------------------------------------------------------------
|
|
@@ -218,10 +236,11 @@ function layoutLine(widget, body) {
|
|
|
218
236
|
if (!series.length)
|
|
219
237
|
return empty(body);
|
|
220
238
|
const named = series.filter((s) => s.name);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const { W, H } = chartBox(body,
|
|
224
|
-
|
|
239
|
+
const { tier, legendShown } = chartTier(body.clientHeight || 0, named.length > 0);
|
|
240
|
+
body.classList.toggle('axdb-has-lg', legendShown);
|
|
241
|
+
const { W, H } = chartBox(body, legendShown);
|
|
242
|
+
// Tier 2 shows no x labels, so the band they lived in goes to the plot.
|
|
243
|
+
const pad = tier === 2 ? { l: 34, r: 12, t: 4, b: 6 } : { l: 34, r: 12, t: 12, b: 22 };
|
|
225
244
|
const iw = W - pad.l - pad.r;
|
|
226
245
|
const ih = H - pad.t - pad.b;
|
|
227
246
|
const all = series.flatMap((s) => s.values);
|
|
@@ -233,9 +252,10 @@ function layoutLine(widget, body) {
|
|
|
233
252
|
const grid = [0, 0.25, 0.5, 0.75, 1]
|
|
234
253
|
.map((f) => {
|
|
235
254
|
const y = pad.t + ih - f * ih;
|
|
236
|
-
|
|
255
|
+
const cls = f === 0.5 ? ' axdb-yt--h' : f === 0.25 || f === 0.75 ? ' axdb-yt--q' : '';
|
|
256
|
+
return (`<line class="axdb-yl${cls.replace('yt', 'yl')}" x1="${pad.l}" y1="${y.toFixed(1)}" x2="${W - pad.r}" y2="${y.toFixed(1)}" ` +
|
|
237
257
|
`stroke="var(--axdb-grid)" stroke-width="1"></line>` +
|
|
238
|
-
`<text x="${pad.l - 6}" y="${(y + 3).toFixed(1)}" text-anchor="end" font-size="9" ` +
|
|
258
|
+
`<text class="axdb-yt${cls}" x="${pad.l - 6}" y="${(y + 3).toFixed(1)}" text-anchor="end" font-size="9" ` +
|
|
239
259
|
`fill="var(--axdb-muted)">${esc(compact(min + f * (max - min)))}</text>`);
|
|
240
260
|
})
|
|
241
261
|
.join('');
|
|
@@ -247,7 +267,7 @@ function layoutLine(widget, body) {
|
|
|
247
267
|
const ticks = labels
|
|
248
268
|
.slice(0, count)
|
|
249
269
|
.map((l, i) => i % every === 0
|
|
250
|
-
? `<text x="${xAt(i).toFixed(1)}" y="${H - 6}" text-anchor="middle" font-size="9" ` +
|
|
270
|
+
? `<text class="axdb-xt" x="${xAt(i).toFixed(1)}" y="${H - 6}" text-anchor="middle" font-size="9" ` +
|
|
251
271
|
`fill="var(--axdb-muted)">${esc(l)}</text>`
|
|
252
272
|
: '')
|
|
253
273
|
.join('');
|
|
@@ -276,9 +296,9 @@ function layoutLine(widget, body) {
|
|
|
276
296
|
})
|
|
277
297
|
.join('');
|
|
278
298
|
body.innerHTML =
|
|
279
|
-
`<svg viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" ` +
|
|
299
|
+
`<svg class="axdb-tier-${tier}" viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" ` +
|
|
280
300
|
`aria-label="${esc(titleOf(widget))}">${grid}${ticks}${marks}</svg>` +
|
|
281
|
-
(named.length ? legend(series.map((s, i) => { var _a; return ({ label: String((_a = s.name) !== null && _a !== void 0 ? _a : ''), color: colorAt(i) }); })) : '') +
|
|
301
|
+
(named.length ? legend(series.map((s, i) => { var _a; return ({ label: String((_a = s.name) !== null && _a !== void 0 ? _a : ''), color: colorAt(i) }); }), false, !legendShown) : '') +
|
|
282
302
|
srTable(titleOf(widget), ['', ...series.map((s, i) => { var _a; return String((_a = s.name) !== null && _a !== void 0 ? _a : `Series ${i + 1}`); })], Array.from({ length: count }, (_, i) => { var _a; return [(_a = labels[i]) !== null && _a !== void 0 ? _a : String(i + 1), ...series.map((s) => { var _a; return (_a = s.values[i]) !== null && _a !== void 0 ? _a : ''; })]; }));
|
|
283
303
|
}
|
|
284
304
|
// -- bar ----------------------------------------------------------------------
|
|
@@ -294,8 +314,9 @@ function layoutBar(widget, body) {
|
|
|
294
314
|
const bars = (Array.isArray(d.bars) ? d.bars : []).filter((b) => !!b);
|
|
295
315
|
if (!bars.length)
|
|
296
316
|
return empty(body);
|
|
317
|
+
const { tier } = chartTier(body.clientHeight || 0, false);
|
|
297
318
|
const { W, H } = chartBox(body);
|
|
298
|
-
const pad = { l: 34, r: 12, t: 12, b: 26 };
|
|
319
|
+
const pad = tier === 2 ? { l: 34, r: 12, t: 4, b: 6 } : { l: 34, r: 12, t: 12, b: 26 };
|
|
299
320
|
const iw = W - pad.l - pad.r;
|
|
300
321
|
const ih = H - pad.t - pad.b;
|
|
301
322
|
const max = niceMax(Math.max(...bars.map((b) => num(b.value))));
|
|
@@ -304,9 +325,10 @@ function layoutBar(widget, body) {
|
|
|
304
325
|
const grid = [0, 0.5, 1]
|
|
305
326
|
.map((f) => {
|
|
306
327
|
const y = pad.t + ih - f * ih;
|
|
307
|
-
|
|
328
|
+
const cls = f === 0.5 ? ' axdb-yt--h' : '';
|
|
329
|
+
return (`<line class="axdb-yl${cls.replace('yt', 'yl')}" x1="${pad.l}" y1="${y.toFixed(1)}" x2="${W - pad.r}" y2="${y.toFixed(1)}" ` +
|
|
308
330
|
`stroke="var(--axdb-grid)" stroke-width="1"></line>` +
|
|
309
|
-
`<text x="${pad.l - 6}" y="${(y + 3).toFixed(1)}" text-anchor="end" font-size="9" ` +
|
|
331
|
+
`<text class="axdb-yt${cls}" x="${pad.l - 6}" y="${(y + 3).toFixed(1)}" text-anchor="end" font-size="9" ` +
|
|
310
332
|
`fill="var(--axdb-muted)">${esc(compact(f * max))}</text>`);
|
|
311
333
|
})
|
|
312
334
|
.join('');
|
|
@@ -319,14 +341,14 @@ function layoutBar(widget, body) {
|
|
|
319
341
|
const y = pad.t + ih - h;
|
|
320
342
|
return (`<rect x="${x.toFixed(1)}" y="${y.toFixed(1)}" width="${bw.toFixed(1)}" height="${h.toFixed(1)}" ` +
|
|
321
343
|
`rx="4" fill="${colorAt(i)}"></rect>` +
|
|
322
|
-
`<text x="${(x + bw / 2).toFixed(1)}" y="${(y - 4).toFixed(1)}" text-anchor="middle" font-size="9.5" ` +
|
|
344
|
+
`<text class="axdb-vt" x="${(x + bw / 2).toFixed(1)}" y="${(y - 4).toFixed(1)}" text-anchor="middle" font-size="9.5" ` +
|
|
323
345
|
`font-weight="600" fill="var(--axdb-ink)">${esc(compact(num(b.value)))}</text>` +
|
|
324
|
-
`<text x="${(x + bw / 2).toFixed(1)}" y="${H - 8}" text-anchor="middle" font-size="9" ` +
|
|
346
|
+
`<text class="axdb-xt" x="${(x + bw / 2).toFixed(1)}" y="${H - 8}" text-anchor="middle" font-size="9" ` +
|
|
325
347
|
`fill="var(--axdb-muted)">${esc((_a = b.label) !== null && _a !== void 0 ? _a : '')}</text>`);
|
|
326
348
|
})
|
|
327
349
|
.join('');
|
|
328
350
|
body.innerHTML =
|
|
329
|
-
`<svg viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" ` +
|
|
351
|
+
`<svg class="axdb-tier-${tier}" viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" ` +
|
|
330
352
|
`aria-label="${esc(titleOf(widget))}">${grid}${marks}</svg>` +
|
|
331
353
|
srTable(titleOf(widget), ['Category', 'Value'], bars.map((b) => { var _a; return [(_a = b.label) !== null && _a !== void 0 ? _a : '', num(b.value)]; }));
|
|
332
354
|
}
|