@loafmarkets/ui 0.1.3 → 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 CHANGED
@@ -280,7 +280,7 @@ declare const YourOrders: React.ForwardRefExoticComponent<React.HTMLAttributes<H
280
280
  renderOrderActions?: (order: YourOrder) => React.ReactNode;
281
281
  } & React.RefAttributes<HTMLDivElement>>;
282
282
 
283
- type PriceChartRange = "15m" | "1h" | "4h" | "24h" | "1W" | "1M";
283
+ type PriceChartRange = "30s" | "1m" | "5m" | "15m" | "1h" | "4h" | "24h" | "1W" | "1M";
284
284
  type PriceChartCandle = {
285
285
  time: LightweightCharts.Time;
286
286
  open: number;
package/dist/index.d.ts CHANGED
@@ -280,7 +280,7 @@ declare const YourOrders: React.ForwardRefExoticComponent<React.HTMLAttributes<H
280
280
  renderOrderActions?: (order: YourOrder) => React.ReactNode;
281
281
  } & React.RefAttributes<HTMLDivElement>>;
282
282
 
283
- type PriceChartRange = "15m" | "1h" | "4h" | "24h" | "1W" | "1M";
283
+ type PriceChartRange = "30s" | "1m" | "5m" | "15m" | "1h" | "4h" | "24h" | "1W" | "1M";
284
284
  type PriceChartCandle = {
285
285
  time: LightweightCharts.Time;
286
286
  open: number;
package/dist/index.js CHANGED
@@ -3300,7 +3300,7 @@ function DesktopOrderbookLayout({
3300
3300
  )
3301
3301
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: rightHeader })
3302
3302
  ] }),
3303
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 pb-3 pt-2", children: [
3303
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 pt-2", children: [
3304
3304
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3 px-3 py-2 text-xs text-white/60", children: [
3305
3305
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: priceLabel }),
3306
3306
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: amountLabel })
@@ -3354,7 +3354,7 @@ function DesktopOrderbookLayout({
3354
3354
  ))
3355
3355
  }
3356
3356
  ),
