@loafmarkets/ui 0.1.3 → 0.1.5

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,11 +3332,11 @@ 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),
3339
- midChangePercent == null ? null : /* @__PURE__ */ jsxs("span", { className: "ml-2 text-sm font-semibold", children: [
3339
+ midChangePercent == null ? null : /* @__PURE__ */ jsxs("span", { className: cn("ml-2 text-sm font-semibold tabular-nums", midClass), children: [
3340
3340
  midChangePercent >= 0 ? "+" : "",
3341
3341
  midChangePercent.toFixed(2),
3342
3342
  "%"
@@ -3391,18 +3391,18 @@ function MobileOrderbookLayout({
3391
3391
  const visibleBids = o.useMemo(() => bids.slice(0, COMPACT_ROWS_VISIBLE), [bids]);
3392
3392
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3393
3393
  /* @__PURE__ */ jsx("div", { className: "px-3 pt-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
3394
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3394
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3395
3395
  /* @__PURE__ */ jsx(
3396
3396
  "span",
3397
3397
  {
3398
3398
  onClick: () => handleTab("orderbook"),
3399
3399
  style: {
3400
3400
  cursor: "pointer",
3401
- fontSize: "0.8rem",
3401
+ fontSize: "0.72rem",
3402
3402
  fontWeight: tab === "orderbook" ? 600 : 400,
3403
3403
  color: tab === "orderbook" ? "#fff" : "rgba(255,255,255,0.6)",
3404
3404
  borderBottom: tab === "orderbook" ? "2px solid #C9A227" : "none",
3405
- paddingBottom: "0.25rem"
3405
+ paddingBottom: "0.2rem"
3406
3406
  },
3407
3407
  children: "Orderbook"
3408
3408
  }
@@ -3413,11 +3413,11 @@ function MobileOrderbookLayout({
3413
3413
  onClick: () => handleTab("trades"),
3414
3414
  style: {
3415
3415
  cursor: "pointer",
3416
- fontSize: "0.8rem",
3416
+ fontSize: "0.72rem",
3417
3417
  fontWeight: tab === "trades" ? 600 : 400,
3418
3418
  color: tab === "trades" ? "#fff" : "rgba(255,255,255,0.6)",
3419
3419
  borderBottom: tab === "trades" ? "2px solid #C9A227" : "none",
3420
- paddingBottom: "0.25rem"
3420
+ paddingBottom: "0.2rem"
3421
3421
  },
3422
3422
  children: "Trades"
3423
3423
  }
@@ -3477,20 +3477,26 @@ function MobileOrderbookLayout({
3477
3477
  gridTemplateColumns: "1.2fr 0.8fr",
3478
3478
  padding: "0.3rem 0",
3479
3479
  fontSize: "0.8rem",
3480
- backgroundColor: "rgba(0, 123, 255, 0.05)",
3481
3480
  borderTop: "1px solid rgba(255,255,255,0.1)",
3482
3481
  borderBottom: "1px solid rgba(255,255,255,0.1)"
3483
3482
  },
3484
3483
  children: [
3485
- /* @__PURE__ */ jsxs("div", { style: { fontWeight: "bold", display: "flex", alignItems: "center", gap: "8px" }, className: midClass, children: [
3486
- "$",
3487
- formatNumber(midPrice, precision),
3488
- midChangePercent != null && /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.8rem" }, className: midClass, children: [
3489
- midChangePercent >= 0 ? "+" : "",
3490
- midChangePercent.toFixed(2),
3491
- "%"
3492
- ] })
3493
- ] }),
3484
+ /* @__PURE__ */ jsxs(
3485
+ "div",
3486
+ {
3487
+ style: { fontWeight: "bold", display: "flex", alignItems: "center", gap: "8px" },
3488
+ className: midClass,
3489
+ children: [
3490
+ "$",
3491
+ formatNumber(midPrice, precision),
3492
+ midChangePercent != null && /* @__PURE__ */ jsxs("span", { className: cn("text-[0.75rem] font-semibold tabular-nums", midClass), children: [
3493
+ midChangePercent >= 0 ? "+" : "",
3494
+ midChangePercent.toFixed(2),
3495
+ "%"
3496
+ ] })
3497
+ ]
3498
+ }
3499
+ ),
3494
3500
  /* @__PURE__ */ jsx("div", {})
3495
3501
  ]
3496
3502
  }
@@ -4383,6 +4389,10 @@ var YourOrders = o.forwardRef(
4383
4389
  const resolvedTabs = tabs ?? DEFAULT_TABS.map((t) => ({ ...t, orders: orders ?? [], emptyState: `No ${t.label.toLowerCase()} data available.` }));
4384
4390
  const activeTab = resolvedTabs.find((t) => t.id === effectiveActiveTabId) ?? resolvedTabs[0];
4385
4391
  const activeOrders = activeTab?.orders ?? orders ?? [];
4392
+ const tabSupportsActions = activeTab?.enableCancel ?? true;
4393
+ const showActionsColumn = Boolean(renderOrderActions && tabSupportsActions);
4394
+ const baseGridTemplate = "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr";
4395
+ const gridTemplateColumns = showActionsColumn ? "1.8fr 0.8fr 0.7fr 0.8fr 0.8fr 0.9fr 0.8fr" : baseGridTemplate;
4386
4396
  const displayTitle = title ?? activeTab?.title ?? activeTab?.label ?? "Portfolio Holdings";
4387
4397
  console.log("[YourOrders] tabs:", tabs?.map((t) => ({ id: t.id, ordersCount: t.orders?.length })));
4388
4398
  console.log("[YourOrders] activeTabId prop:", activeTabId);
@@ -4395,13 +4405,14 @@ var YourOrders = o.forwardRef(
4395
4405
  return /* @__PURE__ */ jsx("div", { style: emptyStateStyle, children: emptyMessage });
4396
4406
  }
4397
4407
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4398
- /* @__PURE__ */ jsxs("div", { style: tableHeaderStyle, children: [
4408
+ /* @__PURE__ */ jsxs("div", { style: { ...tableHeaderStyle, gridTemplateColumns }, children: [
4399
4409
  /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Property" }),
4400
4410
  /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Value" }),
4401
4411
  /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Holding" }),
4402
4412
  /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "% of Property" }),
4403
4413
  /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Avg Price" }),
4404
- /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "P&L" })
4414
+ /* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "P&L" }),
4415
+ showActionsColumn ? /* @__PURE__ */ jsx("div", { style: { ...tableHeaderCell, textAlign: "right" }, children: "Actions" }) : null
4405
4416
  ] }),
4406
4417
  /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.65rem" }, children: activeOrders.map((order) => {
4407
4418
  const propertyName = order.asset;
@@ -4419,7 +4430,7 @@ var YourOrders = o.forwardRef(
4419
4430
  const currentPrice = order.currentPrice ?? order.price;
4420
4431
  const priceChangePercent = order.priceChangePercent ?? order.pnlPercent;
4421
4432
  const priceChangePositive = order.priceChangePositive ?? (priceChangePercent != null ? priceChangePercent >= 0 : void 0);
4422
- return /* @__PURE__ */ jsxs("div", { style: { ...rowStyle, gridTemplateColumns: renderOrderActions ? "1.8fr 0.8fr 0.7fr 0.8fr 0.8fr 0.9fr 0.8fr" : "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr" }, children: [
4433
+ return /* @__PURE__ */ jsxs("div", { style: { ...rowStyle, gridTemplateColumns }, children: [
4423
4434
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1rem", alignItems: "center" }, children: [
4424
4435
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.15rem" }, children: [
4425
4436
  /* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500 }, children: propertyName }),
@@ -4491,7 +4502,7 @@ var YourOrders = o.forwardRef(
4491
4502
  ]
4492
4503
  }
4493
4504
  ),
4494
- renderOrderActions ? /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "flex-end" }, children: renderOrderActions(order) }) : null
4505
+ showActionsColumn ? /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "flex-end" }, children: renderOrderActions?.(order) }) : null
4495
4506
  ] }, order.id);
4496
4507
  }) })
4497
4508
  ] });
@@ -4564,6 +4575,27 @@ var createTickFormatter = (formatOptions) => {
4564
4575
  };
4565
4576
  var getTimeScaleOptions = (range) => {
4566
4577
  switch (range) {
4578
+ case "30s":
4579
+ return {
4580
+ timeVisible: true,
4581
+ secondsVisible: true,
4582
+ borderColor: "rgba(255,255,255,0.06)",
4583
+ tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit", second: "2-digit" })
4584
+ };
4585
+ case "1m":
4586
+ return {
4587
+ timeVisible: true,
4588
+ secondsVisible: true,
4589
+ borderColor: "rgba(255,255,255,0.06)",
4590
+ tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit", second: "2-digit" })
4591
+ };
4592
+ case "5m":
4593
+ return {
4594
+ timeVisible: true,
4595
+ secondsVisible: false,
4596
+ borderColor: "rgba(255,255,255,0.06)",
4597
+ tickMarkFormatter: createTickFormatter({ hour: "2-digit", minute: "2-digit" })
4598
+ };
4567
4599
  case "15m":
4568
4600
  return {
4569
4601
  timeVisible: true,
@@ -4635,7 +4667,8 @@ var getPriceScaleOptions = (data) => {
4635
4667
  scaleMargins
4636
4668
  };
4637
4669
  };
4638
- var defaultRanges = ["15m", "1h", "4h", "24h", "1W", "1M"];
4670
+ var defaultRanges = ["30s", "1m", "5m", "15m", "1h", "4h", "24h", "1W", "1M"];
4671
+ var VISIBLE_RANGE_COUNT = 4;
4639
4672
  var formatPrice = (value, currencySymbol) => {
4640
4673
  return `${currencySymbol}${value.toLocaleString(void 0, {
4641
4674
  minimumFractionDigits: 2,
@@ -4672,6 +4705,21 @@ var PriceChart = o.forwardRef(
4672
4705
  const seriesRef = o.useRef(null);
4673
4706
  const priceLineRef = o.useRef(null);
4674
4707
  const [hoveredRange, setHoveredRange] = o.useState(null);
4708
+ const [dropdownOpen, setDropdownOpen] = o.useState(false);
4709
+ const dropdownRef = o.useRef(null);
4710
+ const isAutoScrollRef = o.useRef(true);
4711
+ const visibleRanges = ranges.slice(0, VISIBLE_RANGE_COUNT);
4712
+ const dropdownRanges = ranges.slice(VISIBLE_RANGE_COUNT);
4713
+ const selectedInDropdown = dropdownRanges.includes(selectedRange);
4714
+ o.useEffect(() => {
4715
+ const handleClickOutside = (e) => {
4716
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
4717
+ setDropdownOpen(false);
4718
+ }
4719
+ };
4720
+ document.addEventListener("mousedown", handleClickOutside);
4721
+ return () => document.removeEventListener("mousedown", handleClickOutside);
4722
+ }, []);
4675
4723
  const resolvedPrice = o.useMemo(() => {
4676
4724
  if (price != null) return price;
4677
4725
  const last = data.at(-1);
@@ -4720,9 +4768,18 @@ var PriceChart = o.forwardRef(
4720
4768
  });
4721
4769
  chartRef.current = chart;
4722
4770
  seriesRef.current = series;
4771
+ const handleVisibleRangeChange = () => {
4772
+ const timeScale2 = chart.timeScale();
4773
+ const position2 = timeScale2.scrollPosition();
4774
+ const atRightEdge = position2 <= 0.01;
4775
+ isAutoScrollRef.current = atRightEdge;
4776
+ };
4777
+ const timeScale = chart.timeScale();
4778
+ timeScale.subscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
4723
4779
  return () => {
4724
4780
  chartRef.current = null;
4725
4781
  seriesRef.current = null;
4782
+ timeScale.unsubscribeVisibleLogicalRangeChange(handleVisibleRangeChange);
4726
4783
  chart.remove();
4727
4784
  };
4728
4785
  }, []);
@@ -4757,7 +4814,9 @@ var PriceChart = o.forwardRef(
4757
4814
  title: resolvedPrice.toFixed(2)
4758
4815
  });
4759
4816
  }
4760
- chart.timeScale().fitContent();
4817
+ if (isAutoScrollRef.current) {
4818
+ chart.timeScale().scrollToPosition(0, true);
4819
+ }
4761
4820
  }, [data, resolvedPrice]);
4762
4821
  const sign = inferredChangePercent == null ? null : inferredChangePercent >= 0 ? "+" : "";
4763
4822
  const changeClass = inferredChangePercent == null ? "" : inferredChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
@@ -4789,30 +4848,97 @@ var PriceChart = o.forwardRef(
4789
4848
  /* @__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
4849
  /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-3 md:w-auto", children: [
4791
4850
  /* @__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(
4851
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4852
+ visibleRanges.map((r2) => {
4853
+ const active = r2 === selectedRange;
4854
+ const hovered = hoveredRange === r2;
4855
+ const style = {
4856
+ ...btnBaseStyle,
4857
+ ...hovered ? btnHoverStyle : null,
4858
+ ...active ? btnActiveStyle : null
4859
+ };
4860
+ return /* @__PURE__ */ jsx(
4861
+ "button",
4862
+ {
4863
+ type: "button",
4864
+ onClick: () => onRangeChange?.(r2),
4865
+ onMouseEnter: () => setHoveredRange(r2),
4866
+ onMouseLeave: () => setHoveredRange((prev2) => prev2 === r2 ? null : prev2),
4867
+ style,
4868
+ className: cn(
4869
+ "rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4870
+ ),
4871
+ children: r2
4872
+ },
4873
+ r2
4874
+ );
4875
+ }),
4876
+ selectedInDropdown && selectedRange ? /* @__PURE__ */ jsx(
4801
4877
  "button",
4802
4878
  {
4803
4879
  type: "button",
4804
- onClick: () => onRangeChange?.(r2),
4805
- onMouseEnter: () => setHoveredRange(r2),
4806
- onMouseLeave: () => setHoveredRange((prev2) => prev2 === r2 ? null : prev2),
4807
- style,
4880
+ style: {
4881
+ ...btnBaseStyle,
4882
+ ...btnActiveStyle
4883
+ },
4808
4884
  className: cn(
4809
4885
  "rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4810
4886
  ),
4811
- children: r2
4812
- },
4813
- r2
4814
- );
4815
- }) })
4887
+ disabled: true,
4888
+ children: selectedRange
4889
+ }
4890
+ ) : null,
4891
+ dropdownRanges.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative", ref: dropdownRef, children: [
4892
+ /* @__PURE__ */ jsxs(
4893
+ "button",
4894
+ {
4895
+ type: "button",
4896
+ onClick: () => setDropdownOpen((prev2) => !prev2),
4897
+ onMouseEnter: () => setHoveredRange("__dropdown__"),
4898
+ onMouseLeave: () => setHoveredRange((prev2) => prev2 === "__dropdown__" ? null : prev2),
4899
+ style: {
4900
+ ...btnBaseStyle,
4901
+ ...hoveredRange === "__dropdown__" ? btnHoverStyle : null
4902
+ },
4903
+ className: cn(
4904
+ "flex items-center gap-1 rounded border px-3 py-1 text-[0.85rem] font-medium transition-all duration-200"
4905
+ ),
4906
+ children: [
4907
+ "More",
4908
+ /* @__PURE__ */ jsx(
4909
+ "svg",
4910
+ {
4911
+ className: cn("h-3 w-3 transition-transform", dropdownOpen && "rotate-180"),
4912
+ fill: "none",
4913
+ stroke: "currentColor",
4914
+ viewBox: "0 0 24 24",
4915
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
4916
+ }
4917
+ )
4918
+ ]
4919
+ }
4920
+ ),
4921
+ 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) => {
4922
+ const active = r2 === selectedRange;
4923
+ return /* @__PURE__ */ jsx(
4924
+ "button",
4925
+ {
4926
+ type: "button",
4927
+ onClick: () => {
4928
+ onRangeChange?.(r2);
4929
+ setDropdownOpen(false);
4930
+ },
4931
+ className: cn(
4932
+ "block w-full px-3 py-1.5 text-left text-[0.85rem] font-medium transition-colors hover:bg-white/10",
4933
+ active ? "bg-[#e6c87e]/20 text-[#e6c87e]" : "text-white/70"
4934
+ ),
4935
+ children: r2
4936
+ },
4937
+ r2
4938
+ );
4939
+ }) })
4940
+ ] })
4941
+ ] })
4816
4942
  ] }),
4817
4943
  resolvedPrice == null && inferredChangePercent == null ? null : /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
4818
4944
  resolvedPrice == null ? null : /* @__PURE__ */ jsx(