traderead-charts 1.1.0 → 1.2.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0 — 2026-09-13
4
+
5
+ - **In-widget timeframe picker** (`timeframes: {list, active, onSelect}`):
6
+ the host page's own TF buttons disappear under a fullscreen widget —
7
+ the pill in the header opens a grid menu that rides inside the frame,
8
+ works in fullscreen, closes on outside tap.
9
+
3
10
  ## 1.1.0 — 2026-09-13
4
11
 
5
12
  First npm release (`npm install traderead-charts`). Since 1.0.0:
package/README.md CHANGED
@@ -49,6 +49,18 @@ params, all persisted per key), a chart-type menu (seven types,
49
49
  Heikin Ashi included) and a PNG snapshot that opens a preview with explicit Download / Copy actions — nothing downloads by itself. Hosts that want only the
50
50
  engine pass `ui: false` and drive the same APIs themselves.
51
51
 
52
+ ## Install
53
+
54
+ ```
55
+ npm install traderead-charts
56
+ ```
57
+
58
+ or straight from a CDN, no build step:
59
+
60
+ ```html
61
+ <script src="https://cdn.jsdelivr.net/npm/traderead-charts@1.1.0/src/traderead-charts.js"></script>
62
+ ```
63
+
52
64
  ## Quick start
53
65
 