3357
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
3357
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
3358
3358
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("text-lg font-semibold tabular-nums", midClass), children: [
3359
3359
  "$",
3360
3360
  formatNumber(midPrice, precision),
@@ -4586,6 +4586,27 @@ var createTickFormatter = (formatOptions) => {
4586
4586
  };
4587
4587
  var getTimeScaleOptions = (range) => {
4588
4588
  switch (range) {
4589
+ case "30s":
4590
+ return {
4591
+ timeVisible: true,
4592
+ secondsVisible: true,
4593
+ borderColor: "rgba(255,255,255,0.06)",
4594
+ tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit", second: "2-digit" })
4595
+ };
4596
+ case "1m":
4597
+ return {
4598
+ timeVisible: true,
4599
+ secondsVisible: true,
4600
+ borderColor: "rgba(255,255,255,0.06)",
4601
+ tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit", second: "2-digit" })
4602
+ };
4603
+ case "5m":
4604
+ return {
4605
+ timeVisible: true,
4606
+ secondsVisible: false,
4607
+ borderColor: "rgba(255,255,255,0.06)",
4608
+ tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit" })
4609
+ };
4589
4610
  case "15m":
4590
4611
  return {
4591
4612
  timeVisible: true,
@@ -4657,7 +4678,8 @@ var getPriceScaleOptions = (data) => {
4657
4678
  scaleMargins
4658
4679
  };
4659
4680
  };
4660
- var defaultRanges = ["15m", "1h", "4h", "24h", "1W", "1M"];
4681
+ var defaultRanges = ["30s", "1m", "5m", "15m", "1h", "4h", "24h", "1W", "1M"];
4682
+ var VISIBLE_RANGE_COUNT = 4;
4661
4683
  var formatPrice = (value, currencySymbol) => {
4662
4684
  return `${currencySymbol}${value.toLocaleString(void 0, {
4663
4685
  minimumFractionDigits: 2,
@@ -4694,6 +4716,21 @@ var PriceChart = o__namespace.forwardRef(
4694
4716
  const seriesRef = o__namespace.useRef(null);
4695
4717
  const priceLineRef = o__namespace.useRef(null);
4696
4718
  const [hoveredRange, setHoveredRange] = o__namespace.useState(null);
4719
+ const [dropdownOpen, setDropdownOpen] = o__namespace.useState(false);
4720
+ const dropdownRef = o__namespace.useRef(null);
4721
+ const isAutoScrollRef = o__namespace.useRef(true);
4722
+ const visibleRanges = ranges.slice(0, VISIBLE_RANGE_COUNT);
4723
+ const dropdownRanges = ranges.slice(VISIBLE_RANGE_COUNT);
4724
+ const selectedInDropdown = dropdownRanges.includes(selectedRange);
4725
+ o__namespace.useEffect(() => {
4726
+ const handleClickOutside = (e) => {
4727
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
4728
+ setDropdownOpen(false);
4729
+ }
4730
+ };
4731
+ document.addEventListener("mousedown", handleClickOutside);
4732
+ return () => document.removeEventListener("mousedown", handleClickOutside);
4733
+ }, []);
4697
4734
  const resolvedPrice = o__namespace.useMemo(() => {
4698
4735
  if (price != null) return price;
4699
4736
  const last = data.at(-1);
@@ -4742,9 +4779,18 @@ var PriceChart = o__namespace.forwardRef(
4742
4779
  });
4743
4780
  chartRef.current = chart;
4744
4781
  seriesRef.current = series;
4782
+ const handleVisibleRangeChange = () => {
4783
+ const timeScale2 = chart.timeScale();
4784
+ const position2 = timeScale2.scrollPosition();
4785
+ const atRightEdge = position2 <= 0.01;
4786
+ isAutoScrollRef.current = atRightEdge;
4787
+ };
4788
+ const timeScale = chart.timeScale();
4789
+ timeScale.subscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
4745
4790
  return () => {
4746
4791
  chartRef.current = null;
4747
4792
  seriesRef.current = null;
4793
+ timeScale.unsubscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
4748
4794
  chart.remove();
4749
4795
  };
4750
4796
  }, []);
@@ -4779,7 +4825,9 @@ var PriceChart = o__namespace.forwardRef(
4779
4825
  title: resolvedPrice.toFixed(2)
4780
4826
  });
4781
4827
  }
4782
- chart.timeScale().fitContent();
4828
+ if (isAutoScrollRef.current) {
4829
+ chart.timeScale().scrollToPosition(0, true);
4830
+ }
4783
4831
  }, [data, resolvedPrice]);
4784
4832
  const sign = inferredChangePercent == null ? null : inferredChangePercent >= 0 ? "+" : "";
4785
4833
  const changeClass = inferredChangePercent == null ? "" : inferredChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
@@ -4811,30 +4859,97 @@ var PriceChart = o__namespace.forwardRef(
4811
4859
  /* @__PURE__ */ jsxRuntime.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: [
4812
4860
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full flex-col gap-3 md:w-auto", children: [
4813
4861
  /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "m-0 text-[1.1rem] font-semibold text-white", children: title }),
4814
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: ranges.map((r2) => {
4815
- const active = r2 === selectedRange;
4816
- const hovered = hoveredRange === r2;
4817
- const style = {
4818
- ...btnBaseStyle,
4819
- ...hovered ? btnHoverStyle : null,
4820
- ...active ? btnActiveStyle : null
4821
- };
4822
- return /* @__PURE__ */ jsxRuntime.jsx(
4862
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4863
+ visibleRanges.map((r2) => {
4864
+ const active = r2 === selectedRange;
4865
+ const hovered = hoveredRange === r2;
4866
+ const style = {
4867
+ ...btnBaseStyle,
4868
+ ...hovered ? btnHoverStyle : null,
4869
+ ...active ? btnActiveStyle : null
4870
+ };
4871
+ return /* @__PURE__ */ jsxRuntime.jsx(
4872
+ "button",
4873
+ {
4874
+ type: "button",
4875
+ onClick: () => onRangeChange?.(r2),
4876
+ onMouseEnter: () => setHoveredRange(r2),
4877
+ onMouseLeave: () => setHoveredRange((prev2) => prev2 === r2 ? null : prev2),
4878
+ style,
4879
+ className: cn(
4880
+ "rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4881
+ ),
4882
+ children: r2
4883
+ },
4884
+ r2
4885
+ );
4886
+ }),
4887
+ selectedInDropdown && selectedRange ? /* @__PURE__ */ jsxRuntime.jsx(
4823
4888
  "button",
4824
4889
  {
4825
4890
  type: "button",
4826
- onClick: () => onRangeChange?.(r2),
4827
- onMouseEnter: () => setHoveredRange(r2),
4828
- onMouseLeave: () => setHoveredRange((prev2) => prev2 === r2 ? null : prev2),
4829
- style,
4891
+ style: {
4892
+ ...btnBaseStyle,
4893
+ ...btnActiveStyle
4894
+ },
4830
4895
  className: cn(
4831
4896
  "rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4832
4897
  ),
4833
- children: r2
4834
- },
4835
- r2
4836
- );
4837
- }) })
4898
+ disabled: true,
4899
+ children: selectedRange
4900
+ }
4901
+ ) : null,
4902
+ dropdownRanges.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: dropdownRef, children: [
4903
+ /* @__PURE__ */ jsxRuntime.jsxs(
4904
+ "button",
4905
+ {
4906
+ type: "button",
4907
+ onClick: () => setDropdownOpen((prev2) => !prev2),
4908
+ onMouseEnter: () => setHoveredRange("__dropdown__"),
4909
+ onMouseLeave: () => setHoveredRange((prev2) => prev2 === "__dropdown__" ? null : prev2),
4910
+ style: {
4911
+ ...btnBaseStyle,
4912
+ ...hoveredRange === "__dropdown__" ? btnHoverStyle : null
4913
+ },
4914
+ className: cn(
4915
+ "flex items-center gap-1 rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4916
+ ),
4917
+ children: [
4918
+ "More",
4919
+ /* @__PURE__ */ jsxRuntime.jsx(
4920
+ "svg",
4921
+ {
4922
+ className: cn("h-3 w-3 transition-transform", dropdownOpen && "rotate-180"),
4923
+ fill: "none",
4924
+ stroke: "currentColor",
4925
+ viewBox: "0 0 24 24",
4926
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
4927
+ }
4928
+ )
4929
+ ]
4930
+ }
4931
+ ),
4932
+ dropdownOpen && /* @__PURE__ */ jsxRuntime.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) => {
4933
+ const active = r2 === selectedRange;
4934
+ return /* @__PURE__ */ jsxRuntime.jsx(
4935
+ "button",
4936
+ {
4937
+ type: "button",
4938
+ onClick: () => {
4939
+ onRangeChange?.(r2);
4940
+ setDropdownOpen(false);
4941
+ },
4942
+ className: cn(
4943
+ "block w-full px-3 py-1.5 text-left text-[0.85rem] font-medium transition-colors hover:bg-white/10",
4944
+ active ? "bg-[#e6c87e]/20 text-[#e6c87e]" : "text-white/70"
4945
+ ),
4946
+ children: r2
4947
+ },
4948
+ r2
4949
+ );
4950
+ }) })
4951
+ ] })
4952
+ ] })
4838
4953
  ] }),
4839
4954
  resolvedPrice == null && inferredChangePercent == null ? null : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
4840
4955
  resolvedPrice == null ? null : /* @__PURE__ */ jsxRuntime.jsx(