@funnest/charts-ui 0.6.9 → 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 +5 -1
- package/dist/context.d.ts +2 -0
- package/dist/i18n.d.ts +24 -2
- package/dist/icons.d.ts +6 -0
- package/dist/index.js +236 -27
- package/dist/pane.d.ts +1 -0
- package/dist/trading-chart.d.ts +5 -0
- package/package.json +2 -2
|
@@ -4,7 +4,11 @@ import type { WidgetContext } from '../context';
|
|
|
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
|
@@ -55,3 +55,9 @@ export declare const SvgIcon: ({ markup }: {
|
|
|
55
55
|
export declare const CornerIcon: () => import("preact").JSX.Element;
|
|
56
56
|
/** Sliders: the 도구 menu. */
|
|
57
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",
|
|
@@ -859,6 +881,15 @@ var ToolsIcon = () => /* @__PURE__ */ a3(Icon, { size: 18, children: [
|
|
|
859
881
|
/* @__PURE__ */ a3("circle", { cx: "12.5", cy: "6", r: "2" }),
|
|
860
882
|
/* @__PURE__ */ a3("circle", { cx: "7.5", cy: "14", r: "2" })
|
|
861
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
|
+
] });
|
|
862
893
|
|
|
863
894
|
// src/store.ts
|
|
864
895
|
var Store = class {
|
|
@@ -943,7 +974,10 @@ function ToolGroup({
|
|
|
943
974
|
};
|
|
944
975
|
return /* @__PURE__ */ a3("div", { class: "fnc-tool-group", ref, "data-open": open, children: [
|
|
945
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 }) }),
|
|
946
|
-
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:
|
|
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
|
+
] }),
|
|
947
981
|
open && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-tool-menu", role: "menu", children: tools.map((def, i3) => {
|
|
948
982
|
const section = def.section?.[s3.locale];
|
|
949
983
|
const heading = section && section !== tools[i3 - 1]?.section?.[s3.locale];
|
|
@@ -1030,24 +1064,153 @@ function DrawingToolbar({ ctx }) {
|
|
|
1030
1064
|
] });
|
|
1031
1065
|
}
|
|
1032
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
|
+
}
|
|
1033
1089
|
function SelectionBar({ ctx }) {
|
|
1034
1090
|
const s3 = useStore(ctx.store);
|
|
1035
1091
|
const t3 = STRINGS[s3.locale];
|
|
1036
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);
|
|
1037
1097
|
if (!d3 || s3.layout.plotWidth <= 0) return null;
|
|
1038
|
-
|
|
1039
|
-
|
|
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(
|
|
1127
|
+
"button",
|
|
1128
|
+
{
|
|
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, {})
|
|
1138
|
+
}
|
|
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
|
+
] }),
|
|
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(
|
|
1040
1178
|
"button",
|
|
1041
1179
|
{
|
|
1042
|
-
class: "fnc-
|
|
1043
|
-
|
|
1044
|
-
"
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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()
|
|
1048
1197
|
}
|
|
1049
|
-
)
|
|
1198
|
+
),
|
|
1050
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
|
+
),
|
|
1051
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, {}) })
|
|
1052
1215
|
] });
|
|
1053
1216
|
}
|
|
@@ -1255,7 +1418,7 @@ function IndicatorPicker({ ctx }) {
|
|
|
1255
1418
|
] })
|
|
1256
1419
|
] });
|
|
1257
1420
|
}
|
|
1258
|
-
var
|
|
1421
|
+
var WIDTHS2 = [1, 2, 3, 4];
|
|
1259
1422
|
function defaultSeries(def, params) {
|
|
1260
1423
|
return def.compute([], params).series.filter((s3) => s3.kind === "line");
|
|
1261
1424
|
}
|
|
@@ -1285,7 +1448,7 @@ function StyleControls({
|
|
|
1285
1448
|
onInput: (e3) => onChange({ color: e3.currentTarget.value })
|
|
1286
1449
|
}
|
|
1287
1450
|
),
|
|
1288
|
-
/* @__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: [
|
|
1289
1452
|
w3,
|
|
1290
1453
|
"px"
|
|
1291
1454
|
] })) })
|
|
@@ -1599,7 +1762,7 @@ function SymbolAvatar({ info, size = 24 }) {
|
|
|
1599
1762
|
// src/components/symbol-search.tsx
|
|
1600
1763
|
import { formatPrice } from "@funnest/charts";
|
|
1601
1764
|
var DEBOUNCE_MS = 150;
|
|
1602
|
-
var
|
|
1765
|
+
var RECENT_KEY2 = "recent-symbols";
|
|
1603
1766
|
var FAVORITE_KEY = "favorite-symbols";
|
|
1604
1767
|
var MAX_RECENT = 8;
|
|
1605
1768
|
var displayCode = (symbol) => symbol.slice(symbol.lastIndexOf(":") + 1);
|
|
@@ -1638,7 +1801,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1638
1801
|
const [exchange, setExchange] = d2(null);
|
|
1639
1802
|
const [results, setResults] = d2(null);
|
|
1640
1803
|
const [loading, setLoading] = d2(true);
|
|
1641
|
-
const [recent, setRecent] = d2(() => ctx.storage.get(
|
|
1804
|
+
const [recent, setRecent] = d2(() => ctx.storage.get(RECENT_KEY2, []));
|
|
1642
1805
|
const [favorites, setFavorites] = d2(() => ctx.storage.get(FAVORITE_KEY, []));
|
|
1643
1806
|
const [active, setActive] = d2(0);
|
|
1644
1807
|
const inputRef = T2(null);
|
|
@@ -1682,7 +1845,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1682
1845
|
const toggleFavorite = (info) => save(FAVORITE_KEY, isFavorite(info) ? favorites.filter((f4) => f4.symbol !== info.symbol) : [...favorites, toStored(info)], setFavorites);
|
|
1683
1846
|
const choose = (info) => {
|
|
1684
1847
|
if (!info) return;
|
|
1685
|
-
save(
|
|
1848
|
+
save(RECENT_KEY2, [toStored(info), ...recent.filter((r3) => r3.symbol !== info.symbol)].slice(0, MAX_RECENT), setRecent);
|
|
1686
1849
|
close();
|
|
1687
1850
|
if (comparing) ctx.chart.addCompare(info.symbol).catch((error) => console.error("funnest-charts: compare failed", error));
|
|
1688
1851
|
else ctx.selectSymbol(info.symbol);
|
|
@@ -1757,7 +1920,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1757
1920
|
"data-tip": t3.removeRecent,
|
|
1758
1921
|
onClick: (e3) => {
|
|
1759
1922
|
e3.stopPropagation();
|
|
1760
|
-
save(
|
|
1923
|
+
save(RECENT_KEY2, recent.filter((r3) => r3.symbol !== info.symbol), setRecent);
|
|
1761
1924
|
},
|
|
1762
1925
|
children: /* @__PURE__ */ a3(SmallCloseIcon, {})
|
|
1763
1926
|
}
|
|
@@ -1808,7 +1971,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1808
1971
|
/* @__PURE__ */ a3("div", { class: "fnc-sr-list fnc-scroll", id: "fnc-search-results", role: "listbox", ref: listRef, children: [
|
|
1809
1972
|
recentRows.length > 0 && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: [
|
|
1810
1973
|
/* @__PURE__ */ a3("span", { children: t3.recentSearches }),
|
|
1811
|
-
/* @__PURE__ */ a3("button", { class: "fnc-link", onClick: () => save(
|
|
1974
|
+
/* @__PURE__ */ a3("button", { class: "fnc-link", onClick: () => save(RECENT_KEY2, [], setRecent), children: t3.clearRecent })
|
|
1812
1975
|
] }),
|
|
1813
1976
|
recentRows.map((info, i3) => row2(info, i3, true)),
|
|
1814
1977
|
(recentRows.length > 0 || view.kind === "favorites") && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: view.kind === "favorites" ? t3.favorites : t3.symbols }),
|
|
@@ -2102,7 +2265,7 @@ function Toolbar({ ctx }) {
|
|
|
2102
2265
|
"data-tip": t3.addIndicator,
|
|
2103
2266
|
"aria-label": t3.addIndicator,
|
|
2104
2267
|
"aria-haspopup": "dialog",
|
|
2105
|
-
onClick: () => ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
2268
|
+
onClick: () => ctx.onIndicatorsClick ? ctx.onIndicatorsClick(ctx.chart) : ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
2106
2269
|
children: [
|
|
2107
2270
|
/* @__PURE__ */ a3(IndicatorIcon, {}),
|
|
2108
2271
|
/* @__PURE__ */ a3("span", { children: t3.indicators })
|
|
@@ -2715,6 +2878,9 @@ var CSS = (
|
|
|
2715
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 */
|
|
2716
2879
|
.fnc-drawbar-host { flex: none; display: flex; }
|
|
2717
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;
|
|
2718
2884
|
display: flex;
|
|
2719
2885
|
flex-direction: column;
|
|
2720
2886
|
align-items: center;
|
|
@@ -2748,6 +2914,16 @@ var CSS = (
|
|
|
2748
2914
|
}
|
|
2749
2915
|
.fnc-swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--fnc-bg), 0 0 0 4px var(--fnc-text-2); }
|
|
2750
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); }
|
|
2751
2927
|
.fnc-overlay { pointer-events: none; }
|
|
2752
2928
|
.fnc-overlay button { pointer-events: auto; }
|
|
2753
2929
|
|
|
@@ -3462,20 +3638,48 @@ var CSS = (
|
|
|
3462
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 */
|
|
3463
3639
|
.fnc-tool-group { position: relative; display: flex; }
|
|
3464
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. */
|
|
3465
3643
|
.fnc-tool-more {
|
|
3466
3644
|
position: absolute;
|
|
3645
|
+
top: 0;
|
|
3467
3646
|
right: -2px;
|
|
3468
|
-
bottom:
|
|
3647
|
+
bottom: 0;
|
|
3469
3648
|
display: flex;
|
|
3470
3649
|
align-items: flex-end;
|
|
3471
3650
|
justify-content: flex-end;
|
|
3472
|
-
width:
|
|
3473
|
-
|
|
3474
|
-
|
|
3651
|
+
width: 12px;
|
|
3652
|
+
padding: 0 0 2px;
|
|
3653
|
+
border-radius: 0 var(--fnc-radius) var(--fnc-radius) 0;
|
|
3475
3654
|
color: var(--fnc-text-2);
|
|
3476
|
-
|
|
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; }
|
|
3477
3682
|
}
|
|
3478
|
-
.fnc-tool-more:hover, .fnc-tool-more[aria-expanded='true'] { opacity: 1; color: var(--fnc-text); }
|
|
3479
3683
|
.fnc-tool-menu { top: 0; left: calc(100% + 8px); min-width: 200px; max-height: min(640px, calc(100vh - 120px)); overflow-y: auto; }
|
|
3480
3684
|
.fnc-tool-section { padding: 8px 10px 4px; color: var(--fnc-text-2); font-size: 11px; font-weight: 600; }
|
|
3481
3685
|
.fnc-tool-section.is-after { margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--fnc-border); }
|
|
@@ -3937,6 +4141,7 @@ function createPane(shell, config) {
|
|
|
3937
4141
|
drawingToolbar: config.drawingToolbar,
|
|
3938
4142
|
attribution: config.attribution,
|
|
3939
4143
|
contextMenuItems: config.contextMenuItems,
|
|
4144
|
+
onIndicatorsClick: config.onIndicatorsClick,
|
|
3940
4145
|
slots: shell.slots,
|
|
3941
4146
|
multi: config.multi,
|
|
3942
4147
|
setDrawbarCollapsed: shell.setDrawbarCollapsed,
|
|
@@ -4012,6 +4217,7 @@ function createTradingChart(container, options) {
|
|
|
4012
4217
|
attribution = true,
|
|
4013
4218
|
colorScheme = "system",
|
|
4014
4219
|
contextMenuItems,
|
|
4220
|
+
onIndicatorsClick,
|
|
4015
4221
|
theme,
|
|
4016
4222
|
...chartOptions
|
|
4017
4223
|
} = options;
|
|
@@ -4036,7 +4242,8 @@ function createTradingChart(container, options) {
|
|
|
4036
4242
|
searchHotkey,
|
|
4037
4243
|
drawingToolbar,
|
|
4038
4244
|
attribution,
|
|
4039
|
-
contextMenuItems
|
|
4245
|
+
contextMenuItems,
|
|
4246
|
+
onIndicatorsClick
|
|
4040
4247
|
});
|
|
4041
4248
|
shell.main.insertBefore(pane.stage, shell.slots.bottom);
|
|
4042
4249
|
shell.setPanes(() => [pane]);
|
|
@@ -4108,6 +4315,7 @@ function createMultiChart(container, options) {
|
|
|
4108
4315
|
attribution = true,
|
|
4109
4316
|
colorScheme = "system",
|
|
4110
4317
|
contextMenuItems,
|
|
4318
|
+
onIndicatorsClick,
|
|
4111
4319
|
theme,
|
|
4112
4320
|
...chartOptions
|
|
4113
4321
|
} = options;
|
|
@@ -4175,6 +4383,7 @@ function createMultiChart(container, options) {
|
|
|
4175
4383
|
// One badge for the whole widget, not one per pane.
|
|
4176
4384
|
attribution: attribution && index === 0,
|
|
4177
4385
|
contextMenuItems,
|
|
4386
|
+
onIndicatorsClick,
|
|
4178
4387
|
multi: controls
|
|
4179
4388
|
});
|
|
4180
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"
|