@funnest/charts-ui 0.6.8 → 0.6.11
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/dist/components/drawing-toolbar.d.ts +6 -2
- package/dist/context.d.ts +2 -0
- package/dist/i18n.d.ts +24 -2
- package/dist/icons.d.ts +7 -0
- package/dist/index.js +254 -33
- package/dist/pane.d.ts +1 -0
- package/dist/trading-chart.d.ts +5 -0
- package/package.json +2 -2
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type JSX } from 'preact';
|
|
2
2
|
import type { WidgetContext } from '../context';
|
|
3
3
|
/** Left vertical tool bar (TradingView layout). On narrow screens it slides in over the chart. */
|
|
4
4
|
export declare function DrawingToolbar({ ctx }: {
|
|
5
5
|
ctx: WidgetContext;
|
|
6
6
|
}): JSX.Element;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Floating bar for the selected drawing: move it by its grip (remembered; double-click resets),
|
|
9
|
+
* color (swatches, a custom one, recent customs), line width and style, text for text tools,
|
|
10
|
+
* lock, duplicate, delete (also the only way to delete on touch).
|
|
11
|
+
*/
|
|
8
12
|
export declare function SelectionBar({ ctx }: {
|
|
9
13
|
ctx: WidgetContext;
|
|
10
14
|
}): JSX.Element | null;
|
package/dist/context.d.ts
CHANGED
|
@@ -79,6 +79,8 @@ export interface WidgetContext {
|
|
|
79
79
|
/** Show the Funnest logo badge on the chart. */
|
|
80
80
|
attribution: boolean;
|
|
81
81
|
contextMenuItems: ContextMenuItems | undefined;
|
|
82
|
+
/** The app's own indicator browser for the 지표 button (instead of the built-in picker). */
|
|
83
|
+
onIndicatorsClick: ((chart: Chart) => void) | undefined;
|
|
82
84
|
slots: {
|
|
83
85
|
toolbar: HTMLElement;
|
|
84
86
|
toolbarEnd: HTMLElement;
|
package/dist/i18n.d.ts
CHANGED
|
@@ -75,6 +75,18 @@ export declare const STRINGS: {
|
|
|
75
75
|
magnet: string;
|
|
76
76
|
remove: string;
|
|
77
77
|
color: string;
|
|
78
|
+
customColor: string;
|
|
79
|
+
lineWidth: string;
|
|
80
|
+
lineStyle: string;
|
|
81
|
+
lineSolid: string;
|
|
82
|
+
lineDashed: string;
|
|
83
|
+
lineDotted: string;
|
|
84
|
+
drawingText: string;
|
|
85
|
+
textPlaceholder: string;
|
|
86
|
+
lock: string;
|
|
87
|
+
unlock: string;
|
|
88
|
+
duplicate: string;
|
|
89
|
+
moveBar: string;
|
|
78
90
|
indicators: string;
|
|
79
91
|
addIndicator: string;
|
|
80
92
|
indicatorSettings: string;
|
|
@@ -91,7 +103,6 @@ export declare const STRINGS: {
|
|
|
91
103
|
removePeriod: string;
|
|
92
104
|
period: string;
|
|
93
105
|
source: string;
|
|
94
|
-
lineWidth: string;
|
|
95
106
|
inputs: string;
|
|
96
107
|
style: string;
|
|
97
108
|
intervalMinutes: string;
|
|
@@ -206,6 +217,18 @@ export declare const STRINGS: {
|
|
|
206
217
|
magnet: string;
|
|
207
218
|
remove: string;
|
|
208
219
|
color: string;
|
|
220
|
+
customColor: string;
|
|
221
|
+
lineWidth: string;
|
|
222
|
+
lineStyle: string;
|
|
223
|
+
lineSolid: string;
|
|
224
|
+
lineDashed: string;
|
|
225
|
+
lineDotted: string;
|
|
226
|
+
drawingText: string;
|
|
227
|
+
textPlaceholder: string;
|
|
228
|
+
lock: string;
|
|
229
|
+
unlock: string;
|
|
230
|
+
duplicate: string;
|
|
231
|
+
moveBar: string;
|
|
209
232
|
indicators: string;
|
|
210
233
|
addIndicator: string;
|
|
211
234
|
indicatorSettings: string;
|
|
@@ -222,7 +245,6 @@ export declare const STRINGS: {
|
|
|
222
245
|
removePeriod: string;
|
|
223
246
|
period: string;
|
|
224
247
|
source: string;
|
|
225
|
-
lineWidth: string;
|
|
226
248
|
inputs: string;
|
|
227
249
|
style: string;
|
|
228
250
|
intervalMinutes: string;
|
package/dist/icons.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare const ChevronDownIcon: () => import("preact").JSX.Element;
|
|
|
13
13
|
export declare const CursorIcon: () => import("preact").JSX.Element;
|
|
14
14
|
export declare const TrendLineIcon: () => import("preact").JSX.Element;
|
|
15
15
|
export declare const HorizontalLineIcon: () => import("preact").JSX.Element;
|
|
16
|
+
/** Retracement levels at their real (uneven) spacing, with the swing they're drawn on. */
|
|
16
17
|
export declare const FibonacciIcon: () => import("preact").JSX.Element;
|
|
17
18
|
export declare const TrashIcon: () => import("preact").JSX.Element;
|
|
18
19
|
export declare const PencilIcon: () => import("preact").JSX.Element;
|
|
@@ -54,3 +55,9 @@ export declare const SvgIcon: ({ markup }: {
|
|
|
54
55
|
export declare const CornerIcon: () => import("preact").JSX.Element;
|
|
55
56
|
/** Sliders: the 도구 menu. */
|
|
56
57
|
export declare const ToolsIcon: () => import("preact").JSX.Element;
|
|
58
|
+
export declare const GripIcon: () => import("preact").JSX.Element;
|
|
59
|
+
export declare const LockIcon: ({ open }: {
|
|
60
|
+
open?: boolean;
|
|
61
|
+
}) => import("preact").JSX.Element;
|
|
62
|
+
export declare const CopyIcon: () => import("preact").JSX.Element;
|
|
63
|
+
export declare const TextIcon: () => import("preact").JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -498,6 +498,18 @@ var STRINGS = {
|
|
|
498
498
|
magnet: "\uC790\uC11D \uBAA8\uB4DC (\uC2DC\xB7\uACE0\xB7\uC800\xB7\uC885\uAC00\uC5D0 \uBD99\uC774\uAE30)",
|
|
499
499
|
remove: "\uC0AD\uC81C",
|
|
500
500
|
color: "\uC0C9\uC0C1",
|
|
501
|
+
customColor: "\uB2E4\uB978 \uC0C9",
|
|
502
|
+
lineWidth: "\uC120 \uAD75\uAE30",
|
|
503
|
+
lineStyle: "\uC120 \uBAA8\uC591",
|
|
504
|
+
lineSolid: "\uC2E4\uC120",
|
|
505
|
+
lineDashed: "\uD30C\uC120",
|
|
506
|
+
lineDotted: "\uC810\uC120",
|
|
507
|
+
drawingText: "\uD14D\uC2A4\uD2B8",
|
|
508
|
+
textPlaceholder: "\uD14D\uC2A4\uD2B8 \uC785\uB825",
|
|
509
|
+
lock: "\uC7A0\uADF8\uAE30",
|
|
510
|
+
unlock: "\uC7A0\uAE08 \uD480\uAE30",
|
|
511
|
+
duplicate: "\uBCF5\uC81C",
|
|
512
|
+
moveBar: "\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30 (\uB450 \uBC88 \uB204\uB974\uBA74 \uC81C\uC790\uB9AC)",
|
|
501
513
|
indicators: "\uC9C0\uD45C",
|
|
502
514
|
addIndicator: "\uC9C0\uD45C \uCD94\uAC00",
|
|
503
515
|
indicatorSettings: "\uC9C0\uD45C \uC124\uC815",
|
|
@@ -514,7 +526,6 @@ var STRINGS = {
|
|
|
514
526
|
removePeriod: "\uAE30\uAC04 \uC0AD\uC81C",
|
|
515
527
|
period: "\uAE30\uAC04",
|
|
516
528
|
source: "\uAC00\uACA9 \uAE30\uC900",
|
|
517
|
-
lineWidth: "\uB450\uAED8",
|
|
518
529
|
inputs: "\uC785\uB825\uAC12",
|
|
519
530
|
style: "\uC2A4\uD0C0\uC77C",
|
|
520
531
|
// intervals
|
|
@@ -631,6 +642,18 @@ var STRINGS = {
|
|
|
631
642
|
magnet: "Magnet (snap to OHLC)",
|
|
632
643
|
remove: "Remove",
|
|
633
644
|
color: "Color",
|
|
645
|
+
customColor: "Other color",
|
|
646
|
+
lineWidth: "Line width",
|
|
647
|
+
lineStyle: "Line style",
|
|
648
|
+
lineSolid: "Solid",
|
|
649
|
+
lineDashed: "Dashed",
|
|
650
|
+
lineDotted: "Dotted",
|
|
651
|
+
drawingText: "Text",
|
|
652
|
+
textPlaceholder: "Type text",
|
|
653
|
+
lock: "Lock",
|
|
654
|
+
unlock: "Unlock",
|
|
655
|
+
duplicate: "Duplicate",
|
|
656
|
+
moveBar: "Drag to move (double-click to reset)",
|
|
634
657
|
indicators: "Indicators",
|
|
635
658
|
addIndicator: "Add indicator",
|
|
636
659
|
indicatorSettings: "Indicator settings",
|
|
@@ -647,7 +670,6 @@ var STRINGS = {
|
|
|
647
670
|
removePeriod: "Remove period",
|
|
648
671
|
period: "Period",
|
|
649
672
|
source: "Source",
|
|
650
|
-
lineWidth: "Width",
|
|
651
673
|
inputs: "Inputs",
|
|
652
674
|
style: "Style",
|
|
653
675
|
intervalMinutes: "Minutes",
|
|
@@ -771,7 +793,10 @@ var HorizontalLineIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
|
771
793
|
/* @__PURE__ */ a3("path", { d: "M3 10h14" }),
|
|
772
794
|
/* @__PURE__ */ a3("circle", { cx: "10", cy: "10", r: "1.6", fill: "currentColor" })
|
|
773
795
|
] });
|
|
774
|
-
var FibonacciIcon = () => /* @__PURE__ */ a3(Icon, { children:
|
|
796
|
+
var FibonacciIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
797
|
+
/* @__PURE__ */ a3("path", { d: "M3 3.5h14M3 7.4h14M3 9.9h14M3 12.3h14M3 16.5h14" }),
|
|
798
|
+
/* @__PURE__ */ a3("path", { d: "M4.5 16.5 15.5 3.5", "stroke-dasharray": "1.6 1.8", opacity: "0.7" })
|
|
799
|
+
] });
|
|
775
800
|
var TrashIcon = () => /* @__PURE__ */ a3(Icon, { children: /* @__PURE__ */ a3("path", { d: "M4.5 6h11M8 6V4.5h4V6M6 6l.7 9.5h6.6L14 6" }) });
|
|
776
801
|
var PencilIcon = () => /* @__PURE__ */ a3(Icon, { children: /* @__PURE__ */ a3("path", { d: "M12.8 4.2l3 3L7.5 15.5 4 16l.5-3.5z" }) });
|
|
777
802
|
var CandlesIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
@@ -856,6 +881,15 @@ var ToolsIcon = () => /* @__PURE__ */ a3(Icon, { size: 18, children: [
|
|
|
856
881
|
/* @__PURE__ */ a3("circle", { cx: "12.5", cy: "6", r: "2" }),
|
|
857
882
|
/* @__PURE__ */ a3("circle", { cx: "7.5", cy: "14", r: "2" })
|
|
858
883
|
] });
|
|
884
|
+
var GripIcon = () => /* @__PURE__ */ a3(Icon, { children: /* @__PURE__ */ a3("path", { d: "M7.5 5h.01M7.5 10h.01M7.5 15h.01M12.5 5h.01M12.5 10h.01M12.5 15h.01", "stroke-width": "2.4" }) });
|
|
885
|
+
var LockIcon = ({ open = false }) => /* @__PURE__ */ a3(Icon, { children: [
|
|
886
|
+
/* @__PURE__ */ a3("rect", { x: "4.5", y: "9", width: "11", height: "8", rx: "1.8" }),
|
|
887
|
+
/* @__PURE__ */ a3("path", { d: open ? "M7 9V6.5a3 3 0 0 1 5.8-1" : "M7 9V6.5a3 3 0 0 1 6 0V9" })
|
|
888
|
+
] });
|
|
889
|
+
var CopyIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
890
|
+
/* @__PURE__ */ a3("rect", { x: "7", y: "7", width: "9.5", height: "9.5", rx: "1.8" }),
|
|
891
|
+
/* @__PURE__ */ a3("path", { d: "M13 7V5a1.5 1.5 0 0 0-1.5-1.5h-6A1.5 1.5 0 0 0 4 5v6a1.5 1.5 0 0 0 1.5 1.5H7" })
|
|
892
|
+
] });
|
|
859
893
|
|
|
860
894
|
// src/store.ts
|
|
861
895
|
var Store = class {
|
|
@@ -940,11 +974,21 @@ function ToolGroup({
|
|
|
940
974
|
};
|
|
941
975
|
return /* @__PURE__ */ a3("div", { class: "fnc-tool-group", ref, "data-open": open, children: [
|
|
942
976
|
/* @__PURE__ */ a3("button", { class: "fnc-btn", "data-tip": name, "aria-label": name, "aria-pressed": !!armed, onClick: () => pick(current), children: /* @__PURE__ */ a3(ToolIcon, { def: current, locale: s3.locale }) }),
|
|
943
|
-
tools.length > 1 && /* @__PURE__ */ a3("button", { class: "fnc-tool-more", "aria-label": `${t3.moreTools}: ${name}`, "aria-haspopup": "menu", "aria-expanded": open, onClick: () => onOpen(!open), children:
|
|
944
|
-
|
|
945
|
-
/* @__PURE__ */ a3(
|
|
946
|
-
|
|
947
|
-
|
|
977
|
+
tools.length > 1 && /* @__PURE__ */ a3("button", { class: "fnc-tool-more", "aria-label": `${t3.moreTools}: ${name}`, "aria-haspopup": "menu", "aria-expanded": open, onClick: () => onOpen(!open), children: [
|
|
978
|
+
/* @__PURE__ */ a3("span", { class: "fnc-tool-corner", children: /* @__PURE__ */ a3(CornerIcon, {}) }),
|
|
979
|
+
/* @__PURE__ */ a3("span", { class: "fnc-tool-chevron", children: /* @__PURE__ */ a3(ChevronRightIcon, {}) })
|
|
980
|
+
] }),
|
|
981
|
+
open && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-tool-menu", role: "menu", children: tools.map((def, i3) => {
|
|
982
|
+
const section = def.section?.[s3.locale];
|
|
983
|
+
const heading = section && section !== tools[i3 - 1]?.section?.[s3.locale];
|
|
984
|
+
return /* @__PURE__ */ a3($, { children: [
|
|
985
|
+
heading && /* @__PURE__ */ a3("div", { class: `fnc-tool-section${i3 > 0 ? " is-after" : ""}`, role: "presentation", children: section }),
|
|
986
|
+
/* @__PURE__ */ a3("button", { class: "fnc-menu-item", role: "menuitemradio", "aria-checked": def.type === current.type, onClick: () => pick(def), children: [
|
|
987
|
+
/* @__PURE__ */ a3(ToolIcon, { def, locale: s3.locale }),
|
|
988
|
+
/* @__PURE__ */ a3("span", { children: def.name[s3.locale] })
|
|
989
|
+
] })
|
|
990
|
+
] }, def.type);
|
|
991
|
+
}) })
|
|
948
992
|
] });
|
|
949
993
|
}
|
|
950
994
|
function DrawingToolbar({ ctx }) {
|
|
@@ -1020,24 +1064,153 @@ function DrawingToolbar({ ctx }) {
|
|
|
1020
1064
|
] });
|
|
1021
1065
|
}
|
|
1022
1066
|
var SWATCHES = ["#3182F6", "#F04452", "#12B886", "#FF9F0A", "#7048E8"];
|
|
1067
|
+
var WIDTHS = [1, 2, 3, 4];
|
|
1068
|
+
var DASH_ORDER = ["solid", "dashed", "dotted"];
|
|
1069
|
+
var RECENT_KEY = "fnc-drawing-colors";
|
|
1070
|
+
var POS_KEY = "fnc-selbar-pos";
|
|
1071
|
+
var readJson = (key, fallback) => {
|
|
1072
|
+
try {
|
|
1073
|
+
const raw = localStorage.getItem(key);
|
|
1074
|
+
return raw ? JSON.parse(raw) : fallback;
|
|
1075
|
+
} catch {
|
|
1076
|
+
return fallback;
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
var writeJson = (key, value) => {
|
|
1080
|
+
try {
|
|
1081
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
1082
|
+
} catch {
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
function DashGlyph({ dash }) {
|
|
1086
|
+
const pattern = dash === "dashed" ? "4 3" : dash === "dotted" ? "1 3" : void 0;
|
|
1087
|
+
return /* @__PURE__ */ a3("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "aria-hidden": "true", children: /* @__PURE__ */ a3("path", { d: "M3 10h14", "stroke-dasharray": pattern }) });
|
|
1088
|
+
}
|
|
1023
1089
|
function SelectionBar({ ctx }) {
|
|
1024
1090
|
const s3 = useStore(ctx.store);
|
|
1025
1091
|
const t3 = STRINGS[s3.locale];
|
|
1026
1092
|
const d3 = s3.selection;
|
|
1093
|
+
const [offset, setOffset] = d2(() => readJson(POS_KEY, [0, 0]));
|
|
1094
|
+
const [recent, setRecent] = d2(() => readJson(RECENT_KEY, []));
|
|
1095
|
+
const [menu, setMenu] = d2(null);
|
|
1096
|
+
const colorInput = T2(null);
|
|
1027
1097
|
if (!d3 || s3.layout.plotWidth <= 0) return null;
|
|
1028
|
-
|
|
1029
|
-
|
|
1098
|
+
const def = ctx.chart.drawingDefinitions().find((x2) => x2.type === d3.type);
|
|
1099
|
+
const style = d3.style ?? {};
|
|
1100
|
+
const setStyle = (patch) => ctx.chart.updateDrawing(d3.id, { style: { ...style, ...patch } });
|
|
1101
|
+
const current = d3.color ?? SWATCHES[0];
|
|
1102
|
+
const pickColor = (color) => ctx.chart.updateDrawing(d3.id, { color });
|
|
1103
|
+
const addRecent = (color) => {
|
|
1104
|
+
const next = [color, ...recent.filter((c3) => c3 !== color)].slice(0, 3);
|
|
1105
|
+
setRecent(next);
|
|
1106
|
+
writeJson(RECENT_KEY, next);
|
|
1107
|
+
};
|
|
1108
|
+
const locked = !!style.locked;
|
|
1109
|
+
const dash = style.dash ?? "solid";
|
|
1110
|
+
const startMove = (e3) => {
|
|
1111
|
+
e3.preventDefault();
|
|
1112
|
+
const start = [e3.clientX, e3.clientY];
|
|
1113
|
+
const from = offset;
|
|
1114
|
+
const move = (ev) => setOffset([from[0] + ev.clientX - start[0], from[1] + ev.clientY - start[1]]);
|
|
1115
|
+
const up = (ev) => {
|
|
1116
|
+
document.removeEventListener("pointermove", move);
|
|
1117
|
+
document.removeEventListener("pointerup", up);
|
|
1118
|
+
const next = [from[0] + ev.clientX - start[0], from[1] + ev.clientY - start[1]];
|
|
1119
|
+
setOffset(next);
|
|
1120
|
+
writeJson(POS_KEY, next);
|
|
1121
|
+
};
|
|
1122
|
+
document.addEventListener("pointermove", move);
|
|
1123
|
+
document.addEventListener("pointerup", up);
|
|
1124
|
+
};
|
|
1125
|
+
return /* @__PURE__ */ a3("div", { class: "fnc-selbar", role: "toolbar", style: { left: `${s3.layout.plotWidth / 2 + offset[0]}px`, top: `${8 + offset[1]}px` }, children: [
|
|
1126
|
+
/* @__PURE__ */ a3(
|
|
1030
1127
|
"button",
|
|
1031
1128
|
{
|
|
1032
|
-
class: "fnc-
|
|
1033
|
-
|
|
1034
|
-
"
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1129
|
+
class: "fnc-btn fnc-selbar-grip",
|
|
1130
|
+
"data-tip": t3.moveBar,
|
|
1131
|
+
"aria-label": t3.moveBar,
|
|
1132
|
+
onPointerDown: startMove,
|
|
1133
|
+
onDblClick: () => {
|
|
1134
|
+
setOffset([0, 0]);
|
|
1135
|
+
writeJson(POS_KEY, [0, 0]);
|
|
1136
|
+
},
|
|
1137
|
+
children: /* @__PURE__ */ a3(GripIcon, {})
|
|
1038
1138
|
}
|
|
1039
|
-
)
|
|
1139
|
+
),
|
|
1140
|
+
[...SWATCHES, ...recent.filter((c3) => !SWATCHES.includes(c3))].map((color) => /* @__PURE__ */ a3("button", { class: "fnc-swatch", style: { background: color }, "data-tip": t3.color, "aria-label": `${t3.color} ${color}`, "aria-pressed": current === color, onClick: () => pickColor(color) })),
|
|
1141
|
+
/* @__PURE__ */ a3("label", { class: "fnc-swatch fnc-swatch-add", "data-tip": t3.customColor, "aria-label": t3.customColor, children: [
|
|
1142
|
+
"+",
|
|
1143
|
+
/* @__PURE__ */ a3(
|
|
1144
|
+
"input",
|
|
1145
|
+
{
|
|
1146
|
+
ref: colorInput,
|
|
1147
|
+
type: "color",
|
|
1148
|
+
value: /^#[0-9a-f]{6}$/i.test(current) ? current : "#3182f6",
|
|
1149
|
+
onInput: (e3) => pickColor(e3.target.value),
|
|
1150
|
+
onChange: (e3) => addRecent(e3.target.value)
|
|
1151
|
+
}
|
|
1152
|
+
)
|
|
1153
|
+
] }),
|
|
1040
1154
|
/* @__PURE__ */ a3("div", { class: "fnc-sep" }),
|
|
1155
|
+
/* @__PURE__ */ a3("div", { class: "fnc-selbar-menu", children: [
|
|
1156
|
+
/* @__PURE__ */ a3("button", { class: "fnc-btn fnc-selbar-width", "data-tip": t3.lineWidth, "aria-label": t3.lineWidth, "aria-expanded": menu === "width", onClick: () => setMenu(menu === "width" ? null : "width"), children: /* @__PURE__ */ a3("svg", { width: "20", height: "20", viewBox: "0 0 20 20", "aria-hidden": "true", children: /* @__PURE__ */ a3("path", { d: "M3 10h14", stroke: "currentColor", "stroke-width": style.width ?? 2, "stroke-linecap": "round" }) }) }),
|
|
1157
|
+
menu === "width" && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-selbar-pop", role: "menu", children: WIDTHS.map((w3) => /* @__PURE__ */ a3(
|
|
1158
|
+
"button",
|
|
1159
|
+
{
|
|
1160
|
+
class: "fnc-menu-item",
|
|
1161
|
+
role: "menuitemradio",
|
|
1162
|
+
"aria-checked": (style.width ?? 2) === w3,
|
|
1163
|
+
onClick: () => {
|
|
1164
|
+
setStyle({ width: w3 });
|
|
1165
|
+
setMenu(null);
|
|
1166
|
+
},
|
|
1167
|
+
children: [
|
|
1168
|
+
/* @__PURE__ */ a3("svg", { width: "40", height: "12", viewBox: "0 0 40 12", "aria-hidden": "true", children: /* @__PURE__ */ a3("path", { d: "M2 6h36", stroke: "currentColor", "stroke-width": w3, "stroke-linecap": "round" }) }),
|
|
1169
|
+
/* @__PURE__ */ a3("span", { children: [
|
|
1170
|
+
w3,
|
|
1171
|
+
"px"
|
|
1172
|
+
] })
|
|
1173
|
+
]
|
|
1174
|
+
}
|
|
1175
|
+
)) })
|
|
1176
|
+
] }),
|
|
1177
|
+
/* @__PURE__ */ a3(
|
|
1178
|
+
"button",
|
|
1179
|
+
{
|
|
1180
|
+
class: "fnc-btn",
|
|
1181
|
+
"data-tip": `${t3.lineStyle}: ${dash === "solid" ? t3.lineSolid : dash === "dashed" ? t3.lineDashed : t3.lineDotted}`,
|
|
1182
|
+
"aria-label": t3.lineStyle,
|
|
1183
|
+
onClick: () => setStyle({ dash: DASH_ORDER[(DASH_ORDER.indexOf(dash) + 1) % DASH_ORDER.length] }),
|
|
1184
|
+
children: /* @__PURE__ */ a3(DashGlyph, { dash })
|
|
1185
|
+
}
|
|
1186
|
+
),
|
|
1187
|
+
def?.text && /* @__PURE__ */ a3(
|
|
1188
|
+
"input",
|
|
1189
|
+
{
|
|
1190
|
+
class: "fnc-selbar-text",
|
|
1191
|
+
value: style.text ?? "",
|
|
1192
|
+
placeholder: t3.textPlaceholder,
|
|
1193
|
+
"aria-label": t3.drawingText,
|
|
1194
|
+
maxLength: 200,
|
|
1195
|
+
onInput: (e3) => setStyle({ text: e3.target.value }),
|
|
1196
|
+
onKeyDown: (e3) => e3.stopPropagation()
|
|
1197
|
+
}
|
|
1198
|
+
),
|
|
1199
|
+
/* @__PURE__ */ a3("div", { class: "fnc-sep" }),
|
|
1200
|
+
/* @__PURE__ */ a3("button", { class: "fnc-btn", "data-tip": locked ? t3.unlock : t3.lock, "aria-label": locked ? t3.unlock : t3.lock, "aria-pressed": locked, onClick: () => setStyle({ locked: !locked }), children: /* @__PURE__ */ a3(LockIcon, { open: !locked }) }),
|
|
1201
|
+
/* @__PURE__ */ a3(
|
|
1202
|
+
"button",
|
|
1203
|
+
{
|
|
1204
|
+
class: "fnc-btn",
|
|
1205
|
+
"data-tip": t3.duplicate,
|
|
1206
|
+
"aria-label": t3.duplicate,
|
|
1207
|
+
onClick: () => {
|
|
1208
|
+
const id = ctx.chart.addDrawing({ type: d3.type, points: d3.points.map((p3) => ({ ...p3 })), ...d3.color ? { color: d3.color } : {}, style: { ...style, locked: false } });
|
|
1209
|
+
ctx.chart.selectDrawing(id);
|
|
1210
|
+
},
|
|
1211
|
+
children: /* @__PURE__ */ a3(CopyIcon, {})
|
|
1212
|
+
}
|
|
1213
|
+
),
|
|
1041
1214
|
/* @__PURE__ */ a3("button", { class: "fnc-btn", "data-tip": t3.remove, "aria-label": t3.remove, onClick: () => ctx.chart.removeDrawing(d3.id), children: /* @__PURE__ */ a3(TrashIcon, {}) })
|
|
1042
1215
|
] });
|
|
1043
1216
|
}
|
|
@@ -1245,7 +1418,7 @@ function IndicatorPicker({ ctx }) {
|
|
|
1245
1418
|
] })
|
|
1246
1419
|
] });
|
|
1247
1420
|
}
|
|
1248
|
-
var
|
|
1421
|
+
var WIDTHS2 = [1, 2, 3, 4];
|
|
1249
1422
|
function defaultSeries(def, params) {
|
|
1250
1423
|
return def.compute([], params).series.filter((s3) => s3.kind === "line");
|
|
1251
1424
|
}
|
|
@@ -1275,7 +1448,7 @@ function StyleControls({
|
|
|
1275
1448
|
onInput: (e3) => onChange({ color: e3.currentTarget.value })
|
|
1276
1449
|
}
|
|
1277
1450
|
),
|
|
1278
|
-
/* @__PURE__ */ a3("select", { class: "fnc-select fnc-select-width", value: row2.width, "aria-label": t3.lineWidth, onChange: (e3) => onChange({ width: Number(e3.currentTarget.value) }), children:
|
|
1451
|
+
/* @__PURE__ */ a3("select", { class: "fnc-select fnc-select-width", value: row2.width, "aria-label": t3.lineWidth, onChange: (e3) => onChange({ width: Number(e3.currentTarget.value) }), children: WIDTHS2.map((w3) => /* @__PURE__ */ a3("option", { value: w3, children: [
|
|
1279
1452
|
w3,
|
|
1280
1453
|
"px"
|
|
1281
1454
|
] })) })
|
|
@@ -1589,7 +1762,7 @@ function SymbolAvatar({ info, size = 24 }) {
|
|
|
1589
1762
|
// src/components/symbol-search.tsx
|
|
1590
1763
|
import { formatPrice } from "@funnest/charts";
|
|
1591
1764
|
var DEBOUNCE_MS = 150;
|
|
1592
|
-
var
|
|
1765
|
+
var RECENT_KEY2 = "recent-symbols";
|
|
1593
1766
|
var FAVORITE_KEY = "favorite-symbols";
|
|
1594
1767
|
var MAX_RECENT = 8;
|
|
1595
1768
|
var displayCode = (symbol) => symbol.slice(symbol.lastIndexOf(":") + 1);
|
|
@@ -1628,7 +1801,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1628
1801
|
const [exchange, setExchange] = d2(null);
|
|
1629
1802
|
const [results, setResults] = d2(null);
|
|
1630
1803
|
const [loading, setLoading] = d2(true);
|
|
1631
|
-
const [recent, setRecent] = d2(() => ctx.storage.get(
|
|
1804
|
+
const [recent, setRecent] = d2(() => ctx.storage.get(RECENT_KEY2, []));
|
|
1632
1805
|
const [favorites, setFavorites] = d2(() => ctx.storage.get(FAVORITE_KEY, []));
|
|
1633
1806
|
const [active, setActive] = d2(0);
|
|
1634
1807
|
const inputRef = T2(null);
|
|
@@ -1672,7 +1845,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1672
1845
|
const toggleFavorite = (info) => save(FAVORITE_KEY, isFavorite(info) ? favorites.filter((f4) => f4.symbol !== info.symbol) : [...favorites, toStored(info)], setFavorites);
|
|
1673
1846
|
const choose = (info) => {
|
|
1674
1847
|
if (!info) return;
|
|
1675
|
-
save(
|
|
1848
|
+
save(RECENT_KEY2, [toStored(info), ...recent.filter((r3) => r3.symbol !== info.symbol)].slice(0, MAX_RECENT), setRecent);
|
|
1676
1849
|
close();
|
|
1677
1850
|
if (comparing) ctx.chart.addCompare(info.symbol).catch((error) => console.error("funnest-charts: compare failed", error));
|
|
1678
1851
|
else ctx.selectSymbol(info.symbol);
|
|
@@ -1747,7 +1920,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1747
1920
|
"data-tip": t3.removeRecent,
|
|
1748
1921
|
onClick: (e3) => {
|
|
1749
1922
|
e3.stopPropagation();
|
|
1750
|
-
save(
|
|
1923
|
+
save(RECENT_KEY2, recent.filter((r3) => r3.symbol !== info.symbol), setRecent);
|
|
1751
1924
|
},
|
|
1752
1925
|
children: /* @__PURE__ */ a3(SmallCloseIcon, {})
|
|
1753
1926
|
}
|
|
@@ -1798,7 +1971,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1798
1971
|
/* @__PURE__ */ a3("div", { class: "fnc-sr-list fnc-scroll", id: "fnc-search-results", role: "listbox", ref: listRef, children: [
|
|
1799
1972
|
recentRows.length > 0 && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: [
|
|
1800
1973
|
/* @__PURE__ */ a3("span", { children: t3.recentSearches }),
|
|
1801
|
-
/* @__PURE__ */ a3("button", { class: "fnc-link", onClick: () => save(
|
|
1974
|
+
/* @__PURE__ */ a3("button", { class: "fnc-link", onClick: () => save(RECENT_KEY2, [], setRecent), children: t3.clearRecent })
|
|
1802
1975
|
] }),
|
|
1803
1976
|
recentRows.map((info, i3) => row2(info, i3, true)),
|
|
1804
1977
|
(recentRows.length > 0 || view.kind === "favorites") && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: view.kind === "favorites" ? t3.favorites : t3.symbols }),
|
|
@@ -2092,7 +2265,7 @@ function Toolbar({ ctx }) {
|
|
|
2092
2265
|
"data-tip": t3.addIndicator,
|
|
2093
2266
|
"aria-label": t3.addIndicator,
|
|
2094
2267
|
"aria-haspopup": "dialog",
|
|
2095
|
-
onClick: () => ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
2268
|
+
onClick: () => ctx.onIndicatorsClick ? ctx.onIndicatorsClick(ctx.chart) : ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
2096
2269
|
children: [
|
|
2097
2270
|
/* @__PURE__ */ a3(IndicatorIcon, {}),
|
|
2098
2271
|
/* @__PURE__ */ a3("span", { children: t3.indicators })
|
|
@@ -2705,6 +2878,9 @@ var CSS = (
|
|
|
2705
2878
|
/* \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 */
|
|
2706
2879
|
.fnc-drawbar-host { flex: none; display: flex; }
|
|
2707
2880
|
.fnc-drawbar {
|
|
2881
|
+
/* Above the plot, so a tool list tab (it sticks out over the chart) can be clicked. */
|
|
2882
|
+
position: relative;
|
|
2883
|
+
z-index: 5;
|
|
2708
2884
|
display: flex;
|
|
2709
2885
|
flex-direction: column;
|
|
2710
2886
|
align-items: center;
|
|
@@ -2738,6 +2914,16 @@ var CSS = (
|
|
|
2738
2914
|
}
|
|
2739
2915
|
.fnc-swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--fnc-bg), 0 0 0 4px var(--fnc-text-2); }
|
|
2740
2916
|
.fnc-selbar .fnc-btn { width: 32px; height: 32px; }
|
|
2917
|
+
.fnc-selbar { padding-left: 4px; }
|
|
2918
|
+
.fnc-selbar-grip { cursor: grab; touch-action: none; }
|
|
2919
|
+
.fnc-selbar-grip:active { cursor: grabbing; }
|
|
2920
|
+
.fnc-swatch-add { position: relative; display: grid; place-items: center; overflow: hidden; border: 1px dashed var(--fnc-text-2); color: var(--fnc-text-2); font-size: 13px; line-height: 1; cursor: pointer; }
|
|
2921
|
+
.fnc-swatch-add input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
|
|
2922
|
+
.fnc-selbar-menu { position: relative; }
|
|
2923
|
+
.fnc-selbar-pop { top: calc(100% + 6px); left: 50%; transform: translateX(-50%); min-width: 110px; }
|
|
2924
|
+
.fnc-selbar-pop .fnc-menu-item { gap: 10px; }
|
|
2925
|
+
.fnc-selbar-text { width: 150px; height: 28px; padding: 0 8px; border: 1px solid var(--fnc-border); border-radius: var(--fnc-radius); background: var(--fnc-surface); color: var(--fnc-text); font: inherit; font-size: 13px; outline: none; }
|
|
2926
|
+
.fnc-selbar-text:focus { border-color: var(--fnc-text-2); }
|
|
2741
2927
|
.fnc-overlay { pointer-events: none; }
|
|
2742
2928
|
.fnc-overlay button { pointer-events: auto; }
|
|
2743
2929
|
|
|
@@ -3452,21 +3638,51 @@ var CSS = (
|
|
|
3452
3638
|
/* \u2500\u2500 Drawing tool groups \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 */
|
|
3453
3639
|
.fnc-tool-group { position: relative; display: flex; }
|
|
3454
3640
|
.fnc-tool-group[data-open='true'] { z-index: 30; }
|
|
3641
|
+
/* The list opener: a corner mark at rest; on hover a tab slides out to the right of the
|
|
3642
|
+
button (a bigger target, like the reference terminals). Touch screens always show the tab. */
|
|
3455
3643
|
.fnc-tool-more {
|
|
3456
3644
|
position: absolute;
|
|
3645
|
+
top: 0;
|
|
3457
3646
|
right: -2px;
|
|
3458
|
-
bottom:
|
|
3647
|
+
bottom: 0;
|
|
3459
3648
|
display: flex;
|
|
3460
3649
|
align-items: flex-end;
|
|
3461
3650
|
justify-content: flex-end;
|
|
3462
|
-
width:
|
|
3463
|
-
|
|
3464
|
-
|
|
3651
|
+
width: 12px;
|
|
3652
|
+
padding: 0 0 2px;
|
|
3653
|
+
border-radius: 0 var(--fnc-radius) var(--fnc-radius) 0;
|
|
3465
3654
|
color: var(--fnc-text-2);
|
|
3466
|
-
|
|
3655
|
+
transition: right 0.12s ease, width 0.12s ease, background 0.12s ease;
|
|
3656
|
+
}
|
|
3657
|
+
.fnc-tool-corner { display: flex; opacity: 0.7; }
|
|
3658
|
+
.fnc-tool-chevron { display: none; }
|
|
3659
|
+
.fnc-tool-chevron svg { width: 12px; height: 12px; }
|
|
3660
|
+
.fnc-tool-group:hover .fnc-tool-more,
|
|
3661
|
+
.fnc-tool-more[aria-expanded='true'],
|
|
3662
|
+
.fnc-tool-more:focus-visible {
|
|
3663
|
+
right: -13px;
|
|
3664
|
+
width: 14px;
|
|
3665
|
+
align-items: center;
|
|
3666
|
+
justify-content: center;
|
|
3667
|
+
padding: 0;
|
|
3668
|
+
background: var(--fnc-surface);
|
|
3669
|
+
color: var(--fnc-text);
|
|
3670
|
+
box-shadow: 0 0 0 1px var(--fnc-border);
|
|
3671
|
+
}
|
|
3672
|
+
.fnc-tool-group:hover .fnc-tool-corner,
|
|
3673
|
+
.fnc-tool-more[aria-expanded='true'] .fnc-tool-corner,
|
|
3674
|
+
.fnc-tool-more:focus-visible .fnc-tool-corner { display: none; }
|
|
3675
|
+
.fnc-tool-group:hover .fnc-tool-chevron,
|
|
3676
|
+
.fnc-tool-more[aria-expanded='true'] .fnc-tool-chevron,
|
|
3677
|
+
.fnc-tool-more:focus-visible .fnc-tool-chevron { display: flex; }
|
|
3678
|
+
@media (hover: none) {
|
|
3679
|
+
.fnc-tool-more { right: -12px; width: 14px; align-items: center; justify-content: center; padding: 0; }
|
|
3680
|
+
.fnc-tool-corner { display: none; }
|
|
3681
|
+
.fnc-tool-chevron { display: flex; }
|
|
3467
3682
|
}
|
|
3468
|
-
.fnc-tool-
|
|
3469
|
-
.fnc-tool-
|
|
3683
|
+
.fnc-tool-menu { top: 0; left: calc(100% + 8px); min-width: 200px; max-height: min(640px, calc(100vh - 120px)); overflow-y: auto; }
|
|
3684
|
+
.fnc-tool-section { padding: 8px 10px 4px; color: var(--fnc-text-2); font-size: 11px; font-weight: 600; }
|
|
3685
|
+
.fnc-tool-section.is-after { margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--fnc-border); }
|
|
3470
3686
|
.fnc-tool-menu .fnc-menu-item { white-space: nowrap; }
|
|
3471
3687
|
.fnc-tool-letter { font-size: 13px; font-weight: 700; }
|
|
3472
3688
|
.fnc-svg-icon { display: inline-flex; }
|
|
@@ -3925,6 +4141,7 @@ function createPane(shell, config) {
|
|
|
3925
4141
|
drawingToolbar: config.drawingToolbar,
|
|
3926
4142
|
attribution: config.attribution,
|
|
3927
4143
|
contextMenuItems: config.contextMenuItems,
|
|
4144
|
+
onIndicatorsClick: config.onIndicatorsClick,
|
|
3928
4145
|
slots: shell.slots,
|
|
3929
4146
|
multi: config.multi,
|
|
3930
4147
|
setDrawbarCollapsed: shell.setDrawbarCollapsed,
|
|
@@ -4000,6 +4217,7 @@ function createTradingChart(container, options) {
|
|
|
4000
4217
|
attribution = true,
|
|
4001
4218
|
colorScheme = "system",
|
|
4002
4219
|
contextMenuItems,
|
|
4220
|
+
onIndicatorsClick,
|
|
4003
4221
|
theme,
|
|
4004
4222
|
...chartOptions
|
|
4005
4223
|
} = options;
|
|
@@ -4024,7 +4242,8 @@ function createTradingChart(container, options) {
|
|
|
4024
4242
|
searchHotkey,
|
|
4025
4243
|
drawingToolbar,
|
|
4026
4244
|
attribution,
|
|
4027
|
-
contextMenuItems
|
|
4245
|
+
contextMenuItems,
|
|
4246
|
+
onIndicatorsClick
|
|
4028
4247
|
});
|
|
4029
4248
|
shell.main.insertBefore(pane.stage, shell.slots.bottom);
|
|
4030
4249
|
shell.setPanes(() => [pane]);
|
|
@@ -4096,6 +4315,7 @@ function createMultiChart(container, options) {
|
|
|
4096
4315
|
attribution = true,
|
|
4097
4316
|
colorScheme = "system",
|
|
4098
4317
|
contextMenuItems,
|
|
4318
|
+
onIndicatorsClick,
|
|
4099
4319
|
theme,
|
|
4100
4320
|
...chartOptions
|
|
4101
4321
|
} = options;
|
|
@@ -4163,6 +4383,7 @@ function createMultiChart(container, options) {
|
|
|
4163
4383
|
// One badge for the whole widget, not one per pane.
|
|
4164
4384
|
attribution: attribution && index === 0,
|
|
4165
4385
|
contextMenuItems,
|
|
4386
|
+
onIndicatorsClick,
|
|
4166
4387
|
multi: controls
|
|
4167
4388
|
});
|
|
4168
4389
|
const cell = div("fnc-pane");
|
package/dist/pane.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface PaneConfig {
|
|
|
17
17
|
/** Ctrl/⌘+K opens this widget's symbol search (its footer shows the hint only then). */
|
|
18
18
|
searchHotkey: boolean;
|
|
19
19
|
contextMenuItems?: ContextMenuItems | undefined;
|
|
20
|
+
onIndicatorsClick?: ((chart: Chart) => void) | undefined;
|
|
20
21
|
multi?: MultiControls | undefined;
|
|
21
22
|
}
|
|
22
23
|
/**
|
package/dist/trading-chart.d.ts
CHANGED
|
@@ -50,6 +50,11 @@ export interface TradingChartOptions extends Omit<ChartOptions, 'datafeed'> {
|
|
|
50
50
|
* e.g. "이 가격에 알림 만들기". Called on every right-click with the price and time under the pointer.
|
|
51
51
|
*/
|
|
52
52
|
contextMenuItems?: ContextMenuItems;
|
|
53
|
+
/**
|
|
54
|
+
* The toolbar's 지표 button opens this instead of the built-in indicator picker (an app's own
|
|
55
|
+
* indicator browser). Called with the pane's chart; add indicators with chart.addIndicator().
|
|
56
|
+
*/
|
|
57
|
+
onIndicatorsClick?: (chart: Chart) => void;
|
|
53
58
|
}
|
|
54
59
|
export interface TradingChart {
|
|
55
60
|
/** The underlying engine, for anything the widget doesn't wrap. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnest/charts-ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"description": "Pro trading UI (toolbar, drawing tools, sheets) for Funnest Charts. Framework-agnostic.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chart",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"NOTICE"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@funnest/charts": "0.6.
|
|
45
|
+
"@funnest/charts": "0.6.11"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"preact": "^11.2.0"
|