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 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,
@@ -64,6 +64,8 @@ export interface RaChartOptions {
64
64
  upColor?: string;
65
65
  downColor?: string;
66
66
  labelRotation?: 'auto' | number;
67
+ labelMaxLength?: number;
68
+ labelEllipsis?: string;
67
69
  zoom?: boolean;
68
70
  pinchZoom?: boolean;
69
71
  legend?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-ra-chart",
3
- "version": "1.0.6",
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
@@ -15,7 +15,7 @@
15
15
  -->
16
16
  <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
17
17
  id="cordova-plugin-ra-chart"
18
- version="1.0.5">
18
+ version="1.0.7">
19
19
 
20
20
  <name>raChart</name>
21
21
  <description>
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.5 — mobile SVG chart engine
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.5";
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 an ellipsis. */
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) + "…", font) <= maxPx) lo = mid; else hi = mid - 1;
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 maxPx = Math.min(maxW, (cap - 12) / sin);
251
- var pad = Math.min(cap, Math.max(24, Math.ceil(maxPx * sin) + 14));
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 = { horizontal: horizontal, isLine: isLine, isLolli: isLolli, depth: depth, cats: cats, nS: nS, nC: nC };
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, cats.map(function (s) { return textW(s); }).concat([12]));
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(cats, g.band, this.h, o.labelRotation);
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(fullY, g.labelMaxPx, 11.5);
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.cats[i]));
1339
- skip = Math.max(1, Math.ceil((widest + 8) / g.band));
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 ? truncate(full, g.labelMaxPx) : full;
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) {