cordova-plugin-ra-chart 1.0.4 → 1.0.6

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
@@ -391,6 +391,7 @@ data: [{ price: 12, rating: 3.8, reach: 20 }]
391
391
  |---|---|---|
392
392
  | `zoom` | `false` | Range slider (XY types + candlestick) |
393
393
  | `pinchZoom` | `true` | Pinch and pan when `zoom` is on |
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 |
394
395
  | `legend` | `true` | Tappable; auto-hidden for single-series XY |
395
396
  | `haptics` | `true` | Vibrate on touch-select |
396
397
  | `sheetTooltip` | `'auto'` | Dock tooltip to bottom; auto = touch device under 480px |
@@ -499,6 +500,54 @@ Force a mode per chart with a class:
499
500
 
500
501
  The default 8-colour palette is ordered so adjacent series stay distinguishable for colourblind viewers. If you pass your own `colors`, keep that in mind. Charts also carry non-colour cues — legends, direct labels, tooltips.
501
502
 
503
+ ### Changing the colours of every chart
504
+
505
+ Three ways, highest precedence first.
506
+
507
+ **Per chart** — `colors` wins over everything:
508
+
509
+ ```html
510
+ <ra-chart [colors]="['#534ab7', '#2196f3']" ...></ra-chart>
511
+ ```
512
+
513
+ **Globally in CSS** — the recommended way for Ionic. Set `--ra-chart-series-N`
514
+ from 1 upward; the engine reads until the first one you leave unset. Because it
515
+ is plain CSS, it is theme-scoped and switches automatically in dark mode:
516
+
517
+ ```css
518
+ /* src/global.scss — applies to every chart in the app */
519
+ .ra-chart {
520
+ --ra-chart-series-1: #534ab7;
521
+ --ra-chart-series-2: #2196f3;
522
+ --ra-chart-series-3: #f0334b;
523
+ --ra-chart-series-4: #eda100;
524
+ }
525
+
526
+ /* different steps for dark mode */
527
+ .ion-palette-dark .ra-chart {
528
+ --ra-chart-series-1: #9085e9;
529
+ --ra-chart-series-2: #3987e5;
530
+ --ra-chart-series-3: #e66767;
531
+ --ra-chart-series-4: #c98500;
532
+ }
533
+ ```
534
+
535
+ Scope it to recolour one section only — `.reports-page .ra-chart { … }`.
536
+
537
+ **Globally in JS** — one call before any chart is created, e.g. in `main.ts`:
538
+
539
+ ```ts
540
+ declare const RaChart: any;
541
+
542
+ RaChart.setPalette(['#534ab7', '#2196f3', '#f0334b', '#eda100']);
543
+ RaChart.setPalette(lightHexes, darkHexes); // separate dark steps
544
+ RaChart.resetPalette(); // back to the built-in palette
545
+ ```
546
+
547
+ Colours cycle when a chart has more series than you supply. Charts that already
548
+ exist keep their palette until they rebuild — call `replay()` on them, or set the
549
+ palette before creating any chart.
550
+
502
551
  ---
503
552
 
504
553
  ## Recipes
@@ -627,7 +676,16 @@ To silence the diagnostics in production: `RaChart.warnings = false;`
627
676
 
628
677
  **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.
629
678
 
630
- **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.
679
+ **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
+
681
+ ```html
682
+ <ra-chart labelRotation="0" ...></ra-chart> <!-- force flat (thins labels out) -->
683
+ <ra-chart [labelRotation]="-90" ...></ra-chart> <!-- force vertical -->
684
+ ```
685
+
686
+ Prefer short category names where you can — tilted labels cost up to a third of the chart height.
687
+
688
+ **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>`.
631
689
 
632
690
  **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.
633
691
 
@@ -63,6 +63,8 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
63
63
  @Input() downColor?: string;
64
64
 
65
65
  // ---- interaction ----
66
+ /** 'auto' tilts long category labels instead of letting them collide. */
67
+ @Input() labelRotation: 'auto' | number = 'auto';
66
68
  @Input() zoom = false;
67
69
  @Input() pinchZoom = true;
68
70
  @Input() legend = true;
@@ -197,6 +199,7 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
197
199
  min: this.min,
198
200
  max: this.max,
199
201
  bands: this.bands,
202
+ labelRotation: this.labelRotation,
200
203
  zoom: this.zoom,
201
204
  pinchZoom: this.pinchZoom,
202
205
  legend: this.legend,
@@ -63,6 +63,7 @@ export interface RaChartOptions {
63
63
  bands?: RaBand[] | null;
64
64
  upColor?: string;
65
65
  downColor?: string;
66
+ labelRotation?: 'auto' | number;
66
67
  zoom?: boolean;
67
68
  pinchZoom?: boolean;
68
69
  legend?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-ra-chart",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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.3">
18
+ version="1.0.5">
19
19
 
20
20
  <name>raChart</name>
21
21
  <description>
package/types/index.d.ts CHANGED
@@ -91,6 +91,14 @@ export interface RaChartOptions {
91
91
  /** Candlestick bear colour. */
92
92
  downColor?: string;
93
93
 
94
+ /**
95
+ * Category-label tilt on vertical charts. `'auto'` (default) keeps labels
96
+ * flat while they fit, tilts to -35° when they would collide, and goes to
97
+ * -90° once columns get narrow. Pass a number to force an angle, or `0` to
98
+ * force flat (labels are then thinned out to avoid overlap).
99
+ */
100
+ labelRotation?: 'auto' | number;
101
+
94
102
  /** Range-slider zoom (XY types + candlestick). */
95
103
  zoom?: boolean;
96
104
  /** Pinch-to-zoom and drag-to-pan when `zoom` is on. Default true. */
@@ -200,6 +208,25 @@ export declare class RaChart {
200
208
  static paletteDark: string[];
201
209
  static types: RaChartType[];
202
210
  static create(host: HTMLElement | string, options?: RaChartOptions): RaChart;
211
+
212
+ /** Silence the "nothing to plot" console diagnostics. */
213
+ static warnings: boolean;
214
+
215
+ /**
216
+ * Recolour every chart created from now on. Pass one array to use it in
217
+ * both light and dark mode, or two for separate dark steps. Overridden per
218
+ * chart by `colors`, and by `--ra-chart-series-N` CSS variables.
219
+ */
220
+ static setPalette(light: string[], dark?: string[]): typeof RaChart;
221
+
222
+ /** Restore the built-in colourblind-safe palette. */
223
+ static resetPalette(): typeof RaChart;
224
+
225
+ /** Register `<ra-chart>` as a custom element (done automatically). */
226
+ static defineElement(tag?: string): boolean;
227
+
228
+ /** Inject the built-in structural stylesheet (done automatically). */
229
+ static injectStyles(): void;
203
230
  }
204
231
 
205
232
  export default RaChart;
package/www/ra-chart.css CHANGED
@@ -37,6 +37,20 @@ ra-chart {
37
37
 
38
38
  --ra-chart-radius: 12px;
39
39
 
40
+ /* ---- series palette ----
41
+ Uncomment and set these to recolour every chart. The engine reads
42
+ --ra-chart-series-1 upward and stops at the first one you leave unset,
43
+ so define as many as your busiest chart needs. Overridden per chart by
44
+ the `colors` option; overrides RaChart.setPalette().
45
+
46
+ --ra-chart-series-1: #534ab7;
47
+ --ra-chart-series-2: #2196f3;
48
+ --ra-chart-series-3: #f0334b;
49
+ --ra-chart-series-4: #eda100;
50
+ --ra-chart-series-5: #1baf7a;
51
+ --ra-chart-series-6: #eb6834;
52
+ */
53
+
40
54
  position: relative;
41
55
  display: block;
42
56
  width: 100%;
package/www/ra-chart.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * raChart Cordova v1.0.3 — mobile SVG chart engine
2
+ * raChart Cordova v1.0.5 — mobile SVG chart engine
3
3
  * RemoteApps UI Components
4
4
  *
5
5
  * Zero dependencies. No jQuery, no chart library, no build step.
@@ -20,14 +20,17 @@
20
20
  "use strict";
21
21
 
22
22
  var NS = "http://www.w3.org/2000/svg";
23
- var VERSION = "1.0.3";
23
+ var VERSION = "1.0.5";
24
24
 
25
25
  /* ============================================================
26
26
  Palettes — CVD-validated order, do not shuffle
27
27
  ============================================================ */
28
28
 
29
- var PALETTE_LIGHT = ["#2a78d6", "#008300", "#e87ba4", "#eda100", "#1baf7a", "#eb6834", "#4a3aa7", "#e34948"];
30
- var PALETTE_DARK = ["#3987e5", "#008300", "#d55181", "#c98500", "#199e70", "#d95926", "#9085e9", "#e66767"];
29
+ var DEFAULT_LIGHT = ["#2a78d6", "#008300", "#e87ba4", "#eda100", "#1baf7a", "#eb6834", "#4a3aa7", "#e34948"];
30
+ var DEFAULT_DARK = ["#3987e5", "#008300", "#d55181", "#c98500", "#199e70", "#d95926", "#9085e9", "#e66767"];
31
+ // live palettes — swapped by RaChart.setPalette()
32
+ var PALETTE_LIGHT = DEFAULT_LIGHT.slice();
33
+ var PALETTE_DARK = DEFAULT_DARK.slice();
31
34
  var SEQ_LIGHT = ["#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#256abf", "#184f95", "#0d366b"];
32
35
  var SEQ_DARK = ["#0d366b", "#184f95", "#256abf", "#3987e5", "#6da7ec", "#9ec5f4", "#cde2fb"];
33
36
 
@@ -51,6 +54,7 @@
51
54
  bands: null,
52
55
  upColor: "#0ca30c",
53
56
  downColor: "#d03b3b",
57
+ labelRotation: "auto", // "auto" tilts long category labels; or a number of degrees
54
58
  zoom: false, // range-slider zoom (XY + candlestick)
55
59
  pinchZoom: true, // two-finger pinch + drag pan when zoom is on
56
60
  legend: true,
@@ -195,6 +199,59 @@
195
199
  return n;
196
200
  }
197
201
 
202
+ var LABEL_SIZE = 11;
203
+ function labelFont(size) {
204
+ return "500 " + (size || LABEL_SIZE) + "px system-ui, -apple-system, 'Segoe UI', sans-serif";
205
+ }
206
+
207
+ /** Shorten to fit maxPx, ending in an ellipsis. */
208
+ function truncate(s, maxPx, size) {
209
+ s = String(s);
210
+ if (!isFinite(maxPx)) return s;
211
+ var font = labelFont(size);
212
+ if (textW(s, font) <= maxPx) return s;
213
+ var lo = 1, hi = s.length;
214
+ while (lo < hi) { // longest prefix that still fits
215
+ var mid = (lo + hi + 1) >> 1;
216
+ if (textW(s.slice(0, mid) + "…", font) <= maxPx) lo = mid; else hi = mid - 1;
217
+ }
218
+ return s.slice(0, lo) + "…";
219
+ }
220
+
221
+ /**
222
+ * Decide how category labels sit under a vertical axis.
223
+ *
224
+ * Long names ("Material Status") are wider than their column, so laying them
225
+ * flat makes them collide — and simply dropping every other one hides real
226
+ * categories. Instead measure the text and tilt it when it cannot fit, going
227
+ * to vertical only when the columns get genuinely narrow. Returns the bottom
228
+ * padding the chosen layout needs.
229
+ */
230
+ function labelLayout(cats, band, totalH, forced) {
231
+ var flat = { angle: 0, maxPx: Infinity, pad: 24 };
232
+ if (!cats.length) return flat;
233
+ var font = labelFont(LABEL_SIZE);
234
+ var maxW = 0;
235
+ for (var i = 0; i < cats.length; i++) {
236
+ var wd = textW(cats[i], font);
237
+ if (wd > maxW) maxW = wd;
238
+ }
239
+ var angle;
240
+ if (typeof forced === "number") angle = forced;
241
+ else if (forced === 0) angle = 0;
242
+ else if (maxW <= band * 0.92) return flat; // fits flat — leave it alone
243
+ else angle = band < 26 ? -90 : -35;
244
+ if (!angle) return { angle: 0, maxPx: Infinity, pad: 24 };
245
+
246
+ // cap how much of the chart the labels may consume
247
+ var cap = Math.max(40, Math.min(totalH * 0.34, 96));
248
+ var rad = Math.abs(angle) * Math.PI / 180;
249
+ 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));
252
+ return { angle: angle, maxPx: maxPx, pad: pad };
253
+ }
254
+
198
255
  function svgText(parent, x, y, text, anchor, size, color, weight) {
199
256
  var n = svgEl("text", {
200
257
  x: x, y: y, "text-anchor": anchor || "middle",
@@ -445,6 +502,16 @@
445
502
  }
446
503
  var dark = v("--ra-chart-scheme", "light").indexOf("dark") >= 0;
447
504
  this.dark = dark;
505
+
506
+ // Palette from CSS custom properties: --ra-chart-series-1 .. -N, read until
507
+ // the first gap. Lets a theme file recolour every chart (and switch palettes
508
+ // in dark mode) with no JavaScript.
509
+ var cssSeries = [];
510
+ for (var i = 1; i <= 16; i++) {
511
+ var c = v("--ra-chart-series-" + i, "");
512
+ if (!c) break;
513
+ cssSeries.push(c);
514
+ }
448
515
  this.theme = {
449
516
  dark: dark,
450
517
  surface: v("--ra-chart-surface", dark ? "#1a1a19" : "#ffffff"),
@@ -455,7 +522,9 @@
455
522
  baseline: v("--ra-chart-baseline", dark ? "#383835" : "#c3c2b7"),
456
523
  body: v("--ra-chart-body", dark ? "#c3c2b7" : "#4b5563")
457
524
  };
458
- this.palette = this.opts.colors || (dark ? PALETTE_DARK : PALETTE_LIGHT);
525
+ // precedence: per-chart colors > CSS variables > global palette
526
+ this.palette = this.opts.colors ||
527
+ (cssSeries.length ? cssSeries : (dark ? PALETTE_DARK : PALETTE_LIGHT));
459
528
  this.seq = dark ? SEQ_DARK : SEQ_LIGHT;
460
529
  };
461
530
 
@@ -1078,12 +1147,27 @@
1078
1147
  g.padL = (horizontal ? Math.min(maxCatW, this.w * 0.34) : maxYLabelW) + 12;
1079
1148
  g.padR = 8 + depth;
1080
1149
  g.padT = 8 + depth * 0.5;
1081
- g.padB = 24;
1082
1150
  g.plotX = g.padL;
1083
1151
  g.plotY = g.padT;
1084
1152
  g.plotW = Math.max(40, this.w - g.padL - g.padR);
1085
- g.plotH = Math.max(40, this.h - g.padT - g.padB);
1086
- g.band = (horizontal ? g.plotH : g.plotW) / Math.max(1, nC);
1153
+
1154
+ if (horizontal) {
1155
+ // category labels live in the left gutter, so the bottom stays shallow
1156
+ g.padB = 24;
1157
+ g.plotH = Math.max(40, this.h - g.padT - g.padB);
1158
+ g.band = g.plotH / Math.max(1, nC);
1159
+ g.labelAngle = 0;
1160
+ g.labelMaxPx = Math.max(24, g.padL - 12);
1161
+ } else {
1162
+ // band does not depend on padB here, so measure the labels first and let
1163
+ // them reserve the bottom space they actually need
1164
+ g.band = g.plotW / Math.max(1, nC);
1165
+ var lay = labelLayout(cats, g.band, this.h, o.labelRotation);
1166
+ g.labelAngle = lay.angle;
1167
+ g.labelMaxPx = lay.maxPx;
1168
+ g.padB = lay.pad;
1169
+ g.plotH = Math.max(40, this.h - g.padT - g.padB);
1170
+ }
1087
1171
 
1088
1172
  this.gGrid = svgEl("g", null, this.svg);
1089
1173
  this.gMarks = svgEl("g", null, this.svg);
@@ -1226,7 +1310,15 @@
1226
1310
  svgText(this.gGrid, pos, g.plotY + g.plotH + 16, fmt(v), "middle", 11, T.label);
1227
1311
  }
1228
1312
  for (i = 0; i < g.nC; i++) {
1229
- svgText(this.gGrid, g.plotX - 7, g.plotY + g.band * (i + .5) + 4, g.cats[i], "end", 11.5, T.label);
1313
+ // the gutter is capped at a third of the width, so clip to fit it
1314
+ var fullY = g.cats[i];
1315
+ var shownY = truncate(fullY, g.labelMaxPx, 11.5);
1316
+ var nodeY = svgText(this.gGrid, g.plotX - 7, g.plotY + g.band * (i + .5) + 4,
1317
+ shownY, "end", 11.5, T.label);
1318
+ if (shownY !== fullY) {
1319
+ var ttY = svgEl("title", null, nodeY);
1320
+ ttY.appendChild(document.createTextNode(fullY));
1321
+ }
1230
1322
  }
1231
1323
  } else {
1232
1324
  for (v = o.min; v <= scale.max + 1e-9; v += scale.step) {
@@ -1237,10 +1329,27 @@
1237
1329
  }, this.gGrid);
1238
1330
  svgText(this.gGrid, g.plotX - 7, pos + 4, fmt(v), "end", 11, T.label);
1239
1331
  }
1240
- var skip = Math.max(1, Math.ceil(32 / g.band));
1332
+ var ang = g.labelAngle || 0;
1333
+ // rotation removes the collision, so only thin out when even tilted
1334
+ // labels would stack on top of each other
1335
+ var skip = ang ? Math.max(1, Math.ceil(11 / g.band)) : 1;
1336
+ if (!ang) {
1337
+ 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));
1340
+ }
1241
1341
  for (i = 0; i < g.nC; i++) {
1242
1342
  if (i % skip) continue;
1243
- svgText(this.gGrid, g.plotX + g.band * (i + .5), g.plotY + g.plotH + 16, g.cats[i], "middle", 11, T.label);
1343
+ var lx = g.plotX + g.band * (i + .5);
1344
+ var ly = g.plotY + g.plotH + (ang ? 14 : 16);
1345
+ var full = g.cats[i];
1346
+ var shown = ang ? truncate(full, g.labelMaxPx) : full;
1347
+ var node = svgText(this.gGrid, lx, ly, shown, ang ? "end" : "middle", 11, T.label);
1348
+ if (ang) node.setAttribute("transform", "rotate(" + ang + " " + lx + " " + ly + ")");
1349
+ if (shown !== full) {
1350
+ var tt = svgEl("title", null, node); // full text still reachable
1351
+ tt.appendChild(document.createTextNode(full));
1352
+ }
1244
1353
  }
1245
1354
  }
1246
1355
  };
@@ -2907,6 +3016,42 @@
2907
3016
  RaChart.defaults = DEFAULTS;
2908
3017
  RaChart.palette = PALETTE_LIGHT;
2909
3018
  RaChart.paletteDark = PALETTE_DARK;
3019
+
3020
+ /**
3021
+ * Recolour every chart created from now on.
3022
+ *
3023
+ * RaChart.setPalette(['#534ab7', '#2196f3', '#f0334b']);
3024
+ * RaChart.setPalette(lightHexes, darkHexes); // separate dark steps
3025
+ *
3026
+ * Pass one array to use it in both modes. Colours cycle if a chart has more
3027
+ * series than you supply. Existing charts keep their palette until they
3028
+ * rebuild — call replay() on them, or set this before creating any chart.
3029
+ * Overridden per chart by `colors`, and by --ra-chart-series-N in CSS.
3030
+ */
3031
+ RaChart.setPalette = function (light, dark) {
3032
+ if (light && light.length) {
3033
+ PALETTE_LIGHT = light.slice();
3034
+ RaChart.palette = PALETTE_LIGHT;
3035
+ if (!dark || !dark.length) {
3036
+ PALETTE_DARK = light.slice();
3037
+ RaChart.paletteDark = PALETTE_DARK;
3038
+ }
3039
+ }
3040
+ if (dark && dark.length) {
3041
+ PALETTE_DARK = dark.slice();
3042
+ RaChart.paletteDark = PALETTE_DARK;
3043
+ }
3044
+ return RaChart;
3045
+ };
3046
+
3047
+ /** Restore the built-in colourblind-safe palette. */
3048
+ RaChart.resetPalette = function () {
3049
+ PALETTE_LIGHT = DEFAULT_LIGHT.slice();
3050
+ PALETTE_DARK = DEFAULT_DARK.slice();
3051
+ RaChart.palette = PALETTE_LIGHT;
3052
+ RaChart.paletteDark = PALETTE_DARK;
3053
+ return RaChart;
3054
+ };
2910
3055
  RaChart.types = Object.keys(KINDS);
2911
3056
  RaChart.create = function (host, options) { return new RaChart(host, options); };
2912
3057