@funnest/charts-ui 0.6.15 → 0.6.17
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/context.d.ts +3 -0
- package/dist/i18n.d.ts +8 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +93 -39
- package/dist/pane.d.ts +2 -0
- package/dist/shell.d.ts +3 -1
- package/dist/trading-chart.d.ts +17 -1
- package/package.json +2 -2
package/dist/context.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
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
|
+
import type { Strings } from './i18n';
|
|
4
5
|
import type { Store } from './store';
|
|
5
6
|
export interface UiState {
|
|
6
7
|
locale: Locale;
|
|
8
|
+
/** The app's replacements for built-in words (TradingChartOptions.strings). */
|
|
9
|
+
strings?: Partial<Strings> | undefined;
|
|
7
10
|
symbol: SymbolInfo | null;
|
|
8
11
|
resolution: Resolution;
|
|
9
12
|
loading: boolean;
|
package/dist/i18n.d.ts
CHANGED
|
@@ -292,6 +292,14 @@ export declare const STRINGS: {
|
|
|
292
292
|
};
|
|
293
293
|
};
|
|
294
294
|
export type Strings = (typeof STRINGS)['ko'];
|
|
295
|
+
/**
|
|
296
|
+
* The widget's words: the built-in ones for its locale with the app's `strings` on top (a few
|
|
297
|
+
* replaced, or every one for a language of your own).
|
|
298
|
+
*/
|
|
299
|
+
export declare function stringsOf(s: {
|
|
300
|
+
locale: Locale;
|
|
301
|
+
strings?: Partial<Strings> | undefined;
|
|
302
|
+
}): Strings;
|
|
295
303
|
/**
|
|
296
304
|
* '1' → 1분, '60' → 1시간, '1D' → 일 (short) / 1일 (long), '12M' → 1년.
|
|
297
305
|
* Short labels are for the toolbar chips, long ones for the interval menu.
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,4 @@ export { LAYOUT_GROUPS, LAYOUTS, MAX_PANES, paneCount } from './layouts';
|
|
|
10
10
|
export type { LayoutId, SplitNode, SplitSizes } from './layouts';
|
|
11
11
|
export type { ContextMenuItem, ContextMenuItems, IntervalGroup, LayoutSync, SearchCategory, SearchExchange } from './context';
|
|
12
12
|
export type { ColorScheme, TradingChart, TradingChartOptions, TradingChartSlots } from './trading-chart';
|
|
13
|
-
export { resolutionLabel } from './i18n';
|
|
13
|
+
export { resolutionLabel, STRINGS, stringsOf, type Strings } from './i18n';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/trading-chart.tsx
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
browserLocale as browserLocale2
|
|
4
|
+
} from "@funnest/charts";
|
|
3
5
|
|
|
4
6
|
// ../../node_modules/.pnpm/preact@11.2.0/node_modules/preact/dist/preact.mjs
|
|
5
7
|
var n;
|
|
@@ -716,6 +718,13 @@ var STRINGS = {
|
|
|
716
718
|
pane: "Pane"
|
|
717
719
|
}
|
|
718
720
|
};
|
|
721
|
+
var merged = /* @__PURE__ */ new WeakMap();
|
|
722
|
+
function stringsOf(s3) {
|
|
723
|
+
if (!s3.strings) return STRINGS[s3.locale];
|
|
724
|
+
let byLocale = merged.get(s3.strings);
|
|
725
|
+
if (!byLocale) merged.set(s3.strings, byLocale = {});
|
|
726
|
+
return byLocale[s3.locale] ??= { ...STRINGS[s3.locale], ...s3.strings };
|
|
727
|
+
}
|
|
719
728
|
function resolutionLabel(resolution, locale, form = "short") {
|
|
720
729
|
const match = /^(\d*)([SDWM]?)$/i.exec(resolution.trim());
|
|
721
730
|
if (!match) return resolution;
|
|
@@ -965,7 +974,7 @@ function ToolGroup({
|
|
|
965
974
|
onPick
|
|
966
975
|
}) {
|
|
967
976
|
const s3 = useStore(ctx.store);
|
|
968
|
-
const t3 =
|
|
977
|
+
const t3 = stringsOf(s3);
|
|
969
978
|
const [chosen, setChosen] = d2(tools[0].type);
|
|
970
979
|
const armed = tools.find((d3) => d3.type === s3.drawingTool);
|
|
971
980
|
const current = armed ?? tools.find((d3) => d3.type === chosen) ?? tools[0];
|
|
@@ -1008,7 +1017,7 @@ function ToolGroup({
|
|
|
1008
1017
|
}
|
|
1009
1018
|
function DrawingToolbar({ ctx }) {
|
|
1010
1019
|
const s3 = useStore(ctx.store);
|
|
1011
|
-
const t3 =
|
|
1020
|
+
const t3 = stringsOf(s3);
|
|
1012
1021
|
const [openGroup, setOpenGroup] = d2(null);
|
|
1013
1022
|
const groups = toolGroups(ctx.chart.drawingDefinitions());
|
|
1014
1023
|
const pick = (tool) => {
|
|
@@ -1103,7 +1112,7 @@ function DashGlyph({ dash }) {
|
|
|
1103
1112
|
}
|
|
1104
1113
|
function SelectionBar({ ctx }) {
|
|
1105
1114
|
const s3 = useStore(ctx.store);
|
|
1106
|
-
const t3 =
|
|
1115
|
+
const t3 = stringsOf(s3);
|
|
1107
1116
|
const d3 = s3.selection;
|
|
1108
1117
|
const [offset, setOffset] = d2(() => readJson(POS_KEY, [0, 0]));
|
|
1109
1118
|
const [recent, setRecent] = d2(() => readJson(RECENT_KEY, []));
|
|
@@ -1313,7 +1322,7 @@ var SCALES = [
|
|
|
1313
1322
|
];
|
|
1314
1323
|
function SettingsDialog({ ctx }) {
|
|
1315
1324
|
const { locale } = useStore(ctx.store);
|
|
1316
|
-
const t3 =
|
|
1325
|
+
const t3 = stringsOf(ctx.store.get());
|
|
1317
1326
|
const [settings, setSettings] = d2(ctx.chart.settings);
|
|
1318
1327
|
A2(() => ctx.chart.on("settingsChange", setSettings), [ctx.chart]);
|
|
1319
1328
|
const close = () => ctx.store.set({ dialog: null });
|
|
@@ -1432,7 +1441,7 @@ function Dialog({ title, onClose, children, wide }) {
|
|
|
1432
1441
|
var SEARCH_THRESHOLD = 8;
|
|
1433
1442
|
function IndicatorPicker({ ctx }) {
|
|
1434
1443
|
const { locale } = useStore(ctx.store);
|
|
1435
|
-
const t3 =
|
|
1444
|
+
const t3 = stringsOf(ctx.store.get());
|
|
1436
1445
|
const [query, setQuery] = d2("");
|
|
1437
1446
|
const close = () => ctx.store.set({ dialog: null });
|
|
1438
1447
|
const add = (type) => {
|
|
@@ -1515,7 +1524,7 @@ function SourceSelect({ value, locale, label, onChange }) {
|
|
|
1515
1524
|
}
|
|
1516
1525
|
function IndicatorSettings({ ctx, id }) {
|
|
1517
1526
|
const { locale, indicators } = useStore(ctx.store);
|
|
1518
|
-
const t3 =
|
|
1527
|
+
const t3 = stringsOf(ctx.store.get());
|
|
1519
1528
|
const config = indicators.find((c3) => c3.id === id);
|
|
1520
1529
|
const def = config ? ctx.chart.indicatorDefinition(config.type) ?? null : null;
|
|
1521
1530
|
const hasLines = !!def?.params.some((p3) => p3.kind === "lines");
|
|
@@ -1695,7 +1704,7 @@ var GROUP_LABEL = {
|
|
|
1695
1704
|
};
|
|
1696
1705
|
function IntervalPicker({ ctx }) {
|
|
1697
1706
|
const s3 = useStore(ctx.store);
|
|
1698
|
-
const t3 =
|
|
1707
|
+
const t3 = stringsOf(s3);
|
|
1699
1708
|
const [open, setOpen] = d2(false);
|
|
1700
1709
|
const ref = T2(null);
|
|
1701
1710
|
const menuRef = useKeepInView(open);
|
|
@@ -1821,6 +1830,8 @@ var DEBOUNCE_MS = 150;
|
|
|
1821
1830
|
var RECENT_KEY2 = "recent-symbols";
|
|
1822
1831
|
var FAVORITE_KEY = "favorite-symbols";
|
|
1823
1832
|
var MAX_RECENT = 8;
|
|
1833
|
+
var FIRST_ROWS = 40;
|
|
1834
|
+
var MORE_ROWS = 80;
|
|
1824
1835
|
var displayCode = (symbol) => symbol.slice(symbol.lastIndexOf(":") + 1);
|
|
1825
1836
|
var toStored = (info) => JSON.parse(JSON.stringify({ ...info, tickSize: 0, price: void 0, changePercent: void 0, sparkline: void 0 }));
|
|
1826
1837
|
function Highlight({ text, query }) {
|
|
@@ -1885,7 +1896,7 @@ function Sparkline({ values, up }) {
|
|
|
1885
1896
|
function SymbolSearch({ ctx }) {
|
|
1886
1897
|
const { locale, symbol: current, searchTarget } = useStore(ctx.store);
|
|
1887
1898
|
const comparing = searchTarget === "compare";
|
|
1888
|
-
const t3 =
|
|
1899
|
+
const t3 = stringsOf(ctx.store.get());
|
|
1889
1900
|
const [query, setQuery] = d2("");
|
|
1890
1901
|
const [category, setCategory] = d2(ctx.searchCategories[0]?.id ?? "all");
|
|
1891
1902
|
const [view, setView] = d2({ kind: "all" });
|
|
@@ -1896,8 +1907,10 @@ function SymbolSearch({ ctx }) {
|
|
|
1896
1907
|
const [recent, setRecent] = d2(() => ctx.storage.get(RECENT_KEY2, []));
|
|
1897
1908
|
const [favorites, setFavorites] = d2(() => ctx.storage.get(FAVORITE_KEY, []));
|
|
1898
1909
|
const [active, setActive] = d2(0);
|
|
1910
|
+
const [limit, setLimit] = d2(FIRST_ROWS);
|
|
1899
1911
|
const inputRef = T2(null);
|
|
1900
1912
|
const listRef = T2(null);
|
|
1913
|
+
const endRef = T2(null);
|
|
1901
1914
|
A2(() => inputRef.current?.focus(), []);
|
|
1902
1915
|
A2(() => {
|
|
1903
1916
|
let alive = true;
|
|
@@ -1923,7 +1936,8 @@ function SymbolSearch({ ctx }) {
|
|
|
1923
1936
|
}, [query]);
|
|
1924
1937
|
const matchCategory = ctx.searchCategories.find((c3) => c3.id === category)?.match ?? (() => true);
|
|
1925
1938
|
const categoryOf = (info) => ctx.searchCategories.find((c3) => c3.id !== "all" && c3.match(info));
|
|
1926
|
-
const
|
|
1939
|
+
const favoriteSet = w2(() => new Set(favorites.map((f4) => f4.symbol)), [favorites]);
|
|
1940
|
+
const isFavorite = (info) => favoriteSet.has(info.symbol);
|
|
1927
1941
|
const filter = (list) => list.filter((i3) => matchCategory(i3) && (!exchange || i3.exchange === exchange));
|
|
1928
1942
|
const exchanges = w2(() => {
|
|
1929
1943
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -1954,11 +1968,36 @@ function SymbolSearch({ ctx }) {
|
|
|
1954
1968
|
}
|
|
1955
1969
|
}
|
|
1956
1970
|
const rows = [...recentRows, ...resultRows];
|
|
1971
|
+
const shownRows = resultRows.length > limit ? resultRows.slice(0, limit) : resultRows;
|
|
1957
1972
|
const exchangeLabel = (name) => ctx.searchExchanges.find((e3) => e3.name === name)?.label?.[locale] ?? name;
|
|
1958
1973
|
A2(() => setActive(0), [query, category, exchange, view, results]);
|
|
1959
1974
|
A2(() => {
|
|
1960
|
-
|
|
1975
|
+
setLimit(FIRST_ROWS);
|
|
1976
|
+
if (listRef.current) listRef.current.scrollTop = 0;
|
|
1977
|
+
}, [query, category, exchange, view]);
|
|
1978
|
+
const hasMore = resultRows.length > limit;
|
|
1979
|
+
A2(() => {
|
|
1980
|
+
const end = endRef.current;
|
|
1981
|
+
if (!hasMore || !end) return;
|
|
1982
|
+
const io = new IntersectionObserver((entries) => entries.some((e3) => e3.isIntersecting) && setLimit((l3) => l3 + MORE_ROWS), {
|
|
1983
|
+
root: listRef.current,
|
|
1984
|
+
rootMargin: "0px 0px 600px 0px"
|
|
1985
|
+
});
|
|
1986
|
+
io.observe(end);
|
|
1987
|
+
return () => io.disconnect();
|
|
1988
|
+
}, [hasMore, limit]);
|
|
1989
|
+
const scrollToActive = T2(false);
|
|
1990
|
+
A2(() => {
|
|
1991
|
+
if (active - recentRows.length >= limit - 1) {
|
|
1992
|
+
scrollToActive.current = true;
|
|
1993
|
+
setLimit(active - recentRows.length + MORE_ROWS);
|
|
1994
|
+
} else listRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
1961
1995
|
}, [active]);
|
|
1996
|
+
A2(() => {
|
|
1997
|
+
if (!scrollToActive.current) return;
|
|
1998
|
+
scrollToActive.current = false;
|
|
1999
|
+
listRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
2000
|
+
}, [limit]);
|
|
1962
2001
|
const close = () => ctx.setSearchOpen(false);
|
|
1963
2002
|
const save = (key, next, set) => {
|
|
1964
2003
|
set(next);
|
|
@@ -2048,7 +2087,8 @@ function SymbolSearch({ ctx }) {
|
|
|
2048
2087
|
}
|
|
2049
2088
|
)
|
|
2050
2089
|
]
|
|
2051
|
-
}
|
|
2090
|
+
},
|
|
2091
|
+
(isRecent ? "recent:" : "") + info.symbol
|
|
2052
2092
|
);
|
|
2053
2093
|
};
|
|
2054
2094
|
const sideItem = (label, selected, onClick, extra, logoUrl) => /* @__PURE__ */ a3("button", { class: "fnc-sr-side-item", "aria-pressed": selected, onClick, children: [
|
|
@@ -2104,13 +2144,14 @@ function SymbolSearch({ ctx }) {
|
|
|
2104
2144
|
] }),
|
|
2105
2145
|
recentRows.map((info, i3) => row2(info, i3, true)),
|
|
2106
2146
|
(recentRows.length > 0 || view.kind === "favorites") && /* @__PURE__ */ a3("div", { class: "fnc-sr-section", children: view.kind === "favorites" ? t3.favorites : t3.symbols }),
|
|
2107
|
-
loading && !results && view.kind === "all" ? Array.from({ length: 6 }, () => /* @__PURE__ */ a3("div", { class: "fnc-sr-skeleton", "aria-hidden": "true" })) :
|
|
2147
|
+
loading && !results && view.kind === "all" ? Array.from({ length: 6 }, () => /* @__PURE__ */ a3("div", { class: "fnc-sr-skeleton", "aria-hidden": "true" })) : shownRows.map((info, i3) => {
|
|
2108
2148
|
const m3 = more.get(i3);
|
|
2109
2149
|
return m3 ? /* @__PURE__ */ a3($, { children: [
|
|
2110
2150
|
row2(info, recentRows.length + i3, false),
|
|
2111
2151
|
/* @__PURE__ */ a3("button", { class: "fnc-sr-more", onClick: () => (setView({ kind: "all" }), setExchange(m3.exchange)), children: t3.showAllIn.replace("{exchange}", exchangeLabel(m3.exchange)).replace("{n}", m3.count.toLocaleString(locale === "ko" ? "ko-KR" : "en-US")) })
|
|
2112
|
-
] }) : row2(info, recentRows.length + i3, false);
|
|
2152
|
+
] }, info.symbol) : row2(info, recentRows.length + i3, false);
|
|
2113
2153
|
}),
|
|
2154
|
+
hasMore && /* @__PURE__ */ a3("div", { ref: endRef, class: "fnc-sr-end", "aria-hidden": "true" }),
|
|
2114
2155
|
!loading && rows.length === 0 && /* @__PURE__ */ a3("div", { class: "fnc-empty", children: view.kind === "favorites" && favorites.length === 0 ? t3.noFavorites : t3.noResults })
|
|
2115
2156
|
] })
|
|
2116
2157
|
] }),
|
|
@@ -2234,7 +2275,7 @@ var SYNC_KEYS = [
|
|
|
2234
2275
|
function LayoutSection({ ctx, multi, close }) {
|
|
2235
2276
|
const s3 = useStore(ctx.store);
|
|
2236
2277
|
const m3 = useStore(multi.store);
|
|
2237
|
-
const t3 =
|
|
2278
|
+
const t3 = stringsOf(s3);
|
|
2238
2279
|
return /* @__PURE__ */ a3($, { children: [
|
|
2239
2280
|
/* @__PURE__ */ a3("div", { class: "fnc-tools-section", children: t3.layout }),
|
|
2240
2281
|
/* @__PURE__ */ a3("div", { class: "fnc-tools-layouts", children: LAYOUT_GROUPS.map((group) => /* @__PURE__ */ a3("div", { class: "fnc-layout-row", children: [
|
|
@@ -2273,7 +2314,7 @@ function LayoutSection({ ctx, multi, close }) {
|
|
|
2273
2314
|
}
|
|
2274
2315
|
function ToolsMenu({ ctx }) {
|
|
2275
2316
|
const s3 = useStore(ctx.store);
|
|
2276
|
-
const t3 =
|
|
2317
|
+
const t3 = stringsOf(s3);
|
|
2277
2318
|
const [open, setOpen] = d2(false);
|
|
2278
2319
|
const [scale, setScale] = d2(ctx.chart.settings.priceScale);
|
|
2279
2320
|
const ref = T2(null);
|
|
@@ -2370,7 +2411,7 @@ function ToolsMenu({ ctx }) {
|
|
|
2370
2411
|
// src/components/toolbar.tsx
|
|
2371
2412
|
function Toolbar({ ctx }) {
|
|
2372
2413
|
const s3 = useStore(ctx.store);
|
|
2373
|
-
const t3 =
|
|
2414
|
+
const t3 = stringsOf(s3);
|
|
2374
2415
|
const canSearch = typeof ctx.datafeed.searchSymbols === "function";
|
|
2375
2416
|
return /* @__PURE__ */ a3("div", { class: "fnc-toolbar", role: "toolbar", children: [
|
|
2376
2417
|
/* @__PURE__ */ a3(
|
|
@@ -2438,7 +2479,7 @@ function Toolbar({ ctx }) {
|
|
|
2438
2479
|
}
|
|
2439
2480
|
|
|
2440
2481
|
// src/pane.tsx
|
|
2441
|
-
import { createChart } from "@funnest/charts";
|
|
2482
|
+
import { browserLocale, createChart } from "@funnest/charts";
|
|
2442
2483
|
|
|
2443
2484
|
// src/components/overlay.tsx
|
|
2444
2485
|
import { formatCompact, formatPrice as formatPrice3 } from "@funnest/charts";
|
|
@@ -2448,7 +2489,7 @@ import { formatPrice as formatPrice2 } from "@funnest/charts";
|
|
|
2448
2489
|
var MARGIN2 = 8;
|
|
2449
2490
|
function ContextMenu({ ctx }) {
|
|
2450
2491
|
const s3 = useStore(ctx.store);
|
|
2451
|
-
const t3 =
|
|
2492
|
+
const t3 = stringsOf(s3);
|
|
2452
2493
|
const [target, setTarget] = d2(null);
|
|
2453
2494
|
const ref = T2(null);
|
|
2454
2495
|
A2(() => ctx.chart.on("contextMenu", setTarget), [ctx.chart]);
|
|
@@ -2527,7 +2568,7 @@ var ZOOM_STEP = 1.25;
|
|
|
2527
2568
|
var CONTROL_OFFSET = 52;
|
|
2528
2569
|
var dirClass = (v3) => v3 > 0 ? "fnc-up" : v3 < 0 ? "fnc-down" : "fnc-flat";
|
|
2529
2570
|
function IndicatorItem({ ctx, item }) {
|
|
2530
|
-
const t3 =
|
|
2571
|
+
const t3 = stringsOf(ctx.store.get());
|
|
2531
2572
|
return /* @__PURE__ */ a3(
|
|
2532
2573
|
"div",
|
|
2533
2574
|
{
|
|
@@ -2585,7 +2626,7 @@ function IndicatorItem({ ctx, item }) {
|
|
|
2585
2626
|
}
|
|
2586
2627
|
function Legend({ ctx }) {
|
|
2587
2628
|
const s3 = useStore(ctx.store);
|
|
2588
|
-
const t3 =
|
|
2629
|
+
const t3 = stringsOf(s3);
|
|
2589
2630
|
const [hover, setHover] = d2(null);
|
|
2590
2631
|
const [, refresh] = _2((n2) => n2 + 1, 0);
|
|
2591
2632
|
const [collapsed, setCollapsed] = d2(false);
|
|
@@ -2697,7 +2738,7 @@ function Legend({ ctx }) {
|
|
|
2697
2738
|
}
|
|
2698
2739
|
function NavControls({ ctx }) {
|
|
2699
2740
|
const s3 = useStore(ctx.store);
|
|
2700
|
-
const t3 =
|
|
2741
|
+
const t3 = stringsOf(s3);
|
|
2701
2742
|
const [showButtons, setShowButtons] = d2(ctx.chart.settings.navigationButtons);
|
|
2702
2743
|
A2(() => ctx.chart.on("settingsChange", (settings) => setShowButtons(settings.navigationButtons)), [ctx.chart]);
|
|
2703
2744
|
const { plotWidth, panesHeight } = s3.layout;
|
|
@@ -2757,7 +2798,7 @@ function PaneMaximize({ ctx, multi }) {
|
|
|
2757
2798
|
const s3 = useStore(ctx.store);
|
|
2758
2799
|
const m3 = useStore(multi.store);
|
|
2759
2800
|
if (s3.layout.plotWidth <= 0 || m3.narrow || m3.layout === "1" && m3.maximized === null) return null;
|
|
2760
|
-
const t3 =
|
|
2801
|
+
const t3 = stringsOf(s3);
|
|
2761
2802
|
const maximized = m3.maximized !== null;
|
|
2762
2803
|
const label = maximized ? t3.restorePane : t3.maximizePane;
|
|
2763
2804
|
return /* @__PURE__ */ a3(
|
|
@@ -2783,7 +2824,7 @@ var REPLAY_SPEEDS = [
|
|
|
2783
2824
|
];
|
|
2784
2825
|
function ReplayBar({ ctx }) {
|
|
2785
2826
|
const s3 = useStore(ctx.store);
|
|
2786
|
-
const t3 =
|
|
2827
|
+
const t3 = stringsOf(s3);
|
|
2787
2828
|
const r3 = s3.replay;
|
|
2788
2829
|
const { plotWidth, panesHeight } = s3.layout;
|
|
2789
2830
|
if (plotWidth <= 0 || !r3.active && !r3.selecting) return null;
|
|
@@ -2829,7 +2870,7 @@ function Overlay({ ctx }) {
|
|
|
2829
2870
|
}
|
|
2830
2871
|
|
|
2831
2872
|
// src/shell.ts
|
|
2832
|
-
import { darkTheme, lightTheme } from "@funnest/charts";
|
|
2873
|
+
import { darkTheme, directionColors, lightTheme } from "@funnest/charts";
|
|
2833
2874
|
|
|
2834
2875
|
// src/storage.ts
|
|
2835
2876
|
function createStorage(prefix) {
|
|
@@ -3255,6 +3296,7 @@ var CSS = (
|
|
|
3255
3296
|
.fnc-sr-spark-slot { display: inline-flex; flex: none; width: 72px; height: 24px; }
|
|
3256
3297
|
.fnc-sr-more { display: block; width: calc(100% - 16px); margin: 2px 8px 8px; padding: 8px 12px; border-radius: var(--fnc-radius); background: var(--fnc-surface); color: var(--fnc-text-2); font-size: 13px; font-weight: 600; text-align: left; }
|
|
3257
3298
|
.fnc-sr-more:hover { color: var(--fnc-text); }
|
|
3299
|
+
.fnc-sr-end { height: 1px; }
|
|
3258
3300
|
.fnc-sr-side-item:hover { background: var(--fnc-surface); }
|
|
3259
3301
|
.fnc-sr-side-item[aria-pressed='true'] { background: var(--fnc-surface); font-weight: 700; }
|
|
3260
3302
|
.fnc-chip-token {
|
|
@@ -4054,7 +4096,7 @@ function createShell(container, options) {
|
|
|
4054
4096
|
let themePatch = { ...options.theme };
|
|
4055
4097
|
let panes = () => [];
|
|
4056
4098
|
const isDark = () => scheme === "system" ? !!darkQuery?.matches : scheme === "dark";
|
|
4057
|
-
const resolveTheme = () => ({ ...isDark() ? darkTheme : lightTheme, ...themePatch });
|
|
4099
|
+
const resolveTheme = () => ({ ...isDark() ? darkTheme : lightTheme, ...directionColors(options.direction), ...themePatch });
|
|
4058
4100
|
let theme = resolveTheme();
|
|
4059
4101
|
const root = div("fnc-root", container);
|
|
4060
4102
|
const applyScheme = () => {
|
|
@@ -4227,7 +4269,8 @@ function createPane(shell, config) {
|
|
|
4227
4269
|
canvasVars(chart.settings);
|
|
4228
4270
|
chart.on("settingsChange", canvasVars);
|
|
4229
4271
|
const store = new Store({
|
|
4230
|
-
locale: config.chartOptions.locale ??
|
|
4272
|
+
locale: config.chartOptions.locale ?? browserLocale(),
|
|
4273
|
+
strings: config.strings,
|
|
4231
4274
|
symbol: null,
|
|
4232
4275
|
resolution: config.resolution,
|
|
4233
4276
|
loading: false,
|
|
@@ -4369,12 +4412,16 @@ function createTradingChart(container, options) {
|
|
|
4369
4412
|
contextMenuItems,
|
|
4370
4413
|
onIndicatorsClick,
|
|
4371
4414
|
theme,
|
|
4415
|
+
strings,
|
|
4416
|
+
direction,
|
|
4372
4417
|
...chartOptions
|
|
4373
4418
|
} = options;
|
|
4374
|
-
const
|
|
4419
|
+
const locale = chartOptions.locale ??= browserLocale2();
|
|
4420
|
+
const displayTimezone = defaultDisplayTimezone(locale, chartOptions.displayTimezone);
|
|
4375
4421
|
const shell = createShell(container, {
|
|
4376
4422
|
colorScheme,
|
|
4377
4423
|
theme,
|
|
4424
|
+
direction: direction ?? (locale === "ko" ? "kr" : "global"),
|
|
4378
4425
|
storageKey,
|
|
4379
4426
|
drawingToolbar,
|
|
4380
4427
|
favoriteIntervals: favoriteIntervals ?? DEFAULT_FAVORITE_INTERVALS,
|
|
@@ -4395,7 +4442,8 @@ function createTradingChart(container, options) {
|
|
|
4395
4442
|
drawingToolbar,
|
|
4396
4443
|
attribution,
|
|
4397
4444
|
contextMenuItems,
|
|
4398
|
-
onIndicatorsClick
|
|
4445
|
+
onIndicatorsClick,
|
|
4446
|
+
strings
|
|
4399
4447
|
});
|
|
4400
4448
|
shell.main.insertBefore(pane.stage, shell.slots.bottom);
|
|
4401
4449
|
shell.setPanes(() => [pane]);
|
|
@@ -4411,9 +4459,9 @@ function createTradingChart(container, options) {
|
|
|
4411
4459
|
const current = pane.store.get().symbol;
|
|
4412
4460
|
return current ? pane.load(current.symbol, res) : Promise.resolve();
|
|
4413
4461
|
},
|
|
4414
|
-
setLocale: (
|
|
4415
|
-
pane.chart.setLocale(
|
|
4416
|
-
pane.store.set({ locale });
|
|
4462
|
+
setLocale: (locale2) => {
|
|
4463
|
+
pane.chart.setLocale(locale2);
|
|
4464
|
+
pane.store.set({ locale: locale2 });
|
|
4417
4465
|
},
|
|
4418
4466
|
save: () => pane.chart.save(),
|
|
4419
4467
|
load: (state) => pane.chart.load(state),
|
|
@@ -4430,11 +4478,11 @@ function createTradingChart(container, options) {
|
|
|
4430
4478
|
}
|
|
4431
4479
|
|
|
4432
4480
|
// src/multi-chart.tsx
|
|
4433
|
-
import { parseChartState } from "@funnest/charts";
|
|
4481
|
+
import { browserLocale as browserLocale3, parseChartState } from "@funnest/charts";
|
|
4434
4482
|
var DEFAULT_SYNC = { crosshair: true, time: false, symbol: false, interval: false };
|
|
4435
4483
|
function PaneTab({ pane, index, active, onSelect }) {
|
|
4436
4484
|
const s3 = useStore(pane.store);
|
|
4437
|
-
const t3 =
|
|
4485
|
+
const t3 = stringsOf(s3);
|
|
4438
4486
|
return /* @__PURE__ */ a3("button", { class: "fnc-pane-tab", role: "tab", "aria-selected": active, onClick: onSelect, children: [
|
|
4439
4487
|
/* @__PURE__ */ a3("span", { class: "fnc-pane-tab-num", children: index + 1 }),
|
|
4440
4488
|
s3.symbol?.name ?? `${t3.pane} ${index + 1}`,
|
|
@@ -4471,13 +4519,17 @@ function createMultiChart(container, options) {
|
|
|
4471
4519
|
contextMenuItems,
|
|
4472
4520
|
onIndicatorsClick,
|
|
4473
4521
|
theme,
|
|
4522
|
+
strings,
|
|
4523
|
+
direction,
|
|
4474
4524
|
...chartOptions
|
|
4475
4525
|
} = options;
|
|
4476
4526
|
if (paneSpecs.length === 0) throw new Error("funnest-charts: createMultiChart needs at least one pane");
|
|
4477
|
-
const
|
|
4527
|
+
const locale = chartOptions.locale ??= browserLocale3();
|
|
4528
|
+
const displayTimezone = defaultDisplayTimezone(locale, chartOptions.displayTimezone);
|
|
4478
4529
|
const shell = createShell(container, {
|
|
4479
4530
|
colorScheme,
|
|
4480
4531
|
theme,
|
|
4532
|
+
direction: direction ?? (locale === "ko" ? "kr" : "global"),
|
|
4481
4533
|
storageKey,
|
|
4482
4534
|
drawingToolbar,
|
|
4483
4535
|
favoriteIntervals: favoriteIntervals ?? DEFAULT_FAVORITE_INTERVALS,
|
|
@@ -4776,10 +4828,10 @@ function createMultiChart(container, options) {
|
|
|
4776
4828
|
const current = pane.store.get().symbol;
|
|
4777
4829
|
return current ? pane.load(current.symbol, res) : Promise.resolve();
|
|
4778
4830
|
},
|
|
4779
|
-
setLocale: (
|
|
4831
|
+
setLocale: (locale2) => {
|
|
4780
4832
|
for (const p3 of panes) {
|
|
4781
|
-
p3.chart.setLocale(
|
|
4782
|
-
p3.store.set({ locale });
|
|
4833
|
+
p3.chart.setLocale(locale2);
|
|
4834
|
+
p3.store.set({ locale: locale2 });
|
|
4783
4835
|
}
|
|
4784
4836
|
},
|
|
4785
4837
|
setTheme: shell.setTheme,
|
|
@@ -4824,8 +4876,10 @@ export {
|
|
|
4824
4876
|
LAYOUTS,
|
|
4825
4877
|
LAYOUT_GROUPS,
|
|
4826
4878
|
MAX_PANES,
|
|
4879
|
+
STRINGS,
|
|
4827
4880
|
createMultiChart,
|
|
4828
4881
|
createTradingChart,
|
|
4829
4882
|
paneCount,
|
|
4830
|
-
resolutionLabel
|
|
4883
|
+
resolutionLabel,
|
|
4884
|
+
stringsOf
|
|
4831
4885
|
};
|
package/dist/pane.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { type Chart, type ChartOptions, type ChartType, type Datafeed, type Resolution } from '@funnest/charts';
|
|
3
3
|
import type { ContextMenuItems, IntervalGroup, MultiControls, SearchCategory, SearchExchange, UiState, WidgetContext } from './context';
|
|
4
4
|
import { type Shell } from './shell';
|
|
5
|
+
import type { Strings } from './i18n';
|
|
5
6
|
import { Store } from './store';
|
|
6
7
|
export interface PaneConfig {
|
|
7
8
|
datafeed: Datafeed;
|
|
@@ -21,6 +22,7 @@ export interface PaneConfig {
|
|
|
21
22
|
contextMenuItems?: ContextMenuItems | undefined;
|
|
22
23
|
onIndicatorsClick?: ((chart: Chart) => void) | undefined;
|
|
23
24
|
multi?: MultiControls | undefined;
|
|
25
|
+
strings?: Partial<Strings> | undefined;
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* One chart with its legend/navigation overlay and UI state. The toolbar and drawing bar
|
package/dist/shell.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Resolution, type ThemeTokens } from '@funnest/charts';
|
|
1
|
+
import { type DirectionPreset, type Resolution, type ThemeTokens } from '@funnest/charts';
|
|
2
2
|
import type { UiState } from './context';
|
|
3
3
|
import { type WidgetStorage } from './storage';
|
|
4
4
|
import type { Store } from './store';
|
|
@@ -35,6 +35,8 @@ export interface ShellPane {
|
|
|
35
35
|
export interface ShellOptions {
|
|
36
36
|
colorScheme: ColorScheme;
|
|
37
37
|
theme: Partial<ThemeTokens> | undefined;
|
|
38
|
+
/** Up/down colors under `theme` (TradingChartOptions.direction, resolved from the locale). */
|
|
39
|
+
direction: DirectionPreset;
|
|
38
40
|
storageKey: string | null;
|
|
39
41
|
drawingToolbar: boolean;
|
|
40
42
|
favoriteIntervals: readonly Resolution[];
|
package/dist/trading-chart.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @jsxImportSource preact */
|
|
2
|
-
import { type Chart, type ChartOptions, type ChartType, type Datafeed, type Locale, type ChartState, type Resolution, type ThemeTokens } from '@funnest/charts';
|
|
2
|
+
import { type Chart, type ChartOptions, type ChartType, type Datafeed, type DirectionPreset, type Locale, type ChartState, type Resolution, type ThemeTokens } from '@funnest/charts';
|
|
3
3
|
import type { ContextMenuItems, IntervalGroup, SearchCategory, SearchExchange } from './context';
|
|
4
|
+
import type { Strings } from './i18n';
|
|
4
5
|
import { type ColorScheme, type TradingChartSlots } from './shell';
|
|
5
6
|
export type { ColorScheme, TradingChartSlots } from './shell';
|
|
6
7
|
/** Interval menu: 분 | 시간 | 일. '12M' is shown as 1년. */
|
|
@@ -62,6 +63,21 @@ export interface TradingChartOptions extends Omit<ChartOptions, 'datafeed'> {
|
|
|
62
63
|
* indicator browser). Called with the pane's chart; add indicators with chart.addIndicator().
|
|
63
64
|
*/
|
|
64
65
|
onIndicatorsClick?: (chart: Chart) => void;
|
|
66
|
+
/**
|
|
67
|
+
* Language of the toolbar, menus and dates. Default: Korean when the browser is set to Korean,
|
|
68
|
+
* English otherwise.
|
|
69
|
+
*/
|
|
70
|
+
locale?: Locale;
|
|
71
|
+
/**
|
|
72
|
+
* Replace built-in words: a few (`{ indicators: 'Studies' }`) or all of them for a language of
|
|
73
|
+
* your own (start from `STRINGS.en`). Applied over whichever locale is active.
|
|
74
|
+
*/
|
|
75
|
+
strings?: Partial<Strings>;
|
|
76
|
+
/**
|
|
77
|
+
* Up/down colors: 'kr' red up · blue down, 'global' green up · red down. Default follows the
|
|
78
|
+
* locale ('kr' for Korean, 'global' otherwise). `theme.up` / `theme.down` still win.
|
|
79
|
+
*/
|
|
80
|
+
direction?: DirectionPreset;
|
|
65
81
|
}
|
|
66
82
|
export interface TradingChart {
|
|
67
83
|
/** 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.17",
|
|
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.17"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"preact": "^11.2.0"
|