54
66
  ```html
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "traderead-charts",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Complete financial charting widget — candlesticks, indicators with a pro-style panel, drawing tools, symbol search, themes. Batteries included, zero dependencies, in production on traderead.ai.",
5
5
  "main": "src/traderead-charts.js",
6
6
  "files": [
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * TradeRead Charts v1.0.0
2
+ * TradeRead Charts v1.2.0
3
3
  * Copyright (c) 2026 Marcel Todea / TradeRead — traderead.ai
4
4
  * Original work, written from first principles.
5
5
  * TradeRead Community License 1.0 (see LICENSE.md): free to use, including
@@ -35,6 +35,11 @@
35
35
  minPricePaneFrac: 0.45, // price pane never shrinks below this share
36
36
  onFullscreenToggle: null, // host override: an iframe cannot fullscreen itself on iOS
37
37
  logoHref: null, // override the badge link (default carries utm_source=widget)
38
+ // {list:[{label,value}...], active, onSelect(value)} — an in-widget
39
+ // timeframe picker. The host page's own TF buttons disappear under a
40
+ // fullscreen widget (owner's find, 12 Sep 2026); inside the frame the
41
+ // picker survives every mode.
42
+ timeframes: null,
38
43
  logo: true,
39
44
  ui: true,
40
45
  maxIndicators: 0, // 0 = unlimited (Pro); Standard builds pass 4 // the bundled rail + indicator panel + type switcher + camera
@@ -1015,14 +1020,21 @@
1015
1020
  var h = document.createElement("div");
1016
1021
  h.className = "trc-header";
1017
1022
  var clickable = !!(this.opt.symbolSearch && this.opt.symbolSearch.search);
1023
+ var hasTf = !!(this.opt.timeframes && this.opt.timeframes.list && this.opt.timeframes.list.length);
1018
1024
  h.style.cssText = "position:absolute;left:" + (this.opt.ui ? 56 : 12) + "px;top:10px;z-index:4;display:flex;align-items:baseline;gap:8px;" +
1019
1025
  "font:800 15px -apple-system,'Segoe UI',sans-serif;user-select:none;" +
1020
- (clickable ? "cursor:pointer;" : "pointer-events:none;");
1021
- if (clickable) {
1022
- var self2 = this;
1023
- h.title = "Search ticker or name";
1024
- h.addEventListener("click", function () { self2._toggleSearch(); });
1025
- }
1026
+ (clickable ? "cursor:pointer;" : (hasTf ? "" : "pointer-events:none;"));
1027
+ var self2 = this;
1028
+ if (clickable) h.title = "Search ticker or name";
1029
+ // delegare: pastila de TF se reconstruieste la fiecare _paintHeader
1030
+ h.addEventListener("click", function (e) {
1031
+ if (hasTf && e.target.closest && e.target.closest(".trc-tf-btn")) {
1032
+ e.stopPropagation();
1033
+ self2._toggleTfMenu();
1034
+ return;
1035
+ }
1036
+ if (clickable) self2._toggleSearch();
1037
+ });
1026
1038
  this.el.appendChild(h);
1027
1039
  this._header = h;
1028
1040
  }
@@ -1042,13 +1054,52 @@
1042
1054
  var up = chg !== null && chg >= 0;
1043
1055
  this._header.innerHTML =
1044
1056
  '<span style="color:' + (o.tagText || "#e6edf3") + ';font-size:17px;">' + (inf.ticker || "") + "</span>" +
1045
- (inf.tf ? '<span style="color:' + o.textColor + ';font-weight:700;font-size:12px;">' + inf.tf + "</span>" : "") +
1057
+ (this.opt.timeframes && this.opt.timeframes.list && this.opt.timeframes.list.length
1058
+ ? '<span class="trc-tf-btn" title="Change timeframe" style="color:' + o.textColor + ';font-weight:700;font-size:12px;' +
1059
+ 'border:1px solid ' + o.separatorColor + ';border-radius:6px;padding:1px 8px;cursor:pointer;">' +
1060
+ (this._activeTfLabel() || inf.tf || "TF") + " \u25BE</span>"
1061
+ : (inf.tf ? '<span style="color:' + o.textColor + ';font-weight:700;font-size:12px;">' + inf.tf + "</span>" : "")) +
1046
1062
  (px ? '<span style="color:' + (up ? o.upColor : o.downColor) + ';">' + px + "</span>" : "") +
1047
1063
  (chg !== null ? '<span style="color:' + (up ? o.upColor : o.downColor) + ';font-weight:700;font-size:12px;">' +
1048
1064
  (up ? "+" : "") + chg.toFixed(2) + "%</span>" : "") +
1049
1065
  (inf.title ? '<span style="color:' + o.textColor + ';font-weight:600;font-size:12px;">' + inf.title + "</span>" : "");
1050
1066
  };
1051
1067
 
1068
+ Chart.prototype._activeTfLabel = function () {
1069
+ var t = this.opt.timeframes;
1070
+ if (!t || !t.list) return null;
1071
+ for (var i = 0; i < t.list.length; i++) if (t.list[i].value === t.active) return t.list[i].label;
1072
+ return null;
1073
+ };
1074
+ Chart.prototype._toggleTfMenu = function () {
1075
+ var self = this, o = this.opt, t = o.timeframes;
1076
+ if (this._tfMenu) { this._tfMenu.remove(); this._tfMenu = null; return; }
1077
+ if (!t || !t.list) return;
1078
+ var m = document.createElement("div");
1079
+ m.className = "trc-tf-menu";
1080
+ m.style.cssText = "position:absolute;left:" + (o.ui ? 56 : 12) + "px;top:34px;z-index:8;" +
1081
+ "background:" + o.background + ";border:1px solid " + o.separatorColor + ";border-radius:12px;padding:6px;" +
1082
+ "color:" + o.textColor + ";font:12px -apple-system,'Segoe UI',sans-serif;box-shadow:0 8px 30px rgba(0,0,0,0.35);" +
1083
+ "display:grid;grid-template-columns:repeat(4,1fr);gap:2px;max-height:70%;overflow:auto;";
1084
+ t.list.forEach(function (it) {
1085
+ var b = document.createElement("button");
1086
+ b.type = "button";
1087
+ b.textContent = it.label;
1088
+ b.style.cssText = "border:none;background:" + (it.value === t.active ? "rgba(99,102,241,0.25)" : "none") +
1089
+ ";color:inherit;font:inherit;font-weight:700;border-radius:8px;padding:6px 8px;cursor:pointer;text-align:center;";
1090
+ b.addEventListener("click", function () {
1091
+ m.remove(); self._tfMenu = null;
1092
+ if (it.value !== t.active && t.onSelect) t.onSelect(it.value);
1093
+ });
1094
+ m.appendChild(b);
1095
+ });
1096
+ this.el.appendChild(m);
1097
+ var hM = m.offsetHeight || 0, maxTopM = this._plotH() - hM - 6;
1098
+ if (34 > maxTopM) m.style.top = Math.max(6, maxTopM) + "px";
1099
+ this._tfMenu = m;
1100
+ this._armOutsideClose(m, this._header, function () { m.remove(); self._tfMenu = null; });
1101
+ };
1102
+
1052
1103
  // The indicator legend with LIVE numbers: label in the series color plus
1053
1104
  // the value under the crosshair (or the last bar when the cursor is away).
1054
1105
  Chart.prototype._paintLegend = function (barTime) {