@loafmarkets/ui 0.1.143 → 0.1.145

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
@@ -8107,8 +8107,7 @@ var Track = styled19.div`
8107
8107
  `;
8108
8108
  var Slide = styled19.div`
8109
8109
  min-width: 100%; position: relative; cursor: pointer;
8110
- img { width: 100%; height: 500px; object-fit: cover; border-radius: 8px; }
8111
- @media (max-width: 768px) { img { height: 280px; } }
8110
+ img { width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: 8px; }
8112
8111
  `;
8113
8112
  var SlideCaption = styled19.div`
8114
8113
  position: absolute; bottom: 0; left: 0; right: 0; padding: 1rem;
@@ -8298,7 +8297,10 @@ function PropertyOverview({
8298
8297
  ticker,
8299
8298
  contractAddress,
8300
8299
  chain = "Base (Ethereum L2)",
8301
- percentageTokenized
8300
+ percentageTokenized,
8301
+ volume24h,
8302
+ openOrdersValue,
8303
+ holderCount
8302
8304
  }) {
8303
8305
  const [isDescExpanded, setDescExpanded] = useState(false);
8304
8306
  const description = descriptionProp ?? overviewData?.description ?? DEFAULT_DESCRIPTION;
@@ -8335,7 +8337,7 @@ function PropertyOverview({
8335
8337
  const tokenPriceValue = midPrice > 0 ? midPrice : null;
8336
8338
  const weeklyRent = overviewData?.weeklyRent ?? 0;
8337
8339
  const annualRent = weeklyRent * 52;
8338
- const dividendYield = resolvedValuation && annualRent > 0 ? (annualRent / resolvedValuation * 100).toFixed(2) : null;
8340
+ resolvedValuation && annualRent > 0 ? (annualRent / resolvedValuation * 100).toFixed(2) : null;
8339
8341
  const monthlyCoupon = annualRent / 12;
8340
8342
  const tokenMarketCap = tokenPriceValue && resolvedTokensIssued ? tokenPriceValue * resolvedTokensIssued : null;
8341
8343
  const currentDividendYield = tokenMarketCap && monthlyCoupon > 0 ? (monthlyCoupon * 12 / tokenMarketCap * 100).toFixed(2) : null;
@@ -8351,11 +8353,19 @@ function PropertyOverview({
8351
8353
  { label: "Property Type", value: resolvedPropertyType ?? "\u2014" },
8352
8354
  { label: "Location", value: location || "\u2014" }
8353
8355
  ];
8356
+ const vol24hDollar = volume24h != null && tokenPriceValue ? volume24h * tokenPriceValue : null;
8357
+ const holdRatio = (() => {
8358
+ if (!tokenMarketCap || tokenMarketCap <= 0 || vol24hDollar == null) return null;
8359
+ const turnover = vol24hDollar / (tokenMarketCap / 2);
8360
+ const holdPct = Math.max(0, Math.min(100, (1 - turnover) * 100));
8361
+ return { holdPct, tradePct: +(100 - holdPct).toFixed(1) };
8362
+ })();
8354
8363
  const tokenStats = [
8355
8364
  { label: "Token Price", value: isLoading && tokenPriceValue == null ? loadingSkeleton : tokenPriceValue ? `$${tokenPriceValue.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : "\u2014", gold: true },
8356
- { label: "Property Value", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
8357
- { label: "Dividend Yield", value: isLoading && dividendYield == null ? loadingSkeleton : dividendYield ? `${dividendYield}%` : "\u2014" },
8358
- { label: "Total Tokens", value: isLoading && resolvedTokensIssued == null ? loadingSkeleton : resolvedTokensIssued?.toLocaleString() ?? "\u2014" }
8365
+ { label: "24h Volume", value: isLoading && vol24hDollar == null ? loadingSkeleton : vol24hDollar != null && vol24hDollar > 0 ? `$${Math.round(vol24hDollar).toLocaleString()}` : "\u2014" },
8366
+ { label: "Current Dividend Yield", value: isLoading && currentDividendYield == null ? loadingSkeleton : currentDividendYield ? `${currentDividendYield}%` : "\u2014" },
8367
+ { label: "Open Orders", value: isLoading && openOrdersValue == null ? loadingSkeleton : openOrdersValue != null && openOrdersValue > 0 ? `$${Math.round(openOrdersValue).toLocaleString()}` : "\u2014" },
8368
+ { label: "Holders", value: isLoading && holderCount == null ? loadingSkeleton : holderCount != null ? holderCount.toLocaleString() : "\u2014" }
8359
8369
  ];
8360
8370
  const propertyStats = [
8361
8371
  { label: "Offering Valuation", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
@@ -8394,7 +8404,30 @@ function PropertyOverview({
8394
8404
  tokenStats.map((item) => /* @__PURE__ */ jsxs(StatRow, { $gold: !!item.gold, children: [
8395
8405
  /* @__PURE__ */ jsx(StatLabel, { children: item.label }),
8396
8406
  /* @__PURE__ */ jsx(StatValue, { $gold: !!item.gold, children: item.value })
8397
- ] }, item.label))
8407
+ ] }, item.label)),
8408
+ holdRatio && /* @__PURE__ */ jsxs(HoldRatioWrap, { children: [
8409
+ /* @__PURE__ */ jsxs(HoldRatioTitle, { children: [
8410
+ /* @__PURE__ */ jsx(HoldRatioTitleText, { children: "Holding Ratio" }),
8411
+ /* @__PURE__ */ jsx(HoldRatioInfo, { "data-tip": "Proportion of token holders vs active traders over the last 24 hours, measured by trading volume against market capitalisation.", children: "?" })
8412
+ ] }),
8413
+ /* @__PURE__ */ jsxs(HoldRatioHeader, { children: [
8414
+ /* @__PURE__ */ jsxs("div", { children: [
8415
+ /* @__PURE__ */ jsx(HoldRatioLabel, { $side: "hold", children: "Holding " }),
8416
+ /* @__PURE__ */ jsxs(HoldRatioValue, { $side: "hold", children: [
8417
+ holdRatio.holdPct.toFixed(1),
8418
+ "%"
8419
+ ] })
8420
+ ] }),
8421
+ /* @__PURE__ */ jsxs("div", { children: [
8422
+ /* @__PURE__ */ jsxs(HoldRatioValue, { $side: "trade", children: [
8423
+ holdRatio.tradePct,
8424
+ "%"
8425
+ ] }),
8426
+ /* @__PURE__ */ jsx(HoldRatioLabel, { $side: "trade", children: " Trading" })
8427
+ ] })
8428
+ ] }),
8429
+ /* @__PURE__ */ jsx(HoldRatioTrack, { children: /* @__PURE__ */ jsx(HoldRatioFill, { $pct: holdRatio.holdPct }) })
8430
+ ] })
8398
8431
  ] }),
8399
8432
  /* @__PURE__ */ jsxs(StatsColumn, { children: [
8400
8433
  /* @__PURE__ */ jsx(StatsColumnHeader, { children: "Property Fundamentals" }),
@@ -8783,6 +8816,101 @@ var ProtectionDesc = styled19.div`
8783
8816
  color: rgba(255,255,255,0.5);
8784
8817
  line-height: 1.5;
8785
8818
  `;
8819
+ var HoldRatioWrap = styled19.div`
8820
+ display: flex;
8821
+ flex-direction: column;
8822
+ gap: 6px;
8823
+ padding: 0.75rem 0 0;
8824
+ margin-top: 0.5rem;
8825
+ border-top: 1px solid rgba(255,255,255,0.04);
8826
+ `;
8827
+ var HoldRatioTitle = styled19.div`
8828
+ display: flex;
8829
+ align-items: center;
8830
+ gap: 5px;
8831
+ `;
8832
+ var HoldRatioTitleText = styled19.span`
8833
+ font-size: 0.65rem;
8834
+ font-weight: 600;
8835
+ text-transform: uppercase;
8836
+ letter-spacing: 0.06em;
8837
+ color: rgba(255,255,255,0.4);
8838
+ `;
8839
+ var HoldRatioInfo = styled19.span`
8840
+ position: relative;
8841
+ display: inline-flex;
8842
+ align-items: center;
8843
+ justify-content: center;
8844
+ width: 13px;
8845
+ height: 13px;
8846
+ border-radius: 50%;
8847
+ border: 1px solid rgba(255,255,255,0.15);
8848
+ font-size: 0.5rem;
8849
+ font-weight: 700;
8850
+ color: rgba(255,255,255,0.3);
8851
+ cursor: help;
8852
+ flex-shrink: 0;
8853
+
8854
+ &::after {
8855
+ content: attr(data-tip);
8856
+ position: absolute;
8857
+ bottom: calc(100% + 6px);
8858
+ left: 0;
8859
+ width: max-content;
8860
+ max-width: 260px;
8861
+ padding: 8px 12px;
8862
+ border-radius: 6px;
8863
+ background: #1a1a1e;
8864
+ border: 1px solid rgba(255,255,255,0.1);
8865
+ color: rgba(255,255,255,0.7);
8866
+ font-size: 0.7rem;
8867
+ font-weight: 400;
8868
+ letter-spacing: 0;
8869
+ text-transform: none;
8870
+ line-height: 1.45;
8871
+ white-space: normal;
8872
+ pointer-events: none;
8873
+ opacity: 0;
8874
+ transition: opacity 0.15s;
8875
+ z-index: 10;
8876
+ }
8877
+
8878
+ &:hover::after {
8879
+ opacity: 1;
8880
+ }
8881
+ `;
8882
+ var HoldRatioHeader = styled19.div`
8883
+ display: flex;
8884
+ align-items: center;
8885
+ justify-content: space-between;
8886
+ `;
8887
+ var HoldRatioLabel = styled19.span`
8888
+ font-size: 0.7rem;
8889
+ font-weight: 600;
8890
+ text-transform: uppercase;
8891
+ letter-spacing: 0.05em;
8892
+ color: ${(p) => p.$side === "trade" ? "rgba(246, 70, 93, 0.6)" : "rgba(14, 203, 129, 0.6)"};
8893
+ `;
8894
+ var HoldRatioValue = styled19.span`
8895
+ font-size: 0.75rem;
8896
+ font-weight: 600;
8897
+ font-variant-numeric: tabular-nums;
8898
+ color: ${(p) => p.$side === "trade" ? "rgba(246, 70, 93, 0.7)" : "rgba(14, 203, 129, 0.7)"};
8899
+ `;
8900
+ var HoldRatioTrack = styled19.div`
8901
+ height: 6px;
8902
+ border-radius: 3px;
8903
+ background: rgba(246, 70, 93, 0.15);
8904
+ overflow: hidden;
8905
+ position: relative;
8906
+ `;
8907
+ var HoldRatioFill = styled19.div`
8908
+ height: 100%;
8909
+ width: ${(p) => p.$pct}%;
8910
+ border-radius: 3px;
8911
+ background: rgba(14, 203, 129, 0.35);
8912
+ transition: width 0.6s ease;
8913
+ `;
8786
8914
  var STATUS_BG = {
8787
8915
  active: "rgba(14,203,129,0.18)",
8788
8916
  rejected: "rgba(246,70,93,0.18)",