cordova-plugin-ra-chart 1.0.6 → 1.0.7
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 +17 -0
- package/ionic/ra-chart.component.ts +6 -0
- package/ionic/ra-chart.types.ts +2 -0
- package/package.json +1 -1
- package/plugin.xml +1 -1
- package/types/index.d.ts +11 -0
- package/www/ra-chart.js +53 -15
package/README.md
CHANGED
|
@@ -392,6 +392,8 @@ data: [{ price: 12, rating: 3.8, reach: 20 }]
|
|
|
392
392
|
| `zoom` | `false` | Range slider (XY types + candlestick) |
|
|
393
393
|
| `pinchZoom` | `true` | Pinch and pan when `zoom` is on |
|
|
394
394
|
| `labelRotation` | `'auto'` | Category-label tilt. `'auto'` keeps labels flat while they fit, tilts to −35° when they would collide, −90° once columns get narrow. A number forces an angle; `0` forces flat |
|
|
395
|
+
| `labelMaxLength` | `0` | Hard character cap on category labels; `0` disables. Full text stays in the tooltip |
|
|
396
|
+
| `labelEllipsis` | `'…'` | Suffix appended when a label is shortened, e.g. `'....'` |
|
|
395
397
|
| `legend` | `true` | Tappable; auto-hidden for single-series XY |
|
|
396
398
|
| `haptics` | `true` | Vibrate on touch-select |
|
|
397
399
|
| `sheetTooltip` | `'auto'` | Dock tooltip to bottom; auto = touch device under 480px |
|
|
@@ -676,6 +678,10 @@ To silence the diagnostics in production: `RaChart.warnings = false;`
|
|
|
676
678
|
|
|
677
679
|
**Page will not scroll over the chart** — only happens with `zoom: true`; vertical scrolling is preserved and horizontal drags pan. Set `pinchZoom: false` to disable gesture capture entirely.
|
|
678
680
|
|
|
681
|
+
**Arabic / Hebrew: tilted labels overlap the plot** — fixed in v1.0.6. SVG resolves `text-anchor` against the inline direction, so under `dir="rtl"` an `"end"` anchor flips to the opposite side and tilted labels swept *upward into* the chart instead of down away from it; axis numbers drifted toward the plot for the same reason. The drawing surface is now pinned to `direction: ltr`, which fixes anchoring while the bidi algorithm still shapes each Arabic run right-to-left. Nothing to configure — RTL pages work as they are.
|
|
682
|
+
|
|
683
|
+
RTL notes: the HTML legend follows the page direction, so series read right-to-left (correct mirroring). Arabic glyphs sit taller than Latin, and the bottom padding now accounts for that, so labels are never clipped.
|
|
684
|
+
|
|
679
685
|
**Category labels overlap or run together** — fixed in v1.0.5. Long names like "Material Status" are wider than their column, and the old spacing logic assumed a fixed 32px per label so it never thinned them out. Labels now tilt to −35° automatically (−90° when columns get narrow), the bottom padding grows to fit, and anything still too long is ellipsised with the full text kept in an SVG `<title>`. Override with `labelRotation`:
|
|
680
686
|
|
|
681
687
|
```html
|
|
@@ -685,6 +691,17 @@ To silence the diagnostics in production: `RaChart.warnings = false;`
|
|
|
685
691
|
|
|
686
692
|
Prefer short category names where you can — tilted labels cost up to a third of the chart height.
|
|
687
693
|
|
|
694
|
+
**Shortening long labels.** `labelMaxLength` caps them at a character count and appends `labelEllipsis`:
|
|
695
|
+
|
|
696
|
+
```html
|
|
697
|
+
<ra-chart [labelMaxLength]="5" labelEllipsis="...." ...></ra-chart>
|
|
698
|
+
<!-- "Total Employees" → "Total...." -->
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
The cap is applied *before* layout, so shortening labels enough to fit lets them stay flat and hands the vertical space back to the plot — in a five-category chart that is 72px of bottom padding down to 24px. The full text is still shown in the tooltip and kept in an SVG `<title>`.
|
|
702
|
+
|
|
703
|
+
Set `labelEllipsis` to `''` for a hard cut with no suffix. Both options apply to vertical category axes and to the left gutter of horizontal `bar` charts.
|
|
704
|
+
|
|
688
705
|
**Labels are cut off** — increase `height`, or shorten category labels. Horizontal `bar` and `gantt` cap the label gutter at about a third of the width; longer names are ellipsised with the full text in a `<title>`.
|
|
689
706
|
|
|
690
707
|
**Legend reads "RevenueTarget" with no colour swatches** — the legend HTML is unstyled, meaning `ra-chart.css` did not reach it. Almost always because it was added to a component's `styleUrls` instead of `angular.json` → `styles`; view encapsulation scopes it to `[_ngcontent-*]` and the runtime-created legend has no such attribute. Fixed by default in v1.0.3 (the engine injects its own structural CSS), so updating the engine resolves it. To theme, move `ra-chart.css` to the global styles array.
|
|
@@ -65,6 +65,10 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|
|
65
65
|
// ---- interaction ----
|
|
66
66
|
/** 'auto' tilts long category labels instead of letting them collide. */
|
|
67
67
|
@Input() labelRotation: 'auto' | number = 'auto';
|
|
68
|
+
/** Hard character cap on category labels; 0 disables it. */
|
|
69
|
+
@Input() labelMaxLength = 0;
|
|
70
|
+
/** Suffix appended when a label is shortened, e.g. '…' or '....'. */
|
|
71
|
+
@Input() labelEllipsis = '…';
|
|
68
72
|
@Input() zoom = false;
|
|
69
73
|
@Input() pinchZoom = true;
|
|
70
74
|
@Input() legend = true;
|
|
@@ -200,6 +204,8 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|
|
200
204
|
max: this.max,
|
|
201
205
|
bands: this.bands,
|
|
202
206
|
labelRotation: this.labelRotation,
|
|
207
|
+
labelMaxLength: this.labelMaxLength,
|
|
208
|
+
labelEllipsis: this.labelEllipsis,
|
|
203
209
|
zoom: this.zoom,
|
|
204
210
|
pinchZoom: this.pinchZoom,
|
|
205
211
|
legend: this.legend,
|
package/ionic/ra-chart.types.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cordova-plugin-ra-chart",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Dependency-free SVG chart engine for Cordova and Ionic — 21 chart types with touch gestures, pinch-to-zoom, haptics and dark-mode theming. No native code, no build step.",
|
|
5
5
|
"main": "www/ra-chart.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
package/plugin.xml
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -99,6 +99,17 @@ export interface RaChartOptions {
|
|
|
99
99
|
*/
|
|
100
100
|
labelRotation?: 'auto' | number;
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Hard character cap on category labels. `0` (default) leaves them alone and
|
|
104
|
+
* relies on tilting. Applied before layout, so capping labels short enough
|
|
105
|
+
* lets them stay flat and gives the plot back its vertical space. The full
|
|
106
|
+
* text stays available in the tooltip and an SVG `<title>`.
|
|
107
|
+
*/
|
|
108
|
+
labelMaxLength?: number;
|
|
109
|
+
|
|
110
|
+
/** Suffix appended when a label is shortened. Default `'…'`; e.g. `'....'`. */
|
|
111
|
+
labelEllipsis?: string;
|
|
112
|
+
|
|
102
113
|
/** Range-slider zoom (XY types + candlestick). */
|
|
103
114
|
zoom?: boolean;
|
|
104
115
|
/** Pinch-to-zoom and drag-to-pan when `zoom` is on. Default true. */
|
package/www/ra-chart.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* raChart Cordova v1.0.
|
|
2
|
+
* raChart Cordova v1.0.7 — mobile SVG chart engine
|
|
3
3
|
* RemoteApps UI Components
|
|
4
4
|
*
|
|
5
5
|
* Zero dependencies. No jQuery, no chart library, no build step.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"use strict";
|
|
21
21
|
|
|
22
22
|
var NS = "http://www.w3.org/2000/svg";
|
|
23
|
-
var VERSION = "1.0.
|
|
23
|
+
var VERSION = "1.0.7";
|
|
24
24
|
|
|
25
25
|
/* ============================================================
|
|
26
26
|
Palettes — CVD-validated order, do not shuffle
|
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
upColor: "#0ca30c",
|
|
56
56
|
downColor: "#d03b3b",
|
|
57
57
|
labelRotation: "auto", // "auto" tilts long category labels; or a number of degrees
|
|
58
|
+
labelMaxLength: 0, // hard character cap on category labels (0 = off)
|
|
59
|
+
labelEllipsis: "…", // suffix appended when a label is shortened
|
|
58
60
|
zoom: false, // range-slider zoom (XY + candlestick)
|
|
59
61
|
pinchZoom: true, // two-finger pinch + drag pan when zoom is on
|
|
60
62
|
legend: true,
|
|
@@ -204,18 +206,26 @@
|
|
|
204
206
|
return "500 " + (size || LABEL_SIZE) + "px system-ui, -apple-system, 'Segoe UI', sans-serif";
|
|
205
207
|
}
|
|
206
208
|
|
|
207
|
-
/** Shorten to fit maxPx, ending in
|
|
208
|
-
function truncate(s, maxPx, size) {
|
|
209
|
+
/** Shorten to fit maxPx, ending in `ell`. */
|
|
210
|
+
function truncate(s, maxPx, size, ell) {
|
|
209
211
|
s = String(s);
|
|
212
|
+
ell = ell == null ? "…" : ell;
|
|
210
213
|
if (!isFinite(maxPx)) return s;
|
|
211
214
|
var font = labelFont(size);
|
|
212
215
|
if (textW(s, font) <= maxPx) return s;
|
|
213
216
|
var lo = 1, hi = s.length;
|
|
214
217
|
while (lo < hi) { // longest prefix that still fits
|
|
215
218
|
var mid = (lo + hi + 1) >> 1;
|
|
216
|
-
if (textW(s.slice(0, mid) +
|
|
219
|
+
if (textW(s.slice(0, mid) + ell, font) <= maxPx) lo = mid; else hi = mid - 1;
|
|
217
220
|
}
|
|
218
|
-
return s.slice(0, lo) +
|
|
221
|
+
return s.slice(0, lo).replace(/\s+$/, "") + ell;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Hard character cap, applied before any pixel-based fitting. */
|
|
225
|
+
function clipChars(s, max, ell) {
|
|
226
|
+
s = String(s);
|
|
227
|
+
if (!max || max <= 0 || s.length <= max) return s;
|
|
228
|
+
return s.slice(0, max).replace(/\s+$/, "") + (ell == null ? "…" : ell);
|
|
219
229
|
}
|
|
220
230
|
|
|
221
231
|
/**
|
|
@@ -247,8 +257,14 @@
|
|
|
247
257
|
var cap = Math.max(40, Math.min(totalH * 0.34, 96));
|
|
248
258
|
var rad = Math.abs(angle) * Math.PI / 180;
|
|
249
259
|
var sin = Math.sin(rad) || 1;
|
|
250
|
-
var
|
|
251
|
-
|
|
260
|
+
var cos = Math.cos(rad);
|
|
261
|
+
// A tilted line of text is as tall as (length x sin) PLUS its own glyph box
|
|
262
|
+
// laid on the diagonal (lineH x cos). Dropping that second term clips
|
|
263
|
+
// descenders — very visible in Arabic, which sits taller than Latin.
|
|
264
|
+
var lineH = LABEL_SIZE * 1.35;
|
|
265
|
+
var room = cap - 8 - lineH * cos;
|
|
266
|
+
var maxPx = Math.min(maxW, Math.max(24, room / sin));
|
|
267
|
+
var pad = Math.min(cap, Math.max(24, Math.ceil(maxPx * sin + lineH * cos) + 8));
|
|
252
268
|
return { angle: angle, maxPx: maxPx, pad: pad };
|
|
253
269
|
}
|
|
254
270
|
|
|
@@ -711,6 +727,12 @@
|
|
|
711
727
|
viewBox: "0 0 " + this.w + " " + this.h,
|
|
712
728
|
preserveAspectRatio: "none"
|
|
713
729
|
});
|
|
730
|
+
// Pin the drawing surface to LTR. text-anchor is resolved against the
|
|
731
|
+
// inline direction, so under an RTL page "end" would flip to the opposite
|
|
732
|
+
// side and tilted labels would sweep up into the plot instead of down away
|
|
733
|
+
// from it. Glyph shaping is unaffected: the bidi algorithm still lays each
|
|
734
|
+
// Arabic/Hebrew run out right-to-left inside the element.
|
|
735
|
+
this.svg.style.direction = "ltr";
|
|
714
736
|
this.stage.appendChild(this.svg);
|
|
715
737
|
|
|
716
738
|
this.tip = el("div", "ra-chart__tip", this.el);
|
|
@@ -1131,18 +1153,29 @@
|
|
|
1131
1153
|
this.viewData = data;
|
|
1132
1154
|
|
|
1133
1155
|
var cats = data.map(function (d) { return String(d[o.category]); });
|
|
1156
|
+
// display copy: the character cap is applied up front so the layout below
|
|
1157
|
+
// measures what will actually be drawn, and can keep labels flat when the
|
|
1158
|
+
// cap makes them short enough to fit
|
|
1159
|
+
var catLabels = cats.map(function (s) {
|
|
1160
|
+
return clipChars(s, o.labelMaxLength, o.labelEllipsis);
|
|
1161
|
+
});
|
|
1134
1162
|
var nS = o.series.length, nC = cats.length;
|
|
1135
1163
|
|
|
1136
1164
|
this.cur = { kind: "xy", vals: this._xyVals(data), scaleMax: null };
|
|
1137
1165
|
|
|
1138
|
-
var g = {
|
|
1166
|
+
var g = {
|
|
1167
|
+
horizontal: horizontal, isLine: isLine, isLolli: isLolli, depth: depth,
|
|
1168
|
+
cats: cats, // full text — tooltips and events
|
|
1169
|
+
catLabels: catLabels, // what the axis draws
|
|
1170
|
+
nS: nS, nC: nC
|
|
1171
|
+
};
|
|
1139
1172
|
this.geom = g;
|
|
1140
1173
|
|
|
1141
1174
|
var scale = this._xyScale();
|
|
1142
1175
|
var yLabels = [];
|
|
1143
1176
|
for (var v = o.min; v <= scale.max + 1e-9; v += scale.step) yLabels.push(fmt(v));
|
|
1144
1177
|
var maxYLabelW = Math.max.apply(null, yLabels.map(function (s) { return textW(s); }).concat([12]));
|
|
1145
|
-
var maxCatW = Math.max.apply(null,
|
|
1178
|
+
var maxCatW = Math.max.apply(null, catLabels.map(function (s) { return textW(s); }).concat([12]));
|
|
1146
1179
|
|
|
1147
1180
|
g.padL = (horizontal ? Math.min(maxCatW, this.w * 0.34) : maxYLabelW) + 12;
|
|
1148
1181
|
g.padR = 8 + depth;
|
|
@@ -1162,7 +1195,7 @@
|
|
|
1162
1195
|
// band does not depend on padB here, so measure the labels first and let
|
|
1163
1196
|
// them reserve the bottom space they actually need
|
|
1164
1197
|
g.band = g.plotW / Math.max(1, nC);
|
|
1165
|
-
var lay = labelLayout(
|
|
1198
|
+
var lay = labelLayout(catLabels, g.band, this.h, o.labelRotation);
|
|
1166
1199
|
g.labelAngle = lay.angle;
|
|
1167
1200
|
g.labelMaxPx = lay.maxPx;
|
|
1168
1201
|
g.padB = lay.pad;
|
|
@@ -1312,7 +1345,7 @@
|
|
|
1312
1345
|
for (i = 0; i < g.nC; i++) {
|
|
1313
1346
|
// the gutter is capped at a third of the width, so clip to fit it
|
|
1314
1347
|
var fullY = g.cats[i];
|
|
1315
|
-
var shownY = truncate(
|
|
1348
|
+
var shownY = truncate(g.catLabels[i], g.labelMaxPx, 11.5, o.labelEllipsis);
|
|
1316
1349
|
var nodeY = svgText(this.gGrid, g.plotX - 7, g.plotY + g.band * (i + .5) + 4,
|
|
1317
1350
|
shownY, "end", 11.5, T.label);
|
|
1318
1351
|
if (shownY !== fullY) {
|
|
@@ -1335,15 +1368,20 @@
|
|
|
1335
1368
|
var skip = ang ? Math.max(1, Math.ceil(11 / g.band)) : 1;
|
|
1336
1369
|
if (!ang) {
|
|
1337
1370
|
var widest = 0;
|
|
1338
|
-
for (i = 0; i < g.nC; i++) widest = Math.max(widest, textW(g.
|
|
1339
|
-
|
|
1371
|
+
for (i = 0; i < g.nC; i++) widest = Math.max(widest, textW(g.catLabels[i]));
|
|
1372
|
+
// Same fit rule labelLayout used to choose "flat", so a layout that
|
|
1373
|
+
// decided the labels fit never has them silently dropped here. Only
|
|
1374
|
+
// a forced labelRotation:0 that genuinely overflows gets thinned.
|
|
1375
|
+
skip = widest <= g.band * 0.92 ? 1 : Math.max(1, Math.ceil((widest + 8) / g.band));
|
|
1340
1376
|
}
|
|
1341
1377
|
for (i = 0; i < g.nC; i++) {
|
|
1342
1378
|
if (i % skip) continue;
|
|
1343
1379
|
var lx = g.plotX + g.band * (i + .5);
|
|
1344
1380
|
var ly = g.plotY + g.plotH + (ang ? 14 : 16);
|
|
1345
1381
|
var full = g.cats[i];
|
|
1346
|
-
var shown = ang
|
|
1382
|
+
var shown = ang
|
|
1383
|
+
? truncate(g.catLabels[i], g.labelMaxPx, LABEL_SIZE, o.labelEllipsis)
|
|
1384
|
+
: g.catLabels[i];
|
|
1347
1385
|
var node = svgText(this.gGrid, lx, ly, shown, ang ? "end" : "middle", 11, T.label);
|
|
1348
1386
|
if (ang) node.setAttribute("transform", "rotate(" + ang + " " + lx + " " + ly + ")");
|
|
1349
1387
|
if (shown !== full) {
|