@loafmarkets/ui 0.1.2 → 0.1.4
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/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +240 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +240 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3278,7 +3278,7 @@ function DesktopOrderbookLayout({
|
|
|
3278
3278
|
)
|
|
3279
3279
|
] }) : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: rightHeader })
|
|
3280
3280
|
] }),
|
|
3281
|
-
/* @__PURE__ */ jsxs("div", { className: "px-4
|
|
3281
|
+
/* @__PURE__ */ jsxs("div", { className: "px-4 pt-2", children: [
|
|
3282
3282
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 px-3 py-2 text-xs text-white/60", children: [
|
|
3283
3283
|
/* @__PURE__ */ jsx("div", { children: priceLabel }),
|
|
3284
3284
|
/* @__PURE__ */ jsx("div", { className: "text-right", children: amountLabel })
|
|
@@ -3332,7 +3332,7 @@ function DesktopOrderbookLayout({
|
|
|
3332
3332
|
))
|
|
3333
3333
|
}
|
|
3334
3334
|
),
|
|
3335
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
3335
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
|
|
3336
3336
|
/* @__PURE__ */ jsxs("div", { className: cn("text-lg font-semibold tabular-nums", midClass), children: [
|
|
3337
3337
|
"$",
|
|
3338
3338
|
formatNumber(midPrice, precision),
|
|
@@ -4545,7 +4545,119 @@ var YourOrders = o.forwardRef(
|
|
|
4545
4545
|
}
|
|
4546
4546
|
);
|
|
4547
4547
|
YourOrders.displayName = "YourOrders";
|
|
4548
|
-
var
|
|
4548
|
+
var timeToDate = (time) => {
|
|
4549
|
+
if (typeof time === "number") {
|
|
4550
|
+
return new Date(time * 1e3);
|
|
4551
|
+
}
|
|
4552
|
+
if (typeof time === "object" && time !== null && "year" in time && "month" in time && "day" in time) {
|
|
4553
|
+
const { year, month, day } = time;
|
|
4554
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4555
|
+
}
|
|
4556
|
+
return null;
|
|
4557
|
+
};
|
|
4558
|
+
var createTickFormatter = (formatOptions) => {
|
|
4559
|
+
return (time, _tickMarkType, locale) => {
|
|
4560
|
+
const date = timeToDate(time);
|
|
4561
|
+
if (!date) return "";
|
|
4562
|
+
return date.toLocaleString(locale || void 0, formatOptions);
|
|
4563
|
+
};
|
|
4564
|
+
};
|
|
4565
|
+
var getTimeScaleOptions = (range) => {
|
|
4566
|
+
switch (range) {
|
|
4567
|
+
case "30s":
|
|
4568
|
+
return {
|
|
4569
|
+
timeVisible: true,
|
|
4570
|
+
secondsVisible: true,
|
|
4571
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4572
|
+
tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit", second: "2-digit" })
|
|
4573
|
+
};
|
|
4574
|
+
case "1m":
|
|
4575
|
+
return {
|
|
4576
|
+
timeVisible: true,
|
|
4577
|
+
secondsVisible: true,
|
|
4578
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4579
|
+
tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit", second: "2-digit" })
|
|
4580
|
+
};
|
|
4581
|
+
case "5m":
|
|
4582
|
+
return {
|
|
4583
|
+
timeVisible: true,
|
|
4584
|
+
secondsVisible: false,
|
|
4585
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4586
|
+
tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit" })
|
|
4587
|
+
};
|
|
4588
|
+
case "15m":
|
|
4589
|
+
return {
|
|
4590
|
+
timeVisible: true,
|
|
4591
|
+
secondsVisible: false,
|
|
4592
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4593
|
+
tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit" })
|
|
4594
|
+
};
|
|
4595
|
+
case "1h":
|
|
4596
|
+
return {
|
|
4597
|
+
timeVisible: true,
|
|
4598
|
+
secondsVisible: false,
|
|
4599
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4600
|
+
tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit" })
|
|
4601
|
+
};
|
|
4602
|
+
case "4h":
|
|
4603
|
+
return {
|
|
4604
|
+
timeVisible: true,
|
|
4605
|
+
secondsVisible: false,
|
|
4606
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4607
|
+
tickMarkFormatter: createTickFormatter({ weekday: "short", hour: "2-digit" })
|
|
4608
|
+
};
|
|
4609
|
+
case "24h":
|
|
4610
|
+
return {
|
|
4611
|
+
timeVisible: true,
|
|
4612
|
+
secondsVisible: false,
|
|
4613
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4614
|
+
tickMarkFormatter: createTickFormatter({ month: "short", day: "numeric" })
|
|
4615
|
+
};
|
|
4616
|
+
case "1W":
|
|
4617
|
+
return {
|
|
4618
|
+
timeVisible: true,
|
|
4619
|
+
secondsVisible: false,
|
|
4620
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4621
|
+
tickMarkFormatter: createTickFormatter({ month: "short", day: "numeric" })
|
|
4622
|
+
};
|
|
4623
|
+
case "1M":
|
|
4624
|
+
return {
|
|
4625
|
+
timeVisible: true,
|
|
4626
|
+
secondsVisible: false,
|
|
4627
|
+
borderColor: "rgba(255,255,255,0.06)",
|
|
4628
|
+
tickMarkFormatter: createTickFormatter({ month: "short", year: "2-digit" })
|
|
4629
|
+
};
|
|
4630
|
+
default:
|
|
4631
|
+
return {
|
|
4632
|
+
timeVisible: true,
|
|
4633
|
+
secondsVisible: false,
|
|
4634
|
+
borderColor: "rgba(255,255,255,0.06)"
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
};
|
|
4638
|
+
var getPriceScaleOptions = (data) => {
|
|
4639
|
+
if (!data.length) return {
|
|
4640
|
+
borderColor: "rgba(230, 200, 126, 0.25)",
|
|
4641
|
+
textColor: "rgba(230, 200, 126, 0.7)"
|
|
4642
|
+
};
|
|
4643
|
+
const prices = data.flatMap((d2) => [d2.open, d2.high, d2.low, d2.close]);
|
|
4644
|
+
const minPrice = Math.min(...prices);
|
|
4645
|
+
const maxPrice = Math.max(...prices);
|
|
4646
|
+
const priceRange = maxPrice - minPrice;
|
|
4647
|
+
let scaleMargins = { top: 0.1, bottom: 0.1 };
|
|
4648
|
+
if (priceRange < 1) {
|
|
4649
|
+
scaleMargins = { top: 0.2, bottom: 0.2 };
|
|
4650
|
+
} else if (priceRange > 1e3) {
|
|
4651
|
+
scaleMargins = { top: 0.05, bottom: 0.05 };
|
|
4652
|
+
}
|
|
4653
|
+
return {
|
|
4654
|
+
borderColor: "rgba(230, 200, 126, 0.25)",
|
|
4655
|
+
textColor: "rgba(230, 200, 126, 0.7)",
|
|
4656
|
+
scaleMargins
|
|
4657
|
+
};
|
|
4658
|
+
};
|
|
4659
|
+
var defaultRanges = ["30s", "1m", "5m", "15m", "1h", "4h", "24h", "1W", "1M"];
|
|
4660
|
+
var VISIBLE_RANGE_COUNT = 4;
|
|
4549
4661
|
var formatPrice = (value, currencySymbol) => {
|
|
4550
4662
|
return `${currencySymbol}${value.toLocaleString(void 0, {
|
|
4551
4663
|
minimumFractionDigits: 2,
|
|
@@ -4582,6 +4694,21 @@ var PriceChart = o.forwardRef(
|
|
|
4582
4694
|
const seriesRef = o.useRef(null);
|
|
4583
4695
|
const priceLineRef = o.useRef(null);
|
|
4584
4696
|
const [hoveredRange, setHoveredRange] = o.useState(null);
|
|
4697
|
+
const [dropdownOpen, setDropdownOpen] = o.useState(false);
|
|
4698
|
+
const dropdownRef = o.useRef(null);
|
|
4699
|
+
const isAutoScrollRef = o.useRef(true);
|
|
4700
|
+
const visibleRanges = ranges.slice(0, VISIBLE_RANGE_COUNT);
|
|
4701
|
+
const dropdownRanges = ranges.slice(VISIBLE_RANGE_COUNT);
|
|
4702
|
+
const selectedInDropdown = dropdownRanges.includes(selectedRange);
|
|
4703
|
+
o.useEffect(() => {
|
|
4704
|
+
const handleClickOutside = (e) => {
|
|
4705
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
4706
|
+
setDropdownOpen(false);
|
|
4707
|
+
}
|
|
4708
|
+
};
|
|
4709
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
4710
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
4711
|
+
}, []);
|
|
4585
4712
|
const resolvedPrice = o.useMemo(() => {
|
|
4586
4713
|
if (price != null) return price;
|
|
4587
4714
|
const last = data.at(-1);
|
|
@@ -4614,15 +4741,8 @@ var PriceChart = o.forwardRef(
|
|
|
4614
4741
|
vertLines: { color: "rgba(255,255,255,0.06)" },
|
|
4615
4742
|
horzLines: { color: "rgba(255,255,255,0.06)" }
|
|
4616
4743
|
},
|
|
4617
|
-
rightPriceScale:
|
|
4618
|
-
|
|
4619
|
-
textColor: "rgba(230, 200, 126, 0.7)"
|
|
4620
|
-
},
|
|
4621
|
-
timeScale: {
|
|
4622
|
-
borderColor: "rgba(255,255,255,0.06)",
|
|
4623
|
-
timeVisible: true,
|
|
4624
|
-
secondsVisible: false
|
|
4625
|
-
},
|
|
4744
|
+
rightPriceScale: getPriceScaleOptions(data),
|
|
4745
|
+
timeScale: getTimeScaleOptions(selectedRange || defaultRanges[0]),
|
|
4626
4746
|
crosshair: {
|
|
4627
4747
|
vertLine: { color: "rgba(255,255,255,0.12)" },
|
|
4628
4748
|
horzLine: { color: "rgba(255,255,255,0.12)" }
|
|
@@ -4637,17 +4757,37 @@ var PriceChart = o.forwardRef(
|
|
|
4637
4757
|
});
|
|
4638
4758
|
chartRef.current = chart;
|
|
4639
4759
|
seriesRef.current = series;
|
|
4760
|
+
const handleVisibleRangeChange = () => {
|
|
4761
|
+
const timeScale2 = chart.timeScale();
|
|
4762
|
+
const position2 = timeScale2.scrollPosition();
|
|
4763
|
+
const atRightEdge = position2 <= 0.01;
|
|
4764
|
+
isAutoScrollRef.current = atRightEdge;
|
|
4765
|
+
};
|
|
4766
|
+
const timeScale = chart.timeScale();
|
|
4767
|
+
timeScale.subscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
|
|
4640
4768
|
return () => {
|
|
4641
4769
|
chartRef.current = null;
|
|
4642
4770
|
seriesRef.current = null;
|
|
4771
|
+
timeScale.unsubscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
|
|
4643
4772
|
chart.remove();
|
|
4644
4773
|
};
|
|
4645
4774
|
}, []);
|
|
4775
|
+
o.useEffect(() => {
|
|
4776
|
+
const chart = chartRef.current;
|
|
4777
|
+
if (!chart) return;
|
|
4778
|
+
const effectiveRange = selectedRange ?? ranges?.[0] ?? "1D";
|
|
4779
|
+
chart.applyOptions({
|
|
4780
|
+
timeScale: getTimeScaleOptions(effectiveRange)
|
|
4781
|
+
});
|
|
4782
|
+
}, [selectedRange, ranges]);
|
|
4646
4783
|
o.useEffect(() => {
|
|
4647
4784
|
const chart = chartRef.current;
|
|
4648
4785
|
const series = seriesRef.current;
|
|
4649
4786
|
if (!chart || !series) return;
|
|
4650
4787
|
series.setData(data);
|
|
4788
|
+
chart.applyOptions({
|
|
4789
|
+
rightPriceScale: getPriceScaleOptions(data)
|
|
4790
|
+
});
|
|
4651
4791
|
if (priceLineRef.current) {
|
|
4652
4792
|
series.removePriceLine(priceLineRef.current);
|
|
4653
4793
|
priceLineRef.current = null;
|
|
@@ -4663,7 +4803,9 @@ var PriceChart = o.forwardRef(
|
|
|
4663
4803
|
title: resolvedPrice.toFixed(2)
|
|
4664
4804
|
});
|
|
4665
4805
|
}
|
|
4666
|
-
|
|
4806
|
+
if (isAutoScrollRef.current) {
|
|
4807
|
+
chart.timeScale().scrollToPosition(0, true);
|
|
4808
|
+
}
|
|
4667
4809
|
}, [data, resolvedPrice]);
|
|
4668
4810
|
const sign = inferredChangePercent == null ? null : inferredChangePercent >= 0 ? "+" : "";
|
|
4669
4811
|
const changeClass = inferredChangePercent == null ? "" : inferredChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
|
|
@@ -4695,30 +4837,97 @@ var PriceChart = o.forwardRef(
|
|
|
4695
4837
|
/* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-4 px-4 pb-2 pt-5 md:flex-row md:items-start md:justify-between md:px-6 md:pt-6", children: [
|
|
4696
4838
|
/* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-3 md:w-auto", children: [
|
|
4697
4839
|
/* @__PURE__ */ jsx(CardTitle, { className: "m-0 text-[1.1rem] font-semibold text-white", children: title }),
|
|
4698
|
-
/* @__PURE__ */
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4840
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
4841
|
+
visibleRanges.map((r2) => {
|
|
4842
|
+
const active = r2 === selectedRange;
|
|
4843
|
+
const hovered = hoveredRange === r2;
|
|
4844
|
+
const style = {
|
|
4845
|
+
...btnBaseStyle,
|
|
4846
|
+
...hovered ? btnHoverStyle : null,
|
|
4847
|
+
...active ? btnActiveStyle : null
|
|
4848
|
+
};
|
|
4849
|
+
return /* @__PURE__ */ jsx(
|
|
4850
|
+
"button",
|
|
4851
|
+
{
|
|
4852
|
+
type: "button",
|
|
4853
|
+
onClick: () => onRangeChange?.(r2),
|
|
4854
|
+
onMouseEnter: () => setHoveredRange(r2),
|
|
4855
|
+
onMouseLeave: () => setHoveredRange((prev2) => prev2 === r2 ? null : prev2),
|
|
4856
|
+
style,
|
|
4857
|
+
className: cn(
|
|
4858
|
+
"rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
|
|
4859
|
+
),
|
|
4860
|
+
children: r2
|
|
4861
|
+
},
|
|
4862
|
+
r2
|
|
4863
|
+
);
|
|
4864
|
+
}),
|
|
4865
|
+
selectedInDropdown && selectedRange ? /* @__PURE__ */ jsx(
|
|
4707
4866
|
"button",
|
|
4708
4867
|
{
|
|
4709
4868
|
type: "button",
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4869
|
+
style: {
|
|
4870
|
+
...btnBaseStyle,
|
|
4871
|
+
...btnActiveStyle
|
|
4872
|
+
},
|
|
4714
4873
|
className: cn(
|
|
4715
4874
|
"rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
|
|
4716
4875
|
),
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
)
|
|
4721
|
-
|
|
4876
|
+
disabled: true,
|
|
4877
|
+
children: selectedRange
|
|
4878
|
+
}
|
|
4879
|
+
) : null,
|
|
4880
|
+
dropdownRanges.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative", ref: dropdownRef, children: [
|
|
4881
|
+
/* @__PURE__ */ jsxs(
|
|
4882
|
+
"button",
|
|
4883
|
+
{
|
|
4884
|
+
type: "button",
|
|
4885
|
+
onClick: () => setDropdownOpen((prev2) => !prev2),
|
|
4886
|
+
onMouseEnter: () => setHoveredRange("__dropdown__"),
|
|
4887
|
+
onMouseLeave: () => setHoveredRange((prev2) => prev2 === "__dropdown__" ? null : prev2),
|
|
4888
|
+
style: {
|
|
4889
|
+
...btnBaseStyle,
|
|
4890
|
+
...hoveredRange === "__dropdown__" ? btnHoverStyle : null
|
|
4891
|
+
},
|
|
4892
|
+
className: cn(
|
|
4893
|
+
"flex items-center gap-1 rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
|
|
4894
|
+
),
|
|
4895
|
+
children: [
|
|
4896
|
+
"More",
|
|
4897
|
+
/* @__PURE__ */ jsx(
|
|
4898
|
+
"svg",
|
|
4899
|
+
{
|
|
4900
|
+
className: cn("h-3 w-3 transition-transform", dropdownOpen && "rotate-180"),
|
|
4901
|
+
fill: "none",
|
|
4902
|
+
stroke: "currentColor",
|
|
4903
|
+
viewBox: "0 0 24 24",
|
|
4904
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
|
|
4905
|
+
}
|
|
4906
|
+
)
|
|
4907
|
+
]
|
|
4908
|
+
}
|
|
4909
|
+
),
|
|
4910
|
+
dropdownOpen && /* @__PURE__ */ jsx("div", { className: "absolute left-0 top-full z-50 mt-1 min-w-[80px] rounded border border-white/10 bg-black/90 py-1 backdrop-blur-md", children: dropdownRanges.map((r2) => {
|
|
4911
|
+
const active = r2 === selectedRange;
|
|
4912
|
+
return /* @__PURE__ */ jsx(
|
|
4913
|
+
"button",
|
|
4914
|
+
{
|
|
4915
|
+
type: "button",
|
|
4916
|
+
onClick: () => {
|
|
4917
|
+
onRangeChange?.(r2);
|
|
4918
|
+
setDropdownOpen(false);
|
|
4919
|
+
},
|
|
4920
|
+
className: cn(
|
|
4921
|
+
"block w-full px-3 py-1.5 text-left text-[0.85rem] font-medium transition-colors hover:bg-white/10",
|
|
4922
|
+
active ? "bg-[#e6c87e]/20 text-[#e6c87e]" : "text-white/70"
|
|
4923
|
+
),
|
|
4924
|
+
children: r2
|
|
4925
|
+
},
|
|
4926
|
+
r2
|
|
4927
|
+
);
|
|
4928
|
+
}) })
|
|
4929
|
+
] })
|
|
4930
|
+
] })
|
|
4722
4931
|
] }),
|
|
4723
4932
|
resolvedPrice == null && inferredChangePercent == null ? null : /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
|
|
4724
4933
|
resolvedPrice == null ? null : /* @__PURE__ */ jsx(
|