@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.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 pb-3 pt-2", children: [
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: "mt-2 grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
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),
@@ -4564,6 +4564,27 @@ var createTickFormatter = (formatOptions) => {
4564
4564
  };
4565
4565
  var getTimeScaleOptions = (range) => {
4566
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
+ };
4567
4588
  case "15m":
4568
4589
  return {
4569
4590
  timeVisible: true,
@@ -4635,7 +4656,8 @@ var getPriceScaleOptions = (data) => {
4635
4656
  scaleMargins
4636
4657
  };
4637
4658
  };
4638
- var defaultRanges = ["15m", "1h", "4h", "24h", "1W", "1M"];
4659
+ var defaultRanges = ["30s", "1m", "5m", "15m", "1h", "4h", "24h", "1W", "1M"];
4660
+ var VISIBLE_RANGE_COUNT = 4;
4639
4661
  var formatPrice = (value, currencySymbol) => {
4640
4662
  return `${currencySymbol}${value.toLocaleString(void 0, {
4641
4663
  minimumFractionDigits: 2,
@@ -4672,6 +4694,21 @@ var PriceChart = o.forwardRef(
4672
4694
  const seriesRef = o.useRef(null);
4673
4695
  const priceLineRef = o.useRef(null);
4674
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
+ }, []);
4675
4712
  const resolvedPrice = o.useMemo(() => {
4676
4713
  if (price != null) return price;
4677
4714
  const last = data.at(-1);
@@ -4720,9 +4757,18 @@ var PriceChart = o.forwardRef(
4720
4757
  });
4721
4758
  chartRef.current = chart;
4722
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);
4723
4768
  return () => {
4724
4769
  chartRef.current = null;
4725
4770
  seriesRef.current = null;
4771
+ timeScale.unsubscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
4726
4772
  chart.remove();
4727
4773
  };
4728
4774
  }, []);
@@ -4757,7 +4803,9 @@ var PriceChart = o.forwardRef(
4757
4803
  title: resolvedPrice.toFixed(2)
4758
4804
  });
4759
4805
  }
4760
- chart.timeScale().fitContent();
4806
+ if (isAutoScrollRef.current) {
4807
+ chart.timeScale().scrollToPosition(0, true);
4808
+ }
4761
4809
  }, [data, resolvedPrice]);
4762
4810
  const sign = inferredChangePercent == null ? null : inferredChangePercent >= 0 ? "+" : "";
4763
4811
  const changeClass = inferredChangePercent == null ? "" : inferredChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
@@ -4789,30 +4837,97 @@ var PriceChart = o.forwardRef(
4789
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: [
4790
4838
  /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-3 md:w-auto", children: [
4791
4839
  /* @__PURE__ */ jsx(CardTitle, { className: "m-0 text-[1.1rem] font-semibold text-white", children: title }),
4792
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ranges.map((r2) => {
4793
- const active = r2 === selectedRange;
4794
- const hovered = hoveredRange === r2;
4795
- const style = {
4796
- ...btnBaseStyle,
4797
- ...hovered ? btnHoverStyle : null,
4798
- ...active ? btnActiveStyle : null
4799
- };
4800
- return /* @__PURE__ */ jsx(
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(
4801
4866
  "button",
4802
4867
  {
4803
4868
  type: "button",
4804
- onClick: () => onRangeChange?.(r2),
4805
- onMouseEnter: () => setHoveredRange(r2),
4806
- onMouseLeave: () => setHoveredRange((prev2) => prev2 === r2 ? null : prev2),
4807
- style,
4869
+ style: {
4870
+ ...btnBaseStyle,
4871
+ ...btnActiveStyle
4872
+ },
4808
4873
  className: cn(
4809
4874
  "rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4810
4875
  ),
4811
- children: r2
4812
- },
4813
- r2
4814
- );
4815
- }) })
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
+ ] })
4816
4931
  ] }),
4817
4932
  resolvedPrice == null && inferredChangePercent == null ? null : /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
4818
4933
  resolvedPrice == null ? null : /* @__PURE__ */ jsx(