@funnest/charts-ui 0.5.1 → 0.5.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/keep-in-view.d.ts +6 -0
- package/dist/index.js +177 -42
- package/package.json +2 -2
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nudges an open dropdown horizontally so it stays inside the widget and the viewport (its CSS
|
|
3
|
+
* anchor may push it off-screen on phones; hosts often clip the widget with overflow: hidden).
|
|
4
|
+
* Attach the returned ref to the menu element.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useKeepInView<T extends HTMLElement>(open: boolean): import("preact").RefObject<T | null>;
|
package/dist/index.js
CHANGED
|
@@ -825,7 +825,7 @@ function DrawingToolbar({ ctx }) {
|
|
|
825
825
|
if (tool && ctx.store.get().drawbarOpen) ctx.store.set({ drawbarOpen: false });
|
|
826
826
|
};
|
|
827
827
|
return /* @__PURE__ */ a2($, { children: [
|
|
828
|
-
s3.drawbarCollapsed && /* @__PURE__ */ a2("button", { class: "fnc-drawbar-tab", "aria-label": t3.expandDrawbar,
|
|
828
|
+
s3.drawbarCollapsed && /* @__PURE__ */ a2("button", { class: "fnc-drawbar-tab", "aria-label": t3.expandDrawbar, "data-tip": t3.expandDrawbar, onClick: () => ctx.setDrawbarCollapsed(false), children: /* @__PURE__ */ a2(ChevronRightIcon, {}) }),
|
|
829
829
|
/* @__PURE__ */ a2(
|
|
830
830
|
"div",
|
|
831
831
|
{
|
|
@@ -840,7 +840,7 @@ function DrawingToolbar({ ctx }) {
|
|
|
840
840
|
"button",
|
|
841
841
|
{
|
|
842
842
|
class: "fnc-btn",
|
|
843
|
-
|
|
843
|
+
"data-tip": t3[label],
|
|
844
844
|
"aria-label": t3[label],
|
|
845
845
|
"aria-pressed": s3.drawingTool === tool,
|
|
846
846
|
onClick: () => pick(tool),
|
|
@@ -852,7 +852,7 @@ function DrawingToolbar({ ctx }) {
|
|
|
852
852
|
"button",
|
|
853
853
|
{
|
|
854
854
|
class: "fnc-btn",
|
|
855
|
-
|
|
855
|
+
"data-tip": t3.magnet,
|
|
856
856
|
"aria-label": t3.magnet,
|
|
857
857
|
"aria-pressed": s3.magnet,
|
|
858
858
|
onClick: () => ctx.chart.setMagnet(!s3.magnet),
|
|
@@ -863,7 +863,7 @@ function DrawingToolbar({ ctx }) {
|
|
|
863
863
|
"button",
|
|
864
864
|
{
|
|
865
865
|
class: "fnc-btn",
|
|
866
|
-
|
|
866
|
+
"data-tip": t3.removeAll,
|
|
867
867
|
"aria-label": t3.removeAll,
|
|
868
868
|
disabled: s3.drawingCount === 0,
|
|
869
869
|
onClick: () => ctx.chart.clearDrawings(),
|
|
@@ -875,7 +875,7 @@ function DrawingToolbar({ ctx }) {
|
|
|
875
875
|
"button",
|
|
876
876
|
{
|
|
877
877
|
class: "fnc-btn fnc-drawbar-collapse",
|
|
878
|
-
|
|
878
|
+
"data-tip": t3.collapseDrawbar,
|
|
879
879
|
"aria-label": t3.collapseDrawbar,
|
|
880
880
|
onClick: () => ctx.setDrawbarCollapsed(true),
|
|
881
881
|
children: /* @__PURE__ */ a2(ChevronLeftIcon, {})
|
|
@@ -898,17 +898,43 @@ function SelectionBar({ ctx }) {
|
|
|
898
898
|
{
|
|
899
899
|
class: "fnc-swatch",
|
|
900
900
|
style: { background: color },
|
|
901
|
-
|
|
901
|
+
"data-tip": t3.color,
|
|
902
902
|
"aria-label": `${t3.color} ${color}`,
|
|
903
903
|
"aria-pressed": (d3.color ?? SWATCHES[0]) === color,
|
|
904
904
|
onClick: () => ctx.chart.updateDrawing(d3.id, { color })
|
|
905
905
|
}
|
|
906
906
|
)),
|
|
907
907
|
/* @__PURE__ */ a2("div", { class: "fnc-sep" }),
|
|
908
|
-
/* @__PURE__ */ a2("button", { class: "fnc-btn",
|
|
908
|
+
/* @__PURE__ */ a2("button", { class: "fnc-btn", "data-tip": t3.remove, "aria-label": t3.remove, onClick: () => ctx.chart.removeDrawing(d3.id), children: /* @__PURE__ */ a2(TrashIcon, {}) })
|
|
909
909
|
] });
|
|
910
910
|
}
|
|
911
911
|
|
|
912
|
+
// src/components/keep-in-view.ts
|
|
913
|
+
var MARGIN = 8;
|
|
914
|
+
function useKeepInView(open) {
|
|
915
|
+
const ref = T2(null);
|
|
916
|
+
F2(() => {
|
|
917
|
+
const el = ref.current;
|
|
918
|
+
if (!open || !el) return;
|
|
919
|
+
el.style.translate = "";
|
|
920
|
+
const fit = () => {
|
|
921
|
+
el.style.translate = "";
|
|
922
|
+
const r3 = el.getBoundingClientRect();
|
|
923
|
+
const root = el.closest(".fnc-root")?.getBoundingClientRect();
|
|
924
|
+
const minX = Math.max(0, root?.left ?? 0) + MARGIN;
|
|
925
|
+
const maxX = Math.min(document.documentElement.clientWidth, root?.right ?? Infinity) - MARGIN;
|
|
926
|
+
let dx = 0;
|
|
927
|
+
if (r3.right > maxX) dx = maxX - r3.right;
|
|
928
|
+
if (r3.left + dx < minX) dx = minX - r3.left;
|
|
929
|
+
if (dx) el.style.translate = `${Math.round(dx)}px 0`;
|
|
930
|
+
};
|
|
931
|
+
fit();
|
|
932
|
+
window.addEventListener("resize", fit);
|
|
933
|
+
return () => window.removeEventListener("resize", fit);
|
|
934
|
+
}, [open]);
|
|
935
|
+
return ref;
|
|
936
|
+
}
|
|
937
|
+
|
|
912
938
|
// src/components/chart-type-menu.tsx
|
|
913
939
|
var TYPES = [
|
|
914
940
|
{ type: "candles", label: "candles", icon: CandlesIcon },
|
|
@@ -920,6 +946,7 @@ function ChartTypeMenu({ ctx }) {
|
|
|
920
946
|
const t3 = STRINGS[s3.locale];
|
|
921
947
|
const [open, setOpen] = d2(false);
|
|
922
948
|
const ref = T2(null);
|
|
949
|
+
const menuRef = useKeepInView(open);
|
|
923
950
|
const current = TYPES.find((x2) => x2.type === s3.chartType) ?? TYPES[0];
|
|
924
951
|
A2(() => {
|
|
925
952
|
if (!open) return;
|
|
@@ -937,12 +964,12 @@ function ChartTypeMenu({ ctx }) {
|
|
|
937
964
|
ctx.store.set({ chartType: type });
|
|
938
965
|
setOpen(false);
|
|
939
966
|
};
|
|
940
|
-
return /* @__PURE__ */ a2("div", { class: "fnc-menu-wrap", ref, children: [
|
|
967
|
+
return /* @__PURE__ */ a2("div", { class: "fnc-menu-wrap fnc-chart-type", ref, children: [
|
|
941
968
|
/* @__PURE__ */ a2(
|
|
942
969
|
"button",
|
|
943
970
|
{
|
|
944
971
|
class: "fnc-btn fnc-btn-wide",
|
|
945
|
-
|
|
972
|
+
"data-tip": t3.chartType,
|
|
946
973
|
"aria-label": t3.chartType,
|
|
947
974
|
"aria-haspopup": "menu",
|
|
948
975
|
"aria-expanded": open,
|
|
@@ -953,7 +980,7 @@ function ChartTypeMenu({ ctx }) {
|
|
|
953
980
|
]
|
|
954
981
|
}
|
|
955
982
|
),
|
|
956
|
-
open && /* @__PURE__ */ a2("div", { class: "fnc-menu", role: "menu", children: TYPES.map(({ type, label, icon: Icon2 }) => /* @__PURE__ */ a2(
|
|
983
|
+
open && /* @__PURE__ */ a2("div", { class: "fnc-menu", role: "menu", ref: menuRef, children: TYPES.map(({ type, label, icon: Icon2 }) => /* @__PURE__ */ a2(
|
|
957
984
|
"button",
|
|
958
985
|
{
|
|
959
986
|
class: "fnc-menu-item",
|
|
@@ -1230,7 +1257,7 @@ function IndicatorSettings({ ctx, id }) {
|
|
|
1230
1257
|
onInput: (e3) => updateLine(i3, { period: e3.currentTarget.value.replace(/\D/g, "") })
|
|
1231
1258
|
}
|
|
1232
1259
|
),
|
|
1233
|
-
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-ind-btn", "aria-label": t3.removePeriod,
|
|
1260
|
+
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-ind-btn", "aria-label": t3.removePeriod, "data-tip": t3.removePeriod, onClick: () => setLines(lines.filter((_3, j2) => j2 !== i3)), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) })
|
|
1234
1261
|
] })),
|
|
1235
1262
|
/* @__PURE__ */ a2("button", { type: "button", class: "fnc-add-row", onClick: addLine, children: [
|
|
1236
1263
|
"+ ",
|
|
@@ -1302,6 +1329,7 @@ function IntervalPicker({ ctx }) {
|
|
|
1302
1329
|
const t3 = STRINGS[s3.locale];
|
|
1303
1330
|
const [open, setOpen] = d2(false);
|
|
1304
1331
|
const ref = T2(null);
|
|
1332
|
+
const menuRef = useKeepInView(open);
|
|
1305
1333
|
A2(() => {
|
|
1306
1334
|
if (!open) return;
|
|
1307
1335
|
const onDown = (e3) => !ref.current?.contains(e3.target) && setOpen(false);
|
|
@@ -1328,7 +1356,7 @@ function IntervalPicker({ ctx }) {
|
|
|
1328
1356
|
class: "fnc-chip",
|
|
1329
1357
|
"aria-pressed": r3 === s3.resolution,
|
|
1330
1358
|
disabled: !isSupported(r3),
|
|
1331
|
-
|
|
1359
|
+
"data-tip": isSupported(r3) ? resolutionLabel(r3, s3.locale, "long") : t3.unsupportedInterval,
|
|
1332
1360
|
onClick: () => select(r3),
|
|
1333
1361
|
children: resolutionLabel(r3, s3.locale)
|
|
1334
1362
|
}
|
|
@@ -1337,7 +1365,7 @@ function IntervalPicker({ ctx }) {
|
|
|
1337
1365
|
"button",
|
|
1338
1366
|
{
|
|
1339
1367
|
class: `fnc-btn fnc-btn-wide fnc-interval-trigger${favorites.includes(s3.resolution) ? "" : " fnc-interval-trigger-active"}`,
|
|
1340
|
-
|
|
1368
|
+
"data-tip": t3.moreIntervals,
|
|
1341
1369
|
"aria-label": t3.moreIntervals,
|
|
1342
1370
|
"aria-haspopup": "menu",
|
|
1343
1371
|
"aria-expanded": open,
|
|
@@ -1348,7 +1376,7 @@ function IntervalPicker({ ctx }) {
|
|
|
1348
1376
|
]
|
|
1349
1377
|
}
|
|
1350
1378
|
),
|
|
1351
|
-
open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-interval-menu", role: "menu", children: ctx.intervalGroups.map((group) => /* @__PURE__ */ a2("div", { class: "fnc-menu-group", role: "group", "aria-label": t3[GROUP_LABEL[group.id]], children: [
|
|
1379
|
+
open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-interval-menu", role: "menu", ref: menuRef, children: ctx.intervalGroups.map((group) => /* @__PURE__ */ a2("div", { class: "fnc-menu-group", role: "group", "aria-label": t3[GROUP_LABEL[group.id]], children: [
|
|
1352
1380
|
/* @__PURE__ */ a2("div", { class: "fnc-menu-title", children: t3[GROUP_LABEL[group.id]] }),
|
|
1353
1381
|
group.intervals.map((r3) => {
|
|
1354
1382
|
const favorite = s3.favoriteIntervals.includes(r3);
|
|
@@ -1361,7 +1389,7 @@ function IntervalPicker({ ctx }) {
|
|
|
1361
1389
|
role: "menuitemradio",
|
|
1362
1390
|
"aria-checked": r3 === s3.resolution,
|
|
1363
1391
|
disabled: !ok,
|
|
1364
|
-
|
|
1392
|
+
"data-tip": ok ? void 0 : t3.unsupportedInterval,
|
|
1365
1393
|
onClick: () => select(r3),
|
|
1366
1394
|
children: resolutionLabel(r3, s3.locale, "long")
|
|
1367
1395
|
}
|
|
@@ -1372,7 +1400,7 @@ function IntervalPicker({ ctx }) {
|
|
|
1372
1400
|
class: "fnc-star",
|
|
1373
1401
|
"aria-pressed": favorite,
|
|
1374
1402
|
"aria-label": `${resolutionLabel(r3, s3.locale, "long")} ${favorite ? t3.removeFavorite : t3.addFavorite}`,
|
|
1375
|
-
|
|
1403
|
+
"data-tip": favorite ? t3.removeFavorite : t3.addFavorite,
|
|
1376
1404
|
onClick: () => ctx.toggleFavoriteInterval(r3),
|
|
1377
1405
|
children: /* @__PURE__ */ a2(StarIcon, { filled: favorite })
|
|
1378
1406
|
}
|
|
@@ -1482,6 +1510,8 @@ function LayoutMenu({ ctx, multi }) {
|
|
|
1482
1510
|
const t3 = STRINGS[s3.locale];
|
|
1483
1511
|
const layout = usePopover();
|
|
1484
1512
|
const sync = usePopover();
|
|
1513
|
+
const layoutMenuRef = useKeepInView(layout.open);
|
|
1514
|
+
const syncMenuRef = useKeepInView(sync.open);
|
|
1485
1515
|
const anySync = Object.values(m3.sync).some(Boolean);
|
|
1486
1516
|
return /* @__PURE__ */ a2($, { children: [
|
|
1487
1517
|
/* @__PURE__ */ a2("div", { class: "fnc-menu-wrap", ref: layout.ref, children: [
|
|
@@ -1489,7 +1519,7 @@ function LayoutMenu({ ctx, multi }) {
|
|
|
1489
1519
|
"button",
|
|
1490
1520
|
{
|
|
1491
1521
|
class: "fnc-btn fnc-btn-wide fnc-btn-layout",
|
|
1492
|
-
|
|
1522
|
+
"data-tip": t3.layout,
|
|
1493
1523
|
"aria-label": t3.layout,
|
|
1494
1524
|
"aria-haspopup": "menu",
|
|
1495
1525
|
"aria-expanded": layout.open,
|
|
@@ -1500,7 +1530,7 @@ function LayoutMenu({ ctx, multi }) {
|
|
|
1500
1530
|
]
|
|
1501
1531
|
}
|
|
1502
1532
|
),
|
|
1503
|
-
layout.open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-layout-menu", role: "menu", "aria-label": t3.layout, children: LAYOUT_GROUPS.map((group) => /* @__PURE__ */ a2("div", { class: "fnc-layout-row", children: [
|
|
1533
|
+
layout.open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-layout-menu", role: "menu", "aria-label": t3.layout, ref: layoutMenuRef, children: LAYOUT_GROUPS.map((group) => /* @__PURE__ */ a2("div", { class: "fnc-layout-row", children: [
|
|
1504
1534
|
/* @__PURE__ */ a2("span", { class: "fnc-layout-count", children: group[0].split("-")[0] }),
|
|
1505
1535
|
group.map((id) => /* @__PURE__ */ a2(
|
|
1506
1536
|
"button",
|
|
@@ -1509,7 +1539,7 @@ function LayoutMenu({ ctx, multi }) {
|
|
|
1509
1539
|
role: "menuitemradio",
|
|
1510
1540
|
"aria-checked": id === m3.layout,
|
|
1511
1541
|
"aria-label": `${t3.layout} ${id}`,
|
|
1512
|
-
|
|
1542
|
+
"data-layout": id,
|
|
1513
1543
|
onClick: () => {
|
|
1514
1544
|
multi.setLayout(id);
|
|
1515
1545
|
layout.setOpen(false);
|
|
@@ -1524,7 +1554,7 @@ function LayoutMenu({ ctx, multi }) {
|
|
|
1524
1554
|
"button",
|
|
1525
1555
|
{
|
|
1526
1556
|
class: "fnc-btn fnc-btn-sync",
|
|
1527
|
-
|
|
1557
|
+
"data-tip": t3.sync,
|
|
1528
1558
|
"aria-label": t3.sync,
|
|
1529
1559
|
"aria-haspopup": "menu",
|
|
1530
1560
|
"aria-expanded": sync.open,
|
|
@@ -1533,7 +1563,7 @@ function LayoutMenu({ ctx, multi }) {
|
|
|
1533
1563
|
children: /* @__PURE__ */ a2(LinkIcon, {})
|
|
1534
1564
|
}
|
|
1535
1565
|
),
|
|
1536
|
-
sync.open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-sync-menu", role: "menu", "aria-label": t3.sync, children: [
|
|
1566
|
+
sync.open && /* @__PURE__ */ a2("div", { class: "fnc-menu fnc-sync-menu", role: "menu", "aria-label": t3.sync, ref: syncMenuRef, children: [
|
|
1537
1567
|
/* @__PURE__ */ a2("div", { class: "fnc-menu-title", children: [
|
|
1538
1568
|
t3.sync,
|
|
1539
1569
|
/* @__PURE__ */ a2("small", { children: t3.syncHint })
|
|
@@ -1712,7 +1742,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1712
1742
|
class: `fnc-sr-star${fav ? " is-on" : ""}`,
|
|
1713
1743
|
"aria-pressed": fav,
|
|
1714
1744
|
"aria-label": `${info.name} ${fav ? t3.removeFavorite : t3.addFavorite}`,
|
|
1715
|
-
|
|
1745
|
+
"data-tip": fav ? t3.removeFavorite : t3.addFavorite,
|
|
1716
1746
|
onClick: (e3) => {
|
|
1717
1747
|
e3.stopPropagation();
|
|
1718
1748
|
toggleFavorite(info);
|
|
@@ -1747,7 +1777,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1747
1777
|
{
|
|
1748
1778
|
class: "fnc-ind-btn",
|
|
1749
1779
|
"aria-label": `${info.name} ${t3.removeRecent}`,
|
|
1750
|
-
|
|
1780
|
+
"data-tip": t3.removeRecent,
|
|
1751
1781
|
onClick: (e3) => {
|
|
1752
1782
|
e3.stopPropagation();
|
|
1753
1783
|
save(RECENT_KEY, recent.filter((r3) => r3.symbol !== info.symbol), setRecent);
|
|
@@ -1785,7 +1815,7 @@ function SymbolSearch({ ctx }) {
|
|
|
1785
1815
|
}
|
|
1786
1816
|
),
|
|
1787
1817
|
query && /* @__PURE__ */ a2("button", { class: "fnc-ind-btn", "aria-label": t3.cancel, onClick: () => (setQuery(""), inputRef.current?.focus()), children: /* @__PURE__ */ a2(SmallCloseIcon, {}) }),
|
|
1788
|
-
/* @__PURE__ */ a2("button", { class: "fnc-kbd fnc-kbd-btn fnc-hide-mobile", "aria-label": t3.close,
|
|
1818
|
+
/* @__PURE__ */ a2("button", { class: "fnc-kbd fnc-kbd-btn fnc-hide-mobile", "aria-label": t3.close, "data-tip": t3.close, onClick: close, children: "ESC" })
|
|
1789
1819
|
] }),
|
|
1790
1820
|
/* @__PURE__ */ a2("div", { class: "fnc-tabs", role: "tablist", children: ctx.searchCategories.map((c3) => /* @__PURE__ */ a2("button", { class: "fnc-tab", role: "tab", "aria-selected": c3.id === category, onClick: () => setCategory(c3.id), children: c3.label[locale] })) }),
|
|
1791
1821
|
/* @__PURE__ */ a2("div", { class: "fnc-sr-body", children: [
|
|
@@ -1849,7 +1879,7 @@ function Toolbar({ ctx }) {
|
|
|
1849
1879
|
{
|
|
1850
1880
|
class: "fnc-symbol",
|
|
1851
1881
|
disabled: !canSearch,
|
|
1852
|
-
|
|
1882
|
+
"data-tip": canSearch ? t3.searchSymbol : void 0,
|
|
1853
1883
|
"aria-haspopup": "dialog",
|
|
1854
1884
|
onClick: () => ctx.setSearchOpen(true),
|
|
1855
1885
|
children: [
|
|
@@ -1868,8 +1898,8 @@ function Toolbar({ ctx }) {
|
|
|
1868
1898
|
/* @__PURE__ */ a2(
|
|
1869
1899
|
"button",
|
|
1870
1900
|
{
|
|
1871
|
-
class: "fnc-btn fnc-btn-wide fnc-btn-label",
|
|
1872
|
-
|
|
1901
|
+
class: "fnc-btn fnc-btn-wide fnc-btn-label fnc-btn-indicators",
|
|
1902
|
+
"data-tip": t3.addIndicator,
|
|
1873
1903
|
"aria-label": t3.addIndicator,
|
|
1874
1904
|
"aria-haspopup": "dialog",
|
|
1875
1905
|
onClick: () => ctx.store.set({ dialog: { kind: "indicators" } }),
|
|
@@ -1886,7 +1916,7 @@ function Toolbar({ ctx }) {
|
|
|
1886
1916
|
"button",
|
|
1887
1917
|
{
|
|
1888
1918
|
class: "fnc-btn fnc-btn-drawbar",
|
|
1889
|
-
|
|
1919
|
+
"data-tip": t3.drawingTools,
|
|
1890
1920
|
"aria-label": t3.drawingTools,
|
|
1891
1921
|
"aria-pressed": s3.drawbarOpen || s3.drawingTool !== null,
|
|
1892
1922
|
onClick: () => ctx.store.set({ drawbarOpen: !s3.drawbarOpen }),
|
|
@@ -1897,20 +1927,20 @@ function Toolbar({ ctx }) {
|
|
|
1897
1927
|
/* @__PURE__ */ a2(
|
|
1898
1928
|
"button",
|
|
1899
1929
|
{
|
|
1900
|
-
class: "fnc-btn",
|
|
1901
|
-
|
|
1930
|
+
class: "fnc-btn fnc-btn-settings",
|
|
1931
|
+
"data-tip": t3.chartSettings,
|
|
1902
1932
|
"aria-label": t3.chartSettings,
|
|
1903
1933
|
"aria-haspopup": "dialog",
|
|
1904
1934
|
onClick: () => ctx.store.set({ dialog: { kind: "chartSettings" } }),
|
|
1905
1935
|
children: /* @__PURE__ */ a2(SettingsIcon, {})
|
|
1906
1936
|
}
|
|
1907
1937
|
),
|
|
1908
|
-
/* @__PURE__ */ a2("button", { class: "fnc-btn fnc-btn-screenshot",
|
|
1938
|
+
/* @__PURE__ */ a2("button", { class: "fnc-btn fnc-btn-screenshot", "data-tip": t3.screenshot, "aria-label": t3.screenshot, onClick: ctx.screenshot, children: /* @__PURE__ */ a2(CameraIcon, {}) }),
|
|
1909
1939
|
/* @__PURE__ */ a2(
|
|
1910
1940
|
"button",
|
|
1911
1941
|
{
|
|
1912
|
-
class: "fnc-btn",
|
|
1913
|
-
|
|
1942
|
+
class: "fnc-btn fnc-btn-fullscreen",
|
|
1943
|
+
"data-tip": s3.fullscreen ? t3.exitFullscreen : t3.fullscreen,
|
|
1914
1944
|
"aria-label": s3.fullscreen ? t3.exitFullscreen : t3.fullscreen,
|
|
1915
1945
|
onClick: ctx.toggleFullscreen,
|
|
1916
1946
|
children: s3.fullscreen ? /* @__PURE__ */ a2(CollapseIcon, {}) : /* @__PURE__ */ a2(ExpandIcon, {})
|
|
@@ -1948,7 +1978,7 @@ function IndicatorItem({ ctx, item }) {
|
|
|
1948
1978
|
"button",
|
|
1949
1979
|
{
|
|
1950
1980
|
class: "fnc-ind-btn fnc-ind-eye",
|
|
1951
|
-
|
|
1981
|
+
"data-tip": item.visible ? t3.hide : t3.show,
|
|
1952
1982
|
"aria-label": `${item.title} ${item.visible ? t3.hide : t3.show}`,
|
|
1953
1983
|
"aria-pressed": !item.visible,
|
|
1954
1984
|
onClick: (e3) => {
|
|
@@ -1959,13 +1989,13 @@ function IndicatorItem({ ctx, item }) {
|
|
|
1959
1989
|
}
|
|
1960
1990
|
),
|
|
1961
1991
|
/* @__PURE__ */ a2("span", { class: "fnc-ind-title", children: item.title }),
|
|
1962
|
-
item.visible && item.values.map((v3) => /* @__PURE__ */ a2("b", { style: { color: v3.color },
|
|
1992
|
+
item.visible && item.values.map((v3) => /* @__PURE__ */ a2("b", { style: { color: v3.color }, "data-tip": v3.label, children: v3.value })),
|
|
1963
1993
|
/* @__PURE__ */ a2("span", { class: "fnc-ind-actions", children: [
|
|
1964
1994
|
/* @__PURE__ */ a2(
|
|
1965
1995
|
"button",
|
|
1966
1996
|
{
|
|
1967
1997
|
class: "fnc-ind-btn",
|
|
1968
|
-
|
|
1998
|
+
"data-tip": t3.indicatorSettings,
|
|
1969
1999
|
"aria-label": `${item.title} ${t3.indicatorSettings}`,
|
|
1970
2000
|
onClick: () => ctx.store.set({ dialog: { kind: "indicatorSettings", id: item.id } }),
|
|
1971
2001
|
children: /* @__PURE__ */ a2(GearIcon, {})
|
|
@@ -1975,7 +2005,7 @@ function IndicatorItem({ ctx, item }) {
|
|
|
1975
2005
|
"button",
|
|
1976
2006
|
{
|
|
1977
2007
|
class: "fnc-ind-btn",
|
|
1978
|
-
|
|
2008
|
+
"data-tip": t3.removeIndicator,
|
|
1979
2009
|
"aria-label": `${item.title} ${t3.removeIndicator}`,
|
|
1980
2010
|
onClick: () => ctx.chart.removeIndicator(item.id),
|
|
1981
2011
|
children: /* @__PURE__ */ a2(SmallCloseIcon, {})
|
|
@@ -2074,7 +2104,7 @@ function Legend({ ctx }) {
|
|
|
2074
2104
|
] }),
|
|
2075
2105
|
values,
|
|
2076
2106
|
!collapsed && overlayItems.map((item) => /* @__PURE__ */ a2(IndicatorItem, { ctx, item })),
|
|
2077
|
-
overlayItems.length > 0 && (collapsed ? /* @__PURE__ */ a2("button", { class: "fnc-ind-handle", "aria-expanded": "false", "aria-label": t3.expand,
|
|
2107
|
+
overlayItems.length > 0 && (collapsed ? /* @__PURE__ */ a2("button", { class: "fnc-ind-handle", "aria-expanded": "false", "aria-label": t3.expand, "data-tip": t3.expand, onClick: () => setCollapsed(false), children: /* @__PURE__ */ a2("span", {}) }) : /* @__PURE__ */ a2("button", { class: "fnc-ind-collapse", "aria-expanded": "true", "aria-label": t3.collapse, "data-tip": t3.collapse, onClick: () => setCollapsed(true), children: /* @__PURE__ */ a2(CollapseLeftIcon, {}) }))
|
|
2078
2108
|
] }),
|
|
2079
2109
|
paneLegends.map(
|
|
2080
2110
|
({ pane, item }) => item && /* @__PURE__ */ a2("div", { class: "fnc-legend fnc-pane-legend", style: { top: `${pane.top + 4}px` }, children: /* @__PURE__ */ a2(IndicatorItem, { ctx, item }) })
|
|
@@ -2090,7 +2120,7 @@ function NavControls({ ctx }) {
|
|
|
2090
2120
|
if (plotWidth <= 0) return null;
|
|
2091
2121
|
const top = panesHeight - CONTROL_OFFSET;
|
|
2092
2122
|
const page = () => Math.max(1, Math.round(plotWidth / ctx.chart.timeScale.barSpacing / 3));
|
|
2093
|
-
const button = (label, onClick, icon) => /* @__PURE__ */ a2("button", { class: "fnc-btn",
|
|
2123
|
+
const button = (label, onClick, icon) => /* @__PURE__ */ a2("button", { class: "fnc-btn", "data-tip": label, "aria-label": label, onClick, children: icon });
|
|
2094
2124
|
return /* @__PURE__ */ a2($, { children: [
|
|
2095
2125
|
showButtons && /* @__PURE__ */ a2("div", { class: "fnc-nav", style: { left: `${plotWidth / 2}px`, top: `${top}px` }, children: [
|
|
2096
2126
|
button(t3.zoomOut, () => ctx.chart.zoom(1 / ZOOM_STEP), /* @__PURE__ */ a2(MinusIcon, {})),
|
|
@@ -2104,7 +2134,7 @@ function NavControls({ ctx }) {
|
|
|
2104
2134
|
{
|
|
2105
2135
|
class: "fnc-realtime",
|
|
2106
2136
|
style: { left: `${plotWidth - 44}px`, top: `${top}px` },
|
|
2107
|
-
|
|
2137
|
+
"data-tip": t3.toRealtime,
|
|
2108
2138
|
"aria-label": t3.toRealtime,
|
|
2109
2139
|
onClick: () => ctx.chart.scrollToRealtime(),
|
|
2110
2140
|
children: /* @__PURE__ */ a2(ToRealtimeIcon, {})
|
|
@@ -2125,7 +2155,7 @@ function Attribution({ ctx }) {
|
|
|
2125
2155
|
href: ATTRIBUTION_URL,
|
|
2126
2156
|
target: "_blank",
|
|
2127
2157
|
rel: "noopener",
|
|
2128
|
-
|
|
2158
|
+
"data-tip": label,
|
|
2129
2159
|
"aria-label": label,
|
|
2130
2160
|
style: { top: `${panesHeight - 36}px` },
|
|
2131
2161
|
children: [
|
|
@@ -2150,7 +2180,7 @@ function PaneMaximize({ ctx, multi }) {
|
|
|
2150
2180
|
{
|
|
2151
2181
|
class: "fnc-btn fnc-pane-max",
|
|
2152
2182
|
style: { left: `${s3.layout.plotWidth - 36}px` },
|
|
2153
|
-
|
|
2183
|
+
"data-tip": `${label} (Alt+Enter)`,
|
|
2154
2184
|
"aria-label": label,
|
|
2155
2185
|
"aria-pressed": maximized,
|
|
2156
2186
|
onClick: () => {
|
|
@@ -2696,6 +2726,38 @@ var CSS = (
|
|
|
2696
2726
|
flex: none;
|
|
2697
2727
|
}
|
|
2698
2728
|
|
|
2729
|
+
/* Tooltip (data-tip). Hosts can restyle it with --fnc-tip-bg / --fnc-tip-text. */
|
|
2730
|
+
.fnc-tip {
|
|
2731
|
+
position: fixed;
|
|
2732
|
+
z-index: 2147483001;
|
|
2733
|
+
max-width: 240px;
|
|
2734
|
+
padding: 6px 10px;
|
|
2735
|
+
border-radius: 8px;
|
|
2736
|
+
background: var(--fnc-tip-bg, #24242b);
|
|
2737
|
+
color: var(--fnc-tip-text, #f2f2f4);
|
|
2738
|
+
font-family: var(--fnc-font);
|
|
2739
|
+
font-size: 12px;
|
|
2740
|
+
font-weight: 500;
|
|
2741
|
+
line-height: 1.4;
|
|
2742
|
+
white-space: nowrap;
|
|
2743
|
+
box-shadow: 0 6px 20px rgb(0 0 0 / 0.28);
|
|
2744
|
+
pointer-events: none;
|
|
2745
|
+
opacity: 0;
|
|
2746
|
+
visibility: hidden;
|
|
2747
|
+
transition: opacity 120ms cubic-bezier(0.4, 0, 1, 1), transform 120ms cubic-bezier(0.4, 0, 1, 1), visibility 0s 120ms;
|
|
2748
|
+
}
|
|
2749
|
+
.fnc-tip[data-side='bottom'] { transform: translateY(-4px); }
|
|
2750
|
+
.fnc-tip[data-side='top'] { transform: translateY(4px); }
|
|
2751
|
+
.fnc-tip[data-side='right'] { transform: translateX(-4px); }
|
|
2752
|
+
.fnc-tip.is-on {
|
|
2753
|
+
opacity: 1;
|
|
2754
|
+
visibility: visible;
|
|
2755
|
+
transform: none;
|
|
2756
|
+
transition: opacity 180ms cubic-bezier(0.16, 1, 0.3, 1), transform 180ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
|
|
2757
|
+
}
|
|
2758
|
+
@media (prefers-reduced-motion: reduce) { .fnc-tip, .fnc-tip.is-on { transform: none; } }
|
|
2759
|
+
@media (hover: none) { .fnc-tip { display: none; } }
|
|
2760
|
+
|
|
2699
2761
|
.fnc-tools { display: flex; align-items: center; gap: 4px; flex: none; }
|
|
2700
2762
|
/* Right-hand menus open toward the left so they stay on screen. */
|
|
2701
2763
|
.fnc-tools .fnc-menu { left: auto; right: 0; }
|
|
@@ -3116,6 +3178,77 @@ function div(className, parent) {
|
|
|
3116
3178
|
parent?.appendChild(el);
|
|
3117
3179
|
return el;
|
|
3118
3180
|
}
|
|
3181
|
+
function bindTooltips(root) {
|
|
3182
|
+
const tip = div("fnc-tip", root);
|
|
3183
|
+
tip.setAttribute("role", "tooltip");
|
|
3184
|
+
tip.id = `fnc-tip-${Math.random().toString(36).slice(2, 8)}`;
|
|
3185
|
+
let current = null;
|
|
3186
|
+
let timer;
|
|
3187
|
+
let hiddenAt = 0;
|
|
3188
|
+
const place = (el) => {
|
|
3189
|
+
const r3 = el.getBoundingClientRect();
|
|
3190
|
+
const w3 = tip.offsetWidth;
|
|
3191
|
+
const h3 = tip.offsetHeight;
|
|
3192
|
+
const vw = window.innerWidth;
|
|
3193
|
+
const vh = window.innerHeight;
|
|
3194
|
+
let side = el.closest(".fnc-drawbar") ? "right" : el.closest(".fnc-nav") ? "top" : "bottom";
|
|
3195
|
+
if (side === "bottom" && r3.bottom + h3 + 10 > vh) side = "top";
|
|
3196
|
+
let left = side === "right" ? r3.right + 8 : r3.left + r3.width / 2 - w3 / 2;
|
|
3197
|
+
const top = side === "right" ? r3.top + r3.height / 2 - h3 / 2 : side === "top" ? r3.top - h3 - 6 : r3.bottom + 6;
|
|
3198
|
+
left = Math.max(4, Math.min(vw - w3 - 4, left));
|
|
3199
|
+
tip.style.left = `${Math.round(left)}px`;
|
|
3200
|
+
tip.style.top = `${Math.round(top)}px`;
|
|
3201
|
+
tip.dataset.side = side;
|
|
3202
|
+
};
|
|
3203
|
+
const show = (el) => {
|
|
3204
|
+
const text = el.dataset.tip;
|
|
3205
|
+
if (!text || !el.isConnected) return;
|
|
3206
|
+
tip.textContent = text;
|
|
3207
|
+
el.setAttribute("aria-describedby", tip.id);
|
|
3208
|
+
place(el);
|
|
3209
|
+
tip.classList.add("is-on");
|
|
3210
|
+
};
|
|
3211
|
+
const hide = () => {
|
|
3212
|
+
clearTimeout(timer);
|
|
3213
|
+
if (tip.classList.contains("is-on")) hiddenAt = Date.now();
|
|
3214
|
+
tip.classList.remove("is-on");
|
|
3215
|
+
current?.removeAttribute("aria-describedby");
|
|
3216
|
+
current = null;
|
|
3217
|
+
};
|
|
3218
|
+
const target = (e3) => e3.target instanceof Element ? e3.target.closest("[data-tip]") : null;
|
|
3219
|
+
const onOver = (e3) => {
|
|
3220
|
+
if (e3.pointerType === "touch") return;
|
|
3221
|
+
const el = target(e3);
|
|
3222
|
+
if (el === current) return;
|
|
3223
|
+
hide();
|
|
3224
|
+
if (!el || !root.contains(el)) return;
|
|
3225
|
+
current = el;
|
|
3226
|
+
timer = setTimeout(() => show(el), Date.now() - hiddenAt < 300 ? 0 : 350);
|
|
3227
|
+
};
|
|
3228
|
+
const onFocus = (e3) => {
|
|
3229
|
+
const el = target(e3);
|
|
3230
|
+
if (!el || !e3.target.matches?.(":focus-visible")) return;
|
|
3231
|
+
hide();
|
|
3232
|
+
current = el;
|
|
3233
|
+
show(el);
|
|
3234
|
+
};
|
|
3235
|
+
root.addEventListener("pointerover", onOver);
|
|
3236
|
+
root.addEventListener("pointerleave", hide);
|
|
3237
|
+
root.addEventListener("pointerdown", hide, true);
|
|
3238
|
+
root.addEventListener("wheel", hide, { passive: true });
|
|
3239
|
+
root.addEventListener("focusin", onFocus);
|
|
3240
|
+
root.addEventListener("focusout", hide);
|
|
3241
|
+
return () => {
|
|
3242
|
+
hide();
|
|
3243
|
+
root.removeEventListener("pointerover", onOver);
|
|
3244
|
+
root.removeEventListener("pointerleave", hide);
|
|
3245
|
+
root.removeEventListener("pointerdown", hide, true);
|
|
3246
|
+
root.removeEventListener("wheel", hide);
|
|
3247
|
+
root.removeEventListener("focusin", onFocus);
|
|
3248
|
+
root.removeEventListener("focusout", hide);
|
|
3249
|
+
tip.remove();
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3119
3252
|
function createShell(container, options) {
|
|
3120
3253
|
injectStyles(container.ownerDocument);
|
|
3121
3254
|
const storage = createStorage(options.storageKey);
|
|
@@ -3142,6 +3275,7 @@ function createShell(container, options) {
|
|
|
3142
3275
|
bottom: div("fnc-slot fnc-slot-bottom"),
|
|
3143
3276
|
footer: div("fnc-slot fnc-slot-footer")
|
|
3144
3277
|
};
|
|
3278
|
+
const unbindTips = bindTooltips(root);
|
|
3145
3279
|
const body = div("fnc-body", root);
|
|
3146
3280
|
const drawbarHost = options.drawingToolbar ? div("fnc-drawbar-host", body) : null;
|
|
3147
3281
|
const main = div("fnc-main", body);
|
|
@@ -3207,6 +3341,7 @@ function createShell(container, options) {
|
|
|
3207
3341
|
options.onFavoriteIntervalsChange?.(favoriteIntervals);
|
|
3208
3342
|
},
|
|
3209
3343
|
destroy: () => {
|
|
3344
|
+
unbindTips();
|
|
3210
3345
|
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
|
3211
3346
|
darkQuery?.removeEventListener?.("change", onSchemeChange);
|
|
3212
3347
|
if (isNativeFullscreen()) void document.exitFullscreen();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnest/charts-ui",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.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.5.
|
|
45
|
+
"@funnest/charts": "0.5.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"preact": "^11.2.0"
|