@funnest/charts-ui 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +29 -2
- package/dist/components/chart-type-menu.d.ts +5 -0
- package/dist/components/drawing-toolbar.d.ts +10 -0
- package/dist/components/indicator-dialogs.d.ts +4 -0
- package/dist/components/interval-picker.d.ts +9 -0
- package/dist/components/overlay.d.ts +5 -0
- package/dist/components/settings-dialog.d.ts +8 -0
- package/dist/components/slot.d.ts +7 -0
- package/dist/components/symbol-avatar.d.ts +7 -0
- package/dist/components/symbol-search.d.ts +9 -0
- package/dist/components/toolbar.d.ts +5 -0
- package/dist/context.d.ts +74 -0
- package/dist/i18n.d.ts +197 -0
- package/dist/icons.d.ts +39 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +3005 -0
- package/dist/storage.d.ts +10 -0
- package/dist/store.d.ts +10 -0
- package/dist/styles.d.ts +1 -0
- package/dist/trading-chart.d.ts +83 -0
- package/package.json +51 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,3005 @@
|
|
|
1
|
+
// src/trading-chart.tsx
|
|
2
|
+
import {
|
|
3
|
+
createChart,
|
|
4
|
+
darkTheme,
|
|
5
|
+
lightTheme
|
|
6
|
+
} from "@funnest/charts";
|
|
7
|
+
|
|
8
|
+
// ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/dist/preact.mjs
|
|
9
|
+
var n;
|
|
10
|
+
var t;
|
|
11
|
+
var r;
|
|
12
|
+
var u;
|
|
13
|
+
var i;
|
|
14
|
+
var f;
|
|
15
|
+
var o;
|
|
16
|
+
var e;
|
|
17
|
+
var l;
|
|
18
|
+
var c;
|
|
19
|
+
var a;
|
|
20
|
+
var s;
|
|
21
|
+
var h = /* @__PURE__ */ Symbol.for("pv");
|
|
22
|
+
var p = {};
|
|
23
|
+
var y = [];
|
|
24
|
+
var v = /^m(i|n|o|s|text|space)$/;
|
|
25
|
+
var w = Array.isArray;
|
|
26
|
+
var d = y.slice;
|
|
27
|
+
var _ = Object.assign;
|
|
28
|
+
function g(n2) {
|
|
29
|
+
n2 && n2.parentNode && n2.remove();
|
|
30
|
+
}
|
|
31
|
+
function m(r3, u3, i3, f4, o4) {
|
|
32
|
+
var e3 = { type: r3, props: u3, key: i3, ref: f4, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: h, __v: o4 || ++t, __i: -1, __u: 0 };
|
|
33
|
+
return !o4 && n.vnode && n.vnode(e3), e3;
|
|
34
|
+
}
|
|
35
|
+
function $(n2) {
|
|
36
|
+
return n2.children;
|
|
37
|
+
}
|
|
38
|
+
function x(n2, t3) {
|
|
39
|
+
this.props = n2, this.context = t3, this.__g = 0;
|
|
40
|
+
}
|
|
41
|
+
function S(n2, t3) {
|
|
42
|
+
if (null == t3) return n2.__ && S(n2.__, n2.__i + 1);
|
|
43
|
+
for (var r3; t3 < n2.__k.length; t3++) if ((r3 = n2.__k[t3]) && r3.__e) return r3.__e;
|
|
44
|
+
return "function" != typeof n2.type || n2.props.__P ? null : S(n2);
|
|
45
|
+
}
|
|
46
|
+
function C(t3) {
|
|
47
|
+
var r3, u3 = t3.__v, i3 = u3.__e, f4 = [], o4 = [], e3 = t3.__P;
|
|
48
|
+
e3 && ((r3 = _({ constructor: h }, u3)).__v = u3.__v + 1, n.vnode && n.vnode(r3), z(e3, r3, u3, t3.__n, e3.namespaceURI, 32 & u3.__u ? [i3] : null, f4, i3 || S(u3), 32 & u3.__u, o4), r3.__v = u3.__v, r3.__.__k[r3.__i] = r3, E(f4, r3, o4), u3.__ = u3.__e = null, r3.__e != i3 && j(r3));
|
|
49
|
+
}
|
|
50
|
+
function j(n2) {
|
|
51
|
+
if ((n2 = n2.__) && n2.__c && !n2.props.__P) return n2.__e = null, n2.__k.some(function(t3) {
|
|
52
|
+
return t3 && (n2.__e = t3.__e);
|
|
53
|
+
}), j(n2);
|
|
54
|
+
}
|
|
55
|
+
function L(t3) {
|
|
56
|
+
(8 & t3.__g || (t3.__g |= 8, u.push(t3), f++)) && i == n.debounceRendering || ((i = n.debounceRendering) || queueMicrotask)(H);
|
|
57
|
+
}
|
|
58
|
+
function H() {
|
|
59
|
+
try {
|
|
60
|
+
for (var n2, t3 = 1; u.length; ) u.length > t3 && u.sort(o), n2 = u.shift(), t3 = u.length, 8 & n2.__g && C(n2);
|
|
61
|
+
} finally {
|
|
62
|
+
(f = u.length) && (i || queueMicrotask)(H);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function I(n2, t3, r3, u3, i3, f4, o4, e3, l3, c3, a4) {
|
|
66
|
+
var s3, h3, v3, w3, d3, _3, g3 = u3.__k || y, k2 = t3.length;
|
|
67
|
+
for (l3 = q(r3, t3, g3, l3, k2), s3 = 0; s3 < k2; s3++) null != (v3 = r3.__k[s3]) && (h3 = ~v3.__i && g3[v3.__i] || p, v3.__i = s3, _3 = z(n2, v3, h3, i3, f4, o4, e3, l3, c3, a4), w3 = v3.__e, (h3.ref != v3.ref || 8 & h3.__u) && (h3.ref && G(h3.ref, null, v3, h3), v3.ref && a4.push(v3.ref, v3.__c || w3, v3)), d3 = d3 || w3, 4 & v3.__u ? (l3 = A(v3, l3, n2, !h3.__v), h3.__e && (h3.__e = null)) : "function" == typeof v3.type && void 0 !== _3 ? l3 = _3 : w3 && (l3 = w3.nextSibling), v3.__u &= -21);
|
|
68
|
+
return r3.__e = d3, l3;
|
|
69
|
+
}
|
|
70
|
+
function q(n2, t3, r3, u3, i3) {
|
|
71
|
+
var f4, o4, e3, l3, c3, a4, s3, p3, y3, v3 = r3.length, d3 = v3, _3 = 0, g3 = 0, k2 = n2.__k = Array(i3);
|
|
72
|
+
for (f4 = 0; f4 < i3; f4++) null != (o4 = t3[f4]) && "boolean" != typeof o4 && "function" != typeof o4 ? ("object" != typeof o4 || o4.constructor === String ? o4 = k2[f4] = m(null, o4) : w(o4) ? o4 = k2[f4] = m($, { children: o4 }) : o4.constructor === h && o4.__b ? o4 = k2[f4] = m(o4.type, o4.props, o4.key, o4.ref, o4.__v) : k2[f4] = o4, l3 = f4 + _3, o4.__ = n2, o4.__b = n2.__b + 1, e3 = null, ~(c3 = o4.__i = P(o4, r3, l3, d3)) && (d3--, (e3 = r3[c3]) && (e3.__u |= 2)), e3 && e3.__v ? (o4.__u |= 16, c3 == l3 - 1 ? _3-- : c3 == l3 + 1 ? _3++ : c3 != l3 && (c3 > l3 ? _3-- : _3++, g3 = 1)) : (~c3 || (i3 > v3 ? _3-- : i3 < v3 && _3++), "function" != typeof o4.type && (o4.__u |= 4))) : k2[f4] = null;
|
|
73
|
+
if (g3) {
|
|
74
|
+
for (a4 = [], s3 = [], f4 = 0; f4 < i3; f4++) if ((o4 = k2[f4]) && 16 & o4.__u) {
|
|
75
|
+
for (_3 = 0, p3 = a4.length; _3 < p3; ) a4[y3 = _3 + p3 >> 1] < o4.__i ? _3 = y3 + 1 : p3 = y3;
|
|
76
|
+
a4[_3] = o4.__i, s3[f4] = _3 + 1;
|
|
77
|
+
}
|
|
78
|
+
for (_3 = a4.length; f4--; ) s3[f4] && (s3[f4] == _3 ? _3-- : k2[f4].__u |= 4);
|
|
79
|
+
}
|
|
80
|
+
if (d3) for (f4 = 0; f4 < v3; f4++) !(e3 = r3[f4]) || 2 & e3.__u || (e3.__e == u3 && (u3 = S(e3)), J(e3, e3));
|
|
81
|
+
return u3;
|
|
82
|
+
}
|
|
83
|
+
function A(n2, t3, r3, u3) {
|
|
84
|
+
var i3, f4, o4, e3;
|
|
85
|
+
if ("function" == typeof n2.type) {
|
|
86
|
+
if (n2.props.__P) return t3;
|
|
87
|
+
if (i3 = n2.__k) for (f4 = 0; f4 < i3.length; f4++) i3[f4] && (i3[f4].__ = n2, t3 = A(i3[f4], t3, r3, false));
|
|
88
|
+
return t3;
|
|
89
|
+
}
|
|
90
|
+
for (t3 && !t3.parentNode && (t3 = S(n2)) && !t3.parentNode && (t3 = null), o4 = n2.__e, e3 = t3; e3 && 8 == e3.nodeType; ) e3 = e3.nextSibling;
|
|
91
|
+
for (o4 != e3 && (!u3 && r3.moveBefore && o4.parentNode ? r3.moveBefore(o4, t3) : r3.insertBefore(o4, t3 || null)), t3 = o4; (t3 = t3 && t3.nextSibling) && 8 == t3.nodeType && !t3.data.startsWith("$s"); ) ;
|
|
92
|
+
return t3;
|
|
93
|
+
}
|
|
94
|
+
function P(n2, t3, r3, u3) {
|
|
95
|
+
var i3, f4 = n2.key, o4 = n2.type, e3 = t3[r3], l3 = e3 && !(2 & e3.__u);
|
|
96
|
+
if (null === e3 && null == f4 || l3 && f4 == e3.key && o4 == e3.type) return r3;
|
|
97
|
+
if (u3 > (l3 ? 1 : 0)) {
|
|
98
|
+
for (i3 = r3 - 1; i3 >= 0; i3--) if ((e3 = t3[i3]) && !(2 & e3.__u) && f4 == e3.key && o4 == e3.type) return i3;
|
|
99
|
+
for (i3 = r3 + 1; i3 < t3.length; i3++) if ((e3 = t3[i3]) && !(2 & e3.__u) && f4 == e3.key && o4 == e3.type) return i3;
|
|
100
|
+
}
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
function T(n2, t3, r3) {
|
|
104
|
+
null == r3 && (r3 = ""), "-" == t3[0] ? n2.setProperty(t3, r3) : n2[t3] = r3;
|
|
105
|
+
}
|
|
106
|
+
function N(n2, t3, r3, u3, i3) {
|
|
107
|
+
var f4, o4;
|
|
108
|
+
if ("style" == t3) if ("string" == typeof r3) n2.style.cssText = r3;
|
|
109
|
+
else {
|
|
110
|
+
if ("string" == typeof u3 && (n2.style.cssText = u3 = ""), u3) for (t3 in u3) r3 && t3 in r3 || T(n2.style, t3);
|
|
111
|
+
if (r3) for (t3 in r3) u3 && r3[t3] == u3[t3] || T(n2.style, t3, r3[t3]);
|
|
112
|
+
}
|
|
113
|
+
else if ("o" == t3[0] && "n" == t3[1]) "function" != typeof r3 && (r3 = null), "function" != typeof u3 && (u3 = null), (n2.__e || (n2.__e = {}))[t3] = r3, r3 && u3 || (o4 = a[t3] || (a[t3] = V(t3)), (n2.__a || (n2.__a = {}))[t3] = c, f4 = t3 != (t3 = t3.replace(l, "$1")), (t3 = t3.slice(2))[0] < "a" && (t3 = t3.toLowerCase()), r3 ? n2.addEventListener(t3, o4, f4) : n2.removeEventListener(t3, o4, f4));
|
|
114
|
+
else {
|
|
115
|
+
if ("http://www.w3.org/2000/svg" == i3) t3 = t3.replace(/xlink(H|:h)/, "h").replace(/sName$/, "s");
|
|
116
|
+
else if ("width" != t3 && "height" != t3 && "href" != t3 && "list" != t3 && "form" != t3 && "tabIndex" != t3 && "download" != t3 && "rowSpan" != t3 && "colSpan" != t3 && "role" != t3 && "popover" != t3 && t3 in n2) try {
|
|
117
|
+
return void (n2[t3] = null == r3 ? "" : r3);
|
|
118
|
+
} catch (n3) {
|
|
119
|
+
}
|
|
120
|
+
if ("function" == typeof r3) return;
|
|
121
|
+
null == r3 || false === r3 && "-" != t3[4] ? n2.removeAttribute(t3) : n2.setAttribute(t3, "popover" == t3 && 1 == r3 ? "" : r3);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function V(t3) {
|
|
125
|
+
return function(r3) {
|
|
126
|
+
var u3 = this.__e;
|
|
127
|
+
if (u3) {
|
|
128
|
+
if (u3 = u3[t3], null == r3[e]) r3[e] = c++;
|
|
129
|
+
else if (r3[e] < this.__a[t3]) return;
|
|
130
|
+
return u3(n.event ? n.event(r3) : r3);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function z(t3, r3, u3, i3, f4, o4, e3, l3, c3, a4) {
|
|
135
|
+
var s3, p3, v3, d3, k2, b, m3, M, C3, j2, L2, H2, q2, A3, O, P2, T3, N2, V2 = r3.type;
|
|
136
|
+
if (r3.constructor !== h) return null;
|
|
137
|
+
128 & u3.__u && (c3 = 32 & u3.__u, s3 = u3.__c.__z) && (r3.__u |= c3, p3 = o4 = [], 8 == s3.nodeType ? l3 = B(s3, o4) : o4.push(s3), l3 = o4[0] || l3), (s3 = n.__b) && s3(r3);
|
|
138
|
+
n: if ("function" == typeof V2) {
|
|
139
|
+
v3 = e3.length;
|
|
140
|
+
try {
|
|
141
|
+
if (M = r3.props, C3 = (s3 = V2.prototype) && s3.render, j2 = (s3 = V2.contextType) && i3[s3.__c], L2 = s3 ? j2 ? j2.props.value : s3.__ : i3, (d3 = u3.__c) ? (r3.__c = d3, 2 & d3.__g && (d3.__g |= 1)) : (C3 ? r3.__c = d3 = new V2(M, L2) : (r3.__c = d3 = new x(M, L2), d3.constructor = V2, d3.render = K), j2 && j2.sub(d3), d3.state || (d3.state = {}), d3.__n = i3, d3.__g |= 8, d3.__h = [], d3.__k = []), C3 && (d3.__s || (d3.__s = d3.state), V2.getDerivedStateFromProps && (d3.__s == d3.state && (d3.__s = _({}, d3.__s)), _(d3.__s, V2.getDerivedStateFromProps(M, d3.__s)))), k2 = d3.props, b = d3.state, d3.__v = r3, u3.__c) {
|
|
142
|
+
if (M !== k2 && C3 && !V2.getDerivedStateFromProps && d3.componentWillReceiveProps && d3.componentWillReceiveProps(M, L2), r3.__v == u3.__v && !(8 & d3.__g) || !(4 & d3.__g) && d3.shouldComponentUpdate && false === d3.shouldComponentUpdate(M, d3.__s, L2)) {
|
|
143
|
+
r3.__v != u3.__v && (d3.props = M, d3.state = d3.__s, d3.__g &= -9), r3.__e = u3.__e, r3.__k = u3.__k, r3.__k.some(function(n2) {
|
|
144
|
+
n2 && (n2.__ = r3);
|
|
145
|
+
}), y.push.apply(d3.__h, d3.__k), d3.__k = [], d3.__h.length && e3.push(d3), l3 = S(u3);
|
|
146
|
+
break n;
|
|
147
|
+
}
|
|
148
|
+
d3.componentWillUpdate && d3.componentWillUpdate(M, d3.__s, L2), C3 && d3.componentDidUpdate && d3.__h.push(function() {
|
|
149
|
+
d3.componentDidUpdate(k2, b, m3);
|
|
150
|
+
});
|
|
151
|
+
} else C3 && !V2.getDerivedStateFromProps && d3.componentWillMount && d3.componentWillMount(), C3 && d3.componentDidMount && d3.__h.push(d3.componentDidMount);
|
|
152
|
+
d3.context = L2, d3.props = M, d3.__P = t3, d3.__g &= -5, H2 = n.__r, q2 = C3 ? 25 : 0;
|
|
153
|
+
do {
|
|
154
|
+
d3.__g &= -9, H2 && H2(r3), s3 = d3.render(d3.props, d3.state = d3.__s, d3.context);
|
|
155
|
+
} while (8 & d3.__g && ++q2 < 25);
|
|
156
|
+
C3 && (y.push.apply(d3.__h, d3.__k), d3.__k = []), d3.state = d3.__s, d3.getChildContext && (i3 = _({}, i3, d3.getChildContext())), C3 && u3.__c && d3.getSnapshotBeforeUpdate && (m3 = d3.getSnapshotBeforeUpdate(k2, b)), A3 = s3 && s3.type === $ && null == s3.key ? s3.props.children : s3, M.__P && (s3 = l3, f4 = (t3 = M.__P).namespaceURI, c3 = o4 = null, u3.props && u3.props.__P != t3 && (u3.__k.some(function(n2) {
|
|
157
|
+
n2 && J(n2, n2);
|
|
158
|
+
}), u3.__k = null), l3 = u3.__k ? S(u3, 0) : null), l3 = I(t3, w(A3) ? A3 : [A3], r3, u3, i3, f4, o4, e3, l3, c3, a4), M.__P && (r3.__e = null, l3 = s3), r3.__u &= -161, p3 && p3.some(g), d3.__h.length && e3.push(d3), 1 & d3.__g && (d3.__g &= -4);
|
|
159
|
+
} catch (t4) {
|
|
160
|
+
if (e3.length = v3, r3.__v = null, c3 || o4) if (t4 && t4.then) {
|
|
161
|
+
if (r3.__u |= 128 | c3, o4) {
|
|
162
|
+
for (~(T3 = o4.indexOf(l3 || void 0)) || (T3 = o4.length); (N2 = o4[T3 - 1]) && 8 == N2.nodeType; ) T3--;
|
|
163
|
+
for (; T3 < o4.length; T3++) if (N2 = o4[T3]) if (o4[T3] = null, O) {
|
|
164
|
+
if (N2 == P2) break;
|
|
165
|
+
} else {
|
|
166
|
+
if (8 != N2.nodeType) break;
|
|
167
|
+
N2.data.startsWith("$s") && (P2 = B(O = N2));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!O) {
|
|
171
|
+
for (; l3 && 8 == l3.nodeType && l3.nextSibling; ) l3 = l3.nextSibling;
|
|
172
|
+
O = l3;
|
|
173
|
+
}
|
|
174
|
+
(s3 = B(O = r3.__c.__z || (r3.__c.__z = O))) ? (r3.__e = O, l3 = s3) : r3.__e = l3, l3 = l3 && l3.nextSibling;
|
|
175
|
+
} else o4 && o4.some(g);
|
|
176
|
+
else r3.__e = u3.__e;
|
|
177
|
+
r3.__k || (r3.__k = u3.__k || []), t4 && t4.then || D(r3), n.__e(t4, r3, u3);
|
|
178
|
+
}
|
|
179
|
+
} else l3 = r3.__e = F(u3.__e, r3, u3, i3, f4, o4, e3, c3, a4, t3);
|
|
180
|
+
return (s3 = n.diffed) && s3(r3), l3;
|
|
181
|
+
}
|
|
182
|
+
function B(n2, t3) {
|
|
183
|
+
if (n2 && 8 == n2.nodeType && n2.data.startsWith("$s")) for (var r3 = 1; n2 = n2.nextSibling; ) {
|
|
184
|
+
if (8 == n2.nodeType) {
|
|
185
|
+
if (n2.data.startsWith("$s")) r3++;
|
|
186
|
+
else if (n2.data.startsWith("/$s") && !--r3) return n2;
|
|
187
|
+
}
|
|
188
|
+
t3 && t3.push(n2);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function D(n2) {
|
|
192
|
+
n2 && (n2.__c && (n2.__c.__g |= 4), n2.__k && n2.__k.some(D));
|
|
193
|
+
}
|
|
194
|
+
function E(t3, r3, u3) {
|
|
195
|
+
for (var i3 = 0; i3 < u3.length; ) G(u3[i3++], u3[i3++], u3[i3++]);
|
|
196
|
+
n.__c && n.__c(r3, t3), t3.some(function(r4) {
|
|
197
|
+
try {
|
|
198
|
+
t3 = r4.__h, r4.__h = [], t3.some(function(n2) {
|
|
199
|
+
n2.call(r4);
|
|
200
|
+
});
|
|
201
|
+
} catch (t4) {
|
|
202
|
+
n.__e(t4, r4.__v);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function F(t3, r3, u3, i3, f4, o4, e3, l3, c3, a4) {
|
|
207
|
+
var s3, h3, y3, _3, k2, b, m3, M, $2 = u3.props || p, x2 = r3.props, C3 = r3.type;
|
|
208
|
+
if ("svg" == C3 ? f4 = "http://www.w3.org/2000/svg" : "math" == C3 ? f4 = "http://www.w3.org/1998/Math/MathML" : f4 || (f4 = "http://www.w3.org/1999/xhtml"), o4) {
|
|
209
|
+
for (s3 = 0; s3 < o4.length; s3++) if ((k2 = o4[s3]) && (C3 ? k2.localName == C3 : 3 == k2.nodeType)) {
|
|
210
|
+
t3 = k2, o4[s3] = null;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!t3) {
|
|
215
|
+
if (M = a4.ownerDocument || document, !C3) return M.createTextNode(x2);
|
|
216
|
+
t3 = M.createElementNS(f4, C3, x2.is ? x2 : a4), l3 && (n.__m && n.__m(r3, o4), l3 = false), o4 = null;
|
|
217
|
+
}
|
|
218
|
+
if (C3) {
|
|
219
|
+
if (a4 = "template" == C3 && t3.content || t3, o4 = "textarea" == C3 && null != x2.defaultValue ? null : o4 && d.call(a4.childNodes), !l3 && o4) for ($2 = {}, s3 = 0; s3 < t3.attributes.length; s3++) $2[(k2 = t3.attributes[s3]).name] = k2.value;
|
|
220
|
+
for (s3 in $2) k2 = $2[s3], "dangerouslySetInnerHTML" == s3 ? y3 = k2 : "children" == s3 || s3 in x2 || "value" == s3 && "defaultValue" in x2 || "checked" == s3 && "defaultChecked" in x2 || N(t3, s3, null, k2, f4);
|
|
221
|
+
for (s3 in x2) k2 = x2[s3], "children" == s3 ? _3 = k2 : "dangerouslySetInnerHTML" == s3 ? h3 = k2 : "value" == s3 ? b = k2 : "checked" == s3 ? m3 = k2 : l3 && "function" != typeof k2 || $2[s3] === k2 || N(t3, s3, k2, $2[s3], f4);
|
|
222
|
+
h3 ? (l3 || y3 && (h3.__html == y3.__html || h3.__html == t3.innerHTML) || (t3.innerHTML = h3.__html), r3.__k = []) : (y3 && (t3.textContent = ""), ("foreignObject" == C3 || "http://www.w3.org/1998/Math/MathML" == f4 && v.test(C3)) && (f4 = "http://www.w3.org/1999/xhtml"), I(a4, w(_3) ? _3 : [_3], r3, u3, i3, f4, o4, e3, o4 ? o4[0] : u3.__k && S(u3, 0), l3, c3), o4 && o4.some(g)), l3 && "textarea" != C3 || (s3 = "value", "progress" == C3 && null == b ? t3.removeAttribute(s3) : null == b || b === t3[s3] && ("progress" != C3 || b) || N(t3, s3, b, $2[s3], f4), s3 = "checked", null != m3 && m3 != t3[s3] && N(t3, s3, m3, $2[s3], f4));
|
|
223
|
+
} else $2 === x2 || l3 && t3.data == x2 || (t3.data = x2);
|
|
224
|
+
return t3;
|
|
225
|
+
}
|
|
226
|
+
function G(t3, r3, u3, i3) {
|
|
227
|
+
try {
|
|
228
|
+
"function" == typeof t3 ? r3 ? r3.__x = t3(r3) || 1 : i3 && (i3 = i3.__c || i3.__e) && (r3 = i3.__x) && (i3.__x = null, "function" == typeof r3 ? r3() : t3(null)) : t3.current = r3;
|
|
229
|
+
} catch (t4) {
|
|
230
|
+
n.__e(t4, u3);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function J(t3, r3, u3) {
|
|
234
|
+
var i3, f4, o4, e3;
|
|
235
|
+
if (n.unmount && n.unmount(t3), !(i3 = t3.ref) || i3.current && i3.current != t3.__e || G(i3, null, r3, t3), i3 = t3.__c) {
|
|
236
|
+
if (i3.componentWillUnmount) try {
|
|
237
|
+
i3.componentWillUnmount();
|
|
238
|
+
} catch (t4) {
|
|
239
|
+
n.__e(t4, r3);
|
|
240
|
+
}
|
|
241
|
+
i3.__P = i3.__n = null;
|
|
242
|
+
}
|
|
243
|
+
if (i3 = t3.__k) for (e3 = 0; e3 < i3.length; e3++) i3[e3] && J(i3[e3], r3, "function" != typeof t3.type || u3 && !t3.props.__P);
|
|
244
|
+
!u3 && (i3 = t3.__c) && (i3 = i3.__z) && (f4 = B(i3, o4 = [])) && (i3 == t3.__e && o4.some(g), g(f4), g(i3)), u3 || g(t3.__e), t3.__e = t3.__c = t3.__ = null;
|
|
245
|
+
}
|
|
246
|
+
function K(n2, t3, r3) {
|
|
247
|
+
return this.constructor(n2, r3);
|
|
248
|
+
}
|
|
249
|
+
function Q(t3, r3) {
|
|
250
|
+
var u3, i3, f4, o4;
|
|
251
|
+
n.__ && n.__(t3, r3), 9 == r3.nodeType && (r3 = r3.documentElement), i3 = (u3 = t3 && 32 & t3.__u) ? null : r3.__k, r3.__k = m($, { children: [t3] }), f4 = [], o4 = [], z(r3, r3.__k, i3 || p, p, r3.namespaceURI, i3 ? null : r3.firstChild ? d.call(r3.childNodes) : null, f4, i3 ? i3.__e : r3.firstChild, u3, o4), E(f4, r3.__k, o4), r3.__k.props.children = null;
|
|
252
|
+
}
|
|
253
|
+
n = { __e: function(n2, t3, r3, u3) {
|
|
254
|
+
for (var i3, o4, e3; t3 = t3.__; ) if ((i3 = t3.__c) && !(1 & i3.__g)) {
|
|
255
|
+
i3.__g |= 4;
|
|
256
|
+
try {
|
|
257
|
+
if ((e3 = (o4 = i3.constructor) && o4.getDerivedStateFromError) && i3.setState(o4.getDerivedStateFromError(n2)), i3.componentDidCatch && (i3.componentDidCatch(n2, u3 || {}), e3 = 1), e3 && 8 & i3.__g) return void (i3.__g |= 2);
|
|
258
|
+
} catch (t4) {
|
|
259
|
+
n2 = t4;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
throw f = 0, n2;
|
|
263
|
+
} }, t = 0, r = function(n2) {
|
|
264
|
+
return null != n2 && n2.constructor === h;
|
|
265
|
+
}, x.prototype.setState = function(n2, t3) {
|
|
266
|
+
var r3 = this.__s;
|
|
267
|
+
r3 && r3 != this.state || (r3 = this.__s = _({}, this.state)), "function" == typeof n2 && (n2 = n2(_({}, r3), this.props)), n2 && (_(r3, n2), this.__v && (t3 && this.__k.push(t3), L(this)));
|
|
268
|
+
}, x.prototype.forceUpdate = function(n2) {
|
|
269
|
+
this.__v && (this.__g |= 4, n2 && this.__h.push(n2), L(this));
|
|
270
|
+
}, x.prototype.render = $, u = [], f = 0, o = function(n2, t3) {
|
|
271
|
+
return n2.__v.__b - t3.__v.__b;
|
|
272
|
+
}, e = /* @__PURE__ */ Symbol(), l = /(PointerCapture)$|Capture$/i, c = 0, a = {}, s = 0;
|
|
273
|
+
|
|
274
|
+
// src/i18n.ts
|
|
275
|
+
var STRINGS = {
|
|
276
|
+
ko: {
|
|
277
|
+
searchSymbol: "\uC885\uBAA9 \uAC80\uC0C9",
|
|
278
|
+
searchPlaceholder: "\uC885\uBAA9\uBA85 \uB610\uB294 \uCF54\uB4DC \uAC80\uC0C9",
|
|
279
|
+
noResults: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC5B4\uC694",
|
|
280
|
+
close: "\uB2EB\uAE30",
|
|
281
|
+
zoomIn: "\uD655\uB300",
|
|
282
|
+
zoomOut: "\uCD95\uC18C",
|
|
283
|
+
scrollBack: "\uC774\uC804 \uAD6C\uAC04",
|
|
284
|
+
scrollForward: "\uB2E4\uC74C \uAD6C\uAC04",
|
|
285
|
+
resetView: "\uCD08\uAE30\uD654",
|
|
286
|
+
toRealtime: "\uCD5C\uC2E0\uC73C\uB85C \uC774\uB3D9",
|
|
287
|
+
screenshot: "\uC2A4\uD06C\uB9B0\uC0F7 \uC800\uC7A5",
|
|
288
|
+
fullscreen: "\uC804\uCCB4\uD654\uBA74",
|
|
289
|
+
exitFullscreen: "\uC804\uCCB4\uD654\uBA74 \uC885\uB8CC",
|
|
290
|
+
open: "\uC2DC",
|
|
291
|
+
high: "\uACE0",
|
|
292
|
+
low: "\uC800",
|
|
293
|
+
close_: "\uC885",
|
|
294
|
+
volume: "\uAC70\uB798\uB7C9",
|
|
295
|
+
chartType: "\uCC28\uD2B8 \uC885\uB958",
|
|
296
|
+
candles: "\uCE94\uB4E4",
|
|
297
|
+
line: "\uB77C\uC778",
|
|
298
|
+
area: "\uC601\uC5ED",
|
|
299
|
+
drawingTools: "\uADF8\uB9AC\uAE30 \uB3C4\uAD6C",
|
|
300
|
+
cursor: "\uCEE4\uC11C",
|
|
301
|
+
trendline: "\uCD94\uC138\uC120",
|
|
302
|
+
horizontal: "\uC218\uD3C9\uC120",
|
|
303
|
+
fibonacci: "\uD53C\uBCF4\uB098\uCE58 \uB418\uB3CC\uB9BC",
|
|
304
|
+
removeAll: "\uADF8\uB9AC\uAE30 \uBAA8\uB450 \uC0AD\uC81C",
|
|
305
|
+
collapseDrawbar: "\uADF8\uB9AC\uAE30 \uB3C4\uAD6C \uC811\uAE30",
|
|
306
|
+
chartSettings: "\uCC28\uD2B8 \uC124\uC815",
|
|
307
|
+
settingsCandles: "\uCE94\uB4E4",
|
|
308
|
+
settingsDisplay: "\uD45C\uC2DC",
|
|
309
|
+
settingsGrid: "\uACA9\uC790",
|
|
310
|
+
settingUpColor: "\uC0C1\uC2B9 \uC0C9",
|
|
311
|
+
settingDownColor: "\uD558\uB77D \uC0C9",
|
|
312
|
+
followTheme: "\uD14C\uB9C8 \uC0C9 \uC0AC\uC6A9",
|
|
313
|
+
settingHighLow: "\uCD5C\uACE0\xB7\uCD5C\uC800 \uD45C\uC2DC",
|
|
314
|
+
settingLastPrice: "\uD604\uC7AC\uAC00 \uC120",
|
|
315
|
+
settingVolume: "\uAC70\uB798\uB7C9",
|
|
316
|
+
settingGridH: "\uAC00\uB85C \uACA9\uC790",
|
|
317
|
+
settingGridV: "\uC138\uB85C \uACA9\uC790",
|
|
318
|
+
resetDefaults: "\uAE30\uBCF8\uAC12\uC73C\uB85C",
|
|
319
|
+
done: "\uC644\uB8CC",
|
|
320
|
+
expandDrawbar: "\uADF8\uB9AC\uAE30 \uB3C4\uAD6C \uD3BC\uCE58\uAE30",
|
|
321
|
+
magnet: "\uC790\uC11D \uBAA8\uB4DC (\uC2DC\xB7\uACE0\xB7\uC800\xB7\uC885\uAC00\uC5D0 \uBD99\uC774\uAE30)",
|
|
322
|
+
remove: "\uC0AD\uC81C",
|
|
323
|
+
color: "\uC0C9\uC0C1",
|
|
324
|
+
indicators: "\uC9C0\uD45C",
|
|
325
|
+
addIndicator: "\uC9C0\uD45C \uCD94\uAC00",
|
|
326
|
+
indicatorSettings: "\uC9C0\uD45C \uC124\uC815",
|
|
327
|
+
removeIndicator: "\uC9C0\uD45C \uC0AD\uC81C",
|
|
328
|
+
apply: "\uC801\uC6A9",
|
|
329
|
+
cancel: "\uCDE8\uC18C",
|
|
330
|
+
numbersHint: "\uC27C\uD45C\uB85C \uAD6C\uBD84 (\uC608: 5, 20, 60)",
|
|
331
|
+
show: "\uBCF4\uC774\uAE30",
|
|
332
|
+
hide: "\uC228\uAE30\uAE30",
|
|
333
|
+
collapse: "\uC9C0\uD45C \uC811\uAE30",
|
|
334
|
+
expand: "\uC9C0\uD45C \uD3BC\uCE58\uAE30",
|
|
335
|
+
indicatorCount: "\uC9C0\uD45C",
|
|
336
|
+
addPeriod: "\uAE30\uAC04 \uCD94\uAC00",
|
|
337
|
+
removePeriod: "\uAE30\uAC04 \uC0AD\uC81C",
|
|
338
|
+
period: "\uAE30\uAC04",
|
|
339
|
+
source: "\uAC00\uACA9 \uAE30\uC900",
|
|
340
|
+
lineWidth: "\uB450\uAED8",
|
|
341
|
+
inputs: "\uC785\uB825\uAC12",
|
|
342
|
+
style: "\uC2A4\uD0C0\uC77C",
|
|
343
|
+
// intervals
|
|
344
|
+
intervalMinutes: "\uBD84",
|
|
345
|
+
intervalHours: "\uC2DC\uAC04",
|
|
346
|
+
intervalDays: "\uC77C",
|
|
347
|
+
moreIntervals: "\uC2DC\uAC04 \uB2E8\uC704 \uB354\uBCF4\uAE30",
|
|
348
|
+
addFavorite: "\uC990\uACA8\uCC3E\uAE30\uC5D0 \uCD94\uAC00",
|
|
349
|
+
removeFavorite: "\uC990\uACA8\uCC3E\uAE30\uC5D0\uC11C \uC81C\uAC70",
|
|
350
|
+
unsupportedInterval: "\uC774 \uC885\uBAA9\uC740 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uB2E8\uC704",
|
|
351
|
+
// search
|
|
352
|
+
catAll: "\uC804\uCCB4",
|
|
353
|
+
catKrStock: "\uAD6D\uB0B4\uC8FC\uC2DD",
|
|
354
|
+
catUsStock: "\uD574\uC678\uC8FC\uC2DD",
|
|
355
|
+
catCrypto: "\uCF54\uC778",
|
|
356
|
+
catFutures: "\uC120\uBB3C",
|
|
357
|
+
recentSearches: "\uCD5C\uADFC \uAC80\uC0C9",
|
|
358
|
+
browse: "\uB458\uB7EC\uBCF4\uAE30",
|
|
359
|
+
allSymbols: "\uC804\uCCB4",
|
|
360
|
+
favorites: "\uC990\uACA8\uCC3E\uAE30",
|
|
361
|
+
exchanges: "\uAC70\uB798\uC18C",
|
|
362
|
+
noFavorites: "\uC990\uACA8\uCC3E\uAE30\uD55C \uC885\uBAA9\uC774 \uC5C6\uC5B4\uC694. \u2606\uB97C \uB20C\uB7EC \uCD94\uAC00\uD574 \uBCF4\uC138\uC694",
|
|
363
|
+
navigate: "\uC774\uB3D9",
|
|
364
|
+
openAction: "\uC5F4\uAE30",
|
|
365
|
+
clearRecent: "\uC804\uCCB4 \uC0AD\uC81C",
|
|
366
|
+
removeRecent: "\uCD5C\uADFC \uAC80\uC0C9\uC5D0\uC11C \uC0AD\uC81C",
|
|
367
|
+
symbols: "\uC885\uBAA9",
|
|
368
|
+
searching: "\uAC80\uC0C9 \uC911",
|
|
369
|
+
searchHint: "\u2191\u2193 \uC774\uB3D9 \xB7 Enter \uC120\uD0DD \xB7 Esc \uB2EB\uAE30",
|
|
370
|
+
current: "\uD604\uC7AC"
|
|
371
|
+
},
|
|
372
|
+
en: {
|
|
373
|
+
searchSymbol: "Symbol search",
|
|
374
|
+
searchPlaceholder: "Search by name or code",
|
|
375
|
+
noResults: "No results",
|
|
376
|
+
close: "Close",
|
|
377
|
+
zoomIn: "Zoom in",
|
|
378
|
+
zoomOut: "Zoom out",
|
|
379
|
+
scrollBack: "Scroll back",
|
|
380
|
+
scrollForward: "Scroll forward",
|
|
381
|
+
resetView: "Reset",
|
|
382
|
+
toRealtime: "Go to realtime",
|
|
383
|
+
screenshot: "Save screenshot",
|
|
384
|
+
fullscreen: "Fullscreen",
|
|
385
|
+
exitFullscreen: "Exit fullscreen",
|
|
386
|
+
open: "O",
|
|
387
|
+
high: "H",
|
|
388
|
+
low: "L",
|
|
389
|
+
close_: "C",
|
|
390
|
+
volume: "Vol",
|
|
391
|
+
chartType: "Chart type",
|
|
392
|
+
candles: "Candles",
|
|
393
|
+
line: "Line",
|
|
394
|
+
area: "Area",
|
|
395
|
+
drawingTools: "Drawing tools",
|
|
396
|
+
cursor: "Cursor",
|
|
397
|
+
trendline: "Trend line",
|
|
398
|
+
horizontal: "Horizontal line",
|
|
399
|
+
fibonacci: "Fib retracement",
|
|
400
|
+
removeAll: "Remove all drawings",
|
|
401
|
+
collapseDrawbar: "Hide drawing tools",
|
|
402
|
+
chartSettings: "Chart settings",
|
|
403
|
+
settingsCandles: "Candles",
|
|
404
|
+
settingsDisplay: "Display",
|
|
405
|
+
settingsGrid: "Grid",
|
|
406
|
+
settingUpColor: "Up color",
|
|
407
|
+
settingDownColor: "Down color",
|
|
408
|
+
followTheme: "Use theme color",
|
|
409
|
+
settingHighLow: "High/low markers",
|
|
410
|
+
settingLastPrice: "Last price line",
|
|
411
|
+
settingVolume: "Volume",
|
|
412
|
+
settingGridH: "Horizontal grid",
|
|
413
|
+
settingGridV: "Vertical grid",
|
|
414
|
+
resetDefaults: "Reset",
|
|
415
|
+
done: "Done",
|
|
416
|
+
expandDrawbar: "Show drawing tools",
|
|
417
|
+
magnet: "Magnet (snap to OHLC)",
|
|
418
|
+
remove: "Remove",
|
|
419
|
+
color: "Color",
|
|
420
|
+
indicators: "Indicators",
|
|
421
|
+
addIndicator: "Add indicator",
|
|
422
|
+
indicatorSettings: "Indicator settings",
|
|
423
|
+
removeIndicator: "Remove indicator",
|
|
424
|
+
apply: "Apply",
|
|
425
|
+
cancel: "Cancel",
|
|
426
|
+
numbersHint: "Comma separated (e.g. 5, 20, 60)",
|
|
427
|
+
show: "Show",
|
|
428
|
+
hide: "Hide",
|
|
429
|
+
collapse: "Collapse indicators",
|
|
430
|
+
expand: "Expand indicators",
|
|
431
|
+
indicatorCount: "Indicators",
|
|
432
|
+
addPeriod: "Add period",
|
|
433
|
+
removePeriod: "Remove period",
|
|
434
|
+
period: "Period",
|
|
435
|
+
source: "Source",
|
|
436
|
+
lineWidth: "Width",
|
|
437
|
+
inputs: "Inputs",
|
|
438
|
+
style: "Style",
|
|
439
|
+
intervalMinutes: "Minutes",
|
|
440
|
+
intervalHours: "Hours",
|
|
441
|
+
intervalDays: "Days",
|
|
442
|
+
moreIntervals: "More intervals",
|
|
443
|
+
addFavorite: "Add to favorites",
|
|
444
|
+
removeFavorite: "Remove from favorites",
|
|
445
|
+
unsupportedInterval: "Not available for this symbol",
|
|
446
|
+
catAll: "All",
|
|
447
|
+
catKrStock: "KR stocks",
|
|
448
|
+
catUsStock: "Global stocks",
|
|
449
|
+
catCrypto: "Crypto",
|
|
450
|
+
catFutures: "Futures",
|
|
451
|
+
recentSearches: "Recent",
|
|
452
|
+
browse: "Browse",
|
|
453
|
+
allSymbols: "All",
|
|
454
|
+
favorites: "Favorites",
|
|
455
|
+
exchanges: "Exchanges",
|
|
456
|
+
noFavorites: "No favorites yet \u2014 tap \u2606 to add one",
|
|
457
|
+
navigate: "navigate",
|
|
458
|
+
openAction: "open",
|
|
459
|
+
clearRecent: "Clear",
|
|
460
|
+
removeRecent: "Remove from recent",
|
|
461
|
+
symbols: "Symbols",
|
|
462
|
+
searching: "Searching",
|
|
463
|
+
searchHint: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc close",
|
|
464
|
+
current: "Current"
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
function resolutionLabel(resolution, locale, form = "short") {
|
|
468
|
+
const match = /^(\d*)([SDWM]?)$/i.exec(resolution.trim());
|
|
469
|
+
if (!match) return resolution;
|
|
470
|
+
const n2 = match[1] ? Number(match[1]) : 1;
|
|
471
|
+
const unit = (match[2] ?? "").toUpperCase();
|
|
472
|
+
const ko = locale === "ko";
|
|
473
|
+
const long = form === "long";
|
|
474
|
+
switch (unit) {
|
|
475
|
+
case "S":
|
|
476
|
+
return ko ? `${n2}\uCD08` : `${n2}s`;
|
|
477
|
+
case "D":
|
|
478
|
+
return ko ? n2 === 1 && !long ? "\uC77C" : `${n2}\uC77C` : `${n2}D`;
|
|
479
|
+
case "W":
|
|
480
|
+
return ko ? n2 === 1 && !long ? "\uC8FC" : `${n2}\uC8FC` : `${n2}W`;
|
|
481
|
+
case "M":
|
|
482
|
+
if (n2 % 12 === 0) return ko ? `${n2 / 12}\uB144` : `${n2 / 12}Y`;
|
|
483
|
+
return ko ? n2 === 1 && !long ? "\uC6D4" : `${n2}\uAC1C\uC6D4` : `${n2}M`;
|
|
484
|
+
default:
|
|
485
|
+
if (n2 >= 60 && n2 % 60 === 0) return ko ? `${n2 / 60}\uC2DC\uAC04` : `${n2 / 60}h`;
|
|
486
|
+
return ko ? `${n2}\uBD84` : `${n2}m`;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
|
|
491
|
+
var f2 = /* @__PURE__ */ Symbol.for("pv");
|
|
492
|
+
var o2 = 0;
|
|
493
|
+
function a2(t3, e3, n2, u3, a4, i3) {
|
|
494
|
+
e3 || (e3 = {});
|
|
495
|
+
var c3, l3, p3 = e3;
|
|
496
|
+
if ("ref" in p3 && "function" != typeof t3) for (l3 in p3 = {}, e3) "ref" == l3 ? c3 = e3[l3] : p3[l3] = e3[l3];
|
|
497
|
+
var s3 = { type: t3, props: p3, key: n2, ref: c3, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: f2, __v: --o2, __i: -1, __u: 0 };
|
|
498
|
+
return (a4 || i3) && (s3.__source = a4, s3.__self = i3), n.vnode && n.vnode(s3), s3;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/icons.tsx
|
|
502
|
+
function Icon({ children, size = 20 }) {
|
|
503
|
+
return /* @__PURE__ */ a2(
|
|
504
|
+
"svg",
|
|
505
|
+
{
|
|
506
|
+
width: size,
|
|
507
|
+
height: size,
|
|
508
|
+
viewBox: "0 0 20 20",
|
|
509
|
+
fill: "none",
|
|
510
|
+
stroke: "currentColor",
|
|
511
|
+
"stroke-width": "1.6",
|
|
512
|
+
"stroke-linecap": "round",
|
|
513
|
+
"stroke-linejoin": "round",
|
|
514
|
+
"aria-hidden": "true",
|
|
515
|
+
children
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
var SearchIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: [
|
|
520
|
+
/* @__PURE__ */ a2("circle", { cx: "9", cy: "9", r: "5.5" }),
|
|
521
|
+
/* @__PURE__ */ a2("path", { d: "M13 13l3.5 3.5" })
|
|
522
|
+
] });
|
|
523
|
+
var CloseIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M5.5 5.5l9 9M14.5 5.5l-9 9" }) });
|
|
524
|
+
var CameraIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
525
|
+
/* @__PURE__ */ a2("path", { d: "M3.5 7.2c0-.9.7-1.6 1.6-1.6h1.6l1.2-1.8h4.2l1.2 1.8h1.6c.9 0 1.6.7 1.6 1.6v6.6c0 .9-.7 1.6-1.6 1.6H5.1c-.9 0-1.6-.7-1.6-1.6z" }),
|
|
526
|
+
/* @__PURE__ */ a2("circle", { cx: "10", cy: "10.4", r: "2.6" })
|
|
527
|
+
] });
|
|
528
|
+
var ExpandIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M4 8V4h4M16 8V4h-4M4 12v4h4M16 12v4h-4" }) });
|
|
529
|
+
var CollapseIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M8 4v4H4M12 4v4h4M8 16v-4H4M12 16v-4h4" }) });
|
|
530
|
+
var MinusIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M5 10h10" }) });
|
|
531
|
+
var PlusIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M5 10h10M10 5v10" }) });
|
|
532
|
+
var ChevronLeftIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M12 5l-5 5 5 5" }) });
|
|
533
|
+
var ChevronRightIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M8 5l5 5-5 5" }) });
|
|
534
|
+
var ResetIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: [
|
|
535
|
+
/* @__PURE__ */ a2("path", { d: "M4.5 10a5.5 5.5 0 1 0 1.7-4" }),
|
|
536
|
+
/* @__PURE__ */ a2("path", { d: "M4.5 3.5V7H8" })
|
|
537
|
+
] });
|
|
538
|
+
var ToRealtimeIcon = () => /* @__PURE__ */ a2(Icon, { size: 18, children: /* @__PURE__ */ a2("path", { d: "M5 5.5l4.5 4.5L5 14.5M10 5.5l4.5 4.5-4.5 4.5" }) });
|
|
539
|
+
var ChevronDownIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M5 8l5 5 5-5" }) });
|
|
540
|
+
var CursorIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M5 3.5l10 6.2-4.4 1.1-2 4.2z" }) });
|
|
541
|
+
var TrendLineIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
542
|
+
/* @__PURE__ */ a2("path", { d: "M5 15L15 5" }),
|
|
543
|
+
/* @__PURE__ */ a2("circle", { cx: "5", cy: "15", r: "1.6", fill: "currentColor" }),
|
|
544
|
+
/* @__PURE__ */ a2("circle", { cx: "15", cy: "5", r: "1.6", fill: "currentColor" })
|
|
545
|
+
] });
|
|
546
|
+
var HorizontalLineIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
547
|
+
/* @__PURE__ */ a2("path", { d: "M3 10h14" }),
|
|
548
|
+
/* @__PURE__ */ a2("circle", { cx: "10", cy: "10", r: "1.6", fill: "currentColor" })
|
|
549
|
+
] });
|
|
550
|
+
var FibonacciIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M3 4.5h14M3 8.2h14M3 11.8h14M3 15.5h14" }) });
|
|
551
|
+
var TrashIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M4.5 6h11M8 6V4.5h4V6M6 6l.7 9.5h6.6L14 6" }) });
|
|
552
|
+
var PencilIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M12.8 4.2l3 3L7.5 15.5 4 16l.5-3.5z" }) });
|
|
553
|
+
var CandlesIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
554
|
+
/* @__PURE__ */ a2("path", { d: "M6.5 3.5v13M13.5 3.5v13" }),
|
|
555
|
+
/* @__PURE__ */ a2("rect", { x: "4.5", y: "6", width: "4", height: "6", rx: "0.8", fill: "currentColor" }),
|
|
556
|
+
/* @__PURE__ */ a2("rect", { x: "11.5", y: "8", width: "4", height: "5", rx: "0.8" })
|
|
557
|
+
] });
|
|
558
|
+
var LineChartIcon = () => /* @__PURE__ */ a2(Icon, { children: /* @__PURE__ */ a2("path", { d: "M3 14l4.5-5 3.5 3 6-7" }) });
|
|
559
|
+
var AreaChartIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
560
|
+
/* @__PURE__ */ a2("path", { d: "M3 14l4.5-5 3.5 3 6-7" }),
|
|
561
|
+
/* @__PURE__ */ a2("path", { d: "M3 14l4.5-5 3.5 3 6-7V17H3z", fill: "currentColor", "fill-opacity": "0.25", stroke: "none" })
|
|
562
|
+
] });
|
|
563
|
+
var IndicatorIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
564
|
+
/* @__PURE__ */ a2("path", { d: "M3 15.5l4-5 3 2.5 3.5-5L17 11" }),
|
|
565
|
+
/* @__PURE__ */ a2("path", { d: "M3 4.5v11.5h14" })
|
|
566
|
+
] });
|
|
567
|
+
var GearIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: [
|
|
568
|
+
/* @__PURE__ */ a2("circle", { cx: "10", cy: "10", r: "2.4" }),
|
|
569
|
+
/* @__PURE__ */ a2("path", { d: "M10 3v2M10 15v2M3 10h2M15 10h2M5 5l1.4 1.4M13.6 13.6L15 15M5 15l1.4-1.4M13.6 6.4L15 5" })
|
|
570
|
+
] });
|
|
571
|
+
var SmallCloseIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M6 6l8 8M14 6l-8 8" }) });
|
|
572
|
+
var EyeIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: [
|
|
573
|
+
/* @__PURE__ */ a2("path", { d: "M2 10s3-5.5 8-5.5S18 10 18 10s-3 5.5-8 5.5S2 10 2 10z" }),
|
|
574
|
+
/* @__PURE__ */ a2("circle", { cx: "10", cy: "10", r: "2.3" })
|
|
575
|
+
] });
|
|
576
|
+
var EyeOffIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M3 3l14 14M8.2 5a8 8 0 0 1 1.8-.5c5 0 8 5.5 8 5.5a14 14 0 0 1-2.2 2.9M5.4 6.4A14 14 0 0 0 2 10s3 5.5 8 5.5a7.7 7.7 0 0 0 3.6-.9" }) });
|
|
577
|
+
var MagnetIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
578
|
+
/* @__PURE__ */ a2("path", { d: "M5 4h3v6.5a2 2 0 0 0 4 0V4h3v6.5a5 5 0 0 1-10 0z" }),
|
|
579
|
+
/* @__PURE__ */ a2("path", { d: "M5 7h3M12 7h3" })
|
|
580
|
+
] });
|
|
581
|
+
var StarIcon = ({ filled = false }) => /* @__PURE__ */ a2(Icon, { size: 16, children: /* @__PURE__ */ a2(
|
|
582
|
+
"path",
|
|
583
|
+
{
|
|
584
|
+
d: "M10 3.2l2.1 4.3 4.7.7-3.4 3.3.8 4.7L10 14l-4.2 2.2.8-4.7-3.4-3.3 4.7-.7z",
|
|
585
|
+
fill: filled ? "currentColor" : "none"
|
|
586
|
+
}
|
|
587
|
+
) });
|
|
588
|
+
var CollapseLeftIcon = () => /* @__PURE__ */ a2(Icon, { size: 14, children: /* @__PURE__ */ a2("path", { d: "M11 5l-5 5 5 5M15 5l-5 5 5 5" }) });
|
|
589
|
+
var SettingsIcon = () => /* @__PURE__ */ a2(Icon, { children: [
|
|
590
|
+
/* @__PURE__ */ a2("path", { d: "M10 12.6a2.6 2.6 0 1 0 0-5.2 2.6 2.6 0 0 0 0 5.2z" }),
|
|
591
|
+
/* @__PURE__ */ a2("path", { d: "M16.2 12a1.3 1.3 0 0 0 .26 1.43l.05.05a1.6 1.6 0 1 1-2.26 2.26l-.05-.05a1.3 1.3 0 0 0-1.43-.26 1.3 1.3 0 0 0-.79 1.19v.14a1.6 1.6 0 0 1-3.2 0v-.07a1.3 1.3 0 0 0-.85-1.19 1.3 1.3 0 0 0-1.43.26l-.05.05a1.6 1.6 0 1 1-2.26-2.26l.05-.05A1.3 1.3 0 0 0 4.5 12a1.3 1.3 0 0 0-1.19-.79h-.14a1.6 1.6 0 0 1 0-3.2h.07a1.3 1.3 0 0 0 1.19-.85 1.3 1.3 0 0 0-.26-1.43l-.05-.05a1.6 1.6 0 1 1 2.26-2.26l.05.05a1.3 1.3 0 0 0 1.43.26h.06a1.3 1.3 0 0 0 .79-1.19v-.14a1.6 1.6 0 0 1 3.2 0v.07a1.3 1.3 0 0 0 .79 1.19 1.3 1.3 0 0 0 1.43-.26l.05-.05a1.6 1.6 0 1 1 2.26 2.26l-.05.05a1.3 1.3 0 0 0-.26 1.43v.06a1.3 1.3 0 0 0 1.19.79h.14a1.6 1.6 0 0 1 0 3.2h-.07a1.3 1.3 0 0 0-1.19.79z" })
|
|
592
|
+
] });
|
|
593
|
+
var FunnestMark = () => /* @__PURE__ */ a2("svg", { width: "20", height: "20", viewBox: "0 0 32 32", fill: "none", "aria-hidden": "true", children: [
|
|
594
|
+
/* @__PURE__ */ a2("rect", { width: "32", height: "32", rx: "9", fill: "#6C4DFF" }),
|
|
595
|
+
/* @__PURE__ */ a2("path", { d: "M7 13.5C9.5 21.5 17 23.5 23.4 11.2", stroke: "#fff", "stroke-width": "3.2", "stroke-linecap": "round" }),
|
|
596
|
+
/* @__PURE__ */ a2("circle", { cx: "24.6", cy: "8.6", r: "2.9", fill: "#FFD43B" })
|
|
597
|
+
] });
|
|
598
|
+
|
|
599
|
+
// ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/hooks/dist/hooks.mjs
|
|
600
|
+
var t2;
|
|
601
|
+
var r2;
|
|
602
|
+
var u2;
|
|
603
|
+
var i2;
|
|
604
|
+
var o3 = Object.is;
|
|
605
|
+
var f3 = 0;
|
|
606
|
+
var c2 = [];
|
|
607
|
+
var e2 = [];
|
|
608
|
+
var a3 = n;
|
|
609
|
+
var v2 = a3.__b;
|
|
610
|
+
var l2 = a3.__r;
|
|
611
|
+
var m2 = a3.diffed;
|
|
612
|
+
var h2 = a3.__c;
|
|
613
|
+
var s2 = a3.unmount;
|
|
614
|
+
var p2 = a3.__;
|
|
615
|
+
function y2(n2, t3) {
|
|
616
|
+
a3.__h && a3.__h(r2, n2, f3 || t3), f3 = 0;
|
|
617
|
+
var u3 = r2.__H || (r2.__H = { __: [], __h: [] });
|
|
618
|
+
return n2 >= u3.__.length && u3.__.push({}), u3.__[n2];
|
|
619
|
+
}
|
|
620
|
+
function d2(n2) {
|
|
621
|
+
return f3 = 1, _2(G2, n2);
|
|
622
|
+
}
|
|
623
|
+
function _2(n2, u3, i3) {
|
|
624
|
+
var f4 = y2(t2++, 2);
|
|
625
|
+
if (f4.t = n2, !f4.__c && (f4.__ = [i3 ? i3(u3) : G2(void 0, u3), function(n3) {
|
|
626
|
+
var t3 = (f4.__N || f4.__)[0], r3 = f4.t(t3, n3);
|
|
627
|
+
o3(t3, r3) || (f4.__N = [r3, f4.__[1]], f4.__c.setState({}));
|
|
628
|
+
}], f4.__c = r2, !r2.__f)) {
|
|
629
|
+
r2.__f = true;
|
|
630
|
+
var c3 = r2.shouldComponentUpdate;
|
|
631
|
+
r2.shouldComponentUpdate = function(n3, t3, r3) {
|
|
632
|
+
var u4 = this.__H;
|
|
633
|
+
if (!u4) return true;
|
|
634
|
+
var i4 = false, f5 = this.props != n3;
|
|
635
|
+
if (u4.__.some(function(n4) {
|
|
636
|
+
n4.__N && (i4 = true, o3(n4.__[0], n4.__N[0]) || (f5 = true));
|
|
637
|
+
}), c3) {
|
|
638
|
+
var e3 = c3.call(this, n3, t3, r3);
|
|
639
|
+
return i4 ? e3 || f5 : e3;
|
|
640
|
+
}
|
|
641
|
+
return !i4 || f5;
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
return f4.__;
|
|
645
|
+
}
|
|
646
|
+
function A2(n2, u3) {
|
|
647
|
+
var i3 = y2(t2++, 3);
|
|
648
|
+
!a3.__s && E2(i3.__H, u3) && (i3.__P = true, i3.__ = n2, i3.u = u3, r2.__H.__h.push(i3));
|
|
649
|
+
}
|
|
650
|
+
function F2(n2, u3) {
|
|
651
|
+
var i3 = y2(t2++, 4);
|
|
652
|
+
!a3.__s && E2(i3.__H, u3) && (i3.__P = false, i3.__ = n2, i3.u = u3, r2.__h.push(i3));
|
|
653
|
+
}
|
|
654
|
+
function T2(n2) {
|
|
655
|
+
return f3 = 5, w2(function() {
|
|
656
|
+
return { current: n2 };
|
|
657
|
+
}, []);
|
|
658
|
+
}
|
|
659
|
+
function w2(n2, r3) {
|
|
660
|
+
var u3 = y2(t2++, 7);
|
|
661
|
+
return E2(u3.__H, r3) && (u3.__ = n2(), u3.__H = r3), u3.__;
|
|
662
|
+
}
|
|
663
|
+
function g2() {
|
|
664
|
+
var n2;
|
|
665
|
+
do {
|
|
666
|
+
for (; n2 = e2.shift(); ) try {
|
|
667
|
+
C2(n2);
|
|
668
|
+
} catch (t4) {
|
|
669
|
+
a3.__e(t4, { __: (n2 = n2.__P) && n2.__v });
|
|
670
|
+
}
|
|
671
|
+
for (; n2 = c2.shift(); ) {
|
|
672
|
+
var t3 = n2.__H;
|
|
673
|
+
if (n2.__P && t3) try {
|
|
674
|
+
t3.__h.some(C2), t3.__h.some(D2), t3.__h = [];
|
|
675
|
+
} catch (r3) {
|
|
676
|
+
t3.__h = [], a3.__e(r3, n2.__v);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
} while (e2.length);
|
|
680
|
+
}
|
|
681
|
+
a3.__b = function(n2) {
|
|
682
|
+
r2 = null, v2 && v2(n2);
|
|
683
|
+
}, a3.__ = function(n2, t3) {
|
|
684
|
+
n2 && t3.__k && t3.__k.__m && (n2.__m = t3.__k.__m), p2 && p2(n2, t3);
|
|
685
|
+
}, a3.__r = function(n2) {
|
|
686
|
+
l2 && l2(n2), t2 = 0;
|
|
687
|
+
var i3 = (r2 = n2.__c).__H;
|
|
688
|
+
i3 && (u2 == r2 ? r2.__h = [] : (i3.__h.some(C2), i3.__h.some(D2), t2 = 0), i3.__h = [], i3.__.some(function(n3) {
|
|
689
|
+
n3.__N && (n3.__ = n3.__N), n3.u = n3.__N = void 0;
|
|
690
|
+
})), u2 = r2;
|
|
691
|
+
}, a3.diffed = function(n2) {
|
|
692
|
+
m2 && m2(n2);
|
|
693
|
+
var t3 = n2.__c;
|
|
694
|
+
t3 && t3.__H && (t3.__H.__h.length && B2(c2.push(t3)), t3.__H.__.some(function(n3) {
|
|
695
|
+
n3.u && (n3.__H = n3.u);
|
|
696
|
+
})), u2 = r2 = null;
|
|
697
|
+
}, a3.__c = function(n2, t3) {
|
|
698
|
+
t3.some(function(n3) {
|
|
699
|
+
try {
|
|
700
|
+
n3.__h.some(C2), n3.__h = n3.__h.filter(function(n4) {
|
|
701
|
+
return !n4.__ || D2(n4);
|
|
702
|
+
});
|
|
703
|
+
} catch (t4) {
|
|
704
|
+
n3.__h = [function() {
|
|
705
|
+
throw t4;
|
|
706
|
+
}];
|
|
707
|
+
}
|
|
708
|
+
}), h2 && h2(n2, t3);
|
|
709
|
+
}, a3.unmount = function(n2) {
|
|
710
|
+
s2 && s2(n2);
|
|
711
|
+
var t3, r3, u3 = n2.__c;
|
|
712
|
+
u3 && u3.__H && (u3.__H.__.some(function(u4) {
|
|
713
|
+
try {
|
|
714
|
+
if (u4.__P && u4.__c) {
|
|
715
|
+
if (void 0 === r3) {
|
|
716
|
+
for (r3 = n2.__; r3 && (!r3.__c || !r3.__c.__P); ) r3 = r3.__;
|
|
717
|
+
r3 = r3 && r3.__c;
|
|
718
|
+
}
|
|
719
|
+
u4.__P = r3, B2(e2.push(u4));
|
|
720
|
+
} else C2(u4);
|
|
721
|
+
} catch (n3) {
|
|
722
|
+
t3 = n3;
|
|
723
|
+
}
|
|
724
|
+
}), u3.__H = void 0, t3 && a3.__e(t3, u3.__v));
|
|
725
|
+
};
|
|
726
|
+
var k = "function" == typeof requestAnimationFrame;
|
|
727
|
+
function z2(n2) {
|
|
728
|
+
var t3, r3 = function() {
|
|
729
|
+
clearTimeout(u3), k && cancelAnimationFrame(t3), setTimeout(n2);
|
|
730
|
+
}, u3 = setTimeout(r3, 35);
|
|
731
|
+
k && (t3 = requestAnimationFrame(r3));
|
|
732
|
+
}
|
|
733
|
+
function B2(n2) {
|
|
734
|
+
1 != n2 && i2 == a3.requestAnimationFrame || ((i2 = a3.requestAnimationFrame) || z2)(g2);
|
|
735
|
+
}
|
|
736
|
+
function C2(n2) {
|
|
737
|
+
var t3 = r2, u3 = n2.__c;
|
|
738
|
+
"function" == typeof u3 && (n2.__c = void 0, u3()), r2 = t3;
|
|
739
|
+
}
|
|
740
|
+
function D2(n2) {
|
|
741
|
+
var t3 = r2;
|
|
742
|
+
n2.__c = n2.__(), r2 = t3;
|
|
743
|
+
}
|
|
744
|
+
function E2(n2, t3) {
|
|
745
|
+
return !n2 || n2.length != t3.length || t3.some(function(t4, r3) {
|
|
746
|
+
return !o3(t4, n2[r3]);
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
function G2(n2, t3) {
|
|
750
|
+
return "function" == typeof t3 ? t3(n2) : t3;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// src/store.ts
|
|
754
|
+
var Store = class {
|
|
755
|
+
constructor(state) {
|
|
756
|
+
this.state = state;
|
|
757
|
+
}
|
|
758
|
+
state;
|
|
759
|
+
listeners = /* @__PURE__ */ new Set();
|
|
760
|
+
get() {
|
|
761
|
+
return this.state;
|
|
762
|
+
}
|
|
763
|
+
set(patch) {
|
|
764
|
+
let changed = false;
|
|
765
|
+
for (const key in patch) {
|
|
766
|
+
if (!Object.is(patch[key], this.state[key])) {
|
|
767
|
+
changed = true;
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (!changed) return;
|
|
772
|
+
this.state = { ...this.state, ...patch };
|
|
773
|
+
for (const listener of this.listeners) listener();
|
|
774
|
+
}
|
|
775
|
+
subscribe(listener) {
|
|
776
|
+
this.listeners.add(listener);
|
|
777
|
+
return () => this.listeners.delete(listener);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
function useStore(store) {
|
|
781
|
+
const [state, setState] = d2(store.get());
|
|
782
|
+
A2(() => {
|
|
783
|
+
setState(store.get());
|
|
784
|
+
return store.subscribe(() => setState(store.get()));
|
|
785
|
+
}, [store]);
|
|
786
|
+
return state;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// src/components/drawing-toolbar.tsx
|
|
790
|
+
var TOOLS = [
|
|
791
|
+
{ tool: null, label: "cursor", icon: CursorIcon },
|
|
792
|
+
{ tool: "trendline", label: "trendline", icon: TrendLineIcon },
|
|
793
|
+
{ tool: "horizontal", label: "horizontal", icon: HorizontalLineIcon },
|
|
794
|
+
{ tool: "fibonacci", label: "fibonacci", icon: FibonacciIcon }
|
|
795
|
+
];
|
|
796
|
+
function DrawingToolbar({ ctx }) {
|
|
797
|
+
const s3 = useStore(ctx.store);
|
|
798
|
+
const t3 = STRINGS[s3.locale];
|
|
799
|
+
const pick = (tool) => {
|
|
800
|
+
ctx.chart.setDrawingTool(tool);
|
|
801
|
+
if (tool && ctx.store.get().drawbarOpen) ctx.store.set({ drawbarOpen: false });
|
|
802
|
+
};
|
|
803
|
+
return /* @__PURE__ */ a2($, { children: [
|
|
804
|
+
s3.drawbarCollapsed && /* @__PURE__ */ a2("button", { class: "fnc-drawbar-tab", "aria-label": t3.expandDrawbar, title: t3.expandDrawbar, onClick: () => ctx.setDrawbarCollapsed(false), children: /* @__PURE__ */ a2(ChevronRightIcon, {}) }),
|
|
805
|
+
/* @__PURE__ */ a2(
|
|
806
|
+
"div",
|
|
807
|
+
{
|
|
808
|
+
class: "fnc-drawbar",
|
|
809
|
+
role: "toolbar",
|
|
810
|
+
"aria-label": t3.drawingTools,
|
|
811
|
+
"aria-orientation": "vertical",
|
|
812
|
+
"data-open": s3.drawbarOpen,
|
|
813
|
+
"data-collapsed": s3.drawbarCollapsed,
|
|
814
|
+
children: [
|
|
815
|
+
TOOLS.map(({ tool, label, icon: Icon2 }) => /* @__PURE__ */ a2(
|
|
816
|
+
"button",
|
|
817
|
+
{
|
|
818
|
+
class: "fnc-btn",
|
|
819
|
+
title: t3[label],
|
|
820
|
+
"aria-label": t3[label],
|
|
821
|
+
"aria-pressed": s3.drawingTool === tool,
|
|
822
|
+
onClick: () => pick(tool),
|
|
823
|
+
children: /* @__PURE__ */ a2(Icon2, {})
|
|
824
|
+
}
|
|
825
|
+
)),
|
|
826
|
+
/* @__PURE__ */ a2("div", { class: "fnc-hsep" }),
|
|
827
|
+
/* @__PURE__ */ a2(
|
|
828
|
+
"button",
|
|
829
|
+
{
|
|
830
|
+
class: "fnc-btn",
|
|
831
|
+
title: t3.magnet,
|
|
832
|
+
"aria-label": t3.magnet,
|
|
833
|
+
"aria-pressed": s3.magnet,
|
|
834
|
+
onClick: () => ctx.chart.setMagnet(!s3.magnet),
|
|
835
|
+
children: /* @__PURE__ */ a2(MagnetIcon, {})
|
|
836
|
+
}
|
|
837
|
+
),
|
|
838
|
+
/* @__PURE__ */ a2(
|
|
839
|
+
"button",
|
|
840
|
+
{
|
|
841
|
+
class: "fnc-btn",
|
|
842
|
+
title: t3.removeAll,
|
|
843
|
+
"aria-label": t3.removeAll,
|
|
844
|
+
disabled: s3.drawingCount === 0,
|
|
845
|
+
onClick: () => ctx.chart.clearDrawings(),
|
|
846
|
+
children: /* @__PURE__ */ a2(TrashIcon, {})
|
|
847
|
+
}
|
|
848
|
+
),
|
|
849
|
+
/* @__PURE__ */ a2("span", { class: "fnc-spacer" }),
|
|
850
|
+
/* @__PURE__ */ a2(
|
|
851
|
+
"button",
|
|
852
|
+
{
|
|
853
|
+
class: "fnc-btn fnc-drawbar-collapse",
|
|
854
|
+
title: t3.collapseDrawbar,
|
|
855
|
+
"aria-label": t3.collapseDrawbar,
|
|
856
|
+
onClick: () => ctx.setDrawbarCollapsed(true),
|
|
857
|
+
children: /* @__PURE__ */ a2(ChevronLeftIcon, {})
|
|
858
|
+
}
|
|
859
|
+
)
|
|
860
|
+
]
|
|
861
|
+
}
|
|
862
|
+
)
|
|
863
|
+
] });
|
|
864
|
+
}
|
|
865
|
+
var SWATCHES = ["#3182F6", "#F04452", "#12B886", "#FF9F0A", "#7048E8"];
|
|
866
|
+
function SelectionBar({ ctx }) {
|
|
867
|
+
const s3 = useStore(ctx.store);
|
|
868
|
+
const t3 = STRINGS[s3.locale];
|
|
869
|
+
const d3 = s3.selection;
|
|
870
|
+
if (!d3 || s3.layout.plotWidth <= 0) return null;
|
|
871
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-selbar", role: "toolbar", style: { left: `${s3.layout.plotWidth / 2}px` }, children: [
|
|
872
|
+
SWATCHES.map((color) => /* @__PURE__ */ a2(
|
|
873
|
+
"button",
|
|
874
|
+
{
|
|
875
|
+
class: "fnc-swatch",
|
|
876
|
+
style: { background: color },
|
|
877
|
+
title: t3.color,
|
|
878
|
+
"aria-label": `${t3.color} ${color}`,
|
|
879
|
+
"aria-pressed": (d3.color ?? SWATCHES[0]) === color,
|
|
880
|
+
onClick: () => ctx.chart.updateDrawing(d3.id, { color })
|
|
881
|
+
}
|
|
882
|
+
)),
|
|
883
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sep" }),
|
|
884
|
+
/* @__PURE__ */ a2("button", { class: "fnc-btn", title: t3.remove, "aria-label": t3.remove, onClick: () => ctx.chart.removeDrawing(d3.id), children: /* @__PURE__ */ a2(TrashIcon, {}) })
|
|
885
|
+
] });
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// src/components/overlay.tsx
|
|
889
|
+
import { formatCompact, formatPrice } from "@funnest/charts";
|
|
890
|
+
|
|
891
|
+
// src/components/symbol-avatar.tsx
|
|
892
|
+
function hue(text) {
|
|
893
|
+
let h3 = 0;
|
|
894
|
+
for (const ch of text) h3 = (h3 * 31 + ch.charCodeAt(0)) % 360;
|
|
895
|
+
return h3;
|
|
896
|
+
}
|
|
897
|
+
function SymbolAvatar({ info, size = 24 }) {
|
|
898
|
+
const [failed, setFailed] = d2(false);
|
|
899
|
+
A2(() => setFailed(false), [info.logoUrl]);
|
|
900
|
+
const style = { width: `${size}px`, height: `${size}px`, fontSize: `${Math.round(size * 0.46)}px` };
|
|
901
|
+
if (info.logoUrl && !failed) {
|
|
902
|
+
return /* @__PURE__ */ a2("img", { class: "fnc-avatar", src: info.logoUrl, alt: "", style, onError: () => setFailed(true) });
|
|
903
|
+
}
|
|
904
|
+
const h3 = hue(info.symbol);
|
|
905
|
+
return /* @__PURE__ */ a2(
|
|
906
|
+
"span",
|
|
907
|
+
{
|
|
908
|
+
class: "fnc-avatar fnc-avatar-initial",
|
|
909
|
+
"aria-hidden": "true",
|
|
910
|
+
style: { ...style, background: `hsl(${h3} 70% 50% / 0.16)`, color: `hsl(${h3} 60% 42%)` },
|
|
911
|
+
children: (info.name || info.symbol).trim().charAt(0).toUpperCase()
|
|
912
|
+
}
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/components/overlay.tsx
|
|
917
|
+
var ZOOM_STEP = 1.25;
|
|
918
|
+
var CONTROL_OFFSET = 52;
|
|
919
|
+
var dirClass = (v3) => v3 > 0 ? "fnc-up" : v3 < 0 ? "fnc-down" : "fnc-flat";
|
|
920
|
+
function IndicatorItem({ ctx, item }) {
|
|
921
|
+
const t3 = STRINGS[ctx.store.get().locale];
|
|
922
|
+
return /* @__PURE__ */ a2(
|
|
923
|
+
"div",
|
|
924
|
+
{
|
|
925
|
+
class: item.visible ? "fnc-ind" : "fnc-ind fnc-ind-hidden",
|
|
926
|
+
role: "button",
|
|
927
|
+
tabIndex: 0,
|
|
928
|
+
"aria-label": `${item.title} ${t3.indicatorSettings}`,
|
|
929
|
+
onClick: (e3) => {
|
|
930
|
+
if (e3.target.closest(".fnc-ind-actions")) return;
|
|
931
|
+
ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } });
|
|
932
|
+
},
|
|
933
|
+
onKeyDown: (e3) => e3.key === "Enter" && ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } }),
|
|
934
|
+
children: [
|
|
935
|
+
/* @__PURE__ */ a2(
|
|
936
|
+
"button",
|
|
937
|
+
{
|
|
938
|
+
class: "fnc-ind-btn fnc-ind-eye",
|
|
939
|
+
title: item.visible ? t3.hide : t3.show,
|
|
940
|
+
"aria-label": `${item.title} ${item.visible ? t3.hide : t3.show}`,
|
|
941
|
+
"aria-pressed": !item.visible,
|
|
942
|
+
onClick: (e3) => {
|
|
943
|
+
e3.stopPropagation();
|
|
944
|
+
ctx.chart.setIndicatorVisible(item.id, !item.visible);
|
|
945
|
+
},
|
|
946
|
+
children: item.visible ? /* @__PURE__ */ a2(EyeIcon, {}) : /* @__PURE__ */ a2(EyeOffIcon, {})
|
|
947
|
+
}
|
|
948
|
+
),
|
|
949
|
+
/* @__PURE__ */ a2("span", { class: "fnc-ind-title", children: item.title }),
|
|
950
|
+
item.visible && item.values.map((v3) => /* @__PURE__ */ a2("b", { style: { color: v3.color }, title: v3.label, children: v3.value })),
|
|
951
|
+
/* @__PURE__ */ a2("span", { class: "fnc-ind-actions", children: [
|
|
952
|
+
/* @__PURE__ */ a2(
|
|
953
|
+
"button",
|
|
954
|
+
{
|
|
955
|
+
class: "fnc-ind-btn",
|
|
956
|
+
title: t3.indicatorSettings,
|
|
957
|
+
"aria-label": `${item.title} ${t3.indicatorSettings}`,
|
|
958
|
+
onClick: () => ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } }),
|
|
959
|
+
children: /* @__PURE__ */ a2(GearIcon, {})
|
|
960
|
+
}
|
|
961
|
+
),
|
|
962
|
+
/* @__PURE__ */ a2(
|
|
963
|
+
"button",
|
|
964
|
+
{
|
|
965
|
+
class: "fnc-ind-btn",
|
|
966
|
+
title: t3.removeIndicator,
|
|
967
|
+
"aria-label": `${item.title} ${t3.removeIndicator}`,
|
|
968
|
+
onClick: () => ctx.chart.removeIndicator(item.id),
|
|
969
|
+
children: /* @__PURE__ */ a2(SmallCloseIcon, {})
|
|
970
|
+
}
|
|
971
|
+
)
|
|
972
|
+
] })
|
|
973
|
+
]
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
function Legend({ ctx }) {
|
|
978
|
+
const s3 = useStore(ctx.store);
|
|
979
|
+
const t3 = STRINGS[s3.locale];
|
|
980
|
+
const [hover, setHover] = d2(null);
|
|
981
|
+
const [, refresh] = _2((n2) => n2 + 1, 0);
|
|
982
|
+
const [collapsed, setCollapsed] = d2(false);
|
|
983
|
+
A2(() => {
|
|
984
|
+
const offs = [
|
|
985
|
+
ctx.chart.on(
|
|
986
|
+
"crosshairMove",
|
|
987
|
+
(info) => setHover(info ? { bar: info.bar, prev: ctx.chart.bars[info.index - 1], index: info.index } : null)
|
|
988
|
+
),
|
|
989
|
+
ctx.chart.on("dataChange", () => refresh(void 0))
|
|
990
|
+
];
|
|
991
|
+
return () => offs.forEach((off) => off());
|
|
992
|
+
}, [ctx.chart]);
|
|
993
|
+
const ref = T2(null);
|
|
994
|
+
A2(() => {
|
|
995
|
+
const el = ref.current;
|
|
996
|
+
if (!el) return;
|
|
997
|
+
const update = () => ctx.chart.setTopInset(el.offsetTop + el.offsetHeight + 4);
|
|
998
|
+
const ro = new ResizeObserver(update);
|
|
999
|
+
ro.observe(el);
|
|
1000
|
+
return () => ro.disconnect();
|
|
1001
|
+
}, [ctx.chart]);
|
|
1002
|
+
if (!s3.symbol) return /* @__PURE__ */ a2("div", { class: "fnc-legend", ref });
|
|
1003
|
+
const bars = ctx.chart.bars;
|
|
1004
|
+
const bar = hover?.bar ?? bars[bars.length - 1];
|
|
1005
|
+
const prev = hover ? hover.prev : bars[bars.length - 2];
|
|
1006
|
+
const fmt = (p3) => formatPrice(p3, s3.symbol.pricePrecision);
|
|
1007
|
+
let values = null;
|
|
1008
|
+
if (bar) {
|
|
1009
|
+
const cls = dirClass(bar.close - bar.open);
|
|
1010
|
+
const change = prev ? bar.close - prev.close : 0;
|
|
1011
|
+
const pct = prev && prev.close ? change / prev.close * 100 : 0;
|
|
1012
|
+
const sign = change > 0 ? "+" : "";
|
|
1013
|
+
values = /* @__PURE__ */ a2("div", { class: "fnc-legend-values", children: [
|
|
1014
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1015
|
+
t3.open,
|
|
1016
|
+
" ",
|
|
1017
|
+
/* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.open) })
|
|
1018
|
+
] }),
|
|
1019
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1020
|
+
t3.high,
|
|
1021
|
+
" ",
|
|
1022
|
+
/* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.high) })
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1025
|
+
t3.low,
|
|
1026
|
+
" ",
|
|
1027
|
+
/* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.low) })
|
|
1028
|
+
] }),
|
|
1029
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1030
|
+
t3.close_,
|
|
1031
|
+
" ",
|
|
1032
|
+
/* @__PURE__ */ a2("b", { class: cls, children: fmt(bar.close) })
|
|
1033
|
+
] }),
|
|
1034
|
+
/* @__PURE__ */ a2("b", { class: dirClass(change), children: [
|
|
1035
|
+
sign,
|
|
1036
|
+
fmt(change),
|
|
1037
|
+
" (",
|
|
1038
|
+
sign,
|
|
1039
|
+
pct.toFixed(2),
|
|
1040
|
+
"%)"
|
|
1041
|
+
] }),
|
|
1042
|
+
bar.volume !== void 0 && /* @__PURE__ */ a2("span", { children: [
|
|
1043
|
+
t3.volume,
|
|
1044
|
+
" ",
|
|
1045
|
+
/* @__PURE__ */ a2("b", { class: "fnc-flat", children: formatCompact(bar.volume, s3.locale) })
|
|
1046
|
+
] })
|
|
1047
|
+
] });
|
|
1048
|
+
}
|
|
1049
|
+
const items = s3.indicators.length ? ctx.chart.indicatorLegend(hover?.index) : [];
|
|
1050
|
+
const overlayItems = items.filter((i3) => i3.overlay);
|
|
1051
|
+
const paneLegends = s3.layout.panes.filter((p3) => p3.kind === "indicator").map((pane) => ({ pane, item: items.find((i3) => i3.id === pane.id) }));
|
|
1052
|
+
return /* @__PURE__ */ a2($, { children: [
|
|
1053
|
+
/* @__PURE__ */ a2("div", { class: "fnc-legend", ref, children: [
|
|
1054
|
+
/* @__PURE__ */ a2("div", { class: "fnc-legend-title", children: [
|
|
1055
|
+
ctx.showLogo && /* @__PURE__ */ a2(SymbolAvatar, { info: s3.symbol, size: 18 }),
|
|
1056
|
+
/* @__PURE__ */ a2("b", { children: s3.symbol.name }),
|
|
1057
|
+
/* @__PURE__ */ a2("span", { children: "\xB7" }),
|
|
1058
|
+
/* @__PURE__ */ a2("span", { children: resolutionLabel(s3.resolution, s3.locale) }),
|
|
1059
|
+
/* @__PURE__ */ a2("span", { children: "\xB7" }),
|
|
1060
|
+
/* @__PURE__ */ a2("span", { children: s3.symbol.exchange }),
|
|
1061
|
+
s3.loading && /* @__PURE__ */ a2("span", { class: "fnc-spinner", role: "status" })
|
|
1062
|
+
] }),
|
|
1063
|
+
values,
|
|
1064
|
+
!collapsed && overlayItems.map((item) => /* @__PURE__ */ a2(IndicatorItem, { ctx, item })),
|
|
1065
|
+
overlayItems.length > 0 && (collapsed ? /* @__PURE__ */ a2("button", { class: "fnc-ind-handle", "aria-expanded": "false", "aria-label": t3.expand, title: t3.expand, onClick: () => setCollapsed(false), children: /* @__PURE__ */ a2("span", {}) }) : /* @__PURE__ */ a2("button", { class: "fnc-ind-collapse", "aria-expanded": "true", "aria-label": t3.collapse, title: t3.collapse, onClick: () => setCollapsed(true), children: /* @__PURE__ */ a2(CollapseLeftIcon, {}) }))
|
|
1066
|
+
] }),
|
|
1067
|
+
paneLegends.map(
|
|
1068
|
+
({ pane, item }) => item && /* @__PURE__ */ a2("div", { class: "fnc-legend fnc-pane-legend", style: { top: `${pane.top + 4}px` }, children: /* @__PURE__ */ a2(IndicatorItem, { ctx, item }) })
|
|
1069
|
+
)
|
|
1070
|
+
] });
|
|
1071
|
+
}
|
|
1072
|
+
function NavControls({ ctx }) {
|
|
1073
|
+
const s3 = useStore(ctx.store);
|
|
1074
|
+
const t3 = STRINGS[s3.locale];
|
|
1075
|
+
const { plotWidth, panesHeight } = s3.layout;
|
|
1076
|
+
if (plotWidth <= 0) return null;
|
|
1077
|
+
const top = panesHeight - CONTROL_OFFSET;
|
|
1078
|
+
const page = () => Math.max(1, Math.round(plotWidth / ctx.chart.timeScale.barSpacing / 3));
|
|
1079
|
+
const button = (label, onClick, icon) => /* @__PURE__ */ a2("button", { class: "fnc-btn", title: label, "aria-label": label, onClick, children: icon });
|
|
1080
|
+
return /* @__PURE__ */ a2($, { children: [
|
|
1081
|
+
/* @__PURE__ */ a2("div", { class: "fnc-nav", style: { left: `${plotWidth / 2}px`, top: `${top}px` }, children: [
|
|
1082
|
+
button(t3.zoomOut, () => ctx.chart.zoom(1 / ZOOM_STEP), /* @__PURE__ */ a2(MinusIcon, {})),
|
|
1083
|
+
button(t3.zoomIn, () => ctx.chart.zoom(ZOOM_STEP), /* @__PURE__ */ a2(PlusIcon, {})),
|
|
1084
|
+
button(t3.scrollBack, () => ctx.chart.scrollBars(-page()), /* @__PURE__ */ a2(ChevronLeftIcon, {})),
|
|
1085
|
+
button(t3.scrollForward, () => ctx.chart.scrollBars(page()), /* @__PURE__ */ a2(ChevronRightIcon, {})),
|
|
1086
|
+
button(t3.resetView, () => ctx.chart.resetView(), /* @__PURE__ */ a2(ResetIcon, {}))
|
|
1087
|
+
] }),
|
|
1088
|
+
!s3.atRealtime && /* @__PURE__ */ a2(
|
|
1089
|
+
"button",
|
|
1090
|
+
{
|
|
1091
|
+
class: "fnc-realtime",
|
|
1092
|
+
style: { left: `${plotWidth - 44}px`, top: `${top}px` },
|
|
1093
|
+
title: t3.toRealtime,
|
|
1094
|
+
"aria-label": t3.toRealtime,
|
|
1095
|
+
onClick: () => ctx.chart.scrollToRealtime(),
|
|
1096
|
+
children: /* @__PURE__ */ a2(ToRealtimeIcon, {})
|
|
1097
|
+
}
|
|
1098
|
+
)
|
|
1099
|
+
] });
|
|
1100
|
+
}
|
|
1101
|
+
var ATTRIBUTION_URL = "https://charts.funnest.xyz/?utm_source=chart&utm_medium=attribution";
|
|
1102
|
+
function Attribution({ ctx }) {
|
|
1103
|
+
const s3 = useStore(ctx.store);
|
|
1104
|
+
const { panesHeight, plotWidth } = s3.layout;
|
|
1105
|
+
if (!ctx.attribution || plotWidth <= 0) return null;
|
|
1106
|
+
const label = s3.locale === "ko" ? "Funnest \uCC28\uD2B8" : "Charts by Funnest";
|
|
1107
|
+
return /* @__PURE__ */ a2(
|
|
1108
|
+
"a",
|
|
1109
|
+
{
|
|
1110
|
+
class: "fnc-attribution",
|
|
1111
|
+
href: ATTRIBUTION_URL,
|
|
1112
|
+
target: "_blank",
|
|
1113
|
+
rel: "noopener",
|
|
1114
|
+
title: label,
|
|
1115
|
+
"aria-label": label,
|
|
1116
|
+
style: { top: `${panesHeight - 36}px` },
|
|
1117
|
+
children: [
|
|
1118
|
+
/* @__PURE__ */ a2(FunnestMark, {}),
|
|
1119
|
+
/* @__PURE__ */ a2("span", { children: "funnest" })
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
function Overlay({ ctx }) {
|
|
1125
|
+
return /* @__PURE__ */ a2($, { children: [
|
|
1126
|
+
/* @__PURE__ */ a2(Attribution, { ctx }),
|
|
1127
|
+
/* @__PURE__ */ a2(Legend, { ctx }),
|
|
1128
|
+
/* @__PURE__ */ a2(NavControls, { ctx }),
|
|
1129
|
+
/* @__PURE__ */ a2(SelectionBar, { ctx })
|
|
1130
|
+
] });
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// src/components/chart-type-menu.tsx
|
|
1134
|
+
var TYPES = [
|
|
1135
|
+
{ type: "candles", label: "candles", icon: CandlesIcon },
|
|
1136
|
+
{ type: "line", label: "line", icon: LineChartIcon },
|
|
1137
|
+
{ type: "area", label: "area", icon: AreaChartIcon }
|
|
1138
|
+
];
|
|
1139
|
+
function ChartTypeMenu({ ctx }) {
|
|
1140
|
+
const s3 = useStore(ctx.store);
|
|
1141
|
+
const t3 = STRINGS[s3.locale];
|
|
1142
|
+
const [open, setOpen] = d2(false);
|
|
1143
|
+
const ref = T2(null);
|
|
1144
|
+
const current = TYPES.find((x2) => x2.type === s3.chartType) ?? TYPES[0];
|
|
1145
|
+
A2(() => {
|
|
1146
|
+
if (!open) return;
|
|
1147
|
+
const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
|
|
1148
|
+
const onKey = (e3) => e3.key === "Escape" && setOpen(false);
|
|
1149
|
+
document.addEventListener("pointerdown", onDown);
|
|
1150
|
+
document.addEventListener("keydown", onKey);
|
|
1151
|
+
return () => {
|
|
1152
|
+
document.removeEventListener("pointerdown", onDown);
|
|
1153
|
+
document.removeEventListener("keydown", onKey);
|
|
1154
|
+
};
|
|
1155
|
+
}, [open]);
|
|
1156
|
+
const choose = (type) => {
|
|
1157
|
+
ctx.chart.setChartType(type);
|
|
1158
|
+
ctx.store.set({ chartType: type });
|
|
1159
|
+
setOpen(false);
|
|
1160
|
+
};
|
|
1161
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-menu-wrap", ref, children: [
|
|
1162
|
+
/* @__PURE__ */ a2(
|
|
1163
|
+
"button",
|
|
1164
|
+
{
|
|
1165
|
+
class: "fnc-btn fnc-btn-wide",
|
|
1166
|
+
title: t3.chartType,
|
|
1167
|
+
"aria-label": t3.chartType,
|
|
1168
|
+
"aria-haspopup": "menu",
|
|
1169
|
+
"aria-expanded": open,
|
|
1170
|
+
onClick: () => setOpen(!open),
|
|
1171
|
+
children: [
|
|
1172
|
+
/* @__PURE__ */ a2(current.icon, {}),
|
|
1173
|
+
/* @__PURE__ */ a2(ChevronDownIcon, {})
|
|
1174
|
+
]
|
|
1175
|
+
}
|
|
1176
|
+
),
|
|
1177
|
+
open && /* @__PURE__ */ a2("div", { class: "fnc-menu", role: "menu", children: TYPES.map(({ type, label, icon: Icon2 }) => /* @__PURE__ */ a2(
|
|
1178
|
+
"button",
|
|
1179
|
+
{
|
|
1180
|
+
class: "fnc-menu-item",
|
|
1181
|
+
role: "menuitemradio",
|
|
1182
|
+
"aria-checked": type === s3.chartType,
|
|
1183
|
+
onClick: () => choose(type),
|
|
1184
|
+
children: [
|
|
1185
|
+
/* @__PURE__ */ a2(Icon2, {}),
|
|
1186
|
+
/* @__PURE__ */ a2("span", { children: t3[label] })
|
|
1187
|
+
]
|
|
1188
|
+
}
|
|
1189
|
+
)) })
|
|
1190
|
+
] });
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// src/components/indicator-dialogs.tsx
|
|
1194
|
+
import {
|
|
1195
|
+
INDICATORS,
|
|
1196
|
+
maLines,
|
|
1197
|
+
PRICE_SOURCES,
|
|
1198
|
+
SERIES_COLORS
|
|
1199
|
+
} from "@funnest/charts";
|
|
1200
|
+
|
|
1201
|
+
// src/components/settings-dialog.tsx
|
|
1202
|
+
import { DEFAULT_CHART_SETTINGS } from "@funnest/charts";
|
|
1203
|
+
function toHex(color) {
|
|
1204
|
+
const ctx = document.createElement("canvas").getContext("2d");
|
|
1205
|
+
if (!ctx) return "#000000";
|
|
1206
|
+
ctx.fillStyle = "#000000";
|
|
1207
|
+
ctx.fillStyle = color;
|
|
1208
|
+
return ctx.fillStyle.startsWith("#") ? ctx.fillStyle : "#000000";
|
|
1209
|
+
}
|
|
1210
|
+
var SECTIONS = [
|
|
1211
|
+
{
|
|
1212
|
+
title: "settingsDisplay",
|
|
1213
|
+
toggles: [
|
|
1214
|
+
{ key: "highLowMarkers", label: "settingHighLow" },
|
|
1215
|
+
{ key: "lastPriceLine", label: "settingLastPrice" },
|
|
1216
|
+
{ key: "volume", label: "settingVolume" }
|
|
1217
|
+
]
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
title: "settingsGrid",
|
|
1221
|
+
toggles: [
|
|
1222
|
+
{ key: "gridHorizontal", label: "settingGridH" },
|
|
1223
|
+
{ key: "gridVertical", label: "settingGridV" }
|
|
1224
|
+
]
|
|
1225
|
+
}
|
|
1226
|
+
];
|
|
1227
|
+
function SettingsDialog({ ctx }) {
|
|
1228
|
+
const { locale } = useStore(ctx.store);
|
|
1229
|
+
const t3 = STRINGS[locale];
|
|
1230
|
+
const [settings, setSettings] = d2(ctx.chart.settings);
|
|
1231
|
+
A2(() => ctx.chart.on("settingsChange", setSettings), [ctx.chart]);
|
|
1232
|
+
const close = () => ctx.store.set({ dialog: null });
|
|
1233
|
+
const set = (patch) => ctx.chart.setSettings(patch);
|
|
1234
|
+
const themeColor = (name) => toHex(getComputedStyle(ctx.root).getPropertyValue(name).trim());
|
|
1235
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-backdrop", onPointerDown: (e3) => e3.target === e3.currentTarget && close(), children: /* @__PURE__ */ a2("div", { class: "fnc-dialog", role: "dialog", "aria-modal": "true", "aria-label": t3.chartSettings, onKeyDown: (e3) => e3.key === "Escape" && close(), children: [
|
|
1236
|
+
/* @__PURE__ */ a2("div", { class: "fnc-dialog-head", children: [
|
|
1237
|
+
/* @__PURE__ */ a2("b", { children: t3.chartSettings }),
|
|
1238
|
+
/* @__PURE__ */ a2("button", { class: "fnc-btn", "aria-label": t3.close, onClick: close, children: /* @__PURE__ */ a2(CloseIcon, {}) })
|
|
1239
|
+
] }),
|
|
1240
|
+
/* @__PURE__ */ a2("form", { class: "fnc-form", onSubmit: (e3) => (e3.preventDefault(), close()), children: [
|
|
1241
|
+
/* @__PURE__ */ a2("div", { class: "fnc-section", children: t3.settingsCandles }),
|
|
1242
|
+
["upColor", "downColor"].map((key) => /* @__PURE__ */ a2("label", { class: "fnc-row", children: [
|
|
1243
|
+
/* @__PURE__ */ a2("span", { class: "fnc-row-label fnc-row-label-wide", children: key === "upColor" ? t3.settingUpColor : t3.settingDownColor }),
|
|
1244
|
+
/* @__PURE__ */ a2(
|
|
1245
|
+
"input",
|
|
1246
|
+
{
|
|
1247
|
+
type: "color",
|
|
1248
|
+
class: "fnc-color",
|
|
1249
|
+
value: settings[key] ?? themeColor(key === "upColor" ? "--fnc-up" : "--fnc-down"),
|
|
1250
|
+
onInput: (e3) => set({ [key]: e3.currentTarget.value })
|
|
1251
|
+
}
|
|
1252
|
+
),
|
|
1253
|
+
settings[key] && /* @__PURE__ */ a2("button", { type: "button", class: "fnc-link", onClick: () => set({ [key]: null }), children: t3.followTheme })
|
|
1254
|
+
] })),
|
|
1255
|
+
SECTIONS.map((section) => /* @__PURE__ */ a2($, { children: [
|
|
1256
|
+
/* @__PURE__ */ a2("div", { class: "fnc-section", children: t3[section.title] }),
|
|
1257
|
+
section.toggles.map(({ key, label }) => /* @__PURE__ */ a2("label", { class: "fnc-row fnc-toggle-row", children: [
|
|
1258
|
+
/* @__PURE__ */ a2("span", { children: t3[label] }),
|
|
1259
|
+
/* @__PURE__ */ a2(
|
|
1260
|
+
"input",
|
|
1261
|
+
{
|
|
1262
|
+
type: "checkbox",
|
|
1263
|
+
role: "switch",
|
|
1264
|
+
class: "fnc-switch",
|
|
1265
|
+
checked: settings[key],
|
|
1266
|
+
onChange: (e3) => set({ [key]: e3.currentTarget.checked })
|
|
1267
|
+
}
|
|
1268
|
+
)
|
|
1269
|
+
] }))
|
|
1270
|
+
] })),
|
|
1271
|
+
/* @__PURE__ */ a2("div", { class: "fnc-form-actions", children: [
|
|
1272
|
+
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-action fnc-action-danger", onClick: () => ctx.chart.setSettings(DEFAULT_CHART_SETTINGS), children: t3.resetDefaults }),
|
|
1273
|
+
/* @__PURE__ */ a2("button", { type: "submit", class: "fnc-action fnc-action-primary", children: t3.done })
|
|
1274
|
+
] })
|
|
1275
|
+
] })
|
|
1276
|
+
] }) });
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/components/indicator-dialogs.tsx
|
|
1280
|
+
function Dialog({ title, onClose, children, wide }) {
|
|
1281
|
+
const ref = T2(null);
|
|
1282
|
+
A2(() => {
|
|
1283
|
+
ref.current?.querySelector("input, button.fnc-sr-row")?.focus();
|
|
1284
|
+
}, []);
|
|
1285
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-backdrop", onPointerDown: (e3) => e3.target === e3.currentTarget && onClose(), children: /* @__PURE__ */ a2(
|
|
1286
|
+
"div",
|
|
1287
|
+
{
|
|
1288
|
+
class: wide ? "fnc-dialog fnc-dialog-wide" : "fnc-dialog",
|
|
1289
|
+
role: "dialog",
|
|
1290
|
+
"aria-modal": "true",
|
|
1291
|
+
"aria-label": title,
|
|
1292
|
+
ref,
|
|
1293
|
+
onKeyDown: (e3) => e3.key === "Escape" && onClose(),
|
|
1294
|
+
children: [
|
|
1295
|
+
/* @__PURE__ */ a2("div", { class: "fnc-dialog-head", children: [
|
|
1296
|
+
/* @__PURE__ */ a2("b", { children: title }),
|
|
1297
|
+
/* @__PURE__ */ a2("button", { class: "fnc-btn", "aria-label": "close", onClick: onClose, children: /* @__PURE__ */ a2(CloseIcon, {}) })
|
|
1298
|
+
] }),
|
|
1299
|
+
children
|
|
1300
|
+
]
|
|
1301
|
+
}
|
|
1302
|
+
) });
|
|
1303
|
+
}
|
|
1304
|
+
function IndicatorPicker({ ctx }) {
|
|
1305
|
+
const { locale } = useStore(ctx.store);
|
|
1306
|
+
const t3 = STRINGS[locale];
|
|
1307
|
+
const close = () => ctx.store.set({ dialog: null });
|
|
1308
|
+
const add = (type) => {
|
|
1309
|
+
ctx.chart.addIndicator(type);
|
|
1310
|
+
close();
|
|
1311
|
+
};
|
|
1312
|
+
const groups = [
|
|
1313
|
+
[locale === "ko" ? "\uAC00\uACA9 \uCC28\uD2B8\uC5D0 \uD45C\uC2DC" : "On price", ["ma", "ema", "bb"]],
|
|
1314
|
+
[locale === "ko" ? "\uC544\uB798 \uC601\uC5ED\uC5D0 \uD45C\uC2DC" : "Separate pane", ["rsi", "macd"]]
|
|
1315
|
+
];
|
|
1316
|
+
return /* @__PURE__ */ a2(Dialog, { title: t3.addIndicator, onClose: close, children: /* @__PURE__ */ a2("div", { class: "fnc-sr-list fnc-scroll", children: groups.map(([label, types]) => /* @__PURE__ */ a2($, { children: [
|
|
1317
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sr-section", children: label }),
|
|
1318
|
+
types.map((type) => {
|
|
1319
|
+
const def = INDICATORS[type];
|
|
1320
|
+
return /* @__PURE__ */ a2("button", { class: "fnc-sr-row", onClick: () => add(type), children: /* @__PURE__ */ a2("span", { class: "fnc-sr-main", children: [
|
|
1321
|
+
/* @__PURE__ */ a2("span", { class: "fnc-sr-name", children: def.name[locale] }),
|
|
1322
|
+
/* @__PURE__ */ a2("span", { class: "fnc-sr-meta", children: def.description[locale] })
|
|
1323
|
+
] }) });
|
|
1324
|
+
})
|
|
1325
|
+
] })) }) });
|
|
1326
|
+
}
|
|
1327
|
+
var WIDTHS = [1, 2, 3, 4];
|
|
1328
|
+
function defaultSeries(type, params) {
|
|
1329
|
+
return INDICATORS[type].compute([], params).series.filter((s3) => s3.kind === "line");
|
|
1330
|
+
}
|
|
1331
|
+
function StyleControls({
|
|
1332
|
+
t: t3,
|
|
1333
|
+
row,
|
|
1334
|
+
onChange
|
|
1335
|
+
}) {
|
|
1336
|
+
return /* @__PURE__ */ a2($, { children: [
|
|
1337
|
+
/* @__PURE__ */ a2(
|
|
1338
|
+
"input",
|
|
1339
|
+
{
|
|
1340
|
+
type: "checkbox",
|
|
1341
|
+
class: "fnc-check",
|
|
1342
|
+
checked: row.visible,
|
|
1343
|
+
"aria-label": row.visible ? t3.hide : t3.show,
|
|
1344
|
+
onChange: (e3) => onChange({ visible: e3.currentTarget.checked })
|
|
1345
|
+
}
|
|
1346
|
+
),
|
|
1347
|
+
/* @__PURE__ */ a2(
|
|
1348
|
+
"input",
|
|
1349
|
+
{
|
|
1350
|
+
type: "color",
|
|
1351
|
+
class: "fnc-color",
|
|
1352
|
+
value: row.color,
|
|
1353
|
+
"aria-label": t3.color,
|
|
1354
|
+
onInput: (e3) => onChange({ color: e3.currentTarget.value })
|
|
1355
|
+
}
|
|
1356
|
+
),
|
|
1357
|
+
/* @__PURE__ */ a2("select", { class: "fnc-select fnc-select-width", value: row.width, "aria-label": t3.lineWidth, onChange: (e3) => onChange({ width: Number(e3.currentTarget.value) }), children: WIDTHS.map((w3) => /* @__PURE__ */ a2("option", { value: w3, children: [
|
|
1358
|
+
w3,
|
|
1359
|
+
"px"
|
|
1360
|
+
] })) })
|
|
1361
|
+
] });
|
|
1362
|
+
}
|
|
1363
|
+
function SourceSelect({ value, locale, label, onChange }) {
|
|
1364
|
+
return /* @__PURE__ */ a2("select", { class: "fnc-select fnc-select-source", value, "aria-label": label, onChange: (e3) => onChange(e3.currentTarget.value), children: PRICE_SOURCES.map((s3) => /* @__PURE__ */ a2("option", { value: s3.value, children: s3.label[locale] })) });
|
|
1365
|
+
}
|
|
1366
|
+
function IndicatorSettings({ ctx, id }) {
|
|
1367
|
+
const { locale, indicators } = useStore(ctx.store);
|
|
1368
|
+
const t3 = STRINGS[locale];
|
|
1369
|
+
const config = indicators.find((c3) => c3.id === id);
|
|
1370
|
+
const def = config ? INDICATORS[config.type] : null;
|
|
1371
|
+
const hasLines = !!def?.params.some((p3) => p3.kind === "lines");
|
|
1372
|
+
const [lines, setLines] = d2(() => {
|
|
1373
|
+
if (!config || !hasLines) return [];
|
|
1374
|
+
const defaults = defaultSeries(config.type, config.params);
|
|
1375
|
+
return maLines(config.params).map((l3, i3) => {
|
|
1376
|
+
const style = config.styles?.[`l${i3}`];
|
|
1377
|
+
return {
|
|
1378
|
+
period: String(l3.period),
|
|
1379
|
+
source: l3.source,
|
|
1380
|
+
color: style?.color ?? defaults[i3]?.color ?? SERIES_COLORS[i3 % SERIES_COLORS.length],
|
|
1381
|
+
width: style?.width ?? defaults[i3]?.width ?? 1,
|
|
1382
|
+
visible: style?.visible ?? true
|
|
1383
|
+
};
|
|
1384
|
+
});
|
|
1385
|
+
});
|
|
1386
|
+
const [values, setValues] = d2(
|
|
1387
|
+
() => Object.fromEntries(Object.entries(config?.params ?? {}).filter(([, v3]) => !Array.isArray(v3)).map(([k2, v3]) => [k2, String(v3)]))
|
|
1388
|
+
);
|
|
1389
|
+
const [styles, setStyles] = d2(() => {
|
|
1390
|
+
if (!config || hasLines) return [];
|
|
1391
|
+
return defaultSeries(config.type, config.params).map((s3) => {
|
|
1392
|
+
const style = config.styles?.[s3.key];
|
|
1393
|
+
return { key: s3.key, label: s3.label, color: style?.color ?? s3.color, width: style?.width ?? s3.width, visible: style?.visible ?? true };
|
|
1394
|
+
});
|
|
1395
|
+
});
|
|
1396
|
+
const close = () => ctx.store.set({ dialog: null });
|
|
1397
|
+
if (!config || !def) return null;
|
|
1398
|
+
const updateLine = (i3, patch) => setLines(lines.map((l3, j2) => j2 === i3 ? { ...l3, ...patch } : l3));
|
|
1399
|
+
const addLine = () => {
|
|
1400
|
+
const i3 = lines.length;
|
|
1401
|
+
const last = lines[i3 - 1];
|
|
1402
|
+
setLines([
|
|
1403
|
+
...lines,
|
|
1404
|
+
{
|
|
1405
|
+
period: String(last ? Number(last.period) * 2 || 10 : 5),
|
|
1406
|
+
source: "close",
|
|
1407
|
+
color: SERIES_COLORS[i3 % SERIES_COLORS.length],
|
|
1408
|
+
width: 1,
|
|
1409
|
+
visible: true
|
|
1410
|
+
}
|
|
1411
|
+
]);
|
|
1412
|
+
};
|
|
1413
|
+
const apply = (e3) => {
|
|
1414
|
+
e3.preventDefault();
|
|
1415
|
+
const params = {};
|
|
1416
|
+
const nextStyles = {};
|
|
1417
|
+
for (const spec of def.params) {
|
|
1418
|
+
const clamp = (n2) => Math.min(spec.max ?? Infinity, Math.max(spec.min ?? -Infinity, n2));
|
|
1419
|
+
if (spec.kind === "lines") {
|
|
1420
|
+
const valid = lines.filter((l3) => Number(l3.period) > 0);
|
|
1421
|
+
params[spec.key] = valid.map((l3) => ({ period: clamp(Math.round(Number(l3.period))), source: l3.source }));
|
|
1422
|
+
valid.forEach((l3, i3) => nextStyles[`l${i3}`] = { color: l3.color, width: l3.width, visible: l3.visible });
|
|
1423
|
+
} else if (spec.kind === "source") {
|
|
1424
|
+
params[spec.key] = values[spec.key] ?? "close";
|
|
1425
|
+
} else {
|
|
1426
|
+
const n2 = Number(values[spec.key]);
|
|
1427
|
+
if (Number.isFinite(n2)) params[spec.key] = clamp(n2);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
for (const s3 of styles) nextStyles[s3.key] = { color: s3.color, width: s3.width, visible: s3.visible };
|
|
1431
|
+
ctx.chart.updateIndicator(id, { params, styles: nextStyles });
|
|
1432
|
+
close();
|
|
1433
|
+
};
|
|
1434
|
+
return /* @__PURE__ */ a2(Dialog, { title: `${def.name[locale]} \xB7 ${t3.indicatorSettings}`, onClose: close, wide: true, children: /* @__PURE__ */ a2("form", { class: "fnc-form", onSubmit: apply, children: [
|
|
1435
|
+
hasLines ? /* @__PURE__ */ a2("div", { class: "fnc-rows", children: [
|
|
1436
|
+
lines.map((l3, i3) => /* @__PURE__ */ a2("div", { class: "fnc-row", children: [
|
|
1437
|
+
/* @__PURE__ */ a2("span", { class: "fnc-row-label", children: [
|
|
1438
|
+
t3.period,
|
|
1439
|
+
i3 + 1
|
|
1440
|
+
] }),
|
|
1441
|
+
/* @__PURE__ */ a2(StyleControls, { t: t3, row: l3, onChange: (p3) => updateLine(i3, p3) }),
|
|
1442
|
+
/* @__PURE__ */ a2(SourceSelect, { value: l3.source, locale, label: t3.source, onChange: (source) => updateLine(i3, { source }) }),
|
|
1443
|
+
/* @__PURE__ */ a2(
|
|
1444
|
+
"input",
|
|
1445
|
+
{
|
|
1446
|
+
class: "fnc-input fnc-input-period",
|
|
1447
|
+
inputMode: "numeric",
|
|
1448
|
+
value: l3.period,
|
|
1449
|
+
"aria-label": `${t3.period}${i3 + 1}`,
|
|
1450
|
+
onInput: (e3) => updateLine(i3, { period: e3.currentTarget.value.replace(/\D/g, "") })
|
|
1451
|
+
}
|
|
1452
|
+
),
|
|
1453
|
+
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-ind-btn", "aria-label": t3.removePeriod, title: t3.removePeriod, onClick: () => setLines(lines.filter((_3, j2) => j2 !== i3)), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) })
|
|
1454
|
+
] })),
|
|
1455
|
+
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-add-row", onClick: addLine, children: [
|
|
1456
|
+
"+ ",
|
|
1457
|
+
t3.addPeriod
|
|
1458
|
+
] })
|
|
1459
|
+
] }) : /* @__PURE__ */ a2($, { children: [
|
|
1460
|
+
/* @__PURE__ */ a2("div", { class: "fnc-section", children: t3.inputs }),
|
|
1461
|
+
def.params.map((spec) => /* @__PURE__ */ a2("div", { class: "fnc-row", children: [
|
|
1462
|
+
/* @__PURE__ */ a2("span", { class: "fnc-row-label fnc-row-label-wide", children: spec.label[locale] }),
|
|
1463
|
+
spec.kind === "source" ? /* @__PURE__ */ a2(
|
|
1464
|
+
SourceSelect,
|
|
1465
|
+
{
|
|
1466
|
+
value: values[spec.key] ?? "close",
|
|
1467
|
+
locale,
|
|
1468
|
+
label: spec.label[locale],
|
|
1469
|
+
onChange: (v3) => setValues({ ...values, [spec.key]: v3 })
|
|
1470
|
+
}
|
|
1471
|
+
) : /* @__PURE__ */ a2(
|
|
1472
|
+
"input",
|
|
1473
|
+
{
|
|
1474
|
+
class: "fnc-input",
|
|
1475
|
+
inputMode: "decimal",
|
|
1476
|
+
value: values[spec.key] ?? "",
|
|
1477
|
+
"aria-label": spec.label[locale],
|
|
1478
|
+
onInput: (e3) => setValues({ ...values, [spec.key]: e3.currentTarget.value })
|
|
1479
|
+
}
|
|
1480
|
+
)
|
|
1481
|
+
] })),
|
|
1482
|
+
styles.length > 0 && /* @__PURE__ */ a2("div", { class: "fnc-section", children: t3.style }),
|
|
1483
|
+
styles.map((row, i3) => /* @__PURE__ */ a2("div", { class: "fnc-row", children: [
|
|
1484
|
+
/* @__PURE__ */ a2("span", { class: "fnc-row-label fnc-row-label-wide", children: row.label }),
|
|
1485
|
+
/* @__PURE__ */ a2(StyleControls, { t: t3, row, onChange: (p3) => setStyles(styles.map((s3, j2) => j2 === i3 ? { ...s3, ...p3 } : s3)) })
|
|
1486
|
+
] }))
|
|
1487
|
+
] }),
|
|
1488
|
+
/* @__PURE__ */ a2("div", { class: "fnc-form-actions", children: [
|
|
1489
|
+
/* @__PURE__ */ a2(
|
|
1490
|
+
"button",
|
|
1491
|
+
{
|
|
1492
|
+
type: "button",
|
|
1493
|
+
class: "fnc-action fnc-action-danger",
|
|
1494
|
+
onClick: () => {
|
|
1495
|
+
ctx.chart.removeIndicator(id);
|
|
1496
|
+
close();
|
|
1497
|
+
},
|
|
1498
|
+
children: t3.remove
|
|
1499
|
+
}
|
|
1500
|
+
),
|
|
1501
|
+
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-action", onClick: close, children: t3.cancel }),
|
|
1502
|
+
/* @__PURE__ */ a2("button", { type: "submit", class: "fnc-action fnc-action-primary", children: t3.apply })
|
|
1503
|
+
] })
|
|
1504
|
+
] }) });
|
|
1505
|
+
}
|
|
1506
|
+
function Dialogs({ ctx }) {
|
|
1507
|
+
const { dialog } = useStore(ctx.store);
|
|
1508
|
+
if (dialog?.kind === "indicators") return /* @__PURE__ */ a2(IndicatorPicker, { ctx });
|
|
1509
|
+
if (dialog?.kind === "indicatorSettings") return /* @__PURE__ */ a2(IndicatorSettings, { ctx, id: dialog.id });
|
|
1510
|
+
if (dialog?.kind === "chartSettings") return /* @__PURE__ */ a2(SettingsDialog, { ctx });
|
|
1511
|
+
return null;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// src/components/interval-picker.tsx
|
|
1515
|
+
var GROUP_LABEL = {
|
|
1516
|
+
minutes: "intervalMinutes",
|
|
1517
|
+
hours: "intervalHours",
|
|
1518
|
+
days: "intervalDays"
|
|
1519
|
+
};
|
|
1520
|
+
function IntervalPicker({ ctx }) {
|
|
1521
|
+
const s3 = useStore(ctx.store);
|
|
1522
|
+
const t3 = STRINGS[s3.locale];
|
|
1523
|
+
const [open, setOpen] = d2(false);
|
|
1524
|
+
const ref = T2(null);
|
|
1525
|
+
A2(() => {
|
|
1526
|
+
if (!open) return;
|
|
1527
|
+
const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
|
|
1528
|
+
const onKey = (e3) => e3.key === "Escape" && setOpen(false);
|
|
1529
|
+
document.addEventListener("pointerdown", onDown);
|
|
1530
|
+
document.addEventListener("keydown", onKey);
|
|
1531
|
+
return () => {
|
|
1532
|
+
document.removeEventListener("pointerdown", onDown);
|
|
1533
|
+
document.removeEventListener("keydown", onKey);
|
|
1534
|
+
};
|
|
1535
|
+
}, [open]);
|
|
1536
|
+
const supported = s3.symbol?.supportedResolutions;
|
|
1537
|
+
const isSupported = (r3) => !supported || supported.includes(r3);
|
|
1538
|
+
const order = ctx.intervalGroups.flatMap((g3) => g3.intervals);
|
|
1539
|
+
const favorites = [...s3.favoriteIntervals].sort((a4, b) => order.indexOf(a4) - order.indexOf(b));
|
|
1540
|
+
const select = (r3) => {
|
|
1541
|
+
setOpen(false);
|
|
1542
|
+
ctx.selectResolution(r3);
|
|
1543
|
+
};
|
|
1544
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-interval-picker", ref, children: [
|
|
1545
|
+
/* @__PURE__ */ a2("div", { class: "fnc-intervals", children: favorites.map((r3) => /* @__PURE__ */ a2(
|
|
1546
|
+
"button",
|
|
1547
|
+
{
|
|
1548
|
+
class: "fnc-chip",
|
|
1549
|
+
"aria-pressed": r3 === s3.resolution,
|
|
1550
|
+
disabled: !isSupported(r3),
|
|
1551
|
+
title: isSupported(r3) ? resolutionLabel(r3, s3.locale, "long") : t3.unsupportedInterval,
|
|
1552
|
+
onClick: () => select(r3),
|
|
1553
|
+
children: resolutionLabel(r3, s3.locale)
|
|
1554
|
+
}
|
|
1555
|
+
)) }),
|
|
1556
|
+
/* @__PURE__ */ a2(
|
|
1557
|
+
"button",
|
|
1558
|
+
{
|
|
1559
|
+
class: `fnc-btn fnc-btn-wide fnc-interval-trigger${favorites.includes(s3.resolution) ? "" : " fnc-interval-trigger-active"}`,
|
|
1560
|
+
title: t3.moreIntervals,
|
|
1561
|
+
"aria-label": t3.moreIntervals,
|
|
1562
|
+
"aria-haspopup": "menu",
|
|
1563
|
+
"aria-expanded": open,
|
|
1564
|
+
onClick: () => setOpen(!open),
|
|
1565
|
+
children: [
|
|
1566
|
+
/* @__PURE__ */ a2("b", { class: "fnc-interval-current", children: resolutionLabel(s3.resolution, s3.locale) }),
|
|
1567
|
+
/* @__PURE__ */ a2(ChevronDownIcon, {})
|
|
1568
|
+
]
|
|
1569
|
+
}
|
|
1570
|
+
),
|
|
1571
|
+
open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-interval-menu", role: "menu", children: ctx.intervalGroups.map((group) => /* @__PURE__ */ a2("div", { class: "fnc-menu-group", role: "group", "aria-label": t3[GROUP_LABEL[group.id]], children: [
|
|
1572
|
+
/* @__PURE__ */ a2("div", { class: "fnc-menu-title", children: t3[GROUP_LABEL[group.id]] }),
|
|
1573
|
+
group.intervals.map((r3) => {
|
|
1574
|
+
const favorite = s3.favoriteIntervals.includes(r3);
|
|
1575
|
+
const ok = isSupported(r3);
|
|
1576
|
+
return /* @__PURE__ */ a2("div", { class: `fnc-menu-row${favorite ? " is-favorite" : ""}`, children: [
|
|
1577
|
+
/* @__PURE__ */ a2(
|
|
1578
|
+
"button",
|
|
1579
|
+
{
|
|
1580
|
+
class: "fnc-menu-item",
|
|
1581
|
+
role: "menuitemradio",
|
|
1582
|
+
"aria-checked": r3 === s3.resolution,
|
|
1583
|
+
disabled: !ok,
|
|
1584
|
+
title: ok ? void 0 : t3.unsupportedInterval,
|
|
1585
|
+
onClick: () => select(r3),
|
|
1586
|
+
children: resolutionLabel(r3, s3.locale, "long")
|
|
1587
|
+
}
|
|
1588
|
+
),
|
|
1589
|
+
/* @__PURE__ */ a2(
|
|
1590
|
+
"button",
|
|
1591
|
+
{
|
|
1592
|
+
class: "fnc-star",
|
|
1593
|
+
"aria-pressed": favorite,
|
|
1594
|
+
"aria-label": `${resolutionLabel(r3, s3.locale, "long")} ${favorite ? t3.removeFavorite : t3.addFavorite}`,
|
|
1595
|
+
title: favorite ? t3.removeFavorite : t3.addFavorite,
|
|
1596
|
+
onClick: () => ctx.toggleFavoriteInterval(r3),
|
|
1597
|
+
children: /* @__PURE__ */ a2(StarIcon, { filled: favorite })
|
|
1598
|
+
}
|
|
1599
|
+
)
|
|
1600
|
+
] });
|
|
1601
|
+
})
|
|
1602
|
+
] })) })
|
|
1603
|
+
] });
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
// src/components/slot.tsx
|
|
1607
|
+
function Slot({ el }) {
|
|
1608
|
+
const ref = T2(null);
|
|
1609
|
+
F2(() => {
|
|
1610
|
+
ref.current?.appendChild(el);
|
|
1611
|
+
return () => el.remove();
|
|
1612
|
+
}, [el]);
|
|
1613
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-slot-host", ref });
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// src/components/symbol-search.tsx
|
|
1617
|
+
import { formatPrice as formatPrice2 } from "@funnest/charts";
|
|
1618
|
+
var DEBOUNCE_MS = 150;
|
|
1619
|
+
var RECENT_KEY = "recent-symbols";
|
|
1620
|
+
var FAVORITE_KEY = "favorite-symbols";
|
|
1621
|
+
var MAX_RECENT = 8;
|
|
1622
|
+
var displayCode = (symbol) => symbol.slice(symbol.lastIndexOf(":") + 1);
|
|
1623
|
+
var toStored = (info) => JSON.parse(JSON.stringify({ ...info, tickSize: 0, price: void 0, changePercent: void 0, sparkline: void 0 }));
|
|
1624
|
+
function Highlight({ text, query }) {
|
|
1625
|
+
const q2 = query.trim();
|
|
1626
|
+
if (!q2) return /* @__PURE__ */ a2($, { children: text });
|
|
1627
|
+
const parts = [];
|
|
1628
|
+
const lower = text.toLowerCase();
|
|
1629
|
+
const needle = q2.toLowerCase();
|
|
1630
|
+
let i3 = 0;
|
|
1631
|
+
for (let at = lower.indexOf(needle); at >= 0; at = lower.indexOf(needle, i3)) {
|
|
1632
|
+
if (at > i3) parts.push(text.slice(i3, at));
|
|
1633
|
+
parts.push(/* @__PURE__ */ a2("mark", { children: text.slice(at, at + q2.length) }));
|
|
1634
|
+
i3 = at + q2.length;
|
|
1635
|
+
}
|
|
1636
|
+
parts.push(text.slice(i3));
|
|
1637
|
+
return /* @__PURE__ */ a2($, { children: parts });
|
|
1638
|
+
}
|
|
1639
|
+
function Sparkline({ values, up }) {
|
|
1640
|
+
if (values.length < 2) return null;
|
|
1641
|
+
const min = Math.min(...values);
|
|
1642
|
+
const max = Math.max(...values);
|
|
1643
|
+
const w3 = 72;
|
|
1644
|
+
const h3 = 24;
|
|
1645
|
+
const points = values.map((v3, i3) => `${(i3 / (values.length - 1) * w3).toFixed(1)},${(h3 - 2 - (v3 - min) / (max - min || 1) * (h3 - 4)).toFixed(1)}`).join(" ");
|
|
1646
|
+
return /* @__PURE__ */ a2("svg", { class: `fnc-sr-spark ${up ? "fnc-up" : "fnc-down"}`, width: w3, height: h3, viewBox: `0 0 ${w3} ${h3}`, "aria-hidden": "true", children: /* @__PURE__ */ a2("polyline", { points, fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) });
|
|
1647
|
+
}
|
|
1648
|
+
function SymbolSearch({ ctx }) {
|
|
1649
|
+
const { locale, symbol: current } = useStore(ctx.store);
|
|
1650
|
+
const t3 = STRINGS[locale];
|
|
1651
|
+
const [query, setQuery] = d2("");
|
|
1652
|
+
const [category, setCategory] = d2(ctx.searchCategories[0]?.id ?? "all");
|
|
1653
|
+
const [view, setView] = d2({ kind: "all" });
|
|
1654
|
+
const [exchange, setExchange] = d2(null);
|
|
1655
|
+
const [results, setResults] = d2(null);
|
|
1656
|
+
const [loading, setLoading] = d2(true);
|
|
1657
|
+
const [recent, setRecent] = d2(() => ctx.storage.get(RECENT_KEY, []));
|
|
1658
|
+
const [favorites, setFavorites] = d2(() => ctx.storage.get(FAVORITE_KEY, []));
|
|
1659
|
+
const [active, setActive] = d2(0);
|
|
1660
|
+
const inputRef = T2(null);
|
|
1661
|
+
const listRef = T2(null);
|
|
1662
|
+
A2(() => inputRef.current?.focus(), []);
|
|
1663
|
+
A2(() => {
|
|
1664
|
+
let alive = true;
|
|
1665
|
+
setLoading(true);
|
|
1666
|
+
const id = setTimeout(() => {
|
|
1667
|
+
ctx.datafeed.searchSymbols?.(query.trim()).then((items) => alive && setResults(items)).catch(() => alive && setResults([])).finally(() => alive && setLoading(false));
|
|
1668
|
+
}, DEBOUNCE_MS);
|
|
1669
|
+
return () => {
|
|
1670
|
+
alive = false;
|
|
1671
|
+
clearTimeout(id);
|
|
1672
|
+
};
|
|
1673
|
+
}, [query]);
|
|
1674
|
+
const matchCategory = ctx.searchCategories.find((c3) => c3.id === category)?.match ?? (() => true);
|
|
1675
|
+
const categoryOf = (info) => ctx.searchCategories.find((c3) => c3.id !== "all" && c3.match(info));
|
|
1676
|
+
const isFavorite = (info) => favorites.some((f4) => f4.symbol === info.symbol);
|
|
1677
|
+
const filter = (list) => list.filter((i3) => matchCategory(i3) && (!exchange || i3.exchange === exchange));
|
|
1678
|
+
const exchanges = w2(() => {
|
|
1679
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1680
|
+
for (const r3 of results ?? []) if (matchCategory(r3)) counts.set(r3.exchange, (counts.get(r3.exchange) ?? 0) + 1);
|
|
1681
|
+
return [...counts.entries()].sort((a4, b) => b[1] - a4[1]);
|
|
1682
|
+
}, [results, category]);
|
|
1683
|
+
const q2 = query.trim().toLowerCase();
|
|
1684
|
+
const favoriteRows = filter(favorites).filter((f4) => !q2 || f4.name.toLowerCase().includes(q2) || f4.symbol.toLowerCase().includes(q2));
|
|
1685
|
+
const showRecent = view.kind === "all" && !q2 && recent.length > 0;
|
|
1686
|
+
const recentRows = showRecent ? filter(recent) : [];
|
|
1687
|
+
const resultRows = view.kind === "favorites" ? favoriteRows : filter(results ?? []);
|
|
1688
|
+
const rows = [...recentRows, ...resultRows];
|
|
1689
|
+
A2(() => setActive(0), [query, category, exchange, view, results]);
|
|
1690
|
+
A2(() => {
|
|
1691
|
+
listRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
1692
|
+
}, [active]);
|
|
1693
|
+
const close = () => ctx.setSearchOpen(false);
|
|
1694
|
+
const save = (key, next, set) => {
|
|
1695
|
+
set(next);
|
|
1696
|
+
ctx.storage.set(key, next);
|
|
1697
|
+
};
|
|
1698
|
+
const toggleFavorite = (info) => save(FAVORITE_KEY, isFavorite(info) ? favorites.filter((f4) => f4.symbol !== info.symbol) : [...favorites, toStored(info)], setFavorites);
|
|
1699
|
+
const choose = (info) => {
|
|
1700
|
+
if (!info) return;
|
|
1701
|
+
save(RECENT_KEY, [toStored(info), ...recent.filter((r3) => r3.symbol !== info.symbol)].slice(0, MAX_RECENT), setRecent);
|
|
1702
|
+
close();
|
|
1703
|
+
ctx.selectSymbol(info.symbol);
|
|
1704
|
+
};
|
|
1705
|
+
const onKeyDown = (e3) => {
|
|
1706
|
+
const count = rows.length;
|
|
1707
|
+
if (e3.key === "Escape") close();
|
|
1708
|
+
else if (e3.key === "ArrowDown" && count) setActive((a4) => (a4 + 1) % count);
|
|
1709
|
+
else if (e3.key === "ArrowUp" && count) setActive((a4) => (a4 - 1 + count) % count);
|
|
1710
|
+
else if (e3.key === "Enter") choose(rows[active]);
|
|
1711
|
+
else if (e3.key === "Backspace" && !query && exchange) setExchange(null);
|
|
1712
|
+
else return;
|
|
1713
|
+
e3.preventDefault();
|
|
1714
|
+
};
|
|
1715
|
+
const row = (info, index, isRecent) => {
|
|
1716
|
+
const cat = categoryOf(info);
|
|
1717
|
+
const fav = isFavorite(info);
|
|
1718
|
+
const change = info.changePercent ?? 0;
|
|
1719
|
+
const up = change >= 0;
|
|
1720
|
+
const trend = change > 0 ? "fnc-up" : change < 0 ? "fnc-down" : "fnc-flat";
|
|
1721
|
+
return /* @__PURE__ */ a2(
|
|
1722
|
+
"div",
|
|
1723
|
+
{
|
|
1724
|
+
class: "fnc-sr-row",
|
|
1725
|
+
role: "option",
|
|
1726
|
+
"aria-selected": index === active,
|
|
1727
|
+
"data-index": index,
|
|
1728
|
+
onPointerMove: () => setActive(index),
|
|
1729
|
+
onClick: () => choose(info),
|
|
1730
|
+
children: [
|
|
1731
|
+
/* @__PURE__ */ a2(
|
|
1732
|
+
"button",
|
|
1733
|
+
{
|
|
1734
|
+
class: `fnc-sr-star${fav ? " is-on" : ""}`,
|
|
1735
|
+
"aria-pressed": fav,
|
|
1736
|
+
"aria-label": `${info.name} ${fav ? t3.removeFavorite : t3.addFavorite}`,
|
|
1737
|
+
title: fav ? t3.removeFavorite : t3.addFavorite,
|
|
1738
|
+
onClick: (e3) => {
|
|
1739
|
+
e3.stopPropagation();
|
|
1740
|
+
toggleFavorite(info);
|
|
1741
|
+
},
|
|
1742
|
+
children: /* @__PURE__ */ a2(StarIcon, { filled: fav })
|
|
1743
|
+
}
|
|
1744
|
+
),
|
|
1745
|
+
ctx.showLogo && /* @__PURE__ */ a2(SymbolAvatar, { info, size: 32 }),
|
|
1746
|
+
/* @__PURE__ */ a2("span", { class: "fnc-sr-main", children: [
|
|
1747
|
+
/* @__PURE__ */ a2("span", { class: "fnc-sr-name", children: [
|
|
1748
|
+
/* @__PURE__ */ a2(Highlight, { text: info.name, query }),
|
|
1749
|
+
current?.symbol === info.symbol && /* @__PURE__ */ a2("span", { class: "fnc-sr-current", children: t3.current })
|
|
1750
|
+
] }),
|
|
1751
|
+
/* @__PURE__ */ a2("span", { class: "fnc-sr-meta", children: [
|
|
1752
|
+
/* @__PURE__ */ a2(Highlight, { text: displayCode(info.symbol), query }),
|
|
1753
|
+
" \xB7 ",
|
|
1754
|
+
info.exchange,
|
|
1755
|
+
cat && ` \xB7 ${cat.label[locale]}`
|
|
1756
|
+
] })
|
|
1757
|
+
] }),
|
|
1758
|
+
info.sparkline && /* @__PURE__ */ a2(Sparkline, { values: info.sparkline, up }),
|
|
1759
|
+
info.price !== void 0 && /* @__PURE__ */ a2("span", { class: "fnc-sr-quote", children: [
|
|
1760
|
+
/* @__PURE__ */ a2("b", { children: formatPrice2(info.price, info.pricePrecision) }),
|
|
1761
|
+
info.changePercent !== void 0 && /* @__PURE__ */ a2("span", { class: trend, children: [
|
|
1762
|
+
change > 0 ? "\u2191 " : change < 0 ? "\u2193 " : "",
|
|
1763
|
+
Math.abs(info.changePercent).toFixed(2),
|
|
1764
|
+
"%"
|
|
1765
|
+
] })
|
|
1766
|
+
] }),
|
|
1767
|
+
isRecent && /* @__PURE__ */ a2(
|
|
1768
|
+
"button",
|
|
1769
|
+
{
|
|
1770
|
+
class: "fnc-ind-btn",
|
|
1771
|
+
"aria-label": `${info.name} ${t3.removeRecent}`,
|
|
1772
|
+
title: t3.removeRecent,
|
|
1773
|
+
onClick: (e3) => {
|
|
1774
|
+
e3.stopPropagation();
|
|
1775
|
+
save(RECENT_KEY, recent.filter((r3) => r3.symbol !== info.symbol), setRecent);
|
|
1776
|
+
},
|
|
1777
|
+
children: /* @__PURE__ */ a2(SmallCloseIcon, {})
|
|
1778
|
+
}
|
|
1779
|
+
)
|
|
1780
|
+
]
|
|
1781
|
+
}
|
|
1782
|
+
);
|
|
1783
|
+
};
|
|
1784
|
+
const sideItem = (label, selected, onClick, extra) => /* @__PURE__ */ a2("button", { class: "fnc-sr-side-item", "aria-pressed": selected, onClick, children: [
|
|
1785
|
+
/* @__PURE__ */ a2("span", { children: label }),
|
|
1786
|
+
extra
|
|
1787
|
+
] });
|
|
1788
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-backdrop", onPointerDown: (e3) => e3.target === e3.currentTarget && close(), children: /* @__PURE__ */ a2("div", { class: "fnc-dialog fnc-search-dialog", role: "dialog", "aria-modal": "true", "aria-label": t3.searchSymbol, onKeyDown, children: [
|
|
1789
|
+
/* @__PURE__ */ a2("div", { class: "fnc-search", children: [
|
|
1790
|
+
/* @__PURE__ */ a2(SearchIcon, {}),
|
|
1791
|
+
exchange && /* @__PURE__ */ a2("span", { class: "fnc-chip-token", children: [
|
|
1792
|
+
exchange,
|
|
1793
|
+
/* @__PURE__ */ a2("button", { "aria-label": `${exchange} ${t3.cancel}`, onClick: () => setExchange(null), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) })
|
|
1794
|
+
] }),
|
|
1795
|
+
/* @__PURE__ */ a2(
|
|
1796
|
+
"input",
|
|
1797
|
+
{
|
|
1798
|
+
ref: inputRef,
|
|
1799
|
+
value: query,
|
|
1800
|
+
placeholder: t3.searchPlaceholder,
|
|
1801
|
+
role: "combobox",
|
|
1802
|
+
"aria-expanded": "true",
|
|
1803
|
+
"aria-controls": "fnc-search-results",
|
|
1804
|
+
autocomplete: "off",
|
|
1805
|
+
spellcheck: false,
|
|
1806
|
+
onInput: (e3) => setQuery(e3.currentTarget.value)
|
|
1807
|
+
}
|
|
1808
|
+
),
|
|
1809
|
+
query && /* @__PURE__ */ a2("button", { class: "fnc-ind-btn", "aria-label": t3.cancel, onClick: () => (setQuery(""), inputRef.current?.focus()), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) }),
|
|
1810
|
+
/* @__PURE__ */ a2("kbd", { class: "fnc-kbd fnc-hide-mobile", children: "ESC" })
|
|
1811
|
+
] }),
|
|
1812
|
+
/* @__PURE__ */ a2("div", { class: "fnc-tabs", role: "tablist", children: ctx.searchCategories.map((c3) => /* @__PURE__ */ a2("button", { class: "fnc-tab", role: "tab", "aria-selected": c3.id === category, onClick: () => setCategory(c3.id), children: c3.label[locale] })) }),
|
|
1813
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sr-body", children: [
|
|
1814
|
+
/* @__PURE__ */ a2("nav", { class: "fnc-sr-side fnc-scroll", "aria-label": t3.searchSymbol, children: [
|
|
1815
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sr-side-title", children: t3.browse }),
|
|
1816
|
+
sideItem(t3.allSymbols, view.kind === "all" && !exchange, () => (setView({ kind: "all" }), setExchange(null))),
|
|
1817
|
+
sideItem(t3.favorites, view.kind === "favorites", () => (setView({ kind: "favorites" }), setExchange(null)), /* @__PURE__ */ a2("em", { children: favorites.length || "" })),
|
|
1818
|
+
exchanges.length > 0 && /* @__PURE__ */ a2("div", { class: "fnc-sr-side-title", children: t3.exchanges }),
|
|
1819
|
+
exchanges.map(
|
|
1820
|
+
([name, count]) => sideItem(name, exchange === name, () => (setView({ kind: "all" }), setExchange(exchange === name ? null : name)), /* @__PURE__ */ a2("em", { children: count }))
|
|
1821
|
+
)
|
|
1822
|
+
] }),
|
|
1823
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sr-list fnc-scroll", id: "fnc-search-results", role: "listbox", ref: listRef, children: [
|
|
1824
|
+
recentRows.length > 0 && /* @__PURE__ */ a2("div", { class: "fnc-sr-section", children: [
|
|
1825
|
+
/* @__PURE__ */ a2("span", { children: t3.recentSearches }),
|
|
1826
|
+
/* @__PURE__ */ a2("button", { class: "fnc-link", onClick: () => save(RECENT_KEY, [], setRecent), children: t3.clearRecent })
|
|
1827
|
+
] }),
|
|
1828
|
+
recentRows.map((info, i3) => row(info, i3, true)),
|
|
1829
|
+
(recentRows.length > 0 || view.kind === "favorites") && /* @__PURE__ */ a2("div", { class: "fnc-sr-section", children: view.kind === "favorites" ? t3.favorites : t3.symbols }),
|
|
1830
|
+
loading && !results && view.kind === "all" ? Array.from({ length: 6 }, () => /* @__PURE__ */ a2("div", { class: "fnc-sr-skeleton", "aria-hidden": "true" })) : resultRows.map((info, i3) => row(info, recentRows.length + i3, false)),
|
|
1831
|
+
!loading && rows.length === 0 && /* @__PURE__ */ a2("div", { class: "fnc-empty", children: view.kind === "favorites" && favorites.length === 0 ? t3.noFavorites : t3.noResults })
|
|
1832
|
+
] })
|
|
1833
|
+
] }),
|
|
1834
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sr-footer", children: [
|
|
1835
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1836
|
+
/* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: "\u2191\u2193" }),
|
|
1837
|
+
" ",
|
|
1838
|
+
t3.navigate
|
|
1839
|
+
] }),
|
|
1840
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1841
|
+
/* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: "\u21B5" }),
|
|
1842
|
+
" ",
|
|
1843
|
+
t3.openAction
|
|
1844
|
+
] }),
|
|
1845
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1846
|
+
/* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: "ESC" }),
|
|
1847
|
+
" ",
|
|
1848
|
+
t3.close
|
|
1849
|
+
] }),
|
|
1850
|
+
/* @__PURE__ */ a2("span", { class: "fnc-spacer" }),
|
|
1851
|
+
/* @__PURE__ */ a2("span", { children: [
|
|
1852
|
+
/* @__PURE__ */ a2("kbd", { class: "fnc-kbd", children: [
|
|
1853
|
+
/Mac|iPhone|iPad/.test(navigator.platform) ? "\u2318" : "Ctrl",
|
|
1854
|
+
" + K"
|
|
1855
|
+
] }),
|
|
1856
|
+
" ",
|
|
1857
|
+
t3.searchSymbol
|
|
1858
|
+
] })
|
|
1859
|
+
] })
|
|
1860
|
+
] }) });
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// src/components/toolbar.tsx
|
|
1864
|
+
function Toolbar({ ctx }) {
|
|
1865
|
+
const s3 = useStore(ctx.store);
|
|
1866
|
+
const t3 = STRINGS[s3.locale];
|
|
1867
|
+
const canSearch = typeof ctx.datafeed.searchSymbols === "function";
|
|
1868
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-toolbar", role: "toolbar", children: [
|
|
1869
|
+
/* @__PURE__ */ a2(
|
|
1870
|
+
"button",
|
|
1871
|
+
{
|
|
1872
|
+
class: "fnc-symbol",
|
|
1873
|
+
disabled: !canSearch,
|
|
1874
|
+
title: canSearch ? t3.searchSymbol : void 0,
|
|
1875
|
+
"aria-haspopup": "dialog",
|
|
1876
|
+
onClick: () => ctx.setSearchOpen(true),
|
|
1877
|
+
children: [
|
|
1878
|
+
ctx.showLogo && s3.symbol ? /* @__PURE__ */ a2(SymbolAvatar, { info: s3.symbol, size: 24 }) : canSearch && /* @__PURE__ */ a2("span", { class: "fnc-icon", children: /* @__PURE__ */ a2(SearchIcon, {}) }),
|
|
1879
|
+
/* @__PURE__ */ a2("span", { class: "fnc-symbol-name", children: s3.symbol?.name ?? "\u2014" }),
|
|
1880
|
+
canSearch && ctx.showLogo && /* @__PURE__ */ a2("span", { class: "fnc-icon", children: /* @__PURE__ */ a2(ChevronDownIcon, {}) })
|
|
1881
|
+
]
|
|
1882
|
+
}
|
|
1883
|
+
),
|
|
1884
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sep" }),
|
|
1885
|
+
/* @__PURE__ */ a2(IntervalPicker, { ctx }),
|
|
1886
|
+
/* @__PURE__ */ a2(Slot, { el: ctx.slots.toolbar }),
|
|
1887
|
+
/* @__PURE__ */ a2("div", { class: "fnc-sep" }),
|
|
1888
|
+
/* @__PURE__ */ a2(ChartTypeMenu, { ctx }),
|
|
1889
|
+
/* @__PURE__ */ a2(
|
|
1890
|
+
"button",
|
|
1891
|
+
{
|
|
1892
|
+
class: "fnc-btn fnc-btn-wide fnc-btn-label",
|
|
1893
|
+
title: t3.addIndicator,
|
|
1894
|
+
"aria-label": t3.addIndicator,
|
|
1895
|
+
"aria-haspopup": "dialog",
|
|
1896
|
+
onClick: () => ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
1897
|
+
children: [
|
|
1898
|
+
/* @__PURE__ */ a2(IndicatorIcon, {}),
|
|
1899
|
+
/* @__PURE__ */ a2("span", { children: t3.indicators })
|
|
1900
|
+
]
|
|
1901
|
+
}
|
|
1902
|
+
),
|
|
1903
|
+
/* @__PURE__ */ a2("div", { class: "fnc-spacer" }),
|
|
1904
|
+
ctx.drawingToolbar && /* @__PURE__ */ a2(
|
|
1905
|
+
"button",
|
|
1906
|
+
{
|
|
1907
|
+
class: "fnc-btn fnc-btn-drawbar",
|
|
1908
|
+
title: t3.drawingTools,
|
|
1909
|
+
"aria-label": t3.drawingTools,
|
|
1910
|
+
"aria-pressed": s3.drawbarOpen || s3.drawingTool !== null,
|
|
1911
|
+
onClick: () => ctx.store.set({ drawbarOpen: !s3.drawbarOpen }),
|
|
1912
|
+
children: /* @__PURE__ */ a2(PencilIcon, {})
|
|
1913
|
+
}
|
|
1914
|
+
),
|
|
1915
|
+
/* @__PURE__ */ a2(Slot, { el: ctx.slots.toolbarEnd }),
|
|
1916
|
+
/* @__PURE__ */ a2(
|
|
1917
|
+
"button",
|
|
1918
|
+
{
|
|
1919
|
+
class: "fnc-btn",
|
|
1920
|
+
title: t3.chartSettings,
|
|
1921
|
+
"aria-label": t3.chartSettings,
|
|
1922
|
+
"aria-haspopup": "dialog",
|
|
1923
|
+
onClick: () => ctx.store.set({ dialog: { kind: "chartSettings" } }),
|
|
1924
|
+
children: /* @__PURE__ */ a2(SettingsIcon, {})
|
|
1925
|
+
}
|
|
1926
|
+
),
|
|
1927
|
+
/* @__PURE__ */ a2("button", { class: "fnc-btn fnc-btn-screenshot", title: t3.screenshot, "aria-label": t3.screenshot, onClick: ctx.screenshot, children: /* @__PURE__ */ a2(CameraIcon, {}) }),
|
|
1928
|
+
/* @__PURE__ */ a2(
|
|
1929
|
+
"button",
|
|
1930
|
+
{
|
|
1931
|
+
class: "fnc-btn",
|
|
1932
|
+
title: s3.fullscreen ? t3.exitFullscreen : t3.fullscreen,
|
|
1933
|
+
"aria-label": s3.fullscreen ? t3.exitFullscreen : t3.fullscreen,
|
|
1934
|
+
onClick: ctx.toggleFullscreen,
|
|
1935
|
+
children: s3.fullscreen ? /* @__PURE__ */ a2(CollapseIcon, {}) : /* @__PURE__ */ a2(ExpandIcon, {})
|
|
1936
|
+
}
|
|
1937
|
+
),
|
|
1938
|
+
s3.searchOpen && canSearch && /* @__PURE__ */ a2(SymbolSearch, { ctx }),
|
|
1939
|
+
/* @__PURE__ */ a2(Dialogs, { ctx })
|
|
1940
|
+
] });
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/storage.ts
|
|
1944
|
+
function createStorage(prefix) {
|
|
1945
|
+
const memory = /* @__PURE__ */ new Map();
|
|
1946
|
+
const ls = (() => {
|
|
1947
|
+
try {
|
|
1948
|
+
return prefix && typeof localStorage !== "undefined" ? localStorage : null;
|
|
1949
|
+
} catch {
|
|
1950
|
+
return null;
|
|
1951
|
+
}
|
|
1952
|
+
})();
|
|
1953
|
+
const full = (key) => `${prefix}:${key}`;
|
|
1954
|
+
return {
|
|
1955
|
+
get(key, fallback) {
|
|
1956
|
+
try {
|
|
1957
|
+
const raw = ls ? ls.getItem(full(key)) : memory.get(key) ?? null;
|
|
1958
|
+
return raw === null ? fallback : JSON.parse(raw);
|
|
1959
|
+
} catch {
|
|
1960
|
+
return fallback;
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
set(key, value) {
|
|
1964
|
+
const raw = JSON.stringify(value);
|
|
1965
|
+
try {
|
|
1966
|
+
if (ls) ls.setItem(full(key), raw);
|
|
1967
|
+
else memory.set(key, raw);
|
|
1968
|
+
} catch {
|
|
1969
|
+
memory.set(key, raw);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
// src/styles.ts
|
|
1976
|
+
var CSS = (
|
|
1977
|
+
/* css */
|
|
1978
|
+
`
|
|
1979
|
+
.fnc-root {
|
|
1980
|
+
position: relative;
|
|
1981
|
+
display: flex;
|
|
1982
|
+
flex-direction: column;
|
|
1983
|
+
width: 100%;
|
|
1984
|
+
height: 100%;
|
|
1985
|
+
min-height: 0;
|
|
1986
|
+
background: var(--fnc-bg);
|
|
1987
|
+
color: var(--fnc-text);
|
|
1988
|
+
font-family: var(--fnc-font);
|
|
1989
|
+
font-size: 14px;
|
|
1990
|
+
line-height: 1.4;
|
|
1991
|
+
-webkit-font-smoothing: antialiased;
|
|
1992
|
+
overflow: hidden;
|
|
1993
|
+
}
|
|
1994
|
+
.fnc-root *, .fnc-root *::before, .fnc-root *::after { box-sizing: border-box; }
|
|
1995
|
+
.fnc-root.fnc-pseudo-fullscreen { position: fixed; inset: 0; z-index: 2147483000; }
|
|
1996
|
+
:where(.fnc-root) button {
|
|
1997
|
+
font: inherit;
|
|
1998
|
+
color: inherit;
|
|
1999
|
+
background: none;
|
|
2000
|
+
border: 0;
|
|
2001
|
+
margin: 0;
|
|
2002
|
+
padding: 0;
|
|
2003
|
+
cursor: pointer;
|
|
2004
|
+
-webkit-tap-highlight-color: transparent;
|
|
2005
|
+
}
|
|
2006
|
+
.fnc-root button:focus-visible { outline: 2px solid var(--fnc-accent); outline-offset: 1px; }
|
|
2007
|
+
|
|
2008
|
+
/* \u2500\u2500 Toolbar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2009
|
+
.fnc-toolbar {
|
|
2010
|
+
display: flex;
|
|
2011
|
+
align-items: center;
|
|
2012
|
+
gap: 4px;
|
|
2013
|
+
height: 48px;
|
|
2014
|
+
padding: 0 8px;
|
|
2015
|
+
border-bottom: 1px solid var(--fnc-border);
|
|
2016
|
+
flex: none;
|
|
2017
|
+
}
|
|
2018
|
+
.fnc-symbol {
|
|
2019
|
+
display: flex;
|
|
2020
|
+
align-items: center;
|
|
2021
|
+
gap: 6px;
|
|
2022
|
+
height: 36px;
|
|
2023
|
+
max-width: 220px;
|
|
2024
|
+
padding: 0 10px 0 8px;
|
|
2025
|
+
border-radius: var(--fnc-radius);
|
|
2026
|
+
font-weight: 700;
|
|
2027
|
+
font-size: 15px;
|
|
2028
|
+
flex: none;
|
|
2029
|
+
}
|
|
2030
|
+
.fnc-symbol .fnc-icon { color: var(--fnc-text-2); display: flex; }
|
|
2031
|
+
.fnc-symbol-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2032
|
+
.fnc-symbol:not(:disabled):hover, .fnc-btn:hover, .fnc-chip:hover { background: var(--fnc-surface); }
|
|
2033
|
+
.fnc-symbol:disabled { cursor: default; }
|
|
2034
|
+
.fnc-sep { width: 1px; height: 20px; background: var(--fnc-border); margin: 0 4px; flex: none; }
|
|
2035
|
+
.fnc-intervals {
|
|
2036
|
+
display: flex;
|
|
2037
|
+
gap: 2px;
|
|
2038
|
+
overflow-x: auto;
|
|
2039
|
+
scrollbar-width: none;
|
|
2040
|
+
min-width: 0;
|
|
2041
|
+
}
|
|
2042
|
+
.fnc-intervals::-webkit-scrollbar { display: none; }
|
|
2043
|
+
.fnc-chip {
|
|
2044
|
+
height: 32px;
|
|
2045
|
+
padding: 0 10px;
|
|
2046
|
+
border-radius: var(--fnc-radius);
|
|
2047
|
+
font-size: 13px;
|
|
2048
|
+
font-weight: 600;
|
|
2049
|
+
color: var(--fnc-text-2);
|
|
2050
|
+
white-space: nowrap;
|
|
2051
|
+
flex: none;
|
|
2052
|
+
}
|
|
2053
|
+
.fnc-chip[aria-pressed='true'] { color: var(--fnc-text); background: var(--fnc-surface); }
|
|
2054
|
+
.fnc-spacer { flex: 1; min-width: 8px; }
|
|
2055
|
+
.fnc-btn {
|
|
2056
|
+
display: flex;
|
|
2057
|
+
align-items: center;
|
|
2058
|
+
justify-content: center;
|
|
2059
|
+
width: 36px;
|
|
2060
|
+
height: 36px;
|
|
2061
|
+
border-radius: var(--fnc-radius);
|
|
2062
|
+
color: var(--fnc-text-2);
|
|
2063
|
+
flex: none;
|
|
2064
|
+
}
|
|
2065
|
+
.fnc-btn:hover { color: var(--fnc-text); }
|
|
2066
|
+
.fnc-btn[aria-pressed='true'] { color: var(--fnc-text); background: var(--fnc-surface); }
|
|
2067
|
+
.fnc-btn:disabled { opacity: 0.35; cursor: default; background: none; }
|
|
2068
|
+
.fnc-btn-wide { width: auto; gap: 2px; padding: 0 6px 0 8px; }
|
|
2069
|
+
.fnc-btn-drawbar { display: none; }
|
|
2070
|
+
.fnc-btn-label { font-size: 13px; font-weight: 600; gap: 4px; padding: 0 10px 0 8px; }
|
|
2071
|
+
|
|
2072
|
+
.fnc-avatar {
|
|
2073
|
+
display: inline-flex;
|
|
2074
|
+
align-items: center;
|
|
2075
|
+
justify-content: center;
|
|
2076
|
+
flex: none;
|
|
2077
|
+
border-radius: 50%;
|
|
2078
|
+
object-fit: cover;
|
|
2079
|
+
font-weight: 700;
|
|
2080
|
+
line-height: 1;
|
|
2081
|
+
background: var(--fnc-surface);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
/* \u2500\u2500 Menus \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2085
|
+
.fnc-menu-wrap { position: relative; flex: none; }
|
|
2086
|
+
.fnc-menu {
|
|
2087
|
+
position: absolute;
|
|
2088
|
+
top: calc(100% + 6px);
|
|
2089
|
+
left: 0;
|
|
2090
|
+
z-index: 20;
|
|
2091
|
+
display: flex;
|
|
2092
|
+
flex-direction: column;
|
|
2093
|
+
min-width: 140px;
|
|
2094
|
+
padding: 6px;
|
|
2095
|
+
border-radius: 12px;
|
|
2096
|
+
background: var(--fnc-bg);
|
|
2097
|
+
box-shadow: 0 8px 28px rgb(0 0 0 / 0.16), 0 0 0 1px var(--fnc-border);
|
|
2098
|
+
}
|
|
2099
|
+
.fnc-menu-item {
|
|
2100
|
+
display: flex;
|
|
2101
|
+
align-items: center;
|
|
2102
|
+
gap: 10px;
|
|
2103
|
+
height: 36px;
|
|
2104
|
+
padding: 0 10px;
|
|
2105
|
+
border-radius: var(--fnc-radius);
|
|
2106
|
+
font-size: 14px;
|
|
2107
|
+
color: var(--fnc-text-2);
|
|
2108
|
+
text-align: left;
|
|
2109
|
+
}
|
|
2110
|
+
.fnc-menu-item:hover, .fnc-menu-item[aria-checked='true'] { background: var(--fnc-surface); color: var(--fnc-text); }
|
|
2111
|
+
|
|
2112
|
+
/* \u2500\u2500 Chart body & overlays \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2113
|
+
.fnc-body { position: relative; display: flex; flex: 1; min-height: 0; }
|
|
2114
|
+
.fnc-stage { position: relative; flex: 1; min-width: 0; }
|
|
2115
|
+
.fnc-chart-host, .fnc-overlay { position: absolute; inset: 0; }
|
|
2116
|
+
|
|
2117
|
+
/* \u2500\u2500 Drawing tool bar (left) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2118
|
+
.fnc-drawbar-host { flex: none; display: flex; }
|
|
2119
|
+
.fnc-drawbar {
|
|
2120
|
+
display: flex;
|
|
2121
|
+
flex-direction: column;
|
|
2122
|
+
align-items: center;
|
|
2123
|
+
gap: 4px;
|
|
2124
|
+
width: 48px;
|
|
2125
|
+
padding: 8px 0;
|
|
2126
|
+
border-right: 1px solid var(--fnc-border);
|
|
2127
|
+
background: var(--fnc-bg);
|
|
2128
|
+
}
|
|
2129
|
+
.fnc-hsep { width: 20px; height: 1px; background: var(--fnc-border); margin: 4px 0; }
|
|
2130
|
+
|
|
2131
|
+
/* Selected drawing: floating color/delete bar */
|
|
2132
|
+
.fnc-selbar {
|
|
2133
|
+
position: absolute;
|
|
2134
|
+
top: 8px;
|
|
2135
|
+
display: flex;
|
|
2136
|
+
align-items: center;
|
|
2137
|
+
gap: 6px;
|
|
2138
|
+
padding: 4px 4px 4px 10px;
|
|
2139
|
+
transform: translateX(-50%);
|
|
2140
|
+
border-radius: calc(var(--fnc-radius) + 4px);
|
|
2141
|
+
background: var(--fnc-bg);
|
|
2142
|
+
box-shadow: 0 2px 12px rgb(0 0 0 / 0.12), 0 0 0 1px var(--fnc-border);
|
|
2143
|
+
pointer-events: auto;
|
|
2144
|
+
}
|
|
2145
|
+
.fnc-swatch {
|
|
2146
|
+
width: 18px;
|
|
2147
|
+
height: 18px;
|
|
2148
|
+
border-radius: 50%;
|
|
2149
|
+
flex: none;
|
|
2150
|
+
}
|
|
2151
|
+
.fnc-swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--fnc-bg), 0 0 0 4px var(--fnc-text-2); }
|
|
2152
|
+
.fnc-selbar .fnc-btn { width: 32px; height: 32px; }
|
|
2153
|
+
.fnc-overlay { pointer-events: none; }
|
|
2154
|
+
.fnc-overlay button { pointer-events: auto; }
|
|
2155
|
+
|
|
2156
|
+
.fnc-legend {
|
|
2157
|
+
position: absolute;
|
|
2158
|
+
left: 12px;
|
|
2159
|
+
top: 8px;
|
|
2160
|
+
right: 80px;
|
|
2161
|
+
display: flex;
|
|
2162
|
+
flex-direction: column;
|
|
2163
|
+
gap: 2px;
|
|
2164
|
+
font-size: 12px;
|
|
2165
|
+
color: var(--fnc-text-2);
|
|
2166
|
+
font-variant-numeric: tabular-nums;
|
|
2167
|
+
}
|
|
2168
|
+
.fnc-legend-title { display: flex; align-items: center; gap: 6px; font-size: 13px; }
|
|
2169
|
+
.fnc-legend-title b { color: var(--fnc-text); font-weight: 700; }
|
|
2170
|
+
.fnc-legend-values { display: flex; flex-wrap: wrap; gap: 0 10px; }
|
|
2171
|
+
.fnc-legend-values b { font-weight: 600; }
|
|
2172
|
+
/* Indicator legend rows */
|
|
2173
|
+
.fnc-ind {
|
|
2174
|
+
display: flex;
|
|
2175
|
+
flex-wrap: wrap;
|
|
2176
|
+
align-items: center;
|
|
2177
|
+
gap: 0 8px;
|
|
2178
|
+
min-height: 20px;
|
|
2179
|
+
pointer-events: auto;
|
|
2180
|
+
width: fit-content;
|
|
2181
|
+
max-width: 100%;
|
|
2182
|
+
border-radius: 6px;
|
|
2183
|
+
}
|
|
2184
|
+
.fnc-ind-title { color: var(--fnc-text-2); }
|
|
2185
|
+
.fnc-ind-hidden .fnc-ind-title { opacity: 0.5; text-decoration: line-through; }
|
|
2186
|
+
.fnc-ind-eye { margin-left: -4px; }
|
|
2187
|
+
.fnc-ind-hidden .fnc-ind-eye { color: var(--fnc-text-2); opacity: 0.7; }
|
|
2188
|
+
.fnc-ind-collapse, .fnc-ind-handle {
|
|
2189
|
+
display: inline-flex;
|
|
2190
|
+
align-items: center;
|
|
2191
|
+
justify-content: center;
|
|
2192
|
+
width: fit-content;
|
|
2193
|
+
height: 20px;
|
|
2194
|
+
margin-top: 2px;
|
|
2195
|
+
border-radius: 6px;
|
|
2196
|
+
color: var(--fnc-text-2);
|
|
2197
|
+
pointer-events: auto;
|
|
2198
|
+
}
|
|
2199
|
+
.fnc-ind-collapse { width: 24px; margin-left: -4px; }
|
|
2200
|
+
.fnc-ind-collapse:hover, .fnc-ind-handle:hover { background: var(--fnc-surface); color: var(--fnc-text); }
|
|
2201
|
+
/* Collapsed: a small horizontal bar centered in a comfortable tap target. */
|
|
2202
|
+
.fnc-ind-handle { width: 40px; margin-left: -6px; }
|
|
2203
|
+
.fnc-ind-handle span { width: 24px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.55; }
|
|
2204
|
+
.fnc-ind-handle:hover span { opacity: 0.9; }
|
|
2205
|
+
.fnc-ind b { font-weight: 600; }
|
|
2206
|
+
.fnc-ind-actions { display: inline-flex; gap: 2px; opacity: 0; transition: opacity 0.12s; }
|
|
2207
|
+
.fnc-ind:hover .fnc-ind-actions, .fnc-ind:focus-within .fnc-ind-actions { opacity: 1; }
|
|
2208
|
+
.fnc-ind-btn {
|
|
2209
|
+
display: inline-flex;
|
|
2210
|
+
align-items: center;
|
|
2211
|
+
justify-content: center;
|
|
2212
|
+
width: 22px;
|
|
2213
|
+
height: 22px;
|
|
2214
|
+
border-radius: 6px;
|
|
2215
|
+
color: var(--fnc-text-2);
|
|
2216
|
+
}
|
|
2217
|
+
.fnc-ind-btn:hover { background: var(--fnc-surface); color: var(--fnc-text); }
|
|
2218
|
+
.fnc-ind { cursor: pointer; }
|
|
2219
|
+
.fnc-ind:hover .fnc-ind-title { color: var(--fnc-text); }
|
|
2220
|
+
/* Touch: no tiny icons; tapping the row opens settings (which can also remove). */
|
|
2221
|
+
@media (hover: none) { .fnc-ind-actions { display: none; } }
|
|
2222
|
+
.fnc-pane-legend { right: auto; }
|
|
2223
|
+
|
|
2224
|
+
/* \u2500\u2500 Thin, quiet scrollbars for every scroll area in the widget \u2500\u2500 */
|
|
2225
|
+
.fnc-root .fnc-scroll, .fnc-root .fnc-menu, .fnc-root .fnc-dialog form {
|
|
2226
|
+
scrollbar-width: thin;
|
|
2227
|
+
scrollbar-color: var(--fnc-border) transparent;
|
|
2228
|
+
}
|
|
2229
|
+
.fnc-root ::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
2230
|
+
.fnc-root ::-webkit-scrollbar-track { background: transparent; }
|
|
2231
|
+
.fnc-root ::-webkit-scrollbar-thumb {
|
|
2232
|
+
background: var(--fnc-border);
|
|
2233
|
+
border-radius: 10px;
|
|
2234
|
+
border: 3px solid transparent;
|
|
2235
|
+
background-clip: padding-box;
|
|
2236
|
+
}
|
|
2237
|
+
.fnc-root ::-webkit-scrollbar-thumb:hover { background-color: var(--fnc-text-2); }
|
|
2238
|
+
|
|
2239
|
+
/* \u2500\u2500 Interval picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2240
|
+
.fnc-interval-picker { position: relative; display: flex; align-items: center; min-width: 0; }
|
|
2241
|
+
.fnc-interval-trigger { gap: 2px; padding: 0 6px; }
|
|
2242
|
+
.fnc-interval-current { display: none; font-size: 13px; color: var(--fnc-text); }
|
|
2243
|
+
.fnc-interval-trigger-active { background: var(--fnc-surface); }
|
|
2244
|
+
.fnc-interval-trigger-active .fnc-interval-current { display: inline; }
|
|
2245
|
+
.fnc-chip:disabled { opacity: 0.35; cursor: default; background: none; }
|
|
2246
|
+
.fnc-interval-menu {
|
|
2247
|
+
width: 208px;
|
|
2248
|
+
max-height: min(440px, 70vh);
|
|
2249
|
+
overflow-y: auto;
|
|
2250
|
+
gap: 0;
|
|
2251
|
+
}
|
|
2252
|
+
.fnc-menu-group + .fnc-menu-group { border-top: 1px solid var(--fnc-border); margin-top: 4px; padding-top: 4px; }
|
|
2253
|
+
.fnc-menu-title { padding: 8px 10px 4px; font-size: 11px; font-weight: 700; color: var(--fnc-text-2); letter-spacing: 0.02em; }
|
|
2254
|
+
.fnc-menu-row { display: flex; align-items: center; border-radius: var(--fnc-radius); }
|
|
2255
|
+
.fnc-menu-row:hover { background: var(--fnc-surface); }
|
|
2256
|
+
.fnc-menu-row .fnc-menu-item { flex: 1; background: none; }
|
|
2257
|
+
.fnc-menu-row .fnc-menu-item[aria-checked='true'] { color: var(--fnc-accent); font-weight: 700; }
|
|
2258
|
+
.fnc-menu-item:disabled { opacity: 0.35; cursor: default; }
|
|
2259
|
+
.fnc-star {
|
|
2260
|
+
display: flex;
|
|
2261
|
+
align-items: center;
|
|
2262
|
+
justify-content: center;
|
|
2263
|
+
width: 32px;
|
|
2264
|
+
height: 32px;
|
|
2265
|
+
flex: none;
|
|
2266
|
+
color: var(--fnc-text-2);
|
|
2267
|
+
opacity: 0;
|
|
2268
|
+
transition: opacity 0.12s, color 0.12s;
|
|
2269
|
+
}
|
|
2270
|
+
.fnc-menu-row:hover .fnc-star, .fnc-star:focus-visible { opacity: 1; }
|
|
2271
|
+
.fnc-star:hover { color: #FF9F0A; }
|
|
2272
|
+
.fnc-menu-row.is-favorite .fnc-star { opacity: 1; color: #FF9F0A; }
|
|
2273
|
+
@media (hover: none) { .fnc-star { opacity: 0.5; } }
|
|
2274
|
+
|
|
2275
|
+
/* Interval buttons keep their width; host content in the toolbar slot shrinks first. */
|
|
2276
|
+
.fnc-interval-picker { flex: none; }
|
|
2277
|
+
|
|
2278
|
+
/* \u2500\u2500 Slots: host-app UI inside the widget (collapse when empty) \u2500\u2500 */
|
|
2279
|
+
.fnc-slot:empty { display: none; }
|
|
2280
|
+
.fnc-slot-host { display: flex; align-self: stretch; min-width: 0; flex: 0 1 auto; }
|
|
2281
|
+
.fnc-slot-toolbar { position: relative; display: flex; align-items: center; min-width: 0; padding-left: 9px; margin-left: 4px; }
|
|
2282
|
+
.fnc-slot-toolbar::before {
|
|
2283
|
+
content: '';
|
|
2284
|
+
position: absolute;
|
|
2285
|
+
left: 0;
|
|
2286
|
+
top: 50%;
|
|
2287
|
+
width: 1px;
|
|
2288
|
+
height: 20px;
|
|
2289
|
+
margin-top: -10px;
|
|
2290
|
+
background: var(--fnc-border);
|
|
2291
|
+
}
|
|
2292
|
+
.fnc-slot-below-toolbar, .fnc-slot-footer { flex: none; min-width: 0; }
|
|
2293
|
+
.fnc-slot-toolbar-end { display: flex; align-items: center; gap: 2px; flex: none; }
|
|
2294
|
+
|
|
2295
|
+
/* \u2500\u2500 Drawing bar fold (desktop) \u2500\u2500 */
|
|
2296
|
+
.fnc-drawbar .fnc-spacer { flex: 1; min-height: 8px; }
|
|
2297
|
+
.fnc-drawbar[data-collapsed='true'] { display: none; }
|
|
2298
|
+
.fnc-drawbar-tab {
|
|
2299
|
+
align-self: center;
|
|
2300
|
+
display: flex;
|
|
2301
|
+
align-items: center;
|
|
2302
|
+
justify-content: center;
|
|
2303
|
+
width: 16px;
|
|
2304
|
+
height: 56px;
|
|
2305
|
+
border: 1px solid var(--fnc-border);
|
|
2306
|
+
border-left: 0;
|
|
2307
|
+
border-radius: 0 8px 8px 0;
|
|
2308
|
+
color: var(--fnc-text-2);
|
|
2309
|
+
background: var(--fnc-bg);
|
|
2310
|
+
}
|
|
2311
|
+
.fnc-drawbar-tab:hover { color: var(--fnc-text); background: var(--fnc-surface); }
|
|
2312
|
+
|
|
2313
|
+
/* \u2500\u2500 Symbol search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2314
|
+
.fnc-dialog.fnc-search-dialog { width: min(880px, calc(100% - 32px)); height: min(640px, 100%); max-height: none; }
|
|
2315
|
+
.fnc-sr-body { display: flex; flex: 1; min-height: 0; }
|
|
2316
|
+
.fnc-sr-side {
|
|
2317
|
+
width: 196px;
|
|
2318
|
+
flex: none;
|
|
2319
|
+
padding: 8px;
|
|
2320
|
+
border-right: 1px solid var(--fnc-border);
|
|
2321
|
+
overflow-y: auto;
|
|
2322
|
+
}
|
|
2323
|
+
.fnc-sr-side-title { padding: 12px 10px 6px; font-size: 12px; font-weight: 700; color: var(--fnc-text-2); }
|
|
2324
|
+
.fnc-sr-side-item {
|
|
2325
|
+
display: flex;
|
|
2326
|
+
align-items: center;
|
|
2327
|
+
gap: 8px;
|
|
2328
|
+
width: 100%;
|
|
2329
|
+
height: 36px;
|
|
2330
|
+
padding: 0 10px;
|
|
2331
|
+
border-radius: var(--fnc-radius);
|
|
2332
|
+
font-size: 14px;
|
|
2333
|
+
color: var(--fnc-text);
|
|
2334
|
+
text-align: left;
|
|
2335
|
+
}
|
|
2336
|
+
.fnc-sr-side-item span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2337
|
+
.fnc-sr-side-item em { font-style: normal; font-size: 12px; color: var(--fnc-text-2); }
|
|
2338
|
+
.fnc-sr-side-item:hover { background: var(--fnc-surface); }
|
|
2339
|
+
.fnc-sr-side-item[aria-pressed='true'] { background: var(--fnc-surface); font-weight: 700; }
|
|
2340
|
+
.fnc-chip-token {
|
|
2341
|
+
display: inline-flex;
|
|
2342
|
+
align-items: center;
|
|
2343
|
+
gap: 2px;
|
|
2344
|
+
flex: none;
|
|
2345
|
+
height: 28px;
|
|
2346
|
+
padding: 0 4px 0 10px;
|
|
2347
|
+
border: 1px solid var(--fnc-border);
|
|
2348
|
+
border-radius: 8px;
|
|
2349
|
+
font-size: 13px;
|
|
2350
|
+
font-weight: 700;
|
|
2351
|
+
color: var(--fnc-text);
|
|
2352
|
+
}
|
|
2353
|
+
.fnc-chip-token button { display: flex; color: var(--fnc-text-2); }
|
|
2354
|
+
.fnc-chip-token button:hover { color: var(--fnc-text); }
|
|
2355
|
+
.fnc-kbd {
|
|
2356
|
+
display: inline-flex;
|
|
2357
|
+
align-items: center;
|
|
2358
|
+
height: 22px;
|
|
2359
|
+
padding: 0 6px;
|
|
2360
|
+
border: 1px solid var(--fnc-border);
|
|
2361
|
+
border-radius: 6px;
|
|
2362
|
+
font: inherit;
|
|
2363
|
+
font-size: 11px;
|
|
2364
|
+
font-weight: 600;
|
|
2365
|
+
color: var(--fnc-text-2);
|
|
2366
|
+
background: var(--fnc-surface);
|
|
2367
|
+
}
|
|
2368
|
+
.fnc-sr-star {
|
|
2369
|
+
display: flex;
|
|
2370
|
+
align-items: center;
|
|
2371
|
+
justify-content: center;
|
|
2372
|
+
width: 28px;
|
|
2373
|
+
height: 28px;
|
|
2374
|
+
flex: none;
|
|
2375
|
+
margin-right: -4px;
|
|
2376
|
+
color: var(--fnc-text-2);
|
|
2377
|
+
opacity: 0.45;
|
|
2378
|
+
border-radius: 6px;
|
|
2379
|
+
}
|
|
2380
|
+
.fnc-sr-star:hover { opacity: 1; color: #ff9f0a; }
|
|
2381
|
+
.fnc-sr-star.is-on { opacity: 1; color: #ff9f0a; }
|
|
2382
|
+
.fnc-sr-spark { flex: none; }
|
|
2383
|
+
.fnc-sr-quote { display: flex; flex-direction: column; align-items: flex-end; flex: none; min-width: 84px; line-height: 1.25; font-variant-numeric: tabular-nums; }
|
|
2384
|
+
.fnc-sr-quote b { font-size: 14px; font-weight: 700; }
|
|
2385
|
+
.fnc-sr-quote span { font-size: 12px; font-weight: 600; }
|
|
2386
|
+
.fnc-search mark { background: none; color: var(--fnc-accent); font-weight: 700; }
|
|
2387
|
+
.fnc-tabs {
|
|
2388
|
+
display: flex;
|
|
2389
|
+
gap: 4px;
|
|
2390
|
+
padding: 8px 12px;
|
|
2391
|
+
border-bottom: 1px solid var(--fnc-border);
|
|
2392
|
+
overflow-x: auto;
|
|
2393
|
+
scrollbar-width: none;
|
|
2394
|
+
flex: none;
|
|
2395
|
+
}
|
|
2396
|
+
.fnc-tabs::-webkit-scrollbar { display: none; }
|
|
2397
|
+
.fnc-tab {
|
|
2398
|
+
height: 32px;
|
|
2399
|
+
padding: 0 12px;
|
|
2400
|
+
border-radius: 999px;
|
|
2401
|
+
font-size: 13px;
|
|
2402
|
+
font-weight: 600;
|
|
2403
|
+
color: var(--fnc-text-2);
|
|
2404
|
+
white-space: nowrap;
|
|
2405
|
+
flex: none;
|
|
2406
|
+
}
|
|
2407
|
+
.fnc-tab:hover { color: var(--fnc-text); }
|
|
2408
|
+
.fnc-tab[aria-selected='true'] { background: var(--fnc-text); color: var(--fnc-bg); }
|
|
2409
|
+
.fnc-sr-list { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 8px 8px; }
|
|
2410
|
+
.fnc-sr-section {
|
|
2411
|
+
display: flex;
|
|
2412
|
+
align-items: center;
|
|
2413
|
+
justify-content: space-between;
|
|
2414
|
+
padding: 12px 8px 6px;
|
|
2415
|
+
font-size: 12px;
|
|
2416
|
+
font-weight: 700;
|
|
2417
|
+
color: var(--fnc-text-2);
|
|
2418
|
+
}
|
|
2419
|
+
.fnc-link { font-size: 12px; color: var(--fnc-text-2); }
|
|
2420
|
+
.fnc-link:hover { color: var(--fnc-text); text-decoration: underline; }
|
|
2421
|
+
.fnc-sr-row {
|
|
2422
|
+
width: 100%;
|
|
2423
|
+
text-align: left;
|
|
2424
|
+
display: flex;
|
|
2425
|
+
align-items: center;
|
|
2426
|
+
gap: 12px;
|
|
2427
|
+
min-height: 56px;
|
|
2428
|
+
padding: 8px;
|
|
2429
|
+
border-radius: 12px;
|
|
2430
|
+
cursor: pointer;
|
|
2431
|
+
}
|
|
2432
|
+
.fnc-sr-row[aria-selected='true'] { background: var(--fnc-surface); }
|
|
2433
|
+
.fnc-sr-main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
|
|
2434
|
+
.fnc-sr-name { font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2435
|
+
.fnc-sr-meta { font-size: 12px; color: var(--fnc-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2436
|
+
.fnc-sr-name mark, .fnc-sr-meta mark { background: none; color: var(--fnc-accent); font-weight: 700; }
|
|
2437
|
+
.fnc-sr-current { margin-left: 6px; font-size: 11px; font-weight: 700; color: var(--fnc-accent); }
|
|
2438
|
+
.fnc-sr-skeleton {
|
|
2439
|
+
height: 48px;
|
|
2440
|
+
margin: 8px;
|
|
2441
|
+
border-radius: 12px;
|
|
2442
|
+
background: linear-gradient(90deg, var(--fnc-surface) 0%, var(--fnc-bg) 50%, var(--fnc-surface) 100%);
|
|
2443
|
+
background-size: 200% 100%;
|
|
2444
|
+
animation: fnc-shimmer 1.2s ease-in-out infinite;
|
|
2445
|
+
}
|
|
2446
|
+
@keyframes fnc-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
|
|
2447
|
+
.fnc-sr-footer {
|
|
2448
|
+
display: flex;
|
|
2449
|
+
align-items: center;
|
|
2450
|
+
gap: 16px;
|
|
2451
|
+
padding: 10px 16px;
|
|
2452
|
+
border-top: 1px solid var(--fnc-border);
|
|
2453
|
+
font-size: 12px;
|
|
2454
|
+
color: var(--fnc-text-2);
|
|
2455
|
+
flex: none;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
.fnc-up { color: var(--fnc-up); }
|
|
2459
|
+
.fnc-down { color: var(--fnc-down); }
|
|
2460
|
+
.fnc-flat { color: var(--fnc-text); }
|
|
2461
|
+
.fnc-spinner {
|
|
2462
|
+
width: 12px;
|
|
2463
|
+
height: 12px;
|
|
2464
|
+
border: 2px solid var(--fnc-border);
|
|
2465
|
+
border-top-color: var(--fnc-text-2);
|
|
2466
|
+
border-radius: 50%;
|
|
2467
|
+
animation: fnc-spin 0.8s linear infinite;
|
|
2468
|
+
}
|
|
2469
|
+
@keyframes fnc-spin { to { transform: rotate(360deg); } }
|
|
2470
|
+
|
|
2471
|
+
.fnc-attribution {
|
|
2472
|
+
position: absolute;
|
|
2473
|
+
left: 10px;
|
|
2474
|
+
z-index: 1;
|
|
2475
|
+
display: flex;
|
|
2476
|
+
align-items: center;
|
|
2477
|
+
gap: 6px;
|
|
2478
|
+
height: 28px;
|
|
2479
|
+
padding: 0 10px 0 4px;
|
|
2480
|
+
border-radius: 14px;
|
|
2481
|
+
color: var(--fnc-text);
|
|
2482
|
+
font-weight: 800;
|
|
2483
|
+
font-size: 13px;
|
|
2484
|
+
letter-spacing: -0.3px;
|
|
2485
|
+
text-decoration: none;
|
|
2486
|
+
pointer-events: auto;
|
|
2487
|
+
opacity: 0.85;
|
|
2488
|
+
transition: opacity 0.15s, background 0.15s;
|
|
2489
|
+
}
|
|
2490
|
+
.fnc-attribution:hover { opacity: 1; background: var(--fnc-bg); box-shadow: 0 0 0 1px var(--fnc-border); }
|
|
2491
|
+
.fnc-attribution svg { flex: none; }
|
|
2492
|
+
.fnc-attribution span { max-width: 0; overflow: hidden; transition: max-width 0.2s; }
|
|
2493
|
+
.fnc-attribution:hover span, .fnc-attribution:focus-visible span { max-width: 80px; }
|
|
2494
|
+
|
|
2495
|
+
.fnc-nav {
|
|
2496
|
+
position: absolute;
|
|
2497
|
+
display: flex;
|
|
2498
|
+
gap: 4px;
|
|
2499
|
+
padding: 4px;
|
|
2500
|
+
transform: translateX(-50%);
|
|
2501
|
+
border-radius: calc(var(--fnc-radius) + 4px);
|
|
2502
|
+
background: var(--fnc-bg);
|
|
2503
|
+
box-shadow: 0 2px 12px rgb(0 0 0 / 0.12), 0 0 0 1px var(--fnc-border);
|
|
2504
|
+
opacity: 0;
|
|
2505
|
+
transition: opacity 0.15s;
|
|
2506
|
+
}
|
|
2507
|
+
.fnc-body:hover .fnc-nav, .fnc-nav:focus-within { opacity: 1; }
|
|
2508
|
+
.fnc-nav .fnc-btn { width: 32px; height: 32px; }
|
|
2509
|
+
.fnc-realtime {
|
|
2510
|
+
position: absolute;
|
|
2511
|
+
display: flex;
|
|
2512
|
+
align-items: center;
|
|
2513
|
+
justify-content: center;
|
|
2514
|
+
width: 32px;
|
|
2515
|
+
height: 32px;
|
|
2516
|
+
border-radius: 50%;
|
|
2517
|
+
background: var(--fnc-bg);
|
|
2518
|
+
color: var(--fnc-text);
|
|
2519
|
+
box-shadow: 0 2px 12px rgb(0 0 0 / 0.12), 0 0 0 1px var(--fnc-border);
|
|
2520
|
+
}
|
|
2521
|
+
@media (hover: none) {
|
|
2522
|
+
.fnc-nav { display: none; }
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
/* \u2500\u2500 Symbol search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2526
|
+
.fnc-backdrop {
|
|
2527
|
+
position: absolute;
|
|
2528
|
+
inset: 0;
|
|
2529
|
+
z-index: 10;
|
|
2530
|
+
display: flex;
|
|
2531
|
+
justify-content: center;
|
|
2532
|
+
align-items: center;
|
|
2533
|
+
padding: 24px 16px;
|
|
2534
|
+
background: rgb(0 0 0 / 0.32);
|
|
2535
|
+
}
|
|
2536
|
+
.fnc-dialog {
|
|
2537
|
+
display: flex;
|
|
2538
|
+
flex-direction: column;
|
|
2539
|
+
width: min(480px, calc(100% - 32px));
|
|
2540
|
+
max-height: min(560px, 100%);
|
|
2541
|
+
border-radius: 16px;
|
|
2542
|
+
background: var(--fnc-bg);
|
|
2543
|
+
box-shadow: 0 12px 40px rgb(0 0 0 / 0.2);
|
|
2544
|
+
overflow: hidden;
|
|
2545
|
+
}
|
|
2546
|
+
.fnc-search {
|
|
2547
|
+
display: flex;
|
|
2548
|
+
align-items: center;
|
|
2549
|
+
gap: 8px;
|
|
2550
|
+
height: 56px;
|
|
2551
|
+
padding: 0 8px 0 16px;
|
|
2552
|
+
border-bottom: 1px solid var(--fnc-border);
|
|
2553
|
+
color: var(--fnc-text-2);
|
|
2554
|
+
}
|
|
2555
|
+
.fnc-search input {
|
|
2556
|
+
flex: 1;
|
|
2557
|
+
min-width: 0;
|
|
2558
|
+
height: 100%;
|
|
2559
|
+
border: 0;
|
|
2560
|
+
outline: 0;
|
|
2561
|
+
background: transparent;
|
|
2562
|
+
color: var(--fnc-text);
|
|
2563
|
+
font: inherit;
|
|
2564
|
+
font-size: 16px;
|
|
2565
|
+
}
|
|
2566
|
+
.fnc-search input::placeholder { color: var(--fnc-text-2); }
|
|
2567
|
+
.fnc-dialog-wide { width: min(600px, calc(100% - 32px)); }
|
|
2568
|
+
.fnc-dialog form { overflow-y: auto; }
|
|
2569
|
+
.fnc-section { font-size: 12px; font-weight: 700; color: var(--fnc-text-2); margin-top: 2px; }
|
|
2570
|
+
.fnc-rows { display: flex; flex-direction: column; gap: 8px; }
|
|
2571
|
+
.fnc-row { display: flex; align-items: center; gap: 8px; min-height: 40px; }
|
|
2572
|
+
.fnc-row-label { width: 44px; flex: none; font-size: 13px; color: var(--fnc-text-2); white-space: nowrap; }
|
|
2573
|
+
.fnc-row-label-wide { width: 88px; }
|
|
2574
|
+
.fnc-check { width: 18px; height: 18px; flex: none; accent-color: var(--fnc-accent); margin: 0; cursor: pointer; }
|
|
2575
|
+
.fnc-color {
|
|
2576
|
+
width: 32px;
|
|
2577
|
+
height: 32px;
|
|
2578
|
+
flex: none;
|
|
2579
|
+
padding: 0;
|
|
2580
|
+
border: 1px solid var(--fnc-border);
|
|
2581
|
+
border-radius: var(--fnc-radius);
|
|
2582
|
+
background: none;
|
|
2583
|
+
cursor: pointer;
|
|
2584
|
+
}
|
|
2585
|
+
.fnc-color::-webkit-color-swatch-wrapper { padding: 3px; }
|
|
2586
|
+
.fnc-color::-webkit-color-swatch { border: 0; border-radius: 4px; }
|
|
2587
|
+
.fnc-select, .fnc-input {
|
|
2588
|
+
height: 40px;
|
|
2589
|
+
min-width: 0;
|
|
2590
|
+
padding: 0 10px;
|
|
2591
|
+
border: 1px solid var(--fnc-border);
|
|
2592
|
+
border-radius: var(--fnc-radius);
|
|
2593
|
+
background: var(--fnc-bg);
|
|
2594
|
+
color: var(--fnc-text);
|
|
2595
|
+
font: inherit;
|
|
2596
|
+
font-size: 14px;
|
|
2597
|
+
outline: none;
|
|
2598
|
+
}
|
|
2599
|
+
.fnc-select:focus, .fnc-input:focus { border-color: var(--fnc-accent); }
|
|
2600
|
+
.fnc-input { flex: 1; font-size: 16px; }
|
|
2601
|
+
.fnc-input-period { flex: 0 0 72px; text-align: right; }
|
|
2602
|
+
.fnc-select-width { flex: 0 0 72px; }
|
|
2603
|
+
.fnc-select-source { flex: 1; }
|
|
2604
|
+
.fnc-toggle-row { justify-content: space-between; min-height: 36px; cursor: pointer; font-size: 14px; }
|
|
2605
|
+
.fnc-switch {
|
|
2606
|
+
appearance: none;
|
|
2607
|
+
-webkit-appearance: none;
|
|
2608
|
+
position: relative;
|
|
2609
|
+
width: 40px;
|
|
2610
|
+
height: 24px;
|
|
2611
|
+
flex: none;
|
|
2612
|
+
margin: 0;
|
|
2613
|
+
border-radius: 12px;
|
|
2614
|
+
background: var(--fnc-border);
|
|
2615
|
+
cursor: pointer;
|
|
2616
|
+
transition: background 0.15s;
|
|
2617
|
+
}
|
|
2618
|
+
.fnc-switch::after {
|
|
2619
|
+
content: '';
|
|
2620
|
+
position: absolute;
|
|
2621
|
+
top: 3px;
|
|
2622
|
+
left: 3px;
|
|
2623
|
+
width: 18px;
|
|
2624
|
+
height: 18px;
|
|
2625
|
+
border-radius: 50%;
|
|
2626
|
+
background: #fff;
|
|
2627
|
+
box-shadow: 0 1px 3px rgb(0 0 0 / 0.2);
|
|
2628
|
+
transition: transform 0.15s;
|
|
2629
|
+
}
|
|
2630
|
+
.fnc-switch:checked { background: var(--fnc-accent); }
|
|
2631
|
+
.fnc-switch:checked::after { transform: translateX(16px); }
|
|
2632
|
+
.fnc-switch:focus-visible { outline: 2px solid var(--fnc-accent); outline-offset: 2px; }
|
|
2633
|
+
.fnc-add-row {
|
|
2634
|
+
height: 40px;
|
|
2635
|
+
border: 1px dashed var(--fnc-border);
|
|
2636
|
+
border-radius: var(--fnc-radius);
|
|
2637
|
+
color: var(--fnc-text-2);
|
|
2638
|
+
font-size: 14px;
|
|
2639
|
+
font-weight: 600;
|
|
2640
|
+
}
|
|
2641
|
+
.fnc-add-row:hover { color: var(--fnc-text); border-color: var(--fnc-text-2); }
|
|
2642
|
+
.fnc-empty { padding: 32px 16px; text-align: center; color: var(--fnc-text-2); }
|
|
2643
|
+
.fnc-dialog-head {
|
|
2644
|
+
display: flex;
|
|
2645
|
+
align-items: center;
|
|
2646
|
+
justify-content: space-between;
|
|
2647
|
+
height: 56px;
|
|
2648
|
+
padding: 0 8px 0 20px;
|
|
2649
|
+
border-bottom: 1px solid var(--fnc-border);
|
|
2650
|
+
font-size: 16px;
|
|
2651
|
+
}
|
|
2652
|
+
.fnc-group { padding: 12px 12px 4px; font-size: 12px; font-weight: 600; color: var(--fnc-text-2); }
|
|
2653
|
+
.fnc-form { display: flex; flex-direction: column; gap: 14px; padding: 20px; }
|
|
2654
|
+
.fnc-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--fnc-text-2); }
|
|
2655
|
+
.fnc-field input {
|
|
2656
|
+
height: 44px;
|
|
2657
|
+
padding: 0 14px;
|
|
2658
|
+
border: 1px solid var(--fnc-border);
|
|
2659
|
+
border-radius: var(--fnc-radius);
|
|
2660
|
+
background: var(--fnc-bg);
|
|
2661
|
+
color: var(--fnc-text);
|
|
2662
|
+
font: inherit;
|
|
2663
|
+
font-size: 16px;
|
|
2664
|
+
outline: none;
|
|
2665
|
+
}
|
|
2666
|
+
.fnc-field input:focus { border-color: var(--fnc-accent); }
|
|
2667
|
+
/* Actions stay pinned to the bottom of long, scrolling dialogs (indicator / chart settings). */
|
|
2668
|
+
.fnc-form-actions {
|
|
2669
|
+
position: sticky;
|
|
2670
|
+
bottom: -20px;
|
|
2671
|
+
display: flex;
|
|
2672
|
+
gap: 8px;
|
|
2673
|
+
margin: 6px -20px -20px;
|
|
2674
|
+
padding: 12px 20px 20px;
|
|
2675
|
+
border-top: 1px solid var(--fnc-border);
|
|
2676
|
+
background: var(--fnc-bg);
|
|
2677
|
+
}
|
|
2678
|
+
.fnc-action {
|
|
2679
|
+
flex: 1;
|
|
2680
|
+
height: 48px;
|
|
2681
|
+
border-radius: var(--fnc-radius);
|
|
2682
|
+
background: var(--fnc-surface);
|
|
2683
|
+
font-size: 15px;
|
|
2684
|
+
font-weight: 600;
|
|
2685
|
+
}
|
|
2686
|
+
.fnc-action-primary { background: var(--fnc-accent); color: #fff; }
|
|
2687
|
+
.fnc-action-danger { flex: 0 0 auto; padding: 0 16px; color: var(--fnc-danger, #F04452); background: none; }
|
|
2688
|
+
|
|
2689
|
+
/* \u2500\u2500 Mobile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2690
|
+
@media (max-width: 640px) {
|
|
2691
|
+
.fnc-toolbar { padding: 0 4px; }
|
|
2692
|
+
.fnc-symbol { max-width: 140px; padding: 0 8px 0 6px; }
|
|
2693
|
+
.fnc-chip { padding: 0 8px; }
|
|
2694
|
+
.fnc-btn-screenshot { display: none; }
|
|
2695
|
+
.fnc-btn-label span { display: none; }
|
|
2696
|
+
.fnc-intervals { display: none; }
|
|
2697
|
+
.fnc-interval-current { display: inline; }
|
|
2698
|
+
.fnc-interval-menu { left: auto; right: -40px; }
|
|
2699
|
+
.fnc-btn-drawbar { display: flex; }
|
|
2700
|
+
.fnc-drawbar-tab, .fnc-drawbar-collapse { display: none; }
|
|
2701
|
+
.fnc-drawbar[data-collapsed='true'] { display: flex; }
|
|
2702
|
+
/* The drawing bar slides over the chart instead of taking width. */
|
|
2703
|
+
.fnc-drawbar-host { position: absolute; left: 0; top: 0; bottom: 0; z-index: 5; pointer-events: none; }
|
|
2704
|
+
.fnc-drawbar {
|
|
2705
|
+
pointer-events: auto;
|
|
2706
|
+
transform: translateX(-100%);
|
|
2707
|
+
transition: transform 0.18s ease;
|
|
2708
|
+
box-shadow: 4px 0 16px rgb(0 0 0 / 0.08);
|
|
2709
|
+
}
|
|
2710
|
+
.fnc-drawbar[data-open='true'] { transform: none; }
|
|
2711
|
+
.fnc-selbar { top: auto; bottom: 56px; }
|
|
2712
|
+
.fnc-legend { left: 10px; right: 64px; }
|
|
2713
|
+
.fnc-backdrop { padding: 0; align-items: stretch; }
|
|
2714
|
+
.fnc-dialog { width: 100%; max-height: none; border-radius: 0; }
|
|
2715
|
+
.fnc-dialog.fnc-search-dialog { width: 100%; height: 100%; }
|
|
2716
|
+
.fnc-sr-footer, .fnc-sr-side, .fnc-hide-mobile, .fnc-sr-spark { display: none; }
|
|
2717
|
+
/* The interval button must stay whole; the symbol name truncates instead. */
|
|
2718
|
+
.fnc-symbol { flex: 0 1 auto; min-width: 0; }
|
|
2719
|
+
/* MA rows wrap to two lines: [label \u2713 color width \xD7] / [source period] */
|
|
2720
|
+
.fnc-rows .fnc-row { flex-wrap: wrap; row-gap: 8px; padding-bottom: 10px; border-bottom: 1px solid var(--fnc-border); }
|
|
2721
|
+
/* Basis = row \u2212 indent(52) \u2212 gap(8) \u2212 period(72): too wide for line 1, so it always wraps. */
|
|
2722
|
+
.fnc-rows .fnc-select-source { order: 10; flex: 0 0 calc(100% - 132px); margin-left: 52px; }
|
|
2723
|
+
.fnc-rows .fnc-input-period { order: 11; }
|
|
2724
|
+
.fnc-rows .fnc-ind-btn { margin-left: auto; }
|
|
2725
|
+
}
|
|
2726
|
+
`
|
|
2727
|
+
);
|
|
2728
|
+
var STYLE_ID = "fnc-styles";
|
|
2729
|
+
function injectStyles(doc = document) {
|
|
2730
|
+
if (doc.getElementById(STYLE_ID)) return;
|
|
2731
|
+
const style = doc.createElement("style");
|
|
2732
|
+
style.id = STYLE_ID;
|
|
2733
|
+
style.textContent = CSS;
|
|
2734
|
+
doc.head.appendChild(style);
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
// src/trading-chart.tsx
|
|
2738
|
+
var DEFAULT_INTERVAL_GROUPS = [
|
|
2739
|
+
{ id: "minutes", intervals: ["1", "5", "15", "30"] },
|
|
2740
|
+
{ id: "hours", intervals: ["60", "240"] },
|
|
2741
|
+
{ id: "days", intervals: ["1D", "1W", "1M", "3M", "6M", "12M"] }
|
|
2742
|
+
];
|
|
2743
|
+
var DEFAULT_FAVORITE_INTERVALS = ["1", "5", "15", "60", "240", "1D"];
|
|
2744
|
+
var stockLike = (i3) => i3.type === "stock" || i3.type === "etf" || i3.type === "index";
|
|
2745
|
+
var DEFAULT_SEARCH_CATEGORIES = [
|
|
2746
|
+
{ id: "all", label: { ko: "\uC804\uCCB4", en: "All" }, match: () => true },
|
|
2747
|
+
{ id: "kr-stock", label: { ko: "\uAD6D\uB0B4\uC8FC\uC2DD", en: "KR stocks" }, match: (i3) => stockLike(i3) && i3.country === "KR" },
|
|
2748
|
+
{ id: "global-stock", label: { ko: "\uD574\uC678\uC8FC\uC2DD", en: "Global stocks" }, match: (i3) => stockLike(i3) && i3.country !== "KR" },
|
|
2749
|
+
{ id: "crypto", label: { ko: "\uCF54\uC778", en: "Crypto" }, match: (i3) => i3.type === "crypto" },
|
|
2750
|
+
{ id: "futures", label: { ko: "\uC120\uBB3C", en: "Futures" }, match: (i3) => i3.type === "futures" }
|
|
2751
|
+
];
|
|
2752
|
+
var THEME_VARS = {
|
|
2753
|
+
background: "--fnc-bg",
|
|
2754
|
+
surface: "--fnc-surface",
|
|
2755
|
+
text: "--fnc-text",
|
|
2756
|
+
textSecondary: "--fnc-text-2",
|
|
2757
|
+
border: "--fnc-border",
|
|
2758
|
+
up: "--fnc-up",
|
|
2759
|
+
down: "--fnc-down",
|
|
2760
|
+
accent: "--fnc-accent",
|
|
2761
|
+
fontFamily: "--fnc-font"
|
|
2762
|
+
};
|
|
2763
|
+
function applyThemeVars(el, theme) {
|
|
2764
|
+
for (const [key, cssVar] of Object.entries(THEME_VARS)) {
|
|
2765
|
+
el.style.setProperty(cssVar, String(theme[key]));
|
|
2766
|
+
}
|
|
2767
|
+
el.style.setProperty("--fnc-radius", `${theme.radius}px`);
|
|
2768
|
+
}
|
|
2769
|
+
function div(className, parent) {
|
|
2770
|
+
const el = document.createElement("div");
|
|
2771
|
+
el.className = className;
|
|
2772
|
+
parent?.appendChild(el);
|
|
2773
|
+
return el;
|
|
2774
|
+
}
|
|
2775
|
+
function createTradingChart(container, options) {
|
|
2776
|
+
const {
|
|
2777
|
+
datafeed,
|
|
2778
|
+
symbol,
|
|
2779
|
+
resolution = "1D",
|
|
2780
|
+
intervalGroups = DEFAULT_INTERVAL_GROUPS,
|
|
2781
|
+
favoriteIntervals,
|
|
2782
|
+
onFavoriteIntervalsChange,
|
|
2783
|
+
searchCategories = DEFAULT_SEARCH_CATEGORIES,
|
|
2784
|
+
storageKey = "funnest-charts",
|
|
2785
|
+
searchHotkey = true,
|
|
2786
|
+
symbolLogo = true,
|
|
2787
|
+
drawingToolbar = true,
|
|
2788
|
+
chartType = "candles",
|
|
2789
|
+
attribution = true,
|
|
2790
|
+
colorScheme = "system",
|
|
2791
|
+
...chartOptions
|
|
2792
|
+
} = options;
|
|
2793
|
+
const locale = chartOptions.locale ?? "ko";
|
|
2794
|
+
const displayTimezone = chartOptions.displayTimezone ?? (locale === "ko" ? "Asia/Seoul" : Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
2795
|
+
const storage = createStorage(storageKey);
|
|
2796
|
+
injectStyles(container.ownerDocument);
|
|
2797
|
+
const win = container.ownerDocument.defaultView;
|
|
2798
|
+
const darkQuery = win?.matchMedia?.("(prefers-color-scheme: dark)") ?? null;
|
|
2799
|
+
let scheme = colorScheme;
|
|
2800
|
+
let themePatch = { ...chartOptions.theme };
|
|
2801
|
+
const isDark = () => scheme === "system" ? !!darkQuery?.matches : scheme === "dark";
|
|
2802
|
+
const resolveTheme = () => ({ ...isDark() ? darkTheme : lightTheme, ...themePatch });
|
|
2803
|
+
let theme = resolveTheme();
|
|
2804
|
+
const root = div("fnc-root", container);
|
|
2805
|
+
const applyScheme = () => {
|
|
2806
|
+
root.dataset.colorScheme = isDark() ? "dark" : "light";
|
|
2807
|
+
root.style.colorScheme = isDark() ? "dark" : "light";
|
|
2808
|
+
applyThemeVars(root, theme);
|
|
2809
|
+
};
|
|
2810
|
+
applyScheme();
|
|
2811
|
+
const toolbarHost = div("fnc-toolbar-host", root);
|
|
2812
|
+
const slots = {
|
|
2813
|
+
toolbar: div("fnc-slot fnc-slot-toolbar"),
|
|
2814
|
+
toolbarEnd: div("fnc-slot fnc-slot-toolbar-end"),
|
|
2815
|
+
belowToolbar: div("fnc-slot fnc-slot-below-toolbar", root),
|
|
2816
|
+
footer: div("fnc-slot fnc-slot-footer")
|
|
2817
|
+
};
|
|
2818
|
+
const body = div("fnc-body", root);
|
|
2819
|
+
const drawbarHost = drawingToolbar ? div("fnc-drawbar-host", body) : null;
|
|
2820
|
+
const stage = div("fnc-stage", body);
|
|
2821
|
+
const chartHost = div("fnc-chart-host", stage);
|
|
2822
|
+
const overlayHost = div("fnc-overlay", stage);
|
|
2823
|
+
root.appendChild(slots.footer);
|
|
2824
|
+
const chart = createChart(chartHost, { ...chartOptions, theme, datafeed, displayTimezone });
|
|
2825
|
+
const refreshScheme = () => {
|
|
2826
|
+
theme = resolveTheme();
|
|
2827
|
+
applyScheme();
|
|
2828
|
+
chart.setTheme(theme);
|
|
2829
|
+
};
|
|
2830
|
+
const onSchemeChange = () => scheme === "system" && refreshScheme();
|
|
2831
|
+
darkQuery?.addEventListener?.("change", onSchemeChange);
|
|
2832
|
+
chart.setChartType(chartType);
|
|
2833
|
+
const store = new Store({
|
|
2834
|
+
locale,
|
|
2835
|
+
symbol: null,
|
|
2836
|
+
resolution,
|
|
2837
|
+
loading: false,
|
|
2838
|
+
layout: chart.layout,
|
|
2839
|
+
atRealtime: true,
|
|
2840
|
+
fullscreen: false,
|
|
2841
|
+
searchOpen: false,
|
|
2842
|
+
chartType,
|
|
2843
|
+
drawingTool: null,
|
|
2844
|
+
selection: null,
|
|
2845
|
+
drawingCount: 0,
|
|
2846
|
+
drawbarOpen: false,
|
|
2847
|
+
magnet: chart.magnetEnabled,
|
|
2848
|
+
indicators: chart.getIndicators(),
|
|
2849
|
+
dialog: null,
|
|
2850
|
+
favoriteIntervals: storage.get("favorite-intervals", [...favoriteIntervals ?? DEFAULT_FAVORITE_INTERVALS]),
|
|
2851
|
+
drawbarCollapsed: storage.get("drawbar-collapsed", false)
|
|
2852
|
+
});
|
|
2853
|
+
const syncRealtime = () => store.set({ atRealtime: chart.isAtRealtime() });
|
|
2854
|
+
const offs = [
|
|
2855
|
+
chart.on("symbolChange", (info) => store.set({ symbol: info, resolution: chart.currentResolution ?? resolution })),
|
|
2856
|
+
chart.on("loadingChange", (loading) => store.set({ loading })),
|
|
2857
|
+
chart.on("layoutChange", (layout) => store.set({ layout })),
|
|
2858
|
+
chart.on("visibleRangeChange", syncRealtime),
|
|
2859
|
+
chart.on("dataChange", syncRealtime),
|
|
2860
|
+
chart.on("drawingToolChange", (drawingTool) => store.set({ drawingTool })),
|
|
2861
|
+
chart.on("selectionChange", (selection) => store.set({ selection })),
|
|
2862
|
+
chart.on("drawingsChange", (drawings) => store.set({ drawingCount: drawings.length })),
|
|
2863
|
+
chart.on("indicatorsChange", (indicators) => store.set({ indicators })),
|
|
2864
|
+
chart.on("magnetChange", (magnet) => store.set({ magnet })),
|
|
2865
|
+
chart.on("chartTypeChange", (chartType2) => store.set({ chartType: chartType2 }))
|
|
2866
|
+
];
|
|
2867
|
+
const load = (sym, res) => {
|
|
2868
|
+
store.set({ resolution: res });
|
|
2869
|
+
return chart.setSymbol(sym, res).catch((error) => {
|
|
2870
|
+
console.error("funnest-charts: failed to load", sym, res, error);
|
|
2871
|
+
store.set({ loading: false });
|
|
2872
|
+
});
|
|
2873
|
+
};
|
|
2874
|
+
const isNativeFullscreen = () => document.fullscreenElement === root;
|
|
2875
|
+
const onFullscreenChange = () => store.set({ fullscreen: isNativeFullscreen() || root.classList.contains("fnc-pseudo-fullscreen") });
|
|
2876
|
+
const onKeyDown = (e3) => {
|
|
2877
|
+
const s3 = store.get();
|
|
2878
|
+
if (searchHotkey && (e3.ctrlKey || e3.metaKey) && e3.key.toLowerCase() === "k" && datafeed.searchSymbols && !s3.dialog) {
|
|
2879
|
+
e3.preventDefault();
|
|
2880
|
+
store.set({ searchOpen: true });
|
|
2881
|
+
return;
|
|
2882
|
+
}
|
|
2883
|
+
if (e3.key === "Escape" && !e3.defaultPrevented && (s3.searchOpen || s3.dialog)) {
|
|
2884
|
+
e3.preventDefault();
|
|
2885
|
+
store.set(s3.searchOpen ? { searchOpen: false } : { dialog: null });
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
if (e3.key === "Escape" && root.classList.contains("fnc-pseudo-fullscreen") && !s3.searchOpen && !s3.dialog) {
|
|
2889
|
+
root.classList.remove("fnc-pseudo-fullscreen");
|
|
2890
|
+
onFullscreenChange();
|
|
2891
|
+
}
|
|
2892
|
+
};
|
|
2893
|
+
const onKeyUp = (e3) => {
|
|
2894
|
+
const s3 = store.get();
|
|
2895
|
+
if (e3.key === "Escape" && (s3.searchOpen || s3.dialog)) store.set(s3.searchOpen ? { searchOpen: false } : { dialog: null });
|
|
2896
|
+
};
|
|
2897
|
+
document.addEventListener("fullscreenchange", onFullscreenChange);
|
|
2898
|
+
document.addEventListener("keydown", onKeyDown);
|
|
2899
|
+
document.addEventListener("keyup", onKeyUp);
|
|
2900
|
+
const ctx = {
|
|
2901
|
+
root,
|
|
2902
|
+
chart,
|
|
2903
|
+
datafeed,
|
|
2904
|
+
store,
|
|
2905
|
+
storage,
|
|
2906
|
+
intervalGroups,
|
|
2907
|
+
searchCategories,
|
|
2908
|
+
displayTimezone,
|
|
2909
|
+
showLogo: symbolLogo,
|
|
2910
|
+
drawingToolbar,
|
|
2911
|
+
attribution,
|
|
2912
|
+
slots,
|
|
2913
|
+
setDrawbarCollapsed: (collapsed) => {
|
|
2914
|
+
store.set({ drawbarCollapsed: collapsed });
|
|
2915
|
+
storage.set("drawbar-collapsed", collapsed);
|
|
2916
|
+
},
|
|
2917
|
+
selectSymbol: (sym) => void load(sym, store.get().resolution),
|
|
2918
|
+
selectResolution: (res) => {
|
|
2919
|
+
const current = store.get().symbol;
|
|
2920
|
+
if (current) void load(current.symbol, res);
|
|
2921
|
+
},
|
|
2922
|
+
toggleFavoriteInterval: (res) => {
|
|
2923
|
+
const current = store.get().favoriteIntervals;
|
|
2924
|
+
const next = current.includes(res) ? current.filter((r3) => r3 !== res) : [...current, res];
|
|
2925
|
+
store.set({ favoriteIntervals: next });
|
|
2926
|
+
storage.set("favorite-intervals", next);
|
|
2927
|
+
onFavoriteIntervalsChange?.(next);
|
|
2928
|
+
},
|
|
2929
|
+
setSearchOpen: (open) => store.set({ searchOpen: open }),
|
|
2930
|
+
screenshot: () => {
|
|
2931
|
+
const s3 = store.get();
|
|
2932
|
+
chart.takeScreenshot().toBlob((blob) => {
|
|
2933
|
+
if (!blob) return;
|
|
2934
|
+
const a4 = document.createElement("a");
|
|
2935
|
+
a4.href = URL.createObjectURL(blob);
|
|
2936
|
+
a4.download = `${s3.symbol?.symbol ?? "chart"}_${s3.resolution}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.png`;
|
|
2937
|
+
a4.click();
|
|
2938
|
+
setTimeout(() => URL.revokeObjectURL(a4.href), 1e3);
|
|
2939
|
+
});
|
|
2940
|
+
},
|
|
2941
|
+
toggleFullscreen: () => {
|
|
2942
|
+
if (isNativeFullscreen()) {
|
|
2943
|
+
void document.exitFullscreen();
|
|
2944
|
+
} else if (root.classList.contains("fnc-pseudo-fullscreen")) {
|
|
2945
|
+
root.classList.remove("fnc-pseudo-fullscreen");
|
|
2946
|
+
onFullscreenChange();
|
|
2947
|
+
} else if (root.requestFullscreen && document.fullscreenEnabled) {
|
|
2948
|
+
root.requestFullscreen().catch(() => {
|
|
2949
|
+
root.classList.add("fnc-pseudo-fullscreen");
|
|
2950
|
+
onFullscreenChange();
|
|
2951
|
+
});
|
|
2952
|
+
} else {
|
|
2953
|
+
root.classList.add("fnc-pseudo-fullscreen");
|
|
2954
|
+
onFullscreenChange();
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
};
|
|
2958
|
+
Q(/* @__PURE__ */ a2(Toolbar, { ctx }), toolbarHost);
|
|
2959
|
+
Q(/* @__PURE__ */ a2(Overlay, { ctx }), overlayHost);
|
|
2960
|
+
if (drawbarHost) Q(/* @__PURE__ */ a2(DrawingToolbar, { ctx }), drawbarHost);
|
|
2961
|
+
void load(symbol, resolution);
|
|
2962
|
+
return {
|
|
2963
|
+
chart,
|
|
2964
|
+
slots,
|
|
2965
|
+
setSymbol: (sym, res) => load(sym, res ?? store.get().resolution),
|
|
2966
|
+
setResolution: (res) => {
|
|
2967
|
+
const current = store.get().symbol;
|
|
2968
|
+
return current ? load(current.symbol, res) : Promise.resolve();
|
|
2969
|
+
},
|
|
2970
|
+
setLocale: (locale2) => {
|
|
2971
|
+
chart.setLocale(locale2);
|
|
2972
|
+
store.set({ locale: locale2 });
|
|
2973
|
+
},
|
|
2974
|
+
save: () => chart.save(),
|
|
2975
|
+
load: (state) => chart.load(state),
|
|
2976
|
+
setTheme: (patch) => {
|
|
2977
|
+
themePatch = { ...themePatch, ...patch };
|
|
2978
|
+
refreshScheme();
|
|
2979
|
+
},
|
|
2980
|
+
setColorScheme: (next) => {
|
|
2981
|
+
scheme = next;
|
|
2982
|
+
refreshScheme();
|
|
2983
|
+
},
|
|
2984
|
+
destroy: () => {
|
|
2985
|
+
offs.forEach((off) => off());
|
|
2986
|
+
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
|
2987
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
2988
|
+
document.removeEventListener("keyup", onKeyUp);
|
|
2989
|
+
darkQuery?.removeEventListener?.("change", onSchemeChange);
|
|
2990
|
+
if (isNativeFullscreen()) void document.exitFullscreen();
|
|
2991
|
+
Q(null, toolbarHost);
|
|
2992
|
+
Q(null, overlayHost);
|
|
2993
|
+
if (drawbarHost) Q(null, drawbarHost);
|
|
2994
|
+
chart.destroy();
|
|
2995
|
+
root.remove();
|
|
2996
|
+
}
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
export {
|
|
3000
|
+
DEFAULT_FAVORITE_INTERVALS,
|
|
3001
|
+
DEFAULT_INTERVAL_GROUPS,
|
|
3002
|
+
DEFAULT_SEARCH_CATEGORIES,
|
|
3003
|
+
createTradingChart,
|
|
3004
|
+
resolutionLabel
|
|
3005
|
+
};
|