@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.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
@@ -8133,8 +8133,7 @@ var Track = styled19__default.default.div`
8133
8133
  `;
8134
8134
  var Slide = styled19__default.default.div`
8135
8135
  min-width: 100%; position: relative; cursor: pointer;
8136
- img { width: 100%; height: 500px; object-fit: cover; border-radius: 8px; }
8137
- @media (max-width: 768px) { img { height: 280px; } }
8136
+ img { width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: 8px; }
8138
8137
  `;
8139
8138
  var SlideCaption = styled19__default.default.div`
8140
8139
  position: absolute; bottom: 0; left: 0; right: 0; padding: 1rem;
@@ -8324,7 +8323,10 @@ function PropertyOverview({
8324
8323
  ticker,
8325
8324
  contractAddress,
8326
8325
  chain = "Base (Ethereum L2)",
8327
- percentageTokenized
8326
+ percentageTokenized,
8327
+ volume24h,
8328
+ openOrdersValue,
8329
+ holderCount
8328
8330
  }) {
8329
8331
  const [isDescExpanded, setDescExpanded] = React5.useState(false);
8330
8332
  const description = descriptionProp ?? overviewData?.description ?? DEFAULT_DESCRIPTION;
@@ -8361,7 +8363,7 @@ function PropertyOverview({
8361
8363
  const tokenPriceValue = midPrice > 0 ? midPrice : null;
8362
8364
  const weeklyRent = overviewData?.weeklyRent ?? 0;
8363
8365
  const annualRent = weeklyRent * 52;
8364
- const dividendYield = resolvedValuation && annualRent > 0 ? (annualRent / resolvedValuation * 100).toFixed(2) : null;
8366
+ resolvedValuation && annualRent > 0 ? (annualRent / resolvedValuation * 100).toFixed(2) : null;
8365
8367
  const monthlyCoupon = annualRent / 12;
8366
8368
  const tokenMarketCap = tokenPriceValue && resolvedTokensIssued ? tokenPriceValue * resolvedTokensIssued : null;
8367
8369
  const currentDividendYield = tokenMarketCap && monthlyCoupon > 0 ? (monthlyCoupon * 12 / tokenMarketCap * 100).toFixed(2) : null;
@@ -8377,11 +8379,19 @@ function PropertyOverview({
8377
8379
  { label: "Property Type", value: resolvedPropertyType ?? "\u2014" },
8378
8380
  { label: "Location", value: location || "\u2014" }
8379
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
+ })();
8380
8389
  const tokenStats = [
8381
8390
  { label: "Token Price", value: isLoading && tokenPriceValue == null ? loadingSkeleton : tokenPriceValue ? `$${tokenPriceValue.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : "\u2014", gold: true },
8382
- { label: "Property Value", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
8383
- { label: "Dividend Yield", value: isLoading && dividendYield == null ? loadingSkeleton : dividendYield ? `${dividendYield}%` : "\u2014" },
8384
- { 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" }
8385
8395
  ];
8386
8396
  const propertyStats = [
8387
8397
  { label: "Offering Valuation", value: isLoading && resolvedValuation == null ? loadingSkeleton : resolvedValuation ? resolvedValuation >= 1e6 ? `$${(resolvedValuation / 1e6).toFixed(2)}M` : `$${resolvedValuation.toLocaleString()}` : "\u2014" },
@@ -8420,7 +8430,30 @@ function PropertyOverview({
8420
8430
  tokenStats.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(StatRow, { $gold: !!item.gold, children: [
8421
8431
  /* @__PURE__ */ jsxRuntime.jsx(StatLabel, { children: item.label }),
8422
8432
  /* @__PURE__ */ jsxRuntime.jsx(StatValue, { $gold: !!item.gold, children: item.value })
8423
- ] }, item.label))
8433
+ ] }, item.label)),
8434
+ holdRatio && /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioWrap, { children: [
8435
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioTitle, { children: [
8436
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioTitleText, { children: "Holding Ratio" }),
8437
+ /* @__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: "?" })
8438
+ ] }),
8439
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioHeader, { children: [
8440
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8441
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioLabel, { $side: "hold", children: "Holding " }),
8442
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioValue, { $side: "hold", children: [
8443
+ holdRatio.holdPct.toFixed(1),
8444
+ "%"
8445
+ ] })
8446
+ ] }),
8447
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
8448
+ /* @__PURE__ */ jsxRuntime.jsxs(HoldRatioValue, { $side: "trade", children: [
8449
+ holdRatio.tradePct,
8450
+ "%"
8451
+ ] }),
8452
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioLabel, { $side: "trade", children: " Trading" })
8453
+ ] })
8454
+ ] }),
8455
+ /* @__PURE__ */ jsxRuntime.jsx(HoldRatioTrack, { children: /* @__PURE__ */ jsxRuntime.jsx(HoldRatioFill, { $pct: holdRatio.holdPct }) })
8456
+ ] })
8424
8457
  ] }),
8425
8458
  /* @__PURE__ */ jsxRuntime.jsxs(StatsColumn, { children: [
8426
8459
  /* @__PURE__ */ jsxRuntime.jsx(StatsColumnHeader, { children: "Property Fundamentals" }),
@@ -8809,6 +8842,101 @@ var ProtectionDesc = styled19__default.default.div`
8809
8842
  color: rgba(255,255,255,0.5);
8810
8843
  line-height: 1.5;
8811
8844
  `;
8845
+ var HoldRatioWrap = styled19__default.default.div`
8846
+ display: flex;
8847
+ flex-direction: column;
8848
+ gap: 6px;
8849
+ padding: 0.75rem 0 0;
8850
+ margin-top: 0.5rem;
8851
+ border-top: 1px solid rgba(255,255,255,0.04);
8852
+ `;
8853
+ var HoldRatioTitle = styled19__default.default.div`
8854
+ display: flex;
8855
+ align-items: center;
8856
+ gap: 5px;
8857
+ `;
8858
+ var HoldRatioTitleText = styled19__default.default.span`
8859
+ font-size: 0.65rem;
8860
+ font-weight: 600;
8861
+ text-transform: uppercase;
8862
+ letter-spacing: 0.06em;
8863
+ color: rgba(255,255,255,0.4);
8864
+ `;
8865
+ var HoldRatioInfo = styled19__default.default.span`
8866
+ position: relative;
8867
+ display: inline-flex;
8868
+ align-items: center;
8869
+ justify-content: center;
8870
+ width: 13px;
8871
+ height: 13px;
8872
+ border-radius: 50%;
8873
+ border: 1px solid rgba(255,255,255,0.15);
8874
+ font-size: 0.5rem;
8875
+ font-weight: 700;
8876
+ color: rgba(255,255,255,0.3);
8877
+ cursor: help;
8878
+ flex-shrink: 0;
8879
+
8880
+ &::after {
8881
+ content: attr(data-tip);
8882
+ position: absolute;
8883
+ bottom: calc(100% + 6px);
8884
+ left: 0;
8885
+ width: max-content;
8886
+ max-width: 260px;
8887
+ padding: 8px 12px;
8888
+ border-radius: 6px;
8889
+ background: #1a1a1e;
8890
+ border: 1px solid rgba(255,255,255,0.1);
8891
+ color: rgba(255,255,255,0.7);
8892
+ font-size: 0.7rem;
8893
+ font-weight: 400;
8894
+ letter-spacing: 0;
8895
+ text-transform: none;
8896
+ line-height: 1.45;
8897
+ white-space: normal;
8898
+ pointer-events: none;
8899
+ opacity: 0;
8900
+ transition: opacity 0.15s;
8901
+ z-index: 10;
8902
+ }
8903
+
8904
+ &:hover::after {
8905
+ opacity: 1;
8906
+ }
8907
+ `;
8908
+ var HoldRatioHeader = styled19__default.default.div`
8909
+ display: flex;
8910
+ align-items: center;
8911
+ justify-content: space-between;
8912
+ `;
8913
+ var HoldRatioLabel = styled19__default.default.span`
8914
+ font-size: 0.7rem;
8915
+ font-weight: 600;
8916
+ text-transform: uppercase;
8917
+ letter-spacing: 0.05em;
8918
+ color: ${(p) => p.$side === "trade" ? "rgba(246, 70, 93, 0.6)" : "rgba(14, 203, 129, 0.6)"};
8919
+ `;
8920
+ var HoldRatioValue = styled19__default.default.span`
8921
+ font-size: 0.75rem;
8922
+ font-weight: 600;
8923
+ font-variant-numeric: tabular-nums;
8924
+ color: ${(p) => p.$side === "trade" ? "rgba(246, 70, 93, 0.7)" : "rgba(14, 203, 129, 0.7)"};
8925
+ `;
8926
+ var HoldRatioTrack = styled19__default.default.div`
8927
+ height: 6px;
8928
+ border-radius: 3px;
8929
+ background: rgba(246, 70, 93, 0.15);
8930
+ overflow: hidden;
8931
+ position: relative;
8932
+ `;
8933
+ var HoldRatioFill = styled19__default.default.div`
8934
+ height: 100%;
8935
+ width: ${(p) => p.$pct}%;
8936
+ border-radius: 3px;
8937
+ background: rgba(14, 203, 129, 0.35);
8938
+ transition: width 0.6s ease;
8939
+ `;
8812
8940
  var STATUS_BG = {
8813
8941
  active: "rgba(14,203,129,0.18)",
8814
8942
  rejected: "rgba(246,70,93,0.18)",