@funnest/charts-ui 0.6.9 → 0.6.12
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 +28 -2
- package/dist/icons.d.ts +8 -0
- package/dist/index.js +298 -28
- 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,20 @@ export declare const STRINGS: {
|
|
|
75
75
|
magnet: string;
|
|
76
76
|
remove: string;
|
|
77
77
|
color: string;
|
|
78
|
+
customColor: string;
|
|
79
|
+
partColors: string;
|
|
80
|
+
resetColors: string;
|
|
81
|
+
lineWidth: string;
|
|
82
|
+
lineStyle: string;
|
|
83
|
+
lineSolid: string;
|
|
84
|
+
lineDashed: string;
|
|
85
|
+
lineDotted: string;
|
|
86
|
+
drawingText: string;
|
|
87
|
+
textPlaceholder: string;
|
|
88
|
+
lock: string;
|
|
89
|
+
unlock: string;
|
|
90
|
+
duplicate: string;
|
|
91
|
+
moveBar: string;
|
|
78
92
|
indicators: string;
|
|
79
93
|
addIndicator: string;
|
|
80
94
|
indicatorSettings: string;
|
|
@@ -91,7 +105,6 @@ export declare const STRINGS: {
|
|
|
91
105
|
removePeriod: string;
|
|
92
106
|
period: string;
|
|
93
107
|
source: string;
|
|
94
|
-
lineWidth: string;
|
|
95
108
|
inputs: string;
|
|
96
109
|
style: string;
|
|
97
110
|
intervalMinutes: string;
|
|
@@ -206,6 +219,20 @@ export declare const STRINGS: {
|
|
|
206
219
|
magnet: string;
|
|
207
220
|
remove: string;
|
|
208
221
|
color: string;
|
|
222
|
+
customColor: string;
|
|
223
|
+
partColors: string;
|
|
224
|
+
resetColors: string;
|
|
225
|
+
lineWidth: string;
|
|
226
|
+
lineStyle: string;
|
|
227
|
+
lineSolid: string;
|
|
228
|
+
lineDashed: string;
|
|
229
|
+
lineDotted: string;
|
|
230
|
+
drawingText: string;
|
|
231
|
+
textPlaceholder: string;
|
|
232
|
+
lock: string;
|
|
233
|
+
unlock: string;
|
|
234
|
+
duplicate: string;
|
|
235
|
+
moveBar: string;
|
|
209
236
|
indicators: string;
|
|
210
237
|
addIndicator: string;
|
|
211
238
|
indicatorSettings: string;
|
|
@@ -222,7 +249,6 @@ export declare const STRINGS: {
|
|
|
222
249
|
removePeriod: string;
|
|
223
250
|
period: string;
|
|
224
251
|
source: string;
|
|
225
|
-
lineWidth: string;
|
|
226
252
|
inputs: string;
|
|
227
253
|
style: string;
|
|
228
254
|
intervalMinutes: string;
|
package/dist/icons.d.ts
CHANGED
|
@@ -55,3 +55,11 @@ 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
|
+
/** Several colors (a tool's parts): a palette. */
|
|
60
|
+
export declare const PaletteIcon: () => import("preact").JSX.Element;
|
|
61
|
+
export declare const LockIcon: ({ open }: {
|
|
62
|
+
open?: boolean;
|
|
63
|
+
}) => import("preact").JSX.Element;
|
|
64
|
+
export declare const CopyIcon: () => import("preact").JSX.Element;
|
|
65
|
+
export declare const TextIcon: () => import("preact").JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -498,6 +498,20 @@ 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
|
+
partColors: "\uBD80\uBD84 \uC0C9\uC0C1",
|
|
503
|
+
resetColors: "\uAE30\uBCF8 \uC0C9\uC73C\uB85C",
|
|
504
|
+
lineWidth: "\uC120 \uAD75\uAE30",
|
|
505
|
+
lineStyle: "\uC120 \uBAA8\uC591",
|
|
506
|
+
lineSolid: "\uC2E4\uC120",
|
|
507
|
+
lineDashed: "\uD30C\uC120",
|
|
508
|
+
lineDotted: "\uC810\uC120",
|
|
509
|
+
drawingText: "\uD14D\uC2A4\uD2B8",
|
|
510
|
+
textPlaceholder: "\uD14D\uC2A4\uD2B8 \uC785\uB825",
|
|
511
|
+
lock: "\uC7A0\uADF8\uAE30",
|
|
512
|
+
unlock: "\uC7A0\uAE08 \uD480\uAE30",
|
|
513
|
+
duplicate: "\uBCF5\uC81C",
|
|
514
|
+
moveBar: "\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30 (\uB450 \uBC88 \uB204\uB974\uBA74 \uC81C\uC790\uB9AC)",
|
|
501
515
|
indicators: "\uC9C0\uD45C",
|
|
502
516
|
addIndicator: "\uC9C0\uD45C \uCD94\uAC00",
|
|
503
517
|
indicatorSettings: "\uC9C0\uD45C \uC124\uC815",
|
|
@@ -514,7 +528,6 @@ var STRINGS = {
|
|
|
514
528
|
removePeriod: "\uAE30\uAC04 \uC0AD\uC81C",
|
|
515
529
|
period: "\uAE30\uAC04",
|
|
516
530
|
source: "\uAC00\uACA9 \uAE30\uC900",
|
|
517
|
-
lineWidth: "\uB450\uAED8",
|
|
518
531
|
inputs: "\uC785\uB825\uAC12",
|
|
519
532
|
style: "\uC2A4\uD0C0\uC77C",
|
|
520
533
|
// intervals
|
|
@@ -631,6 +644,20 @@ var STRINGS = {
|
|
|
631
644
|
magnet: "Magnet (snap to OHLC)",
|
|
632
645
|
remove: "Remove",
|
|
633
646
|
color: "Color",
|
|
647
|
+
customColor: "Other color",
|
|
648
|
+
partColors: "Part colors",
|
|
649
|
+
resetColors: "Reset colors",
|
|
650
|
+
lineWidth: "Line width",
|
|
651
|
+
lineStyle: "Line style",
|
|
652
|
+
lineSolid: "Solid",
|
|
653
|
+
lineDashed: "Dashed",
|
|
654
|
+
lineDotted: "Dotted",
|
|
655
|
+
drawingText: "Text",
|
|
656
|
+
textPlaceholder: "Type text",
|
|
657
|
+
lock: "Lock",
|
|
658
|
+
unlock: "Unlock",
|
|
659
|
+
duplicate: "Duplicate",
|
|
660
|
+
moveBar: "Drag to move (double-click to reset)",
|
|
634
661
|
indicators: "Indicators",
|
|
635
662
|
addIndicator: "Add indicator",
|
|
636
663
|
indicatorSettings: "Indicator settings",
|
|
@@ -647,7 +674,6 @@ var STRINGS = {
|
|
|
647
674
|
removePeriod: "Remove period",
|
|
648
675
|
period: "Period",
|
|
649
676
|
source: "Source",
|
|
650
|
-
lineWidth: "Width",
|
|
651
677
|
inputs: "Inputs",
|
|
652
678
|
style: "Style",
|
|
653
679
|
intervalMinutes: "Minutes",
|
|
@@ -859,6 +885,19 @@ var ToolsIcon = () => /* @__PURE__ */ a3(Icon, { size: 18, children: [
|
|
|
859
885
|
/* @__PURE__ */ a3("circle", { cx: "12.5", cy: "6", r: "2" }),
|
|
860
886
|
/* @__PURE__ */ a3("circle", { cx: "7.5", cy: "14", r: "2" })
|
|
861
887
|
] });
|
|
888
|
+
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" }) });
|
|
889
|
+
var PaletteIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
890
|
+
/* @__PURE__ */ a3("path", { d: "M10 3.5a6.5 6.5 0 1 0 0 13c1 0 1.5-.6 1.5-1.3 0-.9-.8-1.2-.8-2 0-.7.6-1.2 1.4-1.2h1.6a2.8 2.8 0 0 0 2.8-2.8C16.5 6 13.6 3.5 10 3.5Z" }),
|
|
891
|
+
/* @__PURE__ */ a3("path", { d: "M6.6 9.2h.01M8.4 6.6h.01M11.6 6.4h.01M13.6 8.6h.01", "stroke-width": "2.2" })
|
|
892
|
+
] });
|
|
893
|
+
var LockIcon = ({ open = false }) => /* @__PURE__ */ a3(Icon, { children: [
|
|
894
|
+
/* @__PURE__ */ a3("rect", { x: "4.5", y: "9", width: "11", height: "8", rx: "1.8" }),
|
|
895
|
+
/* @__PURE__ */ a3("path", { d: open ? "M7 9V6.5a3 3 0 0 1 5.8-1" : "M7 9V6.5a3 3 0 0 1 6 0V9" })
|
|
896
|
+
] });
|
|
897
|
+
var CopyIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
898
|
+
/* @__PURE__ */ a3("rect", { x: "7", y: "7", width: "9.5", height: "9.5", rx: "1.8" }),
|
|
899
|
+
/* @__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" })
|
|
900
|
+
] });
|
|
862
901
|
|
|
863
902
|
// src/store.ts
|
|
864
903
|
var Store = class {
|
|
@@ -933,8 +972,13 @@ function ToolGroup({
|
|
|
933
972
|
A2(() => {
|
|
934
973
|
if (!open) return;
|
|
935
974
|
const close = (e3) => !ref.current?.contains(e3.target) && onOpen(false);
|
|
975
|
+
const esc = (e3) => e3.key === "Escape" && onOpen(false);
|
|
936
976
|
document.addEventListener("pointerdown", close, true);
|
|
937
|
-
|
|
977
|
+
document.addEventListener("keydown", esc);
|
|
978
|
+
return () => {
|
|
979
|
+
document.removeEventListener("pointerdown", close, true);
|
|
980
|
+
document.removeEventListener("keydown", esc);
|
|
981
|
+
};
|
|
938
982
|
}, [open]);
|
|
939
983
|
const pick = (def) => {
|
|
940
984
|
setChosen(def.type);
|
|
@@ -943,7 +987,10 @@ function ToolGroup({
|
|
|
943
987
|
};
|
|
944
988
|
return /* @__PURE__ */ a3("div", { class: "fnc-tool-group", ref, "data-open": open, children: [
|
|
945
989
|
/* @__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:
|
|
990
|
+
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: [
|
|
991
|
+
/* @__PURE__ */ a3("span", { class: "fnc-tool-corner", children: /* @__PURE__ */ a3(CornerIcon, {}) }),
|
|
992
|
+
/* @__PURE__ */ a3("span", { class: "fnc-tool-chevron", children: /* @__PURE__ */ a3(ChevronRightIcon, {}) })
|
|
993
|
+
] }),
|
|
947
994
|
open && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-tool-menu", role: "menu", children: tools.map((def, i3) => {
|
|
948
995
|
const section = def.section?.[s3.locale];
|
|
949
996
|
const heading = section && section !== tools[i3 - 1]?.section?.[s3.locale];
|
|
@@ -1030,24 +1077,194 @@ function DrawingToolbar({ ctx }) {
|
|
|
1030
1077
|
] });
|
|
1031
1078
|
}
|
|
1032
1079
|
var SWATCHES = ["#3182F6", "#F04452", "#12B886", "#FF9F0A", "#7048E8"];
|
|
1080
|
+
var WIDTHS = [1, 2, 3, 4];
|
|
1081
|
+
var DASH_ORDER = ["solid", "dashed", "dotted"];
|
|
1082
|
+
var RECENT_KEY = "fnc-drawing-colors";
|
|
1083
|
+
var POS_KEY = "fnc-selbar-pos";
|
|
1084
|
+
var readJson = (key, fallback) => {
|
|
1085
|
+
try {
|
|
1086
|
+
const raw = localStorage.getItem(key);
|
|
1087
|
+
return raw ? JSON.parse(raw) : fallback;
|
|
1088
|
+
} catch {
|
|
1089
|
+
return fallback;
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
var writeJson = (key, value) => {
|
|
1093
|
+
try {
|
|
1094
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
1095
|
+
} catch {
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
function DashGlyph({ dash }) {
|
|
1099
|
+
const pattern = dash === "dashed" ? "4 3" : dash === "dotted" ? "1 3" : void 0;
|
|
1100
|
+
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 }) });
|
|
1101
|
+
}
|
|
1033
1102
|
function SelectionBar({ ctx }) {
|
|
1034
1103
|
const s3 = useStore(ctx.store);
|
|
1035
1104
|
const t3 = STRINGS[s3.locale];
|
|
1036
1105
|
const d3 = s3.selection;
|
|
1106
|
+
const [offset, setOffset] = d2(() => readJson(POS_KEY, [0, 0]));
|
|
1107
|
+
const [recent, setRecent] = d2(() => readJson(RECENT_KEY, []));
|
|
1108
|
+
const [menu, setMenu] = d2(null);
|
|
1109
|
+
const colorInput = T2(null);
|
|
1037
1110
|
if (!d3 || s3.layout.plotWidth <= 0) return null;
|
|
1038
|
-
|
|
1039
|
-
|
|
1111
|
+
const def = ctx.chart.drawingDefinitions().find((x2) => x2.type === d3.type);
|
|
1112
|
+
const style = d3.style ?? {};
|
|
1113
|
+
const setStyle = (patch) => ctx.chart.updateDrawing(d3.id, { style: { ...style, ...patch } });
|
|
1114
|
+
const current = d3.color ?? SWATCHES[0];
|
|
1115
|
+
const pickColor = (color) => {
|
|
1116
|
+
if (style.colors) {
|
|
1117
|
+
const { colors: _drop, ...rest } = style;
|
|
1118
|
+
ctx.chart.updateDrawing(d3.id, { color, style: rest });
|
|
1119
|
+
} else ctx.chart.updateDrawing(d3.id, { color });
|
|
1120
|
+
};
|
|
1121
|
+
const slots = def?.colors ?? [];
|
|
1122
|
+
const slotColor = (key, fallback) => style.colors?.[key] ?? d3.color ?? fallback;
|
|
1123
|
+
const addRecent = (color) => {
|
|
1124
|
+
const next = [color, ...recent.filter((c3) => c3 !== color)].slice(0, 3);
|
|
1125
|
+
setRecent(next);
|
|
1126
|
+
writeJson(RECENT_KEY, next);
|
|
1127
|
+
};
|
|
1128
|
+
const locked = !!style.locked;
|
|
1129
|
+
const dash = style.dash ?? "solid";
|
|
1130
|
+
const startMove = (e3) => {
|
|
1131
|
+
e3.preventDefault();
|
|
1132
|
+
const start = [e3.clientX, e3.clientY];
|
|
1133
|
+
const from = offset;
|
|
1134
|
+
const move = (ev) => setOffset([from[0] + ev.clientX - start[0], from[1] + ev.clientY - start[1]]);
|
|
1135
|
+
const up = (ev) => {
|
|
1136
|
+
document.removeEventListener("pointermove", move);
|
|
1137
|
+
document.removeEventListener("pointerup", up);
|
|
1138
|
+
const next = [from[0] + ev.clientX - start[0], from[1] + ev.clientY - start[1]];
|
|
1139
|
+
setOffset(next);
|
|
1140
|
+
writeJson(POS_KEY, next);
|
|
1141
|
+
};
|
|
1142
|
+
document.addEventListener("pointermove", move);
|
|
1143
|
+
document.addEventListener("pointerup", up);
|
|
1144
|
+
};
|
|
1145
|
+
return /* @__PURE__ */ a3("div", { class: "fnc-selbar", role: "toolbar", style: { left: `${s3.layout.plotWidth / 2 + offset[0]}px`, top: `${8 + offset[1]}px` }, children: [
|
|
1146
|
+
/* @__PURE__ */ a3(
|
|
1147
|
+
"button",
|
|
1148
|
+
{
|
|
1149
|
+
class: "fnc-btn fnc-selbar-grip",
|
|
1150
|
+
"data-tip": t3.moveBar,
|
|
1151
|
+
"aria-label": t3.moveBar,
|
|
1152
|
+
onPointerDown: startMove,
|
|
1153
|
+
onDblClick: () => {
|
|
1154
|
+
setOffset([0, 0]);
|
|
1155
|
+
writeJson(POS_KEY, [0, 0]);
|
|
1156
|
+
},
|
|
1157
|
+
children: /* @__PURE__ */ a3(GripIcon, {})
|
|
1158
|
+
}
|
|
1159
|
+
),
|
|
1160
|
+
[...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) })),
|
|
1161
|
+
/* @__PURE__ */ a3("label", { class: "fnc-swatch fnc-swatch-add", "data-tip": t3.customColor, "aria-label": t3.customColor, children: [
|
|
1162
|
+
"+",
|
|
1163
|
+
/* @__PURE__ */ a3(
|
|
1164
|
+
"input",
|
|
1165
|
+
{
|
|
1166
|
+
ref: colorInput,
|
|
1167
|
+
type: "color",
|
|
1168
|
+
value: /^#[0-9a-f]{6}$/i.test(current) ? current : "#3182f6",
|
|
1169
|
+
onInput: (e3) => pickColor(e3.target.value),
|
|
1170
|
+
onChange: (e3) => addRecent(e3.target.value)
|
|
1171
|
+
}
|
|
1172
|
+
)
|
|
1173
|
+
] }),
|
|
1174
|
+
slots.length > 0 && /* @__PURE__ */ a3("div", { class: "fnc-selbar-menu", children: [
|
|
1175
|
+
/* @__PURE__ */ a3("button", { class: "fnc-btn", "data-tip": t3.partColors, "aria-label": t3.partColors, "aria-expanded": menu === "colors", onClick: () => setMenu(menu === "colors" ? null : "colors"), children: /* @__PURE__ */ a3(PaletteIcon, {}) }),
|
|
1176
|
+
menu === "colors" && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-selbar-pop fnc-part-menu", role: "menu", children: [
|
|
1177
|
+
slots.map((slot) => {
|
|
1178
|
+
const c3 = slotColor(slot.key, slot.color);
|
|
1179
|
+
return /* @__PURE__ */ a3("label", { class: "fnc-menu-item fnc-part", children: [
|
|
1180
|
+
/* @__PURE__ */ a3("span", { class: "fnc-swatch", style: { background: c3 } }),
|
|
1181
|
+
/* @__PURE__ */ a3("span", { children: slot.name[s3.locale] }),
|
|
1182
|
+
/* @__PURE__ */ a3(
|
|
1183
|
+
"input",
|
|
1184
|
+
{
|
|
1185
|
+
type: "color",
|
|
1186
|
+
"aria-label": `${slot.name[s3.locale]} ${t3.color}`,
|
|
1187
|
+
value: /^#[0-9a-f]{6}$/i.test(c3) ? c3 : "#3182f6",
|
|
1188
|
+
onInput: (e3) => setStyle({ colors: { ...style.colors, [slot.key]: e3.target.value } })
|
|
1189
|
+
}
|
|
1190
|
+
)
|
|
1191
|
+
] });
|
|
1192
|
+
}),
|
|
1193
|
+
/* @__PURE__ */ a3(
|
|
1194
|
+
"button",
|
|
1195
|
+
{
|
|
1196
|
+
class: "fnc-menu-item fnc-part-reset",
|
|
1197
|
+
role: "menuitem",
|
|
1198
|
+
onClick: () => {
|
|
1199
|
+
const { colors: _drop, ...rest } = style;
|
|
1200
|
+
ctx.chart.updateDrawing(d3.id, { style: rest });
|
|
1201
|
+
setMenu(null);
|
|
1202
|
+
},
|
|
1203
|
+
children: t3.resetColors
|
|
1204
|
+
}
|
|
1205
|
+
)
|
|
1206
|
+
] })
|
|
1207
|
+
] }),
|
|
1208
|
+
/* @__PURE__ */ a3("div", { class: "fnc-sep" }),
|
|
1209
|
+
/* @__PURE__ */ a3("div", { class: "fnc-selbar-menu", children: [
|
|
1210
|
+
/* @__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" }) }) }),
|
|
1211
|
+
menu === "width" && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-selbar-pop", role: "menu", children: WIDTHS.map((w3) => /* @__PURE__ */ a3(
|
|
1212
|
+
"button",
|
|
1213
|
+
{
|
|
1214
|
+
class: "fnc-menu-item",
|
|
1215
|
+
role: "menuitemradio",
|
|
1216
|
+
"aria-checked": (style.width ?? 2) === w3,
|
|
1217
|
+
onClick: () => {
|
|
1218
|
+
setStyle({ width: w3 });
|
|
1219
|
+
setMenu(null);
|
|
1220
|
+
},
|
|
1221
|
+
children: [
|
|
1222
|
+
/* @__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" }) }),
|
|
1223
|
+
/* @__PURE__ */ a3("span", { children: [
|
|
1224
|
+
w3,
|
|
1225
|
+
"px"
|
|
1226
|
+
] })
|
|
1227
|
+
]
|
|
1228
|
+
}
|
|
1229
|
+
)) })
|
|
1230
|
+
] }),
|
|
1231
|
+
/* @__PURE__ */ a3(
|
|
1040
1232
|
"button",
|
|
1041
1233
|
{
|
|
1042
|
-
class: "fnc-
|
|
1043
|
-
|
|
1044
|
-
"
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
onClick: () => ctx.chart.updateDrawing(d3.id, { color })
|
|
1234
|
+
class: "fnc-btn",
|
|
1235
|
+
"data-tip": `${t3.lineStyle}: ${dash === "solid" ? t3.lineSolid : dash === "dashed" ? t3.lineDashed : t3.lineDotted}`,
|
|
1236
|
+
"aria-label": t3.lineStyle,
|
|
1237
|
+
onClick: () => setStyle({ dash: DASH_ORDER[(DASH_ORDER.indexOf(dash) + 1) % DASH_ORDER.length] }),
|
|
1238
|
+
children: /* @__PURE__ */ a3(DashGlyph, { dash })
|
|
1048
1239
|
}
|
|
1049
|
-
)
|
|
1240
|
+
),
|
|
1241
|
+
def?.text && /* @__PURE__ */ a3(
|
|
1242
|
+
"input",
|
|
1243
|
+
{
|
|
1244
|
+
class: "fnc-selbar-text",
|
|
1245
|
+
value: style.text ?? "",
|
|
1246
|
+
placeholder: t3.textPlaceholder,
|
|
1247
|
+
"aria-label": t3.drawingText,
|
|
1248
|
+
maxLength: 200,
|
|
1249
|
+
onInput: (e3) => setStyle({ text: e3.target.value }),
|
|
1250
|
+
onKeyDown: (e3) => e3.stopPropagation()
|
|
1251
|
+
}
|
|
1252
|
+
),
|
|
1050
1253
|
/* @__PURE__ */ a3("div", { class: "fnc-sep" }),
|
|
1254
|
+
/* @__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 }) }),
|
|
1255
|
+
/* @__PURE__ */ a3(
|
|
1256
|
+
"button",
|
|
1257
|
+
{
|
|
1258
|
+
class: "fnc-btn",
|
|
1259
|
+
"data-tip": t3.duplicate,
|
|
1260
|
+
"aria-label": t3.duplicate,
|
|
1261
|
+
onClick: () => {
|
|
1262
|
+
const id = ctx.chart.addDrawing({ type: d3.type, points: d3.points.map((p3) => ({ ...p3 })), ...d3.color ? { color: d3.color } : {}, style: { ...style, locked: false } });
|
|
1263
|
+
ctx.chart.selectDrawing(id);
|
|
1264
|
+
},
|
|
1265
|
+
children: /* @__PURE__ */ a3(CopyIcon, {})
|
|
1266
|
+
}
|
|
1267
|
+
),
|
|
1051
1268
|
/* @__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
1269
|
] });
|
|
1053
1270
|
}
|
|
@@ -1255,7 +1472,7 @@ function IndicatorPicker({ ctx }) {
|
|
|
1255
1472
|
] })
|
|
1256
1473
|
] });
|
|
1257
1474
|
}
|
|
1258
|
-
var
|
|
1475
|
+
var WIDTHS2 = [1, 2, 3, 4];
|
|
1259
1476
|
function defaultSeries(def, params) {
|
|
1260
1477
|
return def.compute([], params).series.filter((s3) => s3.kind === "line");
|
|
1261
1478
|
}
|
|
@@ -1285,7 +1502,7 @@ function StyleControls({
|
|
|
1285
1502
|
onInput: (e3) => onChange({ color: e3.currentTarget.value })
|
|
1286
1503
|
}
|
|
1287
1504
|
),
|
|
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:
|
|
1505
|
+
/* @__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
1506
|
w3,
|
|
1290
1507
|
"px"
|
|
1291
1508
|
] })) })
|
|
@@ -1599,7 +1816,7 @@ function SymbolAvatar({ info, size = 24 }) {
|
|
|
1599
1816
|
// src/components/symbol-search.tsx
|
|
1600
1817
|
import { formatPrice } from "@funnest/charts";
|
|
1601
1818
|
var DEBOUNCE_MS = 150;
|
|
1602
|
-
var
|
|
1819
|
+
var RECENT_KEY2 = "recent-symbols";
|
|
1603
1820
|
var FAVORITE_KEY = "favorite-symbols";
|
|
1604
1821
|
var MAX_RECENT = 8;
|
|
1605
1822
|
var displayCode = (symbol) => symbol.slice(symbol.lastIndexOf(":") + 1);
|
|
@@ -1638,7 +1855,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1638
1855
|
const [exchange, setExchange] = d2(null);
|
|
1639
1856
|
const [results, setResults] = d2(null);
|
|
1640
1857
|
const [loading, setLoading] = d2(true);
|
|
1641
|
-
const [recent, setRecent] = d2(() => ctx.storage.get(
|
|
1858
|
+
const [recent, setRecent] = d2(() => ctx.storage.get(RECENT_KEY2, []));
|
|
1642
1859
|
const [favorites, setFavorites] = d2(() => ctx.storage.get(FAVORITE_KEY, []));
|
|
1643
1860
|
const [active, setActive] = d2(0);
|
|
1644
1861
|
const inputRef = T2(null);
|
|
@@ -1682,7 +1899,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1682
1899
|
const toggleFavorite = (info) => save(FAVORITE_KEY, isFavorite(info) ? favorites.filter((f4) => f4.symbol !== info.symbol) : [...favorites, toStored(info)], setFavorites);
|
|
1683
1900
|
const choose = (info) => {
|
|
1684
1901
|
if (!info) return;
|
|
1685
|
-
save(
|
|
1902
|
+
save(RECENT_KEY2, [toStored(info), ...recent.filter((r3) => r3.symbol !== info.symbol)].slice(0, MAX_RECENT), setRecent);
|
|
1686
1903
|
close();
|
|
1687
1904
|
if (comparing) ctx.chart.addCompare(info.symbol).catch((error) => console.error("funnest-charts: compare failed", error));
|
|
1688
1905
|
else ctx.selectSymbol(info.symbol);
|
|
@@ -1757,7 +1974,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1757
1974
|
"data-tip": t3.removeRecent,
|
|
1758
1975
|
onClick: (e3) => {
|
|
1759
1976
|
e3.stopPropagation();
|
|
1760
|
-
save(
|
|
1977
|
+
save(RECENT_KEY2, recent.filter((r3) => r3.symbol !== info.symbol), setRecent);
|
|
1761
1978
|
},
|
|
1762
1979
|
children: /* @__PURE__ */ a3(SmallCloseIcon, {})
|
|
1763
1980
|
}
|
|
@@ -1808,7 +2025,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1808
2025
|
/* @__PURE__ */ a3("div", { class: "fnc-sr-list fnc-scroll", id: "fnc-search-results", role: "listbox", ref: listRef, children: [
|
|
1809
2026
|
recentRows.length > 0 && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: [
|
|
1810
2027
|
/* @__PURE__ */ a3("span", { children: t3.recentSearches }),
|
|
1811
|
-
/* @__PURE__ */ a3("button", { class: "fnc-link", onClick: () => save(
|
|
2028
|
+
/* @__PURE__ */ a3("button", { class: "fnc-link", onClick: () => save(RECENT_KEY2, [], setRecent), children: t3.clearRecent })
|
|
1812
2029
|
] }),
|
|
1813
2030
|
recentRows.map((info, i3) => row2(info, i3, true)),
|
|
1814
2031
|
(recentRows.length > 0 || view.kind === "favorites") && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: view.kind === "favorites" ? t3.favorites : t3.symbols }),
|
|
@@ -2102,7 +2319,7 @@ function Toolbar({ ctx }) {
|
|
|
2102
2319
|
"data-tip": t3.addIndicator,
|
|
2103
2320
|
"aria-label": t3.addIndicator,
|
|
2104
2321
|
"aria-haspopup": "dialog",
|
|
2105
|
-
onClick: () => ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
2322
|
+
onClick: () => ctx.onIndicatorsClick ? ctx.onIndicatorsClick(ctx.chart) : ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
2106
2323
|
children: [
|
|
2107
2324
|
/* @__PURE__ */ a3(IndicatorIcon, {}),
|
|
2108
2325
|
/* @__PURE__ */ a3("span", { children: t3.indicators })
|
|
@@ -2715,6 +2932,9 @@ var CSS = (
|
|
|
2715
2932
|
/* \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
2933
|
.fnc-drawbar-host { flex: none; display: flex; }
|
|
2717
2934
|
.fnc-drawbar {
|
|
2935
|
+
/* Above the plot, so a tool list tab (it sticks out over the chart) can be clicked. */
|
|
2936
|
+
position: relative;
|
|
2937
|
+
z-index: 5;
|
|
2718
2938
|
display: flex;
|
|
2719
2939
|
flex-direction: column;
|
|
2720
2940
|
align-items: center;
|
|
@@ -2748,6 +2968,21 @@ var CSS = (
|
|
|
2748
2968
|
}
|
|
2749
2969
|
.fnc-swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--fnc-bg), 0 0 0 4px var(--fnc-text-2); }
|
|
2750
2970
|
.fnc-selbar .fnc-btn { width: 32px; height: 32px; }
|
|
2971
|
+
.fnc-selbar { padding-left: 4px; }
|
|
2972
|
+
.fnc-selbar-grip { cursor: grab; touch-action: none; }
|
|
2973
|
+
.fnc-selbar-grip:active { cursor: grabbing; }
|
|
2974
|
+
.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; }
|
|
2975
|
+
.fnc-swatch-add input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
|
|
2976
|
+
.fnc-selbar-menu { position: relative; }
|
|
2977
|
+
.fnc-selbar-pop { top: calc(100% + 6px); left: 50%; transform: translateX(-50%); min-width: 110px; }
|
|
2978
|
+
.fnc-selbar-pop .fnc-menu-item { gap: 10px; }
|
|
2979
|
+
.fnc-part-menu { min-width: 150px; max-height: 320px; overflow-y: auto; }
|
|
2980
|
+
.fnc-part { position: relative; cursor: pointer; }
|
|
2981
|
+
.fnc-part .fnc-swatch { width: 14px; height: 14px; box-shadow: 0 0 0 1px var(--fnc-border); }
|
|
2982
|
+
.fnc-part input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
|
|
2983
|
+
.fnc-part-reset { margin-top: 4px; border-top: 1px solid var(--fnc-border); color: var(--fnc-text-2); }
|
|
2984
|
+
.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; }
|
|
2985
|
+
.fnc-selbar-text:focus { border-color: var(--fnc-text-2); }
|
|
2751
2986
|
.fnc-overlay { pointer-events: none; }
|
|
2752
2987
|
.fnc-overlay button { pointer-events: auto; }
|
|
2753
2988
|
|
|
@@ -3462,20 +3697,50 @@ var CSS = (
|
|
|
3462
3697
|
/* \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
3698
|
.fnc-tool-group { position: relative; display: flex; }
|
|
3464
3699
|
.fnc-tool-group[data-open='true'] { z-index: 30; }
|
|
3700
|
+
/* The list opener: a corner mark at rest; on hover a tab slides out to the right of the
|
|
3701
|
+
button (a bigger target, like the reference terminals). Touch screens always show the tab. */
|
|
3465
3702
|
.fnc-tool-more {
|
|
3466
3703
|
position: absolute;
|
|
3704
|
+
top: 0;
|
|
3467
3705
|
right: -2px;
|
|
3468
|
-
bottom:
|
|
3706
|
+
bottom: 0;
|
|
3469
3707
|
display: flex;
|
|
3470
3708
|
align-items: flex-end;
|
|
3471
3709
|
justify-content: flex-end;
|
|
3472
|
-
width:
|
|
3473
|
-
|
|
3474
|
-
|
|
3710
|
+
width: 12px;
|
|
3711
|
+
padding: 0 0 2px;
|
|
3712
|
+
border-radius: 0 var(--fnc-radius) var(--fnc-radius) 0;
|
|
3475
3713
|
color: var(--fnc-text-2);
|
|
3476
|
-
|
|
3714
|
+
transition: right 0.12s ease, width 0.12s ease, background 0.12s ease;
|
|
3715
|
+
}
|
|
3716
|
+
.fnc-tool-corner { display: flex; opacity: 0.7; }
|
|
3717
|
+
.fnc-tool-chevron { display: none; }
|
|
3718
|
+
.fnc-tool-chevron svg { width: 12px; height: 12px; }
|
|
3719
|
+
.fnc-tool-group:hover .fnc-tool-more,
|
|
3720
|
+
.fnc-tool-more[aria-expanded='true'],
|
|
3721
|
+
.fnc-tool-more:focus-visible {
|
|
3722
|
+
right: -13px;
|
|
3723
|
+
width: 14px;
|
|
3724
|
+
align-items: center;
|
|
3725
|
+
justify-content: center;
|
|
3726
|
+
padding: 0;
|
|
3727
|
+
background: var(--fnc-surface);
|
|
3728
|
+
color: var(--fnc-text);
|
|
3729
|
+
box-shadow: 0 0 0 1px var(--fnc-border);
|
|
3730
|
+
}
|
|
3731
|
+
.fnc-tool-group:hover .fnc-tool-corner,
|
|
3732
|
+
.fnc-tool-more[aria-expanded='true'] .fnc-tool-corner,
|
|
3733
|
+
.fnc-tool-more:focus-visible .fnc-tool-corner { display: none; }
|
|
3734
|
+
.fnc-tool-group:hover .fnc-tool-chevron,
|
|
3735
|
+
.fnc-tool-more[aria-expanded='true'] .fnc-tool-chevron,
|
|
3736
|
+
.fnc-tool-more:focus-visible .fnc-tool-chevron { display: flex; }
|
|
3737
|
+
/* Once the list is open the tab has done its job: it fades out (clicking its spot still closes the list). */
|
|
3738
|
+
.fnc-tool-group[data-open='true'] .fnc-tool-more { opacity: 0; }
|
|
3739
|
+
@media (hover: none) {
|
|
3740
|
+
.fnc-tool-more { right: -12px; width: 14px; align-items: center; justify-content: center; padding: 0; }
|
|
3741
|
+
.fnc-tool-corner { display: none; }
|
|
3742
|
+
.fnc-tool-chevron { display: flex; }
|
|
3477
3743
|
}
|
|
3478
|
-
.fnc-tool-more:hover, .fnc-tool-more[aria-expanded='true'] { opacity: 1; color: var(--fnc-text); }
|
|
3479
3744
|
.fnc-tool-menu { top: 0; left: calc(100% + 8px); min-width: 200px; max-height: min(640px, calc(100vh - 120px)); overflow-y: auto; }
|
|
3480
3745
|
.fnc-tool-section { padding: 8px 10px 4px; color: var(--fnc-text-2); font-size: 11px; font-weight: 600; }
|
|
3481
3746
|
.fnc-tool-section.is-after { margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--fnc-border); }
|
|
@@ -3937,6 +4202,7 @@ function createPane(shell, config) {
|
|
|
3937
4202
|
drawingToolbar: config.drawingToolbar,
|
|
3938
4203
|
attribution: config.attribution,
|
|
3939
4204
|
contextMenuItems: config.contextMenuItems,
|
|
4205
|
+
onIndicatorsClick: config.onIndicatorsClick,
|
|
3940
4206
|
slots: shell.slots,
|
|
3941
4207
|
multi: config.multi,
|
|
3942
4208
|
setDrawbarCollapsed: shell.setDrawbarCollapsed,
|
|
@@ -4012,6 +4278,7 @@ function createTradingChart(container, options) {
|
|
|
4012
4278
|
attribution = true,
|
|
4013
4279
|
colorScheme = "system",
|
|
4014
4280
|
contextMenuItems,
|
|
4281
|
+
onIndicatorsClick,
|
|
4015
4282
|
theme,
|
|
4016
4283
|
...chartOptions
|
|
4017
4284
|
} = options;
|
|
@@ -4036,7 +4303,8 @@ function createTradingChart(container, options) {
|
|
|
4036
4303
|
searchHotkey,
|
|
4037
4304
|
drawingToolbar,
|
|
4038
4305
|
attribution,
|
|
4039
|
-
contextMenuItems
|
|
4306
|
+
contextMenuItems,
|
|
4307
|
+
onIndicatorsClick
|
|
4040
4308
|
});
|
|
4041
4309
|
shell.main.insertBefore(pane.stage, shell.slots.bottom);
|
|
4042
4310
|
shell.setPanes(() => [pane]);
|
|
@@ -4108,6 +4376,7 @@ function createMultiChart(container, options) {
|
|
|
4108
4376
|
attribution = true,
|
|
4109
4377
|
colorScheme = "system",
|
|
4110
4378
|
contextMenuItems,
|
|
4379
|
+
onIndicatorsClick,
|
|
4111
4380
|
theme,
|
|
4112
4381
|
...chartOptions
|
|
4113
4382
|
} = options;
|
|
@@ -4175,6 +4444,7 @@ function createMultiChart(container, options) {
|
|
|
4175
4444
|
// One badge for the whole widget, not one per pane.
|
|
4176
4445
|
attribution: attribution && index === 0,
|
|
4177
4446
|
contextMenuItems,
|
|
4447
|
+
onIndicatorsClick,
|
|
4178
4448
|
multi: controls
|
|
4179
4449
|
});
|
|
4180
4450
|
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.12",
|
|
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.12"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"preact": "^11.2.0"
|