@loafmarkets/ui 0.1.401 → 0.1.403

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
@@ -1269,11 +1269,13 @@ type PropertyPhotoGalleryProps = {
1269
1269
  isOpen: boolean;
1270
1270
  onClose: () => void;
1271
1271
  startIndex?: number;
1272
+ startWithVideo?: boolean;
1272
1273
  images?: PropertyGalleryImage[];
1273
1274
  title?: string;
1274
1275
  subtitle?: string;
1276
+ videoUrl?: string;
1275
1277
  };
1276
- declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, title, subtitle, images, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1278
+ declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, startWithVideo, title, subtitle, images, videoUrl, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1277
1279
 
1278
1280
  type UseAdaptivePollingOptions = {
1279
1281
  enabled: boolean;
package/dist/index.d.ts CHANGED
@@ -1269,11 +1269,13 @@ type PropertyPhotoGalleryProps = {
1269
1269
  isOpen: boolean;
1270
1270
  onClose: () => void;
1271
1271
  startIndex?: number;
1272
+ startWithVideo?: boolean;
1272
1273
  images?: PropertyGalleryImage[];
1273
1274
  title?: string;
1274
1275
  subtitle?: string;
1276
+ videoUrl?: string;
1275
1277
  };
1276
- declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, title, subtitle, images, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1278
+ declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, startWithVideo, title, subtitle, images, videoUrl, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1277
1279
 
1278
1280
  type UseAdaptivePollingOptions = {
1279
1281
  enabled: boolean;
package/dist/index.js CHANGED
@@ -10394,7 +10394,9 @@ function PropertyOverview({
10394
10394
  ];
10395
10395
  const growthPct = overviewData?.growth5Yr;
10396
10396
  const propertyStats = [
10397
- { label: "Property Value", value: isLoading && tokenMarketCap == null ? loadingSkeleton : tokenMarketCap ? fmtDollar(tokenMarketCap) : "\u2014" },
10397
+ // Property Value = the API offeringValuation (OverviewResponse.offeringValuation), NOT
10398
+ // midPrice×tokensIssued market cap (fixed 2026-07-22). null → '—', never a fabricated cap.
10399
+ { label: "Property Value", value: isLoading && overviewData?.offeringValuation == null ? loadingSkeleton : overviewData?.offeringValuation ? fmtDollar(overviewData.offeringValuation) : "\u2014" },
10398
10400
  ...beta ? [] : [{ label: "Offers", value: "3" }],
10399
10401
  { label: "Growth (5yr)", value: growthPct != null ? `${growthPct > 0 ? "+" : ""}${growthPct}%` : "\u2014" },
10400
10402
  { label: "Last Dividend", value: overviewData?.lastDividend != null ? `$${overviewData.lastDividend.toFixed(2)}` : "\u2014" },
@@ -18904,13 +18906,14 @@ var CloseButton3 = styled10__default.default.button`
18904
18906
  var MainImageContainer = styled10__default.default.div`
18905
18907
  position: relative;
18906
18908
  flex: 1;
18909
+ min-height: 0;
18907
18910
  display: flex;
18908
18911
  align-items: center;
18909
18912
  justify-content: center;
18910
- padding: 80px 20px 140px;
18913
+ padding: 20px 20px 160px;
18911
18914
 
18912
18915
  @media (max-width: 768px) {
18913
- padding: 60px 0 110px;
18916
+ padding: 10px 0 130px;
18914
18917
  }
18915
18918
  `;
18916
18919
  var MainImage = styled10__default.default.img`
@@ -18922,8 +18925,11 @@ var MainImage = styled10__default.default.img`
18922
18925
  opacity: ${(p) => p.$visible ? 1 : 0};
18923
18926
  pointer-events: ${(p) => p.$visible ? "auto" : "none"};
18924
18927
  transition: opacity 300ms ease;
18925
- /* Inset box excludes the bottom thumbnail strip so margin:auto centres the image in the
18926
- space ABOVE the thumbnails (absolute ignores the parent's padding). */
18928
+ /* All slides share the same absolute box so switching only crossfades opacity
18929
+ no relative↔absolute size jump (the "grow then shrink" on navigation).
18930
+ The inset box excludes the bottom thumbnail strip so margin:auto centres the image
18931
+ in the space ABOVE the thumbnails (absolute positioning ignores the parent's padding,
18932
+ which otherwise let the image sink behind the thumbnails / "lean down"). */
18927
18933
  position: absolute;
18928
18934
  top: 24px;
18929
18935
  left: 24px;
@@ -19117,9 +19123,11 @@ function PropertyPhotoGallery({
19117
19123
  isOpen,
19118
19124
  onClose,
19119
19125
  startIndex = 0,
19126
+ startWithVideo = false,
19120
19127
  title = "28 Derby Street, Vaucluse",
19121
19128
  subtitle = "Elara \u2014 Luxury Harbour Residence",
19122
- images
19129
+ images,
19130
+ videoUrl
19123
19131
  }) {
19124
19132
  const galleryImages = images?.length ? images : DEFAULT_IMAGES;
19125
19133
  if (!isOpen) return null;
@@ -19128,16 +19136,19 @@ function PropertyPhotoGallery({
19128
19136
  {
19129
19137
  galleryImages,
19130
19138
  startIndex,
19139
+ startWithVideo,
19131
19140
  title,
19132
19141
  subtitle,
19133
- onClose
19142
+ onClose,
19143
+ videoUrl
19134
19144
  },
19135
- `${startIndex}-${galleryImages.length}`
19145
+ `${startIndex}-${galleryImages.length}-${startWithVideo}`
19136
19146
  );
19137
19147
  }
19138
- function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose }) {
19148
+ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
19139
19149
  const [currentIndex, setCurrentIndex] = React5.useState(() => clampIndex(startIndex, galleryImages.length));
19140
19150
  const [activeHotspotId, setActiveHotspotId] = React5.useState(null);
19151
+ const [showVideo, setShowVideo] = React5.useState(startWithVideo ?? false);
19141
19152
  const sliderRef = React5.useRef(null);
19142
19153
  const [isDragging, setIsDragging] = React5.useState(false);
19143
19154
  const wasDraggingRef = React5.useRef(false);
@@ -19187,7 +19198,7 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19187
19198
  ] }),
19188
19199
  /* @__PURE__ */ jsxRuntime.jsx(CloseButton3, { onClick: onClose, "aria-label": "Close gallery", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12M6 18L18 6" }) }) })
19189
19200
  ] }),
19190
- /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19201
+ !showVideo && /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19191
19202
  /* @__PURE__ */ jsxRuntime.jsxs(
19192
19203
  SliderTrack,
19193
19204
  {
@@ -19250,7 +19261,21 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19250
19261
  children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 18l-6-6 6-6" }) })
19251
19262
  }
19252
19263
  ),
19253
- galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19264
+ showVideo && videoUrl ? /* @__PURE__ */ jsxRuntime.jsx(GalleryVideoWrap, { children: /* @__PURE__ */ jsxRuntime.jsx(
19265
+ "video",
19266
+ {
19267
+ autoPlay: true,
19268
+ loop: true,
19269
+ playsInline: true,
19270
+ muted: true,
19271
+ controls: true,
19272
+ onVolumeChange: (e) => {
19273
+ if (!e.currentTarget.muted) e.currentTarget.muted = true;
19274
+ },
19275
+ style: { objectFit: "contain", borderRadius: 12, boxShadow: "0 30px 80px rgba(0,0,0,0.55)" },
19276
+ children: /* @__PURE__ */ jsxRuntime.jsx("source", { src: videoUrl, type: "video/mp4" })
19277
+ }
19278
+ ) }) : galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19254
19279
  galleryImages[currentIndex]?.hotspots?.map((hotspot) => /* @__PURE__ */ jsxRuntime.jsx(
19255
19280
  Hotspot,
19256
19281
  {
@@ -19295,11 +19320,83 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19295
19320
  ]
19296
19321
  }
19297
19322
  ),
19298
- /* @__PURE__ */ jsxRuntime.jsx(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(Thumbnail3, { $active: idx === currentIndex, onClick: () => setCurrentIndex(idx), "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.src, alt: image.alt }) }, image.alt)) })
19323
+ /* @__PURE__ */ jsxRuntime.jsxs(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: [
19324
+ galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(Thumbnail3, { $active: !showVideo && idx === currentIndex, onClick: () => {
19325
+ setShowVideo(false);
19326
+ setCurrentIndex(idx);
19327
+ }, "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.src, alt: image.alt }) }, image.alt)),
19328
+ videoUrl && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19329
+ /* @__PURE__ */ jsxRuntime.jsx(ThumbnailDivider, {}),
19330
+ /* @__PURE__ */ jsxRuntime.jsxs(VideoBtn, { $active: showVideo, onClick: () => setShowVideo(true), children: [
19331
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
19332
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "4", width: "14", height: "16", rx: "2" }),
19333
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 8l6-4v16l-6-4z" })
19334
+ ] }),
19335
+ "Video"
19336
+ ] })
19337
+ ] })
19338
+ ] })
19299
19339
  ]
19300
19340
  }
19301
19341
  );
19302
19342
  }
19343
+ var GalleryVideoWrap = styled10__default.default.div`
19344
+ position: absolute;
19345
+ inset: 0;
19346
+ display: flex;
19347
+ align-items: center;
19348
+ justify-content: center;
19349
+ padding: 20px 20px 160px;
19350
+
19351
+ video {
19352
+ max-width: 90%;
19353
+ max-height: 100%;
19354
+ }
19355
+
19356
+ /* Property videos stay permanently muted — remove the volume + mute buttons
19357
+ from the native player (Chrome/Edge/Safari/iOS/Android). Firefox ignores
19358
+ these and is covered by the onVolumeChange force-mute fallback. */
19359
+ video::-webkit-media-controls-mute-button,
19360
+ video::-webkit-media-controls-volume-slider,
19361
+ video::-webkit-media-controls-volume-control-container {
19362
+ display: none !important;
19363
+ }
19364
+
19365
+ @media (max-width: 768px) {
19366
+ padding: 10px 0 130px;
19367
+
19368
+ video {
19369
+ max-width: 100%;
19370
+ }
19371
+ }
19372
+ `;
19373
+ var ThumbnailDivider = styled10__default.default.div`
19374
+ width: 1px;
19375
+ height: 48px;
19376
+ background: rgba(255, 255, 255, 0.15);
19377
+ flex-shrink: 0;
19378
+ align-self: center;
19379
+ `;
19380
+ var VideoBtn = styled10__default.default.button`
19381
+ display: flex;
19382
+ align-items: center;
19383
+ gap: 6px;
19384
+ padding: 8px 14px;
19385
+ border-radius: 8px;
19386
+ border: 1px solid ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255, 255, 255, 0.2)"};
19387
+ background: ${(p) => p.$active ? "rgba(240, 185, 11, 0.15)" : "rgba(255, 255, 255, 0.08)"};
19388
+ color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "#fff"};
19389
+ font-size: 0.82rem;
19390
+ font-weight: 500;
19391
+ cursor: pointer;
19392
+ flex-shrink: 0;
19393
+ transition: all 0.2s ease;
19394
+
19395
+ &:hover {
19396
+ background: rgba(255, 255, 255, 0.15);
19397
+ border-color: rgba(255, 255, 255, 0.3);
19398
+ }
19399
+ `;
19303
19400
  var slideIn2 = styled10.keyframes`
19304
19401
  from { transform: translateX(110%); opacity: 0; }
19305
19402
  to { transform: translateX(0); opacity: 1; }