@funnest/charts-ui 0.6.0 → 0.6.2
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/context-menu.d.ts +8 -0
- package/dist/components/layout-menu.d.ts +1 -6
- package/dist/components/tools-menu.d.ts +10 -0
- package/dist/context.d.ts +13 -1
- package/dist/i18n.d.ts +8 -0
- package/dist/icons.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +373 -323
- package/dist/pane.d.ts +2 -1
- package/dist/trading-chart.d.ts +6 -1
- package/package.json +2 -2
- package/dist/components/chart-type-menu.d.ts +0 -5
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WidgetContext } from '../context';
|
|
2
|
+
/**
|
|
3
|
+
* Right-click menu on the chart (mouse only): undo/redo, the host's own items
|
|
4
|
+
* (`contextMenuItems`), a horizontal line at the clicked price, reset and settings.
|
|
5
|
+
*/
|
|
6
|
+
export declare function ContextMenu({ ctx }: {
|
|
7
|
+
ctx: WidgetContext;
|
|
8
|
+
}): import("preact").JSX.Element | null;
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
2
|
import { type LayoutId } from '../layouts';
|
|
3
3
|
/** Mini drawing of a split layout. */
|
|
4
4
|
export declare function LayoutGlyph({ id }: {
|
|
5
5
|
id: LayoutId;
|
|
6
6
|
}): import("preact").JSX.Element;
|
|
7
|
-
/** Toolbar buttons of the multi-chart widget: split layout picker and pane sync toggles. */
|
|
8
|
-
export declare function LayoutMenu({ ctx, multi }: {
|
|
9
|
-
ctx: WidgetContext;
|
|
10
|
-
multi: MultiControls;
|
|
11
|
-
}): import("preact").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JSX } from 'preact';
|
|
2
|
+
import type { WidgetContext } from '../context';
|
|
3
|
+
/**
|
|
4
|
+
* One "도구" button for the chart's less frequent tools, so the toolbar keeps symbol, interval,
|
|
5
|
+
* market info and 지표 in view: chart type, split layout (+ sync), compare, replay, price axis,
|
|
6
|
+
* screenshot and chart settings.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ToolsMenu({ ctx }: {
|
|
9
|
+
ctx: WidgetContext;
|
|
10
|
+
}): JSX.Element;
|
package/dist/context.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Chart, ChartLayout, ChartType, CompareInfo, Datafeed, Drawing, DrawingType, IndicatorConfig, Locale, ReplayStatus, Resolution, SymbolInfo } from '@funnest/charts';
|
|
1
|
+
import type { Chart, ChartContextMenu, ChartLayout, ChartType, CompareInfo, Datafeed, Drawing, DrawingType, IndicatorConfig, Locale, ReplayStatus, Resolution, SymbolInfo } from '@funnest/charts';
|
|
2
2
|
import type { LayoutId } from './layouts';
|
|
3
3
|
import type { WidgetStorage } from './storage';
|
|
4
4
|
import type { Store } from './store';
|
|
@@ -39,6 +39,17 @@ export interface UiState {
|
|
|
39
39
|
compares: CompareInfo[];
|
|
40
40
|
replay: ReplayStatus;
|
|
41
41
|
}
|
|
42
|
+
/** A row in the chart's right-click menu, or a divider. */
|
|
43
|
+
export type ContextMenuItem = {
|
|
44
|
+
label: string;
|
|
45
|
+
shortcut?: string;
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
onSelect(): void;
|
|
48
|
+
} | 'separator';
|
|
49
|
+
/** Extra right-click items for a click on `chart` (e.g. "create an alert at this price"). */
|
|
50
|
+
export type ContextMenuItems = (target: ChartContextMenu & {
|
|
51
|
+
chart: Chart;
|
|
52
|
+
}) => ContextMenuItem[];
|
|
42
53
|
export interface IntervalGroup {
|
|
43
54
|
id: 'minutes' | 'hours' | 'days';
|
|
44
55
|
intervals: Resolution[];
|
|
@@ -65,6 +76,7 @@ export interface WidgetContext {
|
|
|
65
76
|
drawingToolbar: boolean;
|
|
66
77
|
/** Show the Funnest logo badge on the chart. */
|
|
67
78
|
attribution: boolean;
|
|
79
|
+
contextMenuItems: ContextMenuItems | undefined;
|
|
68
80
|
slots: {
|
|
69
81
|
toolbar: HTMLElement;
|
|
70
82
|
toolbarEnd: HTMLElement;
|
package/dist/i18n.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ export declare const STRINGS: {
|
|
|
47
47
|
scaleLog: string;
|
|
48
48
|
scalePercent: string;
|
|
49
49
|
undo: string;
|
|
50
|
+
tools: string;
|
|
51
|
+
horizontalAt: string;
|
|
52
|
+
resetChart: string;
|
|
53
|
+
chartSettingsMore: string;
|
|
50
54
|
redo: string;
|
|
51
55
|
compare: string;
|
|
52
56
|
compareSymbol: string;
|
|
@@ -173,6 +177,10 @@ export declare const STRINGS: {
|
|
|
173
177
|
scaleLog: string;
|
|
174
178
|
scalePercent: string;
|
|
175
179
|
undo: string;
|
|
180
|
+
tools: string;
|
|
181
|
+
horizontalAt: string;
|
|
182
|
+
resetChart: string;
|
|
183
|
+
chartSettingsMore: string;
|
|
176
184
|
redo: string;
|
|
177
185
|
compare: string;
|
|
178
186
|
compareSymbol: string;
|
package/dist/icons.d.ts
CHANGED
|
@@ -40,8 +40,6 @@ export declare const FunnestMark: () => import("preact").JSX.Element;
|
|
|
40
40
|
export declare const LinkIcon: () => import("preact").JSX.Element;
|
|
41
41
|
export declare const MaximizeIcon: () => import("preact").JSX.Element;
|
|
42
42
|
export declare const RestoreIcon: () => import("preact").JSX.Element;
|
|
43
|
-
export declare const UndoIcon: () => import("preact").JSX.Element;
|
|
44
|
-
export declare const RedoIcon: () => import("preact").JSX.Element;
|
|
45
43
|
/** Two price lines diverging (compare symbols). */
|
|
46
44
|
export declare const CompareIcon: () => import("preact").JSX.Element;
|
|
47
45
|
export declare const ReplayIcon: () => import("preact").JSX.Element;
|
|
@@ -54,3 +52,5 @@ export declare const SvgIcon: ({ markup }: {
|
|
|
54
52
|
}) => import("preact").JSX.Element;
|
|
55
53
|
/** Tiny corner triangle on drawing-bar buttons that hold more tools. */
|
|
56
54
|
export declare const CornerIcon: () => import("preact").JSX.Element;
|
|
55
|
+
/** Sliders: the 도구 menu. */
|
|
56
|
+
export declare const ToolsIcon: () => import("preact").JSX.Element;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export { createMultiChart } from './multi-chart';
|
|
|
8
8
|
export type { MultiChart, MultiChartEvents, MultiChartOptions, MultiChartState, PaneSpec } from './multi-chart';
|
|
9
9
|
export { LAYOUT_GROUPS, LAYOUTS, MAX_PANES, paneCount } from './layouts';
|
|
10
10
|
export type { LayoutId, SplitNode, SplitSizes } from './layouts';
|
|
11
|
-
export type { IntervalGroup, LayoutSync, SearchCategory } from './context';
|
|
11
|
+
export type { ContextMenuItem, ContextMenuItems, IntervalGroup, LayoutSync, SearchCategory } from './context';
|
|
12
12
|
export type { ColorScheme, TradingChart, TradingChartOptions, TradingChartSlots } from './trading-chart';
|
|
13
13
|
export { resolutionLabel } from './i18n';
|
package/dist/index.js
CHANGED
|
@@ -470,6 +470,10 @@ var STRINGS = {
|
|
|
470
470
|
scaleLog: "\uB85C\uADF8",
|
|
471
471
|
scalePercent: "%",
|
|
472
472
|
undo: "\uB418\uB3CC\uB9AC\uAE30",
|
|
473
|
+
tools: "\uB3C4\uAD6C",
|
|
474
|
+
horizontalAt: "{price}\uC5D0 \uC218\uD3C9\uC120 \uAE0B\uAE30",
|
|
475
|
+
resetChart: "\uCC28\uD2B8 \uCD08\uAE30\uD654",
|
|
476
|
+
chartSettingsMore: "\uCC28\uD2B8 \uC124\uC815\u2026",
|
|
473
477
|
redo: "\uB2E4\uC2DC \uC2E4\uD589",
|
|
474
478
|
compare: "\uBE44\uAD50",
|
|
475
479
|
compareSymbol: "\uBE44\uAD50\uD560 \uC885\uBAA9 \uCD94\uAC00",
|
|
@@ -598,6 +602,10 @@ var STRINGS = {
|
|
|
598
602
|
scaleLog: "Log",
|
|
599
603
|
scalePercent: "%",
|
|
600
604
|
undo: "Undo",
|
|
605
|
+
tools: "Tools",
|
|
606
|
+
horizontalAt: "Horizontal line at {price}",
|
|
607
|
+
resetChart: "Reset chart",
|
|
608
|
+
chartSettingsMore: "Chart settings\u2026",
|
|
601
609
|
redo: "Redo",
|
|
602
610
|
compare: "Compare",
|
|
603
611
|
compareSymbol: "Add symbol to compare",
|
|
@@ -809,20 +817,8 @@ var FunnestMark = () => /* @__PURE__ */ a3("svg", { width: "20", height: "20", v
|
|
|
809
817
|
/* @__PURE__ */ a3("path", { d: "M7 13.5C9.5 21.5 17 23.5 23.4 11.2", stroke: "#fff", "stroke-width": "3.2", "stroke-linecap": "round" }),
|
|
810
818
|
/* @__PURE__ */ a3("circle", { cx: "24.6", cy: "8.6", r: "2.9", fill: "#FFD43B" })
|
|
811
819
|
] });
|
|
812
|
-
var LinkIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
813
|
-
/* @__PURE__ */ a3("path", { d: "M8.5 11.5a3 3 0 0 0 4.2 0l2.6-2.6a3 3 0 0 0-4.2-4.2l-1 1" }),
|
|
814
|
-
/* @__PURE__ */ a3("path", { d: "M11.5 8.5a3 3 0 0 0-4.2 0l-2.6 2.6a3 3 0 0 0 4.2 4.2l1-1" })
|
|
815
|
-
] });
|
|
816
820
|
var MaximizeIcon = () => /* @__PURE__ */ a3(Icon, { size: 16, children: /* @__PURE__ */ a3("path", { d: "M11 4h5v5M9 16H4v-5M16 4l-5 5M4 16l5-5" }) });
|
|
817
821
|
var RestoreIcon = () => /* @__PURE__ */ a3(Icon, { size: 16, children: /* @__PURE__ */ a3("path", { d: "M15 9h-4V5M5 11h4v4M11 9l5-5M9 11l-5 5" }) });
|
|
818
|
-
var UndoIcon = () => /* @__PURE__ */ a3(Icon, { size: 18, children: [
|
|
819
|
-
/* @__PURE__ */ a3("path", { d: "M7 5L3.5 8.5 7 12" }),
|
|
820
|
-
/* @__PURE__ */ a3("path", { d: "M3.5 8.5h8.5a4.5 4.5 0 0 1 0 9H9" })
|
|
821
|
-
] });
|
|
822
|
-
var RedoIcon = () => /* @__PURE__ */ a3(Icon, { size: 18, children: [
|
|
823
|
-
/* @__PURE__ */ a3("path", { d: "M13 5l3.5 3.5L13 12" }),
|
|
824
|
-
/* @__PURE__ */ a3("path", { d: "M16.5 8.5H8a4.5 4.5 0 0 0 0 9h3" })
|
|
825
|
-
] });
|
|
826
822
|
var CompareIcon = () => /* @__PURE__ */ a3(Icon, { children: [
|
|
827
823
|
/* @__PURE__ */ a3("path", { d: "M3 14l4-4 3 2 7-7" }),
|
|
828
824
|
/* @__PURE__ */ a3("path", { d: "M3 9l4 2 3-3 7 5", opacity: "0.55" })
|
|
@@ -853,6 +849,11 @@ var SvgIcon = ({ markup }) => markup.trim().startsWith("<svg") ? /* @__PURE__ */
|
|
|
853
849
|
}
|
|
854
850
|
);
|
|
855
851
|
var CornerIcon = () => /* @__PURE__ */ a3("svg", { width: "6", height: "6", viewBox: "0 0 6 6", "aria-hidden": "true", children: /* @__PURE__ */ a3("path", { d: "M6 0v6H0z", fill: "currentColor" }) });
|
|
852
|
+
var ToolsIcon = () => /* @__PURE__ */ a3(Icon, { size: 18, children: [
|
|
853
|
+
/* @__PURE__ */ a3("path", { d: "M3.5 6h7M14.5 6h2M3.5 14h2M9.5 14h7" }),
|
|
854
|
+
/* @__PURE__ */ a3("circle", { cx: "12.5", cy: "6", r: "2" }),
|
|
855
|
+
/* @__PURE__ */ a3("circle", { cx: "7.5", cy: "14", r: "2" })
|
|
856
|
+
] });
|
|
856
857
|
|
|
857
858
|
// src/store.ts
|
|
858
859
|
var Store = class {
|
|
@@ -1039,93 +1040,6 @@ function SelectionBar({ ctx }) {
|
|
|
1039
1040
|
] });
|
|
1040
1041
|
}
|
|
1041
1042
|
|
|
1042
|
-
// src/components/keep-in-view.ts
|
|
1043
|
-
var MARGIN = 8;
|
|
1044
|
-
function useKeepInView(open) {
|
|
1045
|
-
const ref = T2(null);
|
|
1046
|
-
F2(() => {
|
|
1047
|
-
const el = ref.current;
|
|
1048
|
-
if (!open || !el) return;
|
|
1049
|
-
el.style.translate = "";
|
|
1050
|
-
const fit = () => {
|
|
1051
|
-
el.style.translate = "";
|
|
1052
|
-
const r3 = el.getBoundingClientRect();
|
|
1053
|
-
const root = el.closest(".fnc-root")?.getBoundingClientRect();
|
|
1054
|
-
const minX = Math.max(0, root?.left ?? 0) + MARGIN;
|
|
1055
|
-
const maxX = Math.min(document.documentElement.clientWidth, root?.right ?? Infinity) - MARGIN;
|
|
1056
|
-
let dx = 0;
|
|
1057
|
-
if (r3.right > maxX) dx = maxX - r3.right;
|
|
1058
|
-
if (r3.left + dx < minX) dx = minX - r3.left;
|
|
1059
|
-
if (dx) el.style.translate = `${Math.round(dx)}px 0`;
|
|
1060
|
-
};
|
|
1061
|
-
fit();
|
|
1062
|
-
window.addEventListener("resize", fit);
|
|
1063
|
-
return () => window.removeEventListener("resize", fit);
|
|
1064
|
-
}, [open]);
|
|
1065
|
-
return ref;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// src/components/chart-type-menu.tsx
|
|
1069
|
-
var TYPES = [
|
|
1070
|
-
{ type: "candles", label: "candles", icon: CandlesIcon },
|
|
1071
|
-
{ type: "line", label: "line", icon: LineChartIcon },
|
|
1072
|
-
{ type: "area", label: "area", icon: AreaChartIcon }
|
|
1073
|
-
];
|
|
1074
|
-
function ChartTypeMenu({ ctx }) {
|
|
1075
|
-
const s3 = useStore(ctx.store);
|
|
1076
|
-
const t3 = STRINGS[s3.locale];
|
|
1077
|
-
const [open, setOpen] = d2(false);
|
|
1078
|
-
const ref = T2(null);
|
|
1079
|
-
const menuRef = useKeepInView(open);
|
|
1080
|
-
const current = TYPES.find((x2) => x2.type === s3.chartType) ?? TYPES[0];
|
|
1081
|
-
A2(() => {
|
|
1082
|
-
if (!open) return;
|
|
1083
|
-
const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
|
|
1084
|
-
const onKey = (e3) => e3.key === "Escape" && setOpen(false);
|
|
1085
|
-
document.addEventListener("pointerdown", onDown);
|
|
1086
|
-
document.addEventListener("keydown", onKey);
|
|
1087
|
-
return () => {
|
|
1088
|
-
document.removeEventListener("pointerdown", onDown);
|
|
1089
|
-
document.removeEventListener("keydown", onKey);
|
|
1090
|
-
};
|
|
1091
|
-
}, [open]);
|
|
1092
|
-
const choose = (type) => {
|
|
1093
|
-
ctx.chart.setChartType(type);
|
|
1094
|
-
ctx.store.set({ chartType: type });
|
|
1095
|
-
setOpen(false);
|
|
1096
|
-
};
|
|
1097
|
-
return /* @__PURE__ */ a3("div", { class: "fnc-menu-wrap fnc-chart-type", ref, children: [
|
|
1098
|
-
/* @__PURE__ */ a3(
|
|
1099
|
-
"button",
|
|
1100
|
-
{
|
|
1101
|
-
class: "fnc-btn fnc-btn-wide",
|
|
1102
|
-
"data-tip": t3.chartType,
|
|
1103
|
-
"aria-label": t3.chartType,
|
|
1104
|
-
"aria-haspopup": "menu",
|
|
1105
|
-
"aria-expanded": open,
|
|
1106
|
-
onClick: () => setOpen(!open),
|
|
1107
|
-
children: [
|
|
1108
|
-
/* @__PURE__ */ a3(current.icon, {}),
|
|
1109
|
-
/* @__PURE__ */ a3(ChevronDownIcon, {})
|
|
1110
|
-
]
|
|
1111
|
-
}
|
|
1112
|
-
),
|
|
1113
|
-
open && /* @__PURE__ */ a3("div", { class: "fnc-menu", role: "menu", ref: menuRef, children: TYPES.map(({ type, label, icon: Icon2 }) => /* @__PURE__ */ a3(
|
|
1114
|
-
"button",
|
|
1115
|
-
{
|
|
1116
|
-
class: "fnc-menu-item",
|
|
1117
|
-
role: "menuitemradio",
|
|
1118
|
-
"aria-checked": type === s3.chartType,
|
|
1119
|
-
onClick: () => choose(type),
|
|
1120
|
-
children: [
|
|
1121
|
-
/* @__PURE__ */ a3(Icon2, {}),
|
|
1122
|
-
/* @__PURE__ */ a3("span", { children: t3[label] })
|
|
1123
|
-
]
|
|
1124
|
-
}
|
|
1125
|
-
)) })
|
|
1126
|
-
] });
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
1043
|
// src/components/indicator-dialogs.tsx
|
|
1130
1044
|
import {
|
|
1131
1045
|
maLines,
|
|
@@ -1486,6 +1400,32 @@ function Dialogs({ ctx }) {
|
|
|
1486
1400
|
return null;
|
|
1487
1401
|
}
|
|
1488
1402
|
|
|
1403
|
+
// src/components/keep-in-view.ts
|
|
1404
|
+
var MARGIN = 8;
|
|
1405
|
+
function useKeepInView(open) {
|
|
1406
|
+
const ref = T2(null);
|
|
1407
|
+
F2(() => {
|
|
1408
|
+
const el = ref.current;
|
|
1409
|
+
if (!open || !el) return;
|
|
1410
|
+
el.style.translate = "";
|
|
1411
|
+
const fit = () => {
|
|
1412
|
+
el.style.translate = "";
|
|
1413
|
+
const r3 = el.getBoundingClientRect();
|
|
1414
|
+
const root = el.closest(".fnc-root")?.getBoundingClientRect();
|
|
1415
|
+
const minX = Math.max(0, root?.left ?? 0) + MARGIN;
|
|
1416
|
+
const maxX = Math.min(document.documentElement.clientWidth, root?.right ?? Infinity) - MARGIN;
|
|
1417
|
+
let dx = 0;
|
|
1418
|
+
if (r3.right > maxX) dx = maxX - r3.right;
|
|
1419
|
+
if (r3.left + dx < minX) dx = minX - r3.left;
|
|
1420
|
+
if (dx) el.style.translate = `${Math.round(dx)}px 0`;
|
|
1421
|
+
};
|
|
1422
|
+
fit();
|
|
1423
|
+
window.addEventListener("resize", fit);
|
|
1424
|
+
return () => window.removeEventListener("resize", fit);
|
|
1425
|
+
}, [open]);
|
|
1426
|
+
return ref;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1489
1429
|
// src/components/interval-picker.tsx
|
|
1490
1430
|
var GROUP_LABEL = {
|
|
1491
1431
|
minutes: "intervalMinutes",
|
|
@@ -1579,181 +1519,6 @@ function IntervalPicker({ ctx }) {
|
|
|
1579
1519
|
] });
|
|
1580
1520
|
}
|
|
1581
1521
|
|
|
1582
|
-
// src/layouts.ts
|
|
1583
|
-
var row = (...children) => ({ dir: "row", children });
|
|
1584
|
-
var col = (...children) => ({ dir: "col", children });
|
|
1585
|
-
var LAYOUTS = {
|
|
1586
|
-
"1": 0,
|
|
1587
|
-
"2-cols": row(0, 1),
|
|
1588
|
-
"2-rows": col(0, 1),
|
|
1589
|
-
"3-cols": row(0, 1, 2),
|
|
1590
|
-
"3-rows": col(0, 1, 2),
|
|
1591
|
-
"3-left": row(0, col(1, 2)),
|
|
1592
|
-
"3-top": col(0, row(1, 2)),
|
|
1593
|
-
"4-grid": col(row(0, 1), row(2, 3)),
|
|
1594
|
-
"4-cols": row(0, 1, 2, 3),
|
|
1595
|
-
"4-left": row(0, col(1, 2, 3)),
|
|
1596
|
-
"6-grid": col(row(0, 1, 2), row(3, 4, 5)),
|
|
1597
|
-
"8-grid": col(row(0, 1, 2, 3), row(4, 5, 6, 7))
|
|
1598
|
-
};
|
|
1599
|
-
var LAYOUT_GROUPS = [
|
|
1600
|
-
["1"],
|
|
1601
|
-
["2-cols", "2-rows"],
|
|
1602
|
-
["3-cols", "3-rows", "3-left", "3-top"],
|
|
1603
|
-
["4-grid", "4-cols", "4-left"],
|
|
1604
|
-
["6-grid", "8-grid"]
|
|
1605
|
-
];
|
|
1606
|
-
var MAX_PANES = 8;
|
|
1607
|
-
function isLayoutId(value) {
|
|
1608
|
-
return typeof value === "string" && value in LAYOUTS;
|
|
1609
|
-
}
|
|
1610
|
-
function paneCount(node) {
|
|
1611
|
-
if (typeof node === "number") return 1;
|
|
1612
|
-
let n2 = 0;
|
|
1613
|
-
for (const child of node.children) n2 += paneCount(child);
|
|
1614
|
-
return n2;
|
|
1615
|
-
}
|
|
1616
|
-
function sizesFor(sizes, key, count) {
|
|
1617
|
-
const s3 = sizes[key];
|
|
1618
|
-
return s3 && s3.length === count && s3.every((v3) => Number.isFinite(v3) && v3 > 0) ? s3 : Array(count).fill(1);
|
|
1619
|
-
}
|
|
1620
|
-
function resizePair(weights, i3, deltaFraction, min = 0.12) {
|
|
1621
|
-
const total = weights.reduce((a5, b2) => a5 + b2, 0);
|
|
1622
|
-
const a4 = weights[i3];
|
|
1623
|
-
const b = weights[i3 + 1];
|
|
1624
|
-
const pair = a4 + b;
|
|
1625
|
-
const delta = deltaFraction * total;
|
|
1626
|
-
const nextA = Math.min(pair * (1 - min), Math.max(pair * min, a4 + delta));
|
|
1627
|
-
const next = [...weights];
|
|
1628
|
-
next[i3] = nextA;
|
|
1629
|
-
next[i3 + 1] = pair - nextA;
|
|
1630
|
-
return next;
|
|
1631
|
-
}
|
|
1632
|
-
function layoutRects(node, x2 = 0, y3 = 0, w3 = 20, h3 = 16, gap = 1.6) {
|
|
1633
|
-
if (typeof node === "number") return [{ x: x2, y: y3, w: w3, h: h3 }];
|
|
1634
|
-
const n2 = node.children.length;
|
|
1635
|
-
const out = [];
|
|
1636
|
-
node.children.forEach((child, i3) => {
|
|
1637
|
-
if (node.dir === "row") {
|
|
1638
|
-
const cw = (w3 - gap * (n2 - 1)) / n2;
|
|
1639
|
-
out.push(...layoutRects(child, x2 + i3 * (cw + gap), y3, cw, h3, gap));
|
|
1640
|
-
} else {
|
|
1641
|
-
const ch = (h3 - gap * (n2 - 1)) / n2;
|
|
1642
|
-
out.push(...layoutRects(child, x2, y3 + i3 * (ch + gap), w3, ch, gap));
|
|
1643
|
-
}
|
|
1644
|
-
});
|
|
1645
|
-
return out;
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
// src/components/layout-menu.tsx
|
|
1649
|
-
var LAYOUT_PANES = (id) => paneCount(LAYOUTS[id]);
|
|
1650
|
-
function LayoutGlyph({ id }) {
|
|
1651
|
-
return /* @__PURE__ */ a3("svg", { width: "20", height: "16", viewBox: "0 0 20 16", "aria-hidden": "true", children: layoutRects(LAYOUTS[id]).map((r3) => /* @__PURE__ */ a3("rect", { x: r3.x + 0.5, y: r3.y + 0.5, width: Math.max(0, r3.w - 1), height: Math.max(0, r3.h - 1), rx: "1.5", fill: "none", stroke: "currentColor", "stroke-width": "1.2" })) });
|
|
1652
|
-
}
|
|
1653
|
-
function usePopover() {
|
|
1654
|
-
const [open, setOpen] = d2(false);
|
|
1655
|
-
const ref = T2(null);
|
|
1656
|
-
A2(() => {
|
|
1657
|
-
if (!open) return;
|
|
1658
|
-
const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
|
|
1659
|
-
const onKey = (e3) => e3.key === "Escape" && setOpen(false);
|
|
1660
|
-
document.addEventListener("pointerdown", onDown);
|
|
1661
|
-
document.addEventListener("keydown", onKey);
|
|
1662
|
-
return () => {
|
|
1663
|
-
document.removeEventListener("pointerdown", onDown);
|
|
1664
|
-
document.removeEventListener("keydown", onKey);
|
|
1665
|
-
};
|
|
1666
|
-
}, [open]);
|
|
1667
|
-
return { open, setOpen, ref };
|
|
1668
|
-
}
|
|
1669
|
-
var SYNC_KEYS = [
|
|
1670
|
-
{ key: "crosshair", label: "syncCrosshair" },
|
|
1671
|
-
{ key: "time", label: "syncTime" },
|
|
1672
|
-
{ key: "symbol", label: "syncSymbol" },
|
|
1673
|
-
{ key: "interval", label: "syncInterval" }
|
|
1674
|
-
];
|
|
1675
|
-
function LayoutMenu({ ctx, multi }) {
|
|
1676
|
-
const s3 = useStore(ctx.store);
|
|
1677
|
-
const m3 = useStore(multi.store);
|
|
1678
|
-
const t3 = STRINGS[s3.locale];
|
|
1679
|
-
const layout = usePopover();
|
|
1680
|
-
const sync = usePopover();
|
|
1681
|
-
const layoutMenuRef = useKeepInView(layout.open);
|
|
1682
|
-
const syncMenuRef = useKeepInView(sync.open);
|
|
1683
|
-
const anySync = Object.values(m3.sync).some(Boolean);
|
|
1684
|
-
return /* @__PURE__ */ a3($, { children: [
|
|
1685
|
-
/* @__PURE__ */ a3("div", { class: "fnc-menu-wrap", ref: layout.ref, children: [
|
|
1686
|
-
/* @__PURE__ */ a3(
|
|
1687
|
-
"button",
|
|
1688
|
-
{
|
|
1689
|
-
class: "fnc-btn fnc-btn-wide fnc-btn-layout",
|
|
1690
|
-
"data-tip": t3.layout,
|
|
1691
|
-
"aria-label": t3.layout,
|
|
1692
|
-
"aria-haspopup": "menu",
|
|
1693
|
-
"aria-expanded": layout.open,
|
|
1694
|
-
onClick: () => layout.setOpen(!layout.open),
|
|
1695
|
-
children: [
|
|
1696
|
-
/* @__PURE__ */ a3(LayoutGlyph, { id: m3.layout }),
|
|
1697
|
-
/* @__PURE__ */ a3(ChevronDownIcon, {})
|
|
1698
|
-
]
|
|
1699
|
-
}
|
|
1700
|
-
),
|
|
1701
|
-
layout.open && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-layout-menu", role: "menu", "aria-label": t3.layout, ref: layoutMenuRef, children: LAYOUT_GROUPS.map((group) => /* @__PURE__ */ a3("div", { class: "fnc-layout-row", children: [
|
|
1702
|
-
/* @__PURE__ */ a3("span", { class: "fnc-layout-count", children: group[0].split("-")[0] }),
|
|
1703
|
-
group.map((id) => /* @__PURE__ */ a3(
|
|
1704
|
-
"button",
|
|
1705
|
-
{
|
|
1706
|
-
class: "fnc-layout-opt",
|
|
1707
|
-
role: "menuitemradio",
|
|
1708
|
-
"aria-checked": id === m3.layout,
|
|
1709
|
-
"aria-label": `${t3.layout} ${id}`,
|
|
1710
|
-
"data-layout": id,
|
|
1711
|
-
onClick: () => {
|
|
1712
|
-
multi.setLayout(id);
|
|
1713
|
-
layout.setOpen(false);
|
|
1714
|
-
},
|
|
1715
|
-
children: /* @__PURE__ */ a3(LayoutGlyph, { id })
|
|
1716
|
-
}
|
|
1717
|
-
))
|
|
1718
|
-
] })) })
|
|
1719
|
-
] }),
|
|
1720
|
-
LAYOUT_PANES(m3.layout) > 1 && /* @__PURE__ */ a3("div", { class: "fnc-menu-wrap", ref: sync.ref, children: [
|
|
1721
|
-
/* @__PURE__ */ a3(
|
|
1722
|
-
"button",
|
|
1723
|
-
{
|
|
1724
|
-
class: "fnc-btn fnc-btn-sync",
|
|
1725
|
-
"data-tip": t3.sync,
|
|
1726
|
-
"aria-label": t3.sync,
|
|
1727
|
-
"aria-haspopup": "menu",
|
|
1728
|
-
"aria-expanded": sync.open,
|
|
1729
|
-
"aria-pressed": anySync,
|
|
1730
|
-
onClick: () => sync.setOpen(!sync.open),
|
|
1731
|
-
children: /* @__PURE__ */ a3(LinkIcon, {})
|
|
1732
|
-
}
|
|
1733
|
-
),
|
|
1734
|
-
sync.open && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-sync-menu", role: "menu", "aria-label": t3.sync, ref: syncMenuRef, children: [
|
|
1735
|
-
/* @__PURE__ */ a3("div", { class: "fnc-menu-title", children: [
|
|
1736
|
-
t3.sync,
|
|
1737
|
-
/* @__PURE__ */ a3("small", { children: t3.syncHint })
|
|
1738
|
-
] }),
|
|
1739
|
-
SYNC_KEYS.map(({ key, label }) => /* @__PURE__ */ a3(
|
|
1740
|
-
"button",
|
|
1741
|
-
{
|
|
1742
|
-
class: "fnc-menu-item fnc-sync-item",
|
|
1743
|
-
role: "menuitemcheckbox",
|
|
1744
|
-
"aria-checked": m3.sync[key],
|
|
1745
|
-
onClick: () => multi.setSync({ [key]: !m3.sync[key] }),
|
|
1746
|
-
children: [
|
|
1747
|
-
/* @__PURE__ */ a3("span", { children: t3[label] }),
|
|
1748
|
-
/* @__PURE__ */ a3("span", { class: "fnc-switch", "data-on": m3.sync[key] ? "true" : "false", "aria-hidden": "true" })
|
|
1749
|
-
]
|
|
1750
|
-
}
|
|
1751
|
-
))
|
|
1752
|
-
] })
|
|
1753
|
-
] })
|
|
1754
|
-
] });
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
1522
|
// src/components/slot.tsx
|
|
1758
1523
|
function Slot({ el, fixed = false }) {
|
|
1759
1524
|
const ref = T2(null);
|
|
@@ -2038,12 +1803,235 @@ function SymbolSearch({ ctx }) {
|
|
|
2038
1803
|
] }) });
|
|
2039
1804
|
}
|
|
2040
1805
|
|
|
1806
|
+
// src/layouts.ts
|
|
1807
|
+
var row = (...children) => ({ dir: "row", children });
|
|
1808
|
+
var col = (...children) => ({ dir: "col", children });
|
|
1809
|
+
var LAYOUTS = {
|
|
1810
|
+
"1": 0,
|
|
1811
|
+
"2-cols": row(0, 1),
|
|
1812
|
+
"2-rows": col(0, 1),
|
|
1813
|
+
"3-cols": row(0, 1, 2),
|
|
1814
|
+
"3-rows": col(0, 1, 2),
|
|
1815
|
+
"3-left": row(0, col(1, 2)),
|
|
1816
|
+
"3-top": col(0, row(1, 2)),
|
|
1817
|
+
"4-grid": col(row(0, 1), row(2, 3)),
|
|
1818
|
+
"4-cols": row(0, 1, 2, 3),
|
|
1819
|
+
"4-left": row(0, col(1, 2, 3)),
|
|
1820
|
+
"6-grid": col(row(0, 1, 2), row(3, 4, 5)),
|
|
1821
|
+
"8-grid": col(row(0, 1, 2, 3), row(4, 5, 6, 7))
|
|
1822
|
+
};
|
|
1823
|
+
var LAYOUT_GROUPS = [
|
|
1824
|
+
["1"],
|
|
1825
|
+
["2-cols", "2-rows"],
|
|
1826
|
+
["3-cols", "3-rows", "3-left", "3-top"],
|
|
1827
|
+
["4-grid", "4-cols", "4-left"],
|
|
1828
|
+
["6-grid", "8-grid"]
|
|
1829
|
+
];
|
|
1830
|
+
var MAX_PANES = 8;
|
|
1831
|
+
function isLayoutId(value) {
|
|
1832
|
+
return typeof value === "string" && value in LAYOUTS;
|
|
1833
|
+
}
|
|
1834
|
+
function paneCount(node) {
|
|
1835
|
+
if (typeof node === "number") return 1;
|
|
1836
|
+
let n2 = 0;
|
|
1837
|
+
for (const child of node.children) n2 += paneCount(child);
|
|
1838
|
+
return n2;
|
|
1839
|
+
}
|
|
1840
|
+
function sizesFor(sizes, key, count) {
|
|
1841
|
+
const s3 = sizes[key];
|
|
1842
|
+
return s3 && s3.length === count && s3.every((v3) => Number.isFinite(v3) && v3 > 0) ? s3 : Array(count).fill(1);
|
|
1843
|
+
}
|
|
1844
|
+
function resizePair(weights, i3, deltaFraction, min = 0.12) {
|
|
1845
|
+
const total = weights.reduce((a5, b2) => a5 + b2, 0);
|
|
1846
|
+
const a4 = weights[i3];
|
|
1847
|
+
const b = weights[i3 + 1];
|
|
1848
|
+
const pair = a4 + b;
|
|
1849
|
+
const delta = deltaFraction * total;
|
|
1850
|
+
const nextA = Math.min(pair * (1 - min), Math.max(pair * min, a4 + delta));
|
|
1851
|
+
const next = [...weights];
|
|
1852
|
+
next[i3] = nextA;
|
|
1853
|
+
next[i3 + 1] = pair - nextA;
|
|
1854
|
+
return next;
|
|
1855
|
+
}
|
|
1856
|
+
function layoutRects(node, x2 = 0, y3 = 0, w3 = 20, h3 = 16, gap = 1.6) {
|
|
1857
|
+
if (typeof node === "number") return [{ x: x2, y: y3, w: w3, h: h3 }];
|
|
1858
|
+
const n2 = node.children.length;
|
|
1859
|
+
const out = [];
|
|
1860
|
+
node.children.forEach((child, i3) => {
|
|
1861
|
+
if (node.dir === "row") {
|
|
1862
|
+
const cw = (w3 - gap * (n2 - 1)) / n2;
|
|
1863
|
+
out.push(...layoutRects(child, x2 + i3 * (cw + gap), y3, cw, h3, gap));
|
|
1864
|
+
} else {
|
|
1865
|
+
const ch = (h3 - gap * (n2 - 1)) / n2;
|
|
1866
|
+
out.push(...layoutRects(child, x2, y3 + i3 * (ch + gap), w3, ch, gap));
|
|
1867
|
+
}
|
|
1868
|
+
});
|
|
1869
|
+
return out;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// src/components/layout-menu.tsx
|
|
1873
|
+
function LayoutGlyph({ id }) {
|
|
1874
|
+
return /* @__PURE__ */ a3("svg", { width: "20", height: "16", viewBox: "0 0 20 16", "aria-hidden": "true", children: layoutRects(LAYOUTS[id]).map((r3) => /* @__PURE__ */ a3("rect", { x: r3.x + 0.5, y: r3.y + 0.5, width: Math.max(0, r3.w - 1), height: Math.max(0, r3.h - 1), rx: "1.5", fill: "none", stroke: "currentColor", "stroke-width": "1.2" })) });
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// src/components/tools-menu.tsx
|
|
1878
|
+
var TYPES = [
|
|
1879
|
+
{ type: "candles", label: "candles", icon: CandlesIcon },
|
|
1880
|
+
{ type: "line", label: "line", icon: LineChartIcon },
|
|
1881
|
+
{ type: "area", label: "area", icon: AreaChartIcon }
|
|
1882
|
+
];
|
|
1883
|
+
var SCALES2 = [
|
|
1884
|
+
{ mode: "normal", label: "scaleNormal" },
|
|
1885
|
+
{ mode: "log", label: "scaleLog" },
|
|
1886
|
+
{ mode: "percent", label: "scalePercent" }
|
|
1887
|
+
];
|
|
1888
|
+
var SYNC_KEYS = [
|
|
1889
|
+
{ key: "crosshair", label: "syncCrosshair" },
|
|
1890
|
+
{ key: "time", label: "syncTime" },
|
|
1891
|
+
{ key: "symbol", label: "syncSymbol" },
|
|
1892
|
+
{ key: "interval", label: "syncInterval" }
|
|
1893
|
+
];
|
|
1894
|
+
function LayoutSection({ ctx, multi, close }) {
|
|
1895
|
+
const s3 = useStore(ctx.store);
|
|
1896
|
+
const m3 = useStore(multi.store);
|
|
1897
|
+
const t3 = STRINGS[s3.locale];
|
|
1898
|
+
return /* @__PURE__ */ a3($, { children: [
|
|
1899
|
+
/* @__PURE__ */ a3("div", { class: "fnc-tools-section", children: t3.layout }),
|
|
1900
|
+
/* @__PURE__ */ a3("div", { class: "fnc-tools-layouts", children: LAYOUT_GROUPS.map((group) => /* @__PURE__ */ a3("div", { class: "fnc-layout-row", children: [
|
|
1901
|
+
/* @__PURE__ */ a3("span", { class: "fnc-layout-count", children: group[0].split("-")[0] }),
|
|
1902
|
+
group.map((id) => /* @__PURE__ */ a3(
|
|
1903
|
+
"button",
|
|
1904
|
+
{
|
|
1905
|
+
class: "fnc-layout-opt",
|
|
1906
|
+
role: "menuitemradio",
|
|
1907
|
+
"aria-checked": id === m3.layout,
|
|
1908
|
+
"aria-label": `${t3.layout} ${id}`,
|
|
1909
|
+
"data-layout": id,
|
|
1910
|
+
onClick: () => {
|
|
1911
|
+
multi.setLayout(id);
|
|
1912
|
+
close();
|
|
1913
|
+
},
|
|
1914
|
+
children: /* @__PURE__ */ a3(LayoutGlyph, { id })
|
|
1915
|
+
}
|
|
1916
|
+
))
|
|
1917
|
+
] })) }),
|
|
1918
|
+
paneCount(LAYOUTS[m3.layout]) > 1 && /* @__PURE__ */ a3("div", { class: "fnc-tools-section", children: t3.sync }),
|
|
1919
|
+
paneCount(LAYOUTS[m3.layout]) > 1 && SYNC_KEYS.map(({ key, label }) => /* @__PURE__ */ a3(
|
|
1920
|
+
"button",
|
|
1921
|
+
{
|
|
1922
|
+
class: "fnc-menu-item fnc-sync-item",
|
|
1923
|
+
role: "menuitemcheckbox",
|
|
1924
|
+
"aria-checked": m3.sync[key],
|
|
1925
|
+
onClick: () => multi.setSync({ [key]: !m3.sync[key] }),
|
|
1926
|
+
children: [
|
|
1927
|
+
/* @__PURE__ */ a3("span", { children: t3[label] }),
|
|
1928
|
+
/* @__PURE__ */ a3("span", { class: "fnc-switch", "data-on": m3.sync[key] ? "true" : "false", "aria-hidden": "true" })
|
|
1929
|
+
]
|
|
1930
|
+
}
|
|
1931
|
+
))
|
|
1932
|
+
] });
|
|
1933
|
+
}
|
|
1934
|
+
function ToolsMenu({ ctx }) {
|
|
1935
|
+
const s3 = useStore(ctx.store);
|
|
1936
|
+
const t3 = STRINGS[s3.locale];
|
|
1937
|
+
const [open, setOpen] = d2(false);
|
|
1938
|
+
const [scale, setScale] = d2(ctx.chart.settings.priceScale);
|
|
1939
|
+
const ref = T2(null);
|
|
1940
|
+
const menuRef = useKeepInView(open);
|
|
1941
|
+
const canSearch = typeof ctx.datafeed.searchSymbols === "function";
|
|
1942
|
+
const close = () => setOpen(false);
|
|
1943
|
+
A2(() => ctx.chart.on("settingsChange", (settings) => setScale(settings.priceScale)), [ctx.chart]);
|
|
1944
|
+
A2(() => {
|
|
1945
|
+
if (!open) return;
|
|
1946
|
+
const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
|
|
1947
|
+
const onKey = (e3) => e3.key === "Escape" && setOpen(false);
|
|
1948
|
+
document.addEventListener("pointerdown", onDown);
|
|
1949
|
+
document.addEventListener("keydown", onKey);
|
|
1950
|
+
return () => {
|
|
1951
|
+
document.removeEventListener("pointerdown", onDown);
|
|
1952
|
+
document.removeEventListener("keydown", onKey);
|
|
1953
|
+
};
|
|
1954
|
+
}, [open]);
|
|
1955
|
+
const item = (cls, label, icon, onClick, opts = {}) => /* @__PURE__ */ a3(
|
|
1956
|
+
"button",
|
|
1957
|
+
{
|
|
1958
|
+
class: `fnc-menu-item fnc-tools-item ${cls}`,
|
|
1959
|
+
role: "menuitem",
|
|
1960
|
+
disabled: opts.disabled,
|
|
1961
|
+
"aria-pressed": opts.pressed,
|
|
1962
|
+
onClick: () => {
|
|
1963
|
+
close();
|
|
1964
|
+
onClick();
|
|
1965
|
+
},
|
|
1966
|
+
children: [
|
|
1967
|
+
icon,
|
|
1968
|
+
/* @__PURE__ */ a3("span", { children: label })
|
|
1969
|
+
]
|
|
1970
|
+
}
|
|
1971
|
+
);
|
|
1972
|
+
return /* @__PURE__ */ a3("div", { class: "fnc-menu-wrap fnc-tools-wrap", ref, children: [
|
|
1973
|
+
/* @__PURE__ */ a3(
|
|
1974
|
+
"button",
|
|
1975
|
+
{
|
|
1976
|
+
class: "fnc-btn fnc-btn-wide fnc-btn-label fnc-btn-tools",
|
|
1977
|
+
"data-tip": t3.tools,
|
|
1978
|
+
"aria-label": t3.tools,
|
|
1979
|
+
"aria-haspopup": "menu",
|
|
1980
|
+
"aria-expanded": open,
|
|
1981
|
+
"aria-pressed": s3.replay.active || s3.replay.selecting || s3.compares.length > 0,
|
|
1982
|
+
onClick: () => setOpen(!open),
|
|
1983
|
+
children: [
|
|
1984
|
+
/* @__PURE__ */ a3(ToolsIcon, {}),
|
|
1985
|
+
/* @__PURE__ */ a3("span", { children: t3.tools }),
|
|
1986
|
+
/* @__PURE__ */ a3(ChevronDownIcon, {})
|
|
1987
|
+
]
|
|
1988
|
+
}
|
|
1989
|
+
),
|
|
1990
|
+
open && /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-tools-menu", role: "menu", "aria-label": t3.tools, ref: menuRef, children: [
|
|
1991
|
+
/* @__PURE__ */ a3("div", { class: "fnc-tools-section", children: t3.chartType }),
|
|
1992
|
+
/* @__PURE__ */ a3("div", { class: "fnc-seg fnc-tools-seg", role: "radiogroup", "aria-label": t3.chartType, children: TYPES.map(({ type, label, icon: Icon2 }) => /* @__PURE__ */ a3(
|
|
1993
|
+
"button",
|
|
1994
|
+
{
|
|
1995
|
+
type: "button",
|
|
1996
|
+
role: "radio",
|
|
1997
|
+
"aria-checked": s3.chartType === type,
|
|
1998
|
+
"data-type": type,
|
|
1999
|
+
onClick: () => {
|
|
2000
|
+
ctx.chart.setChartType(type);
|
|
2001
|
+
ctx.store.set({ chartType: type });
|
|
2002
|
+
},
|
|
2003
|
+
children: [
|
|
2004
|
+
/* @__PURE__ */ a3(Icon2, {}),
|
|
2005
|
+
/* @__PURE__ */ a3("span", { children: t3[label] })
|
|
2006
|
+
]
|
|
2007
|
+
}
|
|
2008
|
+
)) }),
|
|
2009
|
+
ctx.multi && /* @__PURE__ */ a3(LayoutSection, { ctx, multi: ctx.multi, close }),
|
|
2010
|
+
/* @__PURE__ */ a3("div", { class: "fnc-tools-sep" }),
|
|
2011
|
+
canSearch && item("fnc-btn-compare", t3.compareSymbol, /* @__PURE__ */ a3(CompareIcon, {}), () => ctx.setSearchOpen(true, "compare"), {
|
|
2012
|
+
disabled: s3.compares.length >= 5
|
|
2013
|
+
}),
|
|
2014
|
+
item(
|
|
2015
|
+
"fnc-btn-replay",
|
|
2016
|
+
s3.replay.active ? t3.replayExit : t3.replay,
|
|
2017
|
+
/* @__PURE__ */ a3(ReplayIcon, {}),
|
|
2018
|
+
() => s3.replay.active ? ctx.chart.stopReplay() : ctx.chart.setReplaySelecting(true),
|
|
2019
|
+
{ pressed: s3.replay.active || s3.replay.selecting }
|
|
2020
|
+
),
|
|
2021
|
+
item("fnc-btn-screenshot", t3.screenshot, /* @__PURE__ */ a3(CameraIcon, {}), ctx.screenshot),
|
|
2022
|
+
/* @__PURE__ */ a3("div", { class: "fnc-tools-section", children: t3.settingsScale }),
|
|
2023
|
+
/* @__PURE__ */ a3("div", { class: "fnc-seg fnc-tools-seg", role: "radiogroup", "aria-label": t3.settingsScale, children: SCALES2.map(({ mode, label }) => /* @__PURE__ */ a3("button", { type: "button", role: "radio", "aria-checked": scale === mode, onClick: () => ctx.chart.setSettings({ priceScale: mode }), children: /* @__PURE__ */ a3("span", { children: t3[label] }) })) }),
|
|
2024
|
+
/* @__PURE__ */ a3("div", { class: "fnc-tools-sep" }),
|
|
2025
|
+
item("fnc-btn-settings", t3.chartSettingsMore, /* @__PURE__ */ a3(SettingsIcon, {}), () => ctx.store.set({ dialog: { kind: "chartSettings" } }))
|
|
2026
|
+
] })
|
|
2027
|
+
] });
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2041
2030
|
// src/components/toolbar.tsx
|
|
2042
2031
|
function Toolbar({ ctx }) {
|
|
2043
2032
|
const s3 = useStore(ctx.store);
|
|
2044
2033
|
const t3 = STRINGS[s3.locale];
|
|
2045
2034
|
const canSearch = typeof ctx.datafeed.searchSymbols === "function";
|
|
2046
|
-
const mod = /Mac|iPhone|iPad/.test(navigator.platform) ? "\u2318" : "Ctrl";
|
|
2047
2035
|
return /* @__PURE__ */ a3("div", { class: "fnc-toolbar", role: "toolbar", children: [
|
|
2048
2036
|
/* @__PURE__ */ a3(
|
|
2049
2037
|
"button",
|
|
@@ -2065,7 +2053,6 @@ function Toolbar({ ctx }) {
|
|
|
2065
2053
|
/* @__PURE__ */ a3(Slot, { el: ctx.slots.toolbar }),
|
|
2066
2054
|
/* @__PURE__ */ a3("div", { class: "fnc-spacer" }),
|
|
2067
2055
|
/* @__PURE__ */ a3("div", { class: "fnc-tools", children: [
|
|
2068
|
-
/* @__PURE__ */ a3(ChartTypeMenu, { ctx }),
|
|
2069
2056
|
/* @__PURE__ */ a3(
|
|
2070
2057
|
"button",
|
|
2071
2058
|
{
|
|
@@ -2080,30 +2067,7 @@ function Toolbar({ ctx }) {
|
|
|
2080
2067
|
]
|
|
2081
2068
|
}
|
|
2082
2069
|
),
|
|
2083
|
-
|
|
2084
|
-
"button",
|
|
2085
|
-
{
|
|
2086
|
-
class: "fnc-btn fnc-btn-compare",
|
|
2087
|
-
"data-tip": t3.compareSymbol,
|
|
2088
|
-
"aria-label": t3.compareSymbol,
|
|
2089
|
-
"aria-haspopup": "dialog",
|
|
2090
|
-
disabled: s3.compares.length >= 5,
|
|
2091
|
-
onClick: () => ctx.setSearchOpen(true, "compare"),
|
|
2092
|
-
children: /* @__PURE__ */ a3(CompareIcon, {})
|
|
2093
|
-
}
|
|
2094
|
-
),
|
|
2095
|
-
/* @__PURE__ */ a3(
|
|
2096
|
-
"button",
|
|
2097
|
-
{
|
|
2098
|
-
class: "fnc-btn fnc-btn-replay",
|
|
2099
|
-
"data-tip": s3.replay.selecting ? t3.replayPick : t3.replay,
|
|
2100
|
-
"aria-label": t3.replay,
|
|
2101
|
-
"aria-pressed": s3.replay.selecting || s3.replay.active,
|
|
2102
|
-
onClick: () => s3.replay.active ? ctx.chart.stopReplay() : ctx.chart.setReplaySelecting(!s3.replay.selecting),
|
|
2103
|
-
children: /* @__PURE__ */ a3(ReplayIcon, {})
|
|
2104
|
-
}
|
|
2105
|
-
),
|
|
2106
|
-
ctx.multi && /* @__PURE__ */ a3(LayoutMenu, { ctx, multi: ctx.multi })
|
|
2070
|
+
/* @__PURE__ */ a3(ToolsMenu, { ctx })
|
|
2107
2071
|
] }),
|
|
2108
2072
|
/* @__PURE__ */ a3("div", { class: "fnc-sep" }),
|
|
2109
2073
|
ctx.drawingToolbar && /* @__PURE__ */ a3(
|
|
@@ -2117,21 +2081,7 @@ function Toolbar({ ctx }) {
|
|
|
2117
2081
|
children: /* @__PURE__ */ a3(PencilIcon, {})
|
|
2118
2082
|
}
|
|
2119
2083
|
),
|
|
2120
|
-
/* @__PURE__ */ a3("button", { class: "fnc-btn fnc-btn-undo", "data-tip": `${t3.undo} (${mod}+Z)`, "aria-label": t3.undo, disabled: !s3.canUndo, onClick: () => ctx.chart.undo(), children: /* @__PURE__ */ a3(UndoIcon, {}) }),
|
|
2121
|
-
/* @__PURE__ */ a3("button", { class: "fnc-btn fnc-btn-undo", "data-tip": `${t3.redo} (${mod}+Shift+Z)`, "aria-label": t3.redo, disabled: !s3.canRedo, onClick: () => ctx.chart.redo(), children: /* @__PURE__ */ a3(RedoIcon, {}) }),
|
|
2122
2084
|
/* @__PURE__ */ a3(Slot, { el: ctx.slots.toolbarEnd, fixed: true }),
|
|
2123
|
-
/* @__PURE__ */ a3(
|
|
2124
|
-
"button",
|
|
2125
|
-
{
|
|
2126
|
-
class: "fnc-btn fnc-btn-settings",
|
|
2127
|
-
"data-tip": t3.chartSettings,
|
|
2128
|
-
"aria-label": t3.chartSettings,
|
|
2129
|
-
"aria-haspopup": "dialog",
|
|
2130
|
-
onClick: () => ctx.store.set({ dialog: { kind: "chartSettings" } }),
|
|
2131
|
-
children: /* @__PURE__ */ a3(SettingsIcon, {})
|
|
2132
|
-
}
|
|
2133
|
-
),
|
|
2134
|
-
/* @__PURE__ */ a3("button", { class: "fnc-btn fnc-btn-screenshot", "data-tip": t3.screenshot, "aria-label": t3.screenshot, onClick: ctx.screenshot, children: /* @__PURE__ */ a3(CameraIcon, {}) }),
|
|
2135
2085
|
/* @__PURE__ */ a3(
|
|
2136
2086
|
"button",
|
|
2137
2087
|
{
|
|
@@ -2151,7 +2101,88 @@ function Toolbar({ ctx }) {
|
|
|
2151
2101
|
import { createChart } from "@funnest/charts";
|
|
2152
2102
|
|
|
2153
2103
|
// src/components/overlay.tsx
|
|
2154
|
-
import { formatCompact, formatPrice as
|
|
2104
|
+
import { formatCompact, formatPrice as formatPrice3 } from "@funnest/charts";
|
|
2105
|
+
|
|
2106
|
+
// src/components/context-menu.tsx
|
|
2107
|
+
import { formatPrice as formatPrice2 } from "@funnest/charts";
|
|
2108
|
+
var MARGIN2 = 8;
|
|
2109
|
+
function ContextMenu({ ctx }) {
|
|
2110
|
+
const s3 = useStore(ctx.store);
|
|
2111
|
+
const t3 = STRINGS[s3.locale];
|
|
2112
|
+
const [target, setTarget] = d2(null);
|
|
2113
|
+
const ref = T2(null);
|
|
2114
|
+
A2(() => ctx.chart.on("contextMenu", setTarget), [ctx.chart]);
|
|
2115
|
+
A2(() => {
|
|
2116
|
+
if (!target) return;
|
|
2117
|
+
const close = () => setTarget(null);
|
|
2118
|
+
const onDown = (e3) => !ref.current?.contains(e3.target) && close();
|
|
2119
|
+
const onKey = (e3) => e3.key === "Escape" && close();
|
|
2120
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
2121
|
+
document.addEventListener("keydown", onKey);
|
|
2122
|
+
window.addEventListener("wheel", close, { passive: true });
|
|
2123
|
+
window.addEventListener("resize", close);
|
|
2124
|
+
return () => {
|
|
2125
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
2126
|
+
document.removeEventListener("keydown", onKey);
|
|
2127
|
+
window.removeEventListener("wheel", close);
|
|
2128
|
+
window.removeEventListener("resize", close);
|
|
2129
|
+
};
|
|
2130
|
+
}, [target]);
|
|
2131
|
+
F2(() => {
|
|
2132
|
+
const el = ref.current;
|
|
2133
|
+
const stage = el?.closest(".fnc-stage");
|
|
2134
|
+
if (!el || !target || !stage) return;
|
|
2135
|
+
const w3 = el.offsetWidth;
|
|
2136
|
+
const h3 = el.offsetHeight;
|
|
2137
|
+
const { width, height } = stage.getBoundingClientRect();
|
|
2138
|
+
const x2 = target.x + w3 + MARGIN2 > width ? Math.max(MARGIN2, target.x - w3) : target.x;
|
|
2139
|
+
const y3 = target.y + h3 + MARGIN2 > height ? Math.max(MARGIN2, target.y - h3) : target.y;
|
|
2140
|
+
el.style.transform = `translate(${Math.round(x2)}px, ${Math.round(y3)}px)`;
|
|
2141
|
+
}, [target]);
|
|
2142
|
+
if (!target) return null;
|
|
2143
|
+
const mac = /Mac|iPhone|iPad/.test(navigator.platform);
|
|
2144
|
+
const mod = mac ? "\u2318" : "Ctrl+";
|
|
2145
|
+
const price = target.price;
|
|
2146
|
+
const precision = s3.symbol?.pricePrecision ?? 2;
|
|
2147
|
+
const extra = ctx.contextMenuItems?.({ ...target, chart: ctx.chart }) ?? [];
|
|
2148
|
+
const items = [
|
|
2149
|
+
{ label: t3.undo, shortcut: `${mod}Z`, disabled: !s3.canUndo, onSelect: () => ctx.chart.undo() },
|
|
2150
|
+
{ label: t3.redo, shortcut: `${mod}${mac ? "\u21E7" : "Shift+"}Z`, disabled: !s3.canRedo, onSelect: () => ctx.chart.redo() },
|
|
2151
|
+
"separator",
|
|
2152
|
+
...extra.length ? [...extra, "separator"] : []
|
|
2153
|
+
];
|
|
2154
|
+
if (price !== null) {
|
|
2155
|
+
const time = target.time ?? ctx.chart.bars[ctx.chart.bars.length - 1]?.time ?? Date.now();
|
|
2156
|
+
items.push({
|
|
2157
|
+
label: t3.horizontalAt.replace("{price}", formatPrice2(price, precision)),
|
|
2158
|
+
onSelect: () => ctx.chart.addDrawing({ type: "horizontal", points: [{ time, price }] })
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
items.push(
|
|
2162
|
+
{ label: t3.resetChart, onSelect: () => ctx.chart.resetView() },
|
|
2163
|
+
{ label: t3.chartSettingsMore, onSelect: () => ctx.store.set({ dialog: { kind: "chartSettings" } }) }
|
|
2164
|
+
);
|
|
2165
|
+
return /* @__PURE__ */ a3("div", { class: "fnc-menu fnc-ctx-menu", role: "menu", ref, onContextMenu: (e3) => e3.preventDefault(), children: items.map(
|
|
2166
|
+
(item) => item === "separator" ? /* @__PURE__ */ a3("div", { class: "fnc-ctx-sep", role: "separator" }) : /* @__PURE__ */ a3(
|
|
2167
|
+
"button",
|
|
2168
|
+
{
|
|
2169
|
+
class: "fnc-menu-item",
|
|
2170
|
+
role: "menuitem",
|
|
2171
|
+
disabled: item.disabled,
|
|
2172
|
+
onClick: () => {
|
|
2173
|
+
setTarget(null);
|
|
2174
|
+
item.onSelect();
|
|
2175
|
+
},
|
|
2176
|
+
children: [
|
|
2177
|
+
/* @__PURE__ */ a3("span", { children: item.label }),
|
|
2178
|
+
item.shortcut && /* @__PURE__ */ a3("kbd", { children: item.shortcut })
|
|
2179
|
+
]
|
|
2180
|
+
}
|
|
2181
|
+
)
|
|
2182
|
+
) });
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// src/components/overlay.tsx
|
|
2155
2186
|
var ZOOM_STEP = 1.25;
|
|
2156
2187
|
var CONTROL_OFFSET = 52;
|
|
2157
2188
|
var dirClass = (v3) => v3 > 0 ? "fnc-up" : v3 < 0 ? "fnc-down" : "fnc-flat";
|
|
@@ -2243,7 +2274,7 @@ function Legend({ ctx }) {
|
|
|
2243
2274
|
const bars = ctx.chart.bars;
|
|
2244
2275
|
const bar = hover?.bar ?? bars[bars.length - 1];
|
|
2245
2276
|
const prev = hover ? hover.prev : bars[bars.length - 2];
|
|
2246
|
-
const fmt = (p3) =>
|
|
2277
|
+
const fmt = (p3) => formatPrice3(p3, s3.symbol.pricePrecision);
|
|
2247
2278
|
let values = null;
|
|
2248
2279
|
if (bar) {
|
|
2249
2280
|
const cls = dirClass(bar.close - bar.open);
|
|
@@ -2452,7 +2483,8 @@ function Overlay({ ctx }) {
|
|
|
2452
2483
|
/* @__PURE__ */ a3(NavControls, { ctx }),
|
|
2453
2484
|
/* @__PURE__ */ a3(ReplayBar, { ctx }),
|
|
2454
2485
|
/* @__PURE__ */ a3(SelectionBar, { ctx }),
|
|
2455
|
-
/* @__PURE__ */ a3(PaneTools, { ctx })
|
|
2486
|
+
/* @__PURE__ */ a3(PaneTools, { ctx }),
|
|
2487
|
+
/* @__PURE__ */ a3(ContextMenu, { ctx })
|
|
2456
2488
|
] });
|
|
2457
2489
|
}
|
|
2458
2490
|
|
|
@@ -3255,14 +3287,12 @@ var CSS = (
|
|
|
3255
3287
|
every toolbar button stays reachable. Ctrl/\u2318+Z still undoes when the buttons are hidden. \u2500\u2500 */
|
|
3256
3288
|
.fnc-root { container: fnc / inline-size; }
|
|
3257
3289
|
@container fnc (max-width: 1100px) {
|
|
3258
|
-
.fnc-btn-undo { display: none; }
|
|
3259
3290
|
.fnc-btn-label span { display: none; }
|
|
3260
3291
|
.fnc-toolbar .fnc-btn-label { padding: 0 6px; }
|
|
3261
3292
|
}
|
|
3262
3293
|
@container fnc (max-width: 940px) {
|
|
3263
3294
|
.fnc-intervals { display: none; }
|
|
3264
3295
|
.fnc-interval-current { display: inline; }
|
|
3265
|
-
.fnc-btn-screenshot { display: none; }
|
|
3266
3296
|
}
|
|
3267
3297
|
|
|
3268
3298
|
/* \u2500\u2500 Narrow phones (\u2264 400px): drop the menu chevrons and separators so every button fits \u2500\u2500 */
|
|
@@ -3369,6 +3399,24 @@ var CSS = (
|
|
|
3369
3399
|
.fnc-switch[data-on='true'] { background: var(--fnc-text-2); }
|
|
3370
3400
|
.fnc-switch[data-on='true']::after { transform: translateX(16px); }
|
|
3371
3401
|
|
|
3402
|
+
/* \u2500\u2500 Right-click menu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
3403
|
+
.fnc-ctx-menu { top: 0; left: 0; z-index: 40; min-width: 220px; pointer-events: auto; }
|
|
3404
|
+
.fnc-ctx-menu .fnc-menu-item { justify-content: space-between; gap: 24px; color: var(--fnc-text); }
|
|
3405
|
+
.fnc-ctx-menu .fnc-menu-item:disabled { color: var(--fnc-text-2); }
|
|
3406
|
+
.fnc-ctx-menu kbd { font: inherit; font-size: 12px; color: var(--fnc-text-2); }
|
|
3407
|
+
.fnc-ctx-sep { height: 1px; margin: 4px 6px; background: var(--fnc-border); }
|
|
3408
|
+
|
|
3409
|
+
/* \u2500\u2500 \uB3C4\uAD6C menu: chart type, split, compare, replay, axis, screenshot, settings \u2500\u2500 */
|
|
3410
|
+
.fnc-tools-menu { width: 300px; padding: 8px; gap: 2px; max-height: 72vh; overflow-y: auto; }
|
|
3411
|
+
.fnc-tools-section { padding: 8px 6px 6px; font-size: 12px; font-weight: 700; color: var(--fnc-text-2); }
|
|
3412
|
+
.fnc-tools-seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; margin: 0 2px 4px; }
|
|
3413
|
+
.fnc-tools-seg button { display: inline-flex; align-items: center; justify-content: center; gap: 4px; min-width: 0; padding: 0 6px; }
|
|
3414
|
+
.fnc-tools-seg svg { width: 16px; height: 16px; flex: none; }
|
|
3415
|
+
.fnc-tools-layouts { display: flex; flex-direction: column; gap: 2px; padding: 0 2px 4px; }
|
|
3416
|
+
.fnc-tools-sep { height: 1px; margin: 6px 4px; background: var(--fnc-border); }
|
|
3417
|
+
.fnc-tools-item svg { flex: none; color: var(--fnc-text-2); }
|
|
3418
|
+
.fnc-tools-item[aria-pressed='true'] { background: var(--fnc-surface); color: var(--fnc-text); }
|
|
3419
|
+
|
|
3372
3420
|
/* \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 */
|
|
3373
3421
|
.fnc-tool-group { position: relative; display: flex; }
|
|
3374
3422
|
.fnc-tool-group[data-open='true'] { z-index: 30; }
|
|
@@ -3441,7 +3489,6 @@ var CSS = (
|
|
|
3441
3489
|
|
|
3442
3490
|
/* \u2500\u2500 Mobile \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
3443
3491
|
@media (max-width: 640px) {
|
|
3444
|
-
.fnc-btn-compare, .fnc-btn-replay, .fnc-btn-undo { display: none; }
|
|
3445
3492
|
.fnc-toolbar { padding: 0 4px; gap: 2px; }
|
|
3446
3493
|
.fnc-toolbar .fnc-btn { width: 32px; height: 32px; }
|
|
3447
3494
|
.fnc-toolbar .fnc-btn-wide, .fnc-toolbar .fnc-btn-label { width: auto; padding: 0 6px; }
|
|
@@ -3450,9 +3497,7 @@ var CSS = (
|
|
|
3450
3497
|
/* Logo only on phones; the name is in the chart legend right below. */
|
|
3451
3498
|
.fnc-toolbar .fnc-symbol:has(.fnc-avatar) :is(.fnc-symbol-name, .fnc-icon) { display: none; }
|
|
3452
3499
|
.fnc-chip { padding: 0 8px; }
|
|
3453
|
-
.fnc-btn-screenshot { display: none; }
|
|
3454
3500
|
.fnc-btn-label span { display: none; }
|
|
3455
|
-
.fnc-btn-sync { display: none; }
|
|
3456
3501
|
.fnc-intervals { display: none; }
|
|
3457
3502
|
.fnc-interval-current { display: inline; }
|
|
3458
3503
|
.fnc-interval-menu { left: auto; right: -40px; }
|
|
@@ -3809,6 +3854,7 @@ function createPane(shell, config) {
|
|
|
3809
3854
|
showLogo: config.symbolLogo,
|
|
3810
3855
|
drawingToolbar: config.drawingToolbar,
|
|
3811
3856
|
attribution: config.attribution,
|
|
3857
|
+
contextMenuItems: config.contextMenuItems,
|
|
3812
3858
|
slots: shell.slots,
|
|
3813
3859
|
multi: config.multi,
|
|
3814
3860
|
setDrawbarCollapsed: shell.setDrawbarCollapsed,
|
|
@@ -3883,6 +3929,7 @@ function createTradingChart(container, options) {
|
|
|
3883
3929
|
chartType = "candles",
|
|
3884
3930
|
attribution = true,
|
|
3885
3931
|
colorScheme = "system",
|
|
3932
|
+
contextMenuItems,
|
|
3886
3933
|
theme,
|
|
3887
3934
|
...chartOptions
|
|
3888
3935
|
} = options;
|
|
@@ -3905,7 +3952,8 @@ function createTradingChart(container, options) {
|
|
|
3905
3952
|
searchCategories,
|
|
3906
3953
|
symbolLogo,
|
|
3907
3954
|
drawingToolbar,
|
|
3908
|
-
attribution
|
|
3955
|
+
attribution,
|
|
3956
|
+
contextMenuItems
|
|
3909
3957
|
});
|
|
3910
3958
|
shell.main.insertBefore(pane.stage, shell.slots.bottom);
|
|
3911
3959
|
shell.setPanes(() => [pane]);
|
|
@@ -3976,6 +4024,7 @@ function createMultiChart(container, options) {
|
|
|
3976
4024
|
chartType = "candles",
|
|
3977
4025
|
attribution = true,
|
|
3978
4026
|
colorScheme = "system",
|
|
4027
|
+
contextMenuItems,
|
|
3979
4028
|
theme,
|
|
3980
4029
|
...chartOptions
|
|
3981
4030
|
} = options;
|
|
@@ -4041,6 +4090,7 @@ function createMultiChart(container, options) {
|
|
|
4041
4090
|
drawingToolbar,
|
|
4042
4091
|
// One badge for the whole widget, not one per pane.
|
|
4043
4092
|
attribution: attribution && index === 0,
|
|
4093
|
+
contextMenuItems,
|
|
4044
4094
|
multi: controls
|
|
4045
4095
|
});
|
|
4046
4096
|
const cell = div("fnc-pane");
|
package/dist/pane.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @jsxImportSource preact */
|
|
2
2
|
import { type Chart, type ChartOptions, type ChartType, type Datafeed, type Resolution } from '@funnest/charts';
|
|
3
|
-
import type { IntervalGroup, MultiControls, SearchCategory, UiState, WidgetContext } from './context';
|
|
3
|
+
import type { ContextMenuItems, IntervalGroup, MultiControls, SearchCategory, UiState, WidgetContext } from './context';
|
|
4
4
|
import { type Shell } from './shell';
|
|
5
5
|
import { Store } from './store';
|
|
6
6
|
export interface PaneConfig {
|
|
@@ -14,6 +14,7 @@ export interface PaneConfig {
|
|
|
14
14
|
symbolLogo: boolean;
|
|
15
15
|
drawingToolbar: boolean;
|
|
16
16
|
attribution: boolean;
|
|
17
|
+
contextMenuItems?: ContextMenuItems | undefined;
|
|
17
18
|
multi?: MultiControls | undefined;
|
|
18
19
|
}
|
|
19
20
|
/**
|
package/dist/trading-chart.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @jsxImportSource preact */
|
|
2
2
|
import { type Chart, type ChartOptions, type ChartType, type Datafeed, type Locale, type ChartState, type Resolution, type ThemeTokens } from '@funnest/charts';
|
|
3
|
-
import type { IntervalGroup, SearchCategory } from './context';
|
|
3
|
+
import type { ContextMenuItems, IntervalGroup, SearchCategory } from './context';
|
|
4
4
|
import { type ColorScheme, type TradingChartSlots } from './shell';
|
|
5
5
|
export type { ColorScheme, TradingChartSlots } from './shell';
|
|
6
6
|
/** Interval menu: 분 | 시간 | 일. '12M' is shown as 1년. */
|
|
@@ -45,6 +45,11 @@ export interface TradingChartOptions extends Omit<ChartOptions, 'datafeed'> {
|
|
|
45
45
|
* `theme` overrides are applied on top of whichever base is active.
|
|
46
46
|
*/
|
|
47
47
|
colorScheme?: ColorScheme;
|
|
48
|
+
/**
|
|
49
|
+
* Extra rows for the chart's right-click menu (between undo/redo and the built-in actions),
|
|
50
|
+
* e.g. "이 가격에 알림 만들기". Called on every right-click with the price and time under the pointer.
|
|
51
|
+
*/
|
|
52
|
+
contextMenuItems?: ContextMenuItems;
|
|
48
53
|
}
|
|
49
54
|
export interface TradingChart {
|
|
50
55
|
/** 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.2",
|
|
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.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"preact": "^11.2.0"
|