@loafmarkets/ui 0.1.144 → 0.1.146

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
@@ -755,8 +755,11 @@ type PropertyOverviewProps = {
755
755
  contractAddress?: string;
756
756
  chain?: string;
757
757
  percentageTokenized?: number | null;
758
+ volume24h?: number | null;
759
+ openOrdersValue?: number | null;
760
+ holderCount?: number | null;
758
761
  };
759
- declare function PropertyOverview({ propertyName, location, midPrice, onTradeClick, onPhotosClick, description: descriptionProp, tradeButtonLabel, images, galleryCategories, videoUrl, tokenName, landSizeSqm: landProp, buildingSizeSqm: buildingProp, features: featuresProp, propertyInfo: propertyInfoProp, overviewData, bedrooms, bathrooms, carSpaces, propertyTypeLabel, tokensIssued: tokensIssuedProp, isLoading, ticker, contractAddress, chain, percentageTokenized, }: PropertyOverviewProps): react_jsx_runtime.JSX.Element;
762
+ declare function PropertyOverview({ propertyName, location, midPrice, onTradeClick, onPhotosClick, description: descriptionProp, tradeButtonLabel, images, galleryCategories, videoUrl, tokenName, landSizeSqm: landProp, buildingSizeSqm: buildingProp, features: featuresProp, propertyInfo: propertyInfoProp, overviewData, bedrooms, bathrooms, carSpaces, propertyTypeLabel, tokensIssued: tokensIssuedProp, isLoading, ticker, contractAddress, chain, percentageTokenized, volume24h, openOrdersValue, holderCount, }: PropertyOverviewProps): react_jsx_runtime.JSX.Element;
760
763
 
761
764
  type OfferStatus = 'active' | 'rejected' | 'expired' | 'historical' | 'pending';
762
765
  type PropertyOffer = {
package/dist/index.d.ts CHANGED
@@ -755,8 +755,11 @@ type PropertyOverviewProps = {
755
755
  contractAddress?: string;
756
756
  chain?: string;
757
757
  percentageTokenized?: number | null;
758
+ volume24h?: number | null;
759
+ openOrdersValue?: number | null;
760
+ holderCount?: number | null;
758
761
  };
759
- declare function PropertyOverview({ propertyName, location, midPrice, onTradeClick, onPhotosClick, description: descriptionProp, tradeButtonLabel, images, galleryCategories, videoUrl, tokenName, landSizeSqm: landProp, buildingSizeSqm: buildingProp, features: featuresProp, propertyInfo: propertyInfoProp, overviewData, bedrooms, bathrooms, carSpaces, propertyTypeLabel, tokensIssued: tokensIssuedProp, isLoading, ticker, contractAddress, chain, percentageTokenized, }: PropertyOverviewProps): react_jsx_runtime.JSX.Element;
762
+ declare function PropertyOverview({ propertyName, location, midPrice, onTradeClick, onPhotosClick, description: descriptionProp, tradeButtonLabel, images, galleryCategories, videoUrl, tokenName, landSizeSqm: landProp, buildingSizeSqm: buildingProp, features: featuresProp, propertyInfo: propertyInfoProp, overviewData, bedrooms, bathrooms, carSpaces, propertyTypeLabel, tokensIssued: tokensIssuedProp, isLoading, ticker, contractAddress, chain, percentageTokenized, volume24h, openOrdersValue, holderCount, }: PropertyOverviewProps): react_jsx_runtime.JSX.Element;
760
763
 
761
764
  type OfferStatus = 'active' | 'rejected' | 'expired' | 'historical' | 'pending';
762
765
  type PropertyOffer = {
package/dist/index.js CHANGED
@@ -8323,7 +8323,10 @@ function PropertyOverview({
8323
8323
  ticker,
8324
8324
  contractAddress,
8325
8325
  chain = "Base (Ethereum L2)",
8326
- percentageTokenized
8326
+ percentageTokenized,
8327
+ volume24h,
8328
+ openOrdersValue,
8329
+ holderCount
8327
8330
  }) {
8328
8331
  const [isDescExpanded, setDescExpanded] = React5.useState(false);
8329
8332
  const description = descriptionProp ?? overviewData?.description ?? DEFAULT_DESCRIPTION;
@@ -8360,7 +8363,7 @@ function PropertyOverview({
8360
8363
  const tokenPriceValue = midPrice > 0 ? midPrice : null;
8361
8364
  const weeklyRent = overviewData?.weeklyRent ?? 0;
8362
8365
  const annualRent = weeklyRent * 52;
8363
- const dividendYield = resolvedValuation && annualRent > 0 ? (annualRent / resolvedValuation * 100).toFixed(2) : null;
8366
+ resolvedValuation && annualRent > 0 ? (annualRent / resolvedValuation * 100).toFixed(2) : null;
8364
8367
  const monthlyCoupon = annualRent / 12;
8365
8368
  const tokenMarketCap = tokenPriceValue && resolvedTokensIssued ? tokenPriceValue * resolvedTokensIssued : null;
8366
8369
  const currentDividendYield = tokenMarketCap && monthlyCoupon > 0 ? (monthlyCoupon * 12 / tokenMarketCap * 100).toFixed(2) : null;
@@ -8376,17 +8379,27 @@ function PropertyOverview({
8376
8379
  { label: "Property Type", value: resolvedPropertyType ?? "\u2014" },
8377
8380
  { label: "Location", value: location || "\u2014" }
8378
8381
  ];
8382
+ const vol24hDollar = volume24h != null && tokenPriceValue ? volume24h * tokenPriceValue : null;
8383
+ const holdRatio = (() => {
8384
+ if (!tokenMarketCap || tokenMarketCap <= 0 || vol24hDollar == null) return null;
8385
+ const turnover = vol24hDollar / (tokenMarketCap / 2);
8386
+ const holdPct = Math.max(0, Math.min(100, (1 - turnover) * 100));
8387
+ return { holdPct, tradePct: +(100 - holdPct).toFixed(1) };
8388
+ })();
8379
8389
  const tokenStats = [
8380
8390
  { label: "Token Price", value: isLoading && tokenPriceValue == null ? loadingSkeleton : tokenPriceValue ? `$${tokenPriceValue.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : "\u2014", gold: true },
8381
- { label: "Property Value", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
8382
- { label: "Dividend Yield", value: isLoading && dividendYield == null ? loadingSkeleton : dividendYield ? `${dividendYield}%` : "\u2014" },
8383
- { label: "Total Tokens", value: isLoading && resolvedTokensIssued == null ? loadingSkeleton : resolvedTokensIssued?.toLocaleString() ?? "\u2014" }
8391
+ { label: "24h Volume", value: isLoading && vol24hDollar == null ? loadingSkeleton : vol24hDollar != null && vol24hDollar > 0 ? `$${Math.round(vol24hDollar).toLocaleString()}` : "\u2014" },
8392
+ { label: "Current Dividend Yield", value: isLoading && currentDividendYield == null ? loadingSkeleton : currentDividendYield ? `${currentDividendYield}%` : "\u2014" },
8393
+ { label: "Open Orders", value: isLoading && openOrdersValue == null ? loadingSkeleton : openOrdersValue != null && openOrdersValue > 0 ? `$${Math.round(openOrdersValue).toLocaleString()}` : "\u2014" },
8394
+ { label: "Holders", value: isLoading && holderCount == null ? loadingSkeleton : holderCount != null ? holderCount.toLocaleString() : "\u2014" }
8384
8395
  ];
8396
+ const cashFlowPerShare = annualRent > 0 && resolvedTokensIssued ? annualRent / resolvedTokensIssued : null;
8385
8397
  const propertyStats = [
8386
- { label: "Offering Valuation", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
8387
- { label: "Current Dividend Yield", value: isLoading && currentDividendYield == null ? loadingSkeleton : currentDividendYield ? `${currentDividendYield}%` : "\u2014" },
8388
- { label: "Annual Rent", value: isLoading && !overviewData ? loadingSkeleton : annualRent > 0 ? `$${annualRent.toLocaleString()}/yr` : "\u2014" },
8389
- { label: "Year Built", value: isLoading && !overviewData ? loadingSkeleton : overviewData?.yearBuilt ? String(overviewData.yearBuilt) : "\u2014" }
8398
+ { label: "Last Valuation", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
8399
+ { label: "Number of Offers", value: "\u2014" },
8400
+ { label: "Growth (Past 5 Years)", value: "\u2014" },
8401
+ { label: "Security Interest in Asset", value: "Yes, secured to Land Registry" },
8402
+ { label: "Cash Flow Per Share", value: isLoading && cashFlowPerShare == null ? loadingSkeleton : cashFlowPerShare != null ? `$${cashFlowPerShare.toFixed(4)}/yr` : "\u2014" }
8390
8403
  ];
8391
8404
  return /* @__PURE__ */ jsxRuntime.jsxs(Wrapper, { children: [
8392
8405
  /* @__PURE__ */ jsxRuntime.jsxs(Section, { children: [
@@ -8419,7 +8432,30 @@ function PropertyOverview({
8419
8432
  tokenStats.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(StatRow, { $gold: !!item.gold, children: [
8420
8433
  /* @__PURE__ */ jsxRuntime.jsx(StatLabel, { children: item.label }),
8421
8434
  /* @__PURE__ */ jsxRuntime.jsx(StatValue, { $gold: !!item.gold, children: item.value })
8422
- ] }, item.label))
8435
+ ] }, item.label)),
8436
+ holdRatio && /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioWrap, { children: [
8437
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioTitle, { children: [
8438
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioTitleText, { children: "Holding Ratio" }),
8439
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioInfo, { "data-tip": "Proportion of token holders vs active traders over the last 24 hours, measured by trading volume against market capitalisation.", children: "?" })
8440
+ ] }),
8441
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioHeader, { children: [
8442
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8443
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioLabel, { $side: "hold", children: "Holding " }),
8444
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioValue, { $side: "hold", children: [
8445
+ holdRatio.holdPct.toFixed(1),
8446
+ "%"
8447
+ ] })
8448
+ ] }),
8449
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8450
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioValue, { $side: "trade", children: [
8451
+ holdRatio.tradePct,
8452
+ "%"
8453
+ ] }),
8454
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioLabel, { $side: "trade", children: " Trading" })
8455
+ ] })
8456
+ ] }),
8457
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioTrack, { children: /* @__PURE__ */ jsxRuntime.jsx(HoldRatioFill, { $pct: holdRatio.holdPct }) })
8458
+ ] })
8423
8459
  ] }),
8424
8460
  /* @__PURE__ */ jsxRuntime.jsxs(StatsColumn, { children: [
8425
8461
  /* @__PURE__ */ jsxRuntime.jsx(StatsColumnHeader, { children: "Property Fundamentals" }),
@@ -8808,6 +8844,101 @@ var ProtectionDesc = styled19__default.default.div`
8808
8844
  color: rgba(255,255,255,0.5);
8809
8845
  line-height: 1.5;
8810
8846
  `;
8847
+ var HoldRatioWrap = styled19__default.default.div`
8848
+ display: flex;
8849
+ flex-direction: column;
8850
+ gap: 6px;
8851
+ padding: 0.75rem 0 0;
8852
+ margin-top: 0.5rem;
8853
+ border-top: 1px solid rgba(255,255,255,0.04);
8854
+ `;
8855
+ var HoldRatioTitle = styled19__default.default.div`
8856
+ display: flex;
8857
+ align-items: center;
8858
+ gap: 5px;
8859
+ `;
8860
+ var HoldRatioTitleText = styled19__default.default.span`
8861
+ font-size: 0.65rem;
8862
+ font-weight: 600;
8863
+ text-transform: uppercase;
8864
+ letter-spacing: 0.06em;
8865
+ color: rgba(255,255,255,0.4);
8866
+ `;
8867
+ var HoldRatioInfo = styled19__default.default.span`
8868
+ position: relative;
8869
+ display: inline-flex;
8870
+ align-items: center;
8871
+ justify-content: center;
8872
+ width: 13px;
8873
+ height: 13px;
8874
+ border-radius: 50%;
8875
+ border: 1px solid rgba(255,255,255,0.15);
8876
+ font-size: 0.5rem;
8877
+ font-weight: 700;
8878
+ color: rgba(255,255,255,0.3);
8879
+ cursor: help;
8880
+ flex-shrink: 0;
8881
+
8882
+ &::after {
8883
+ content: attr(data-tip);
8884
+ position: absolute;
8885
+ bottom: calc(100% + 6px);
8886
+ left: 0;
8887
+ width: max-content;
8888
+ max-width: 260px;
8889
+ padding: 8px 12px;
8890
+ border-radius: 6px;
8891
+ background: #1a1a1e;
8892
+ border: 1px solid rgba(255,255,255,0.1);
8893
+ color: rgba(255,255,255,0.7);
8894
+ font-size: 0.7rem;
8895
+ font-weight: 400;
8896
+ letter-spacing: 0;
8897
+ text-transform: none;
8898
+ line-height: 1.45;
8899
+ white-space: normal;
8900
+ pointer-events: none;
8901
+ opacity: 0;
8902
+ transition: opacity 0.15s;
8903
+ z-index: 10;
8904
+ }
8905
+
8906
+ &:hover::after {
8907
+ opacity: 1;
8908
+ }
8909
+ `;
8910
+ var HoldRatioHeader = styled19__default.default.div`
8911
+ display: flex;
8912
+ align-items: center;
8913
+ justify-content: space-between;
8914
+ `;
8915
+ var HoldRatioLabel = styled19__default.default.span`
8916
+ font-size: 0.7rem;
8917
+ font-weight: 600;
8918
+ text-transform: uppercase;
8919
+ letter-spacing: 0.05em;
8920
+ color: ${(p) => p.$side === "trade" ? "rgba(246, 70, 93, 0.6)" : "rgba(14, 203, 129, 0.6)"};
8921
+ `;
8922
+ var HoldRatioValue = styled19__default.default.span`
8923
+ font-size: 0.75rem;
8924
+ font-weight: 600;
8925
+ font-variant-numeric: tabular-nums;
8926
+ color: ${(p) => p.$side === "trade" ? "rgba(246, 70, 93, 0.7)" : "rgba(14, 203, 129, 0.7)"};
8927
+ `;
8928
+ var HoldRatioTrack = styled19__default.default.div`
8929
+ height: 6px;
8930
+ border-radius: 3px;
8931
+ background: rgba(246, 70, 93, 0.15);
8932
+ overflow: hidden;
8933
+ position: relative;
8934
+ `;
8935
+ var HoldRatioFill = styled19__default.default.div`
8936
+ height: 100%;
8937
+ width: ${(p) => p.$pct}%;
8938
+ border-radius: 3px;
8939
+ background: rgba(14, 203, 129, 0.35);
8940
+ transition: width 0.6s ease;
8941
+ `;
8811
8942
  var STATUS_BG = {
8812
8943
  active: "rgba(14,203,129,0.18)",
8813
8944
  rejected: "rgba(246,70,93,0.18)",