cordova-plugin-ra-chart 1.0.0 → 1.0.2

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
@@ -44,8 +44,14 @@ ra-chart-cordova/
44
44
 
45
45
  ## Install
46
46
 
47
+ > **Ionic/Angular app? Use Option B.** The Cordova plugin route only exposes
48
+ > `window.RaChart` after `deviceready`, so charts stay blank under `ionic serve`.
49
+ > raChart has no native code, so the plugin buys you nothing there.
50
+
47
51
  ### Option A — Cordova plugin
48
52
 
53
+ Run from your Cordova project root, pointing at the folder that contains `plugin.xml`:
54
+
49
55
  ```bash
50
56
  cordova plugin add ./ra-chart-cordova
51
57
  ```
@@ -518,7 +524,59 @@ ready(chart: RaChartInstance) {
518
524
 
519
525
  ## Troubleshooting
520
526
 
521
- **"Chart engine failed to load"** — `ra-chart.js` is not at the loader's path. Confirm it sits in `src/assets/ra-chart/`, that `angular.json` copies `src/assets`, and override `RA_CHART_SCRIPT_URL` if you keep it elsewhere.
527
+ ### Install errors
528
+
529
+ **`Failed to fetch plugin cordova-plugin-ra-chart via registry`** — the plugin is not published to npm. Install from the local folder instead, with a path (the leading `./` matters):
530
+
531
+ ```bash
532
+ cordova plugin add ./ra-chart-cordova
533
+ ```
534
+
535
+ Run it from your Cordova project root, and point at wherever the folder actually lives, e.g. `cordova plugin add C:/Users/You/Desktop/AI/ra-chart-cordova`.
536
+
537
+ **`Cannot find plugin.xml for plugin "..."`** — the path is wrong or points one level off. It must be the folder that directly contains `plugin.xml`, not its parent and not `www/`.
538
+
539
+ **`Plugin doesn't support this project's cordova version`** — this came from a version requirement that v1.0.1 removed. Update to v1.0.1, or delete the `<engines>` block from `plugin.xml`.
540
+
541
+ **`npm ERR! code ENOLOCAL` / `Could not install from "ra-chart-cordova"`** — you ran `npm install` instead of `cordova plugin add`. This is a Cordova plugin, not an npm package.
542
+
543
+ **Using Capacitor, not Cordova?** `cordova plugin add` does not exist there. Skip the plugin entirely and use Option B — copy the two files into `src/assets/` and use the `<ra-chart>` component. Nothing in raChart needs a native bridge.
544
+
545
+ **In an Ionic/Angular app, prefer Option B over the plugin.** The Cordova route only sets `window.RaChart` after `deviceready`, which means charts do not render in `ionic serve` or in unit tests. Copying to `src/assets/` works everywhere.
546
+
547
+ ### No data showing / blank chart
548
+
549
+ Work down this list — the first two cover almost every case.
550
+
551
+ **1. Check the console.** Since v1.0.1 the engine reports naming mismatches itself:
552
+
553
+ ```
554
+ [raChart] nothing to plot — series "Revenue" field "revenue" not found in the data.
555
+ Your rows have: month, total. Check the `field`/`category` names against your data keys.
556
+ ```
557
+
558
+ That means `field: 'revenue'` does not match your rows, so every value read as 0. The `field` in `series` and the `category` string must match your data object keys **exactly**, including case. An API returning `Total` will not bind to `field: 'total'`.
559
+
560
+ **2. Async data with `<ra-chart>`** — fixed in v1.0.1. On v1.0.0 a chart whose `data` was still `[]` when the view initialised never built once the API responded, leaving it permanently blank. Update to v1.0.1. There is no workaround on v1.0.0 other than `*ngIf`-ing the chart until data arrives.
561
+
562
+ **3. Mutating the array instead of replacing it.** The component is `OnPush`, so `push()` is invisible to it:
563
+
564
+ ```ts
565
+ this.rows = [...this.rows, row]; // ✅
566
+ this.rows.push(row); // ❌ nothing redraws
567
+ ```
568
+
569
+ **4. Values are strings.** `"42"` from JSON coerces fine, but `"1,234"` or `"42%"` becomes 0. Parse before binding.
570
+
571
+ **5. Zero-width container.** A chart built inside a hidden tab, collapsed accordion or `display:none` parent measures 0 and draws nothing. Call `resize()` when it becomes visible.
572
+
573
+ **6. Plain Cordova: building before `deviceready`.** `window.RaChart` does not exist until the plugin bootstraps. Wrap construction in a `deviceready` listener.
574
+
575
+ To silence the diagnostics in production: `RaChart.warnings = false;`
576
+
577
+ ### Runtime issues
578
+
579
+ **"Chart engine failed to load"** — `ra-chart.js` is not on any path the loader tries. It attempts your `RA_CHART_SCRIPT_URL` first, then `assets/ra-chart/ra-chart.js`, `assets/js/ra-chart.js`, `assets/ra-chart.js`, and `plugins/cordova-plugin-ra-chart/www/ra-chart.js`. Confirm the file is in one of those, that `angular.json` copies `src/assets`, and check the browser Network tab for a 404.
522
580
 
523
581
  **Chart is blank but no error** — the host had zero width at build time (a hidden tab, a collapsed accordion). Call `chart.resize()` or the component's `resize()` when the container becomes visible.
524
582
 
@@ -88,6 +88,8 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
88
88
 
89
89
  private chart: RaChartInstance | null = null;
90
90
  private booted = false;
91
+ /** Guards against building before the host div has been measured. */
92
+ private viewReady = false;
91
93
  private listeners: Array<[string, EventListener]> = [];
92
94
 
93
95
  constructor(
@@ -106,11 +108,20 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
106
108
  }
107
109
 
108
110
  ngAfterViewInit(): void {
111
+ this.viewReady = true;
109
112
  this.boot();
110
113
  }
111
114
 
112
115
  ngOnChanges(changes: SimpleChanges): void {
113
- if (!this.chart) return;
116
+ if (!this.chart) {
117
+ // No chart yet. This is the normal async case: the component rendered
118
+ // while `data` was still an empty array, so boot() bailed out as empty.
119
+ // Now that inputs have arrived, build it — otherwise the chart would
120
+ // stay blank forever.
121
+ if (this.viewReady && !this.isEmpty) this.boot();
122
+ else this.cdr.markForCheck();
123
+ return;
124
+ }
114
125
 
115
126
  // data-only change on a stable shape → let the engine morph the values
116
127
  const keys = Object.keys(changes);
@@ -229,8 +240,12 @@ export class RaChartComponent implements AfterViewInit, OnChanges, OnDestroy {
229
240
  const host = this.hostRef.nativeElement;
230
241
  const bridge = (name: string, out: EventEmitter<any>) => {
231
242
  const fn = ((e: CustomEvent) => {
232
- // only surface to Angular if somebody is actually listening
233
- if (out.observers.length === 0) return;
243
+ // Skip the zone re-entry when nobody is bound, so touch scrubbing stays
244
+ // cheap. `observed` is RxJS 7+; `observers` is the older spelling and is
245
+ // gone in RxJS 8 — feature-detect rather than pin to either.
246
+ const subj = out as unknown as { observed?: boolean; observers?: unknown[] };
247
+ if (subj.observed === false) return;
248
+ if (subj.observed === undefined && subj.observers && subj.observers.length === 0) return;
234
249
  this.zone.run(() => out.emit(e.detail));
235
250
  }) as EventListener;
236
251
  host.addEventListener(name, fn);
@@ -2,9 +2,9 @@ import { InjectionToken } from '@angular/core';
2
2
  import type { RaChartConstructor } from './ra-chart.types';
3
3
 
4
4
  /**
5
- * Where `ra-chart.js` lives when it is NOT bundled via angular.json "scripts".
6
- * Override in your app config to change the path:
5
+ * Where `ra-chart.js` lives when it is NOT already on `window`.
7
6
  *
7
+ * Override in your app config to pin an exact path:
8
8
  * providers: [{ provide: RA_CHART_SCRIPT_URL, useValue: 'assets/vendor/ra-chart.js' }]
9
9
  */
10
10
  export const RA_CHART_SCRIPT_URL = new InjectionToken<string>('RA_CHART_SCRIPT_URL', {
@@ -12,6 +12,18 @@ export const RA_CHART_SCRIPT_URL = new InjectionToken<string>('RA_CHART_SCRIPT_U
12
12
  factory: () => 'assets/ra-chart/ra-chart.js'
13
13
  });
14
14
 
15
+ /**
16
+ * Paths tried after the configured one, so a copy that landed in a slightly
17
+ * different place still works instead of failing the whole chart. Covers the
18
+ * Cordova plugin install location and the usual assets spellings.
19
+ */
20
+ const FALLBACK_URLS = [
21
+ 'assets/ra-chart/ra-chart.js',
22
+ 'assets/js/ra-chart.js',
23
+ 'assets/ra-chart.js',
24
+ 'plugins/cordova-plugin-ra-chart/www/ra-chart.js'
25
+ ];
26
+
15
27
  let pending: Promise<RaChartConstructor> | null = null;
16
28
 
17
29
  /** The engine attaches itself to `window`; grab it if it is already there. */
@@ -20,44 +32,64 @@ function fromWindow(): RaChartConstructor | null {
20
32
  return w && w.RaChart ? (w.RaChart as RaChartConstructor) : null;
21
33
  }
22
34
 
23
- /**
24
- * Resolve the engine, lazy-injecting the script the first time if needed.
25
- * Repeat calls share one in-flight promise, so many charts on one page
26
- * trigger exactly one network request.
27
- */
28
- export function ensureRaChart(scriptUrl: string): Promise<RaChartConstructor> {
29
- const existing = fromWindow();
30
- if (existing) return Promise.resolve(existing);
31
- if (pending) return pending;
32
-
33
- pending = new Promise<RaChartConstructor>((resolve, reject) => {
34
- // another loader may have injected the same tag already
35
- const selector = `script[data-ra-chart="1"]`;
36
- let tag = document.querySelector<HTMLScriptElement>(selector);
37
-
38
- const done = () => {
35
+ function injectOnce(url: string): Promise<RaChartConstructor> {
36
+ return new Promise<RaChartConstructor>((resolve, reject) => {
37
+ const existing = document.querySelector<HTMLScriptElement>(
38
+ `script[data-ra-chart="${CSS && CSS.escape ? CSS.escape(url) : url}"]`
39
+ );
40
+ const settle = () => {
39
41
  const ctor = fromWindow();
40
42
  if (ctor) resolve(ctor);
41
- else reject(new Error('ra-chart.js loaded but window.RaChart is missing'));
43
+ else reject(new Error(`Loaded ${url} but window.RaChart is missing`));
42
44
  };
43
45
 
44
- if (tag) {
45
- tag.addEventListener('load', done);
46
- tag.addEventListener('error', () => reject(new Error('Failed to load ' + scriptUrl)));
46
+ if (existing) {
47
+ existing.addEventListener('load', settle);
48
+ existing.addEventListener('error', () => reject(new Error(`Failed to load ${url}`)));
47
49
  return;
48
50
  }
49
51
 
50
- tag = document.createElement('script');
51
- tag.src = scriptUrl;
52
+ const tag = document.createElement('script');
53
+ tag.src = url;
52
54
  tag.async = true;
53
- tag.dataset['raChart'] = '1';
54
- tag.onload = done;
55
- tag.onerror = () => {
56
- pending = null;
57
- reject(new Error('Failed to load ' + scriptUrl));
58
- };
55
+ tag.dataset['raChart'] = url;
56
+ tag.onload = settle;
57
+ tag.onerror = () => reject(new Error(`Failed to load ${url}`));
59
58
  document.head.appendChild(tag);
60
59
  });
60
+ }
61
+
62
+ /**
63
+ * Resolve the engine, lazy-injecting the script the first time if needed.
64
+ * Repeat calls share one in-flight promise, so many charts on one page trigger
65
+ * a single load. Tries `scriptUrl` first, then the well-known fallbacks.
66
+ */
67
+ export function ensureRaChart(scriptUrl: string): Promise<RaChartConstructor> {
68
+ const already = fromWindow();
69
+ if (already) return Promise.resolve(already);
70
+ if (pending) return pending;
71
+
72
+ const candidates = [scriptUrl, ...FALLBACK_URLS.filter(u => u !== scriptUrl)];
73
+
74
+ pending = candidates
75
+ .reduce<Promise<RaChartConstructor | null>>(
76
+ (chain, url) => chain.then(found => (found ? found : injectOnce(url).catch(() => null))),
77
+ Promise.resolve(null)
78
+ )
79
+ .then(found => {
80
+ if (found) return found;
81
+ pending = null; // let a later call retry after a fix
82
+ throw new Error(
83
+ 'ra-chart.js not found. Tried: ' + candidates.join(', ') +
84
+ '. Copy www/ra-chart.js into src/assets/ra-chart/, or provide ' +
85
+ 'RA_CHART_SCRIPT_URL with the correct path.'
86
+ );
87
+ });
61
88
 
62
89
  return pending;
63
90
  }
91
+
92
+ /** Test seam — forget any cached load so the next call re-resolves. */
93
+ export function resetRaChartLoader(): void {
94
+ pending = null;
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-ra-chart",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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",
@@ -8,12 +8,7 @@
8
8
  "author": "RemoteApps",
9
9
  "cordova": {
10
10
  "id": "cordova-plugin-ra-chart",
11
- "platforms": [
12
- "android",
13
- "ios",
14
- "browser",
15
- "electron"
16
- ]
11
+ "platforms": []
17
12
  },
18
13
  "keywords": [
19
14
  "ecosystem:cordova",
@@ -38,17 +33,10 @@
38
33
  "plugin.xml",
39
34
  "README.md"
40
35
  ],
41
- "engines": {
42
- "cordovaDependencies": {
43
- "1.0.0": {
44
- "cordova": ">=9.0.0"
45
- }
46
- }
47
- },
48
36
  "dependencies": {},
49
37
  "peerDependencies": {},
50
38
  "sideEffects": [
51
39
  "www/ra-chart.js",
52
40
  "www/ra-chart.css"
53
41
  ]
54
- }
42
+ }
package/plugin.xml CHANGED
@@ -3,14 +3,19 @@
3
3
  raChart Cordova plugin descriptor.
4
4
 
5
5
  Install: cordova plugin add ./ra-chart-cordova
6
- After install, `window.RaChart` is available on deviceready.
6
+ After install, `window.RaChart` is available once deviceready fires.
7
7
 
8
- This is a pure web-asset plugin: it ships JS + CSS only, so it works on
9
- every Cordova platform (and in the browser) with no native compilation.
8
+ This is a pure web-asset plugin: JS + CSS only, no native code and no
9
+ compilation, so it installs on every Cordova platform. Deliberately declares
10
+ no <engine> requirement and no <platform> filters — adding either only makes
11
+ the install fail on setups the plugin actually supports fine.
12
+
13
+ Cordova does not inject stylesheets. Link the CSS yourself in index.html:
14
+ <link rel="stylesheet" href="plugins/cordova-plugin-ra-chart/www/ra-chart.css">
10
15
  -->
11
16
  <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
12
17
  id="cordova-plugin-ra-chart"
13
- version="1.0.0">
18
+ version="1.0.1">
14
19
 
15
20
  <name>raChart</name>
16
21
  <description>
@@ -21,25 +26,27 @@
21
26
  <keywords>cordova,ionic,chart,charts,svg,graph,dashboard,mobile,offline</keywords>
22
27
  <author>RemoteApps</author>
23
28
 
24
- <engines>
25
- <engine name="cordova" version=">=9.0.0" />
26
- </engines>
27
-
28
29
  <!-- window.RaChart -->
29
30
  <js-module src="www/ra-chart.js" name="RaChart">
30
31
  <clobbers target="RaChart" />
31
32
  </js-module>
32
33
 
33
- <!--
34
- Cordova does not inject stylesheets, so link the CSS yourself in index.html:
35
- <link rel="stylesheet" href="plugins/cordova-plugin-ra-chart/www/ra-chart.css">
36
- Ionic apps normally add it to angular.json "styles" instead.
37
- -->
34
+ <!-- copied to www/css/ra-chart.css in the built app -->
38
35
  <asset src="www/ra-chart.css" target="css/ra-chart.css" />
39
36
 
40
- <platform name="android" />
41
- <platform name="ios" />
42
- <platform name="browser" />
43
- <platform name="electron" />
37
+ <info>
38
+ raChart installed.
39
+
40
+ Add the stylesheet to your index.html:
41
+ &lt;link rel="stylesheet" href="css/ra-chart.css"&gt;
42
+
43
+ Then, after deviceready:
44
+ new RaChart('#myChart', { type: 'column', category: 'month',
45
+ series: [{ name: 'Revenue', field: 'revenue' }], data: rows });
46
+
47
+ Ionic/Angular apps do not need this plugin — copy www/ra-chart.js and
48
+ www/ra-chart.css into src/assets/ra-chart/ and use the &lt;ra-chart&gt;
49
+ component in ionic/ instead. See README.md.
50
+ </info>
44
51
 
45
52
  </plugin>
package/www/ra-chart.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * raChart Cordova v1.0.0 — mobile SVG chart engine
2
+ * raChart Cordova v1.0.1 — 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.0";
23
+ var VERSION = "1.0.1";
24
24
 
25
25
  /* ============================================================
26
26
  Palettes — CVD-validated order, do not shuffle
@@ -527,11 +527,54 @@
527
527
  this.kind = KINDS[o.type] || "xy";
528
528
  this.zoomable = !!o.zoom && (this.kind === "xy" || this.kind === "candle");
529
529
 
530
+ this._validate();
530
531
  if (this.zoomable) this._buildZoomBar();
531
532
  this._scaffoldBody();
532
533
  this._dispatchBuild(entrance);
533
534
  };
534
535
 
536
+ /**
537
+ * A blank chart is almost always a naming mismatch rather than a rendering
538
+ * fault: `field` or `category` pointing at keys the rows do not have reads
539
+ * every value as 0 and draws nothing. Say so instead of failing silently.
540
+ * Warns once per chart, and never in production-silent mode.
541
+ */
542
+ RaChart.prototype._validate = function () {
543
+ if (this._validated || !RaChart.warnings) return;
544
+ this._validated = true;
545
+
546
+ var o = this.opts;
547
+ var rows = o.data || [];
548
+ if (!rows.length || !rows[0] || typeof rows[0] !== "object") return;
549
+ var keys = Object.keys(rows[0]);
550
+ var problems = [];
551
+
552
+ if (this.kind === "sankey") {
553
+ var F = merge({ from: "from", to: "to", value: "value" }, o.fields || {});
554
+ ["from", "to", "value"].forEach(function (k) {
555
+ if (keys.indexOf(F[k]) < 0) problems.push('sankey ' + k + ' field "' + F[k] + '"');
556
+ });
557
+ } else {
558
+ (o.series || []).forEach(function (s) {
559
+ if (!s || !s.field) { problems.push("a series with no `field`"); return; }
560
+ var found = rows.some(function (r) { return r && r[s.field] !== undefined; });
561
+ if (!found) problems.push('series "' + (s.name || s.field) + '" field "' + s.field + '"');
562
+ });
563
+ if (this.kind !== "scatter" && this.kind !== "gauge" &&
564
+ rows[0][o.category] === undefined) {
565
+ problems.push('category "' + o.category + '"');
566
+ }
567
+ }
568
+
569
+ if (problems.length) {
570
+ console.warn(
571
+ "[raChart] nothing to plot — " + problems.join(", ") +
572
+ " not found in the data. Your rows have: " + keys.join(", ") +
573
+ ". Check the `field`/`category` names against your data keys."
574
+ );
575
+ }
576
+ };
577
+
535
578
  RaChart.prototype._scaffoldBody = function () {
536
579
  var o = this.opts;
537
580
  this.stage = el("div", "ra-chart__stage", this.el);
@@ -2804,6 +2847,8 @@
2804
2847
  /* ---------- statics ---------- */
2805
2848
 
2806
2849
  RaChart.version = VERSION;
2850
+ /** Set false to silence the "nothing to plot" console diagnostics. */
2851
+ RaChart.warnings = true;
2807
2852
  RaChart.defaults = DEFAULTS;
2808
2853
  RaChart.palette = PALETTE_LIGHT;
2809
2854
  RaChart.paletteDark = PALETTE_DARK;