@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.mjs CHANGED
@@ -10369,7 +10369,9 @@ function PropertyOverview({
10369
10369
  ];
10370
10370
  const growthPct = overviewData?.growth5Yr;
10371
10371
  const propertyStats = [
10372
- { label: "Property Value", value: isLoading && tokenMarketCap == null ? loadingSkeleton : tokenMarketCap ? fmtDollar(tokenMarketCap) : "\u2014" },
10372
+ // Property Value = the API offeringValuation (OverviewResponse.offeringValuation), NOT
10373
+ // midPrice×tokensIssued market cap (fixed 2026-07-22). null → '—', never a fabricated cap.
10374
+ { label: "Property Value", value: isLoading && overviewData?.offeringValuation == null ? loadingSkeleton : overviewData?.offeringValuation ? fmtDollar(overviewData.offeringValuation) : "\u2014" },
10373
10375
  ...beta ? [] : [{ label: "Offers", value: "3" }],
10374
10376
  { label: "Growth (5yr)", value: growthPct != null ? `${growthPct > 0 ? "+" : ""}${growthPct}%` : "\u2014" },
10375
10377
  { label: "Last Dividend", value: overviewData?.lastDividend != null ? `$${overviewData.lastDividend.toFixed(2)}` : "\u2014" },
@@ -18879,13 +18881,14 @@ var CloseButton3 = styled10.button`
18879
18881
  var MainImageContainer = styled10.div`
18880
18882
  position: relative;
18881
18883
  flex: 1;
18884
+ min-height: 0;
18882
18885
  display: flex;
18883
18886
  align-items: center;
18884
18887
  justify-content: center;
18885
- padding: 80px 20px 140px;
18888
+ padding: 20px 20px 160px;
18886
18889
 
18887
18890
  @media (max-width: 768px) {
18888
- padding: 60px 0 110px;
18891
+ padding: 10px 0 130px;
18889
18892
  }
18890
18893
  `;
18891
18894
  var MainImage = styled10.img`
@@ -18897,8 +18900,11 @@ var MainImage = styled10.img`
18897
18900
  opacity: ${(p) => p.$visible ? 1 : 0};
18898
18901
  pointer-events: ${(p) => p.$visible ? "auto" : "none"};
18899
18902
  transition: opacity 300ms ease;
18900
- /* Inset box excludes the bottom thumbnail strip so margin:auto centres the image in the
18901
- space ABOVE the thumbnails (absolute ignores the parent's padding). */
18903
+ /* All slides share the same absolute box so switching only crossfades opacity
18904
+ no relative↔absolute size jump (the "grow then shrink" on navigation).
18905
+ The inset box excludes the bottom thumbnail strip so margin:auto centres the image
18906
+ in the space ABOVE the thumbnails (absolute positioning ignores the parent's padding,
18907
+ which otherwise let the image sink behind the thumbnails / "lean down"). */
18902
18908
  position: absolute;
18903
18909
  top: 24px;
18904
18910
  left: 24px;
@@ -19092,9 +19098,11 @@ function PropertyPhotoGallery({
19092
19098
  isOpen,
19093
19099
  onClose,
19094
19100
  startIndex = 0,
19101
+ startWithVideo = false,
19095
19102
  title = "28 Derby Street, Vaucluse",
19096
19103
  subtitle = "Elara \u2014 Luxury Harbour Residence",
19097
- images
19104
+ images,
19105
+ videoUrl
19098
19106
  }) {
19099
19107
  const galleryImages = images?.length ? images : DEFAULT_IMAGES;
19100
19108
  if (!isOpen) return null;
@@ -19103,16 +19111,19 @@ function PropertyPhotoGallery({
19103
19111
  {
19104
19112
  galleryImages,
19105
19113
  startIndex,
19114
+ startWithVideo,
19106
19115
  title,
19107
19116
  subtitle,
19108
- onClose
19117
+ onClose,
19118
+ videoUrl
19109
19119
  },
19110
- `${startIndex}-${galleryImages.length}`
19120
+ `${startIndex}-${galleryImages.length}-${startWithVideo}`
19111
19121
  );
19112
19122
  }
19113
- function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose }) {
19123
+ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
19114
19124
  const [currentIndex, setCurrentIndex] = useState(() => clampIndex(startIndex, galleryImages.length));
19115
19125
  const [activeHotspotId, setActiveHotspotId] = useState(null);
19126
+ const [showVideo, setShowVideo] = useState(startWithVideo ?? false);
19116
19127
  const sliderRef = useRef(null);
19117
19128
  const [isDragging, setIsDragging] = useState(false);
19118
19129
  const wasDraggingRef = useRef(false);
@@ -19162,7 +19173,7 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19162
19173
  ] }),
19163
19174
  /* @__PURE__ */ jsx(CloseButton3, { onClick: onClose, "aria-label": "Close gallery", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M6 18L18 6" }) }) })
19164
19175
  ] }),
19165
- /* @__PURE__ */ jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19176
+ !showVideo && /* @__PURE__ */ jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19166
19177
  /* @__PURE__ */ jsxs(
19167
19178
  SliderTrack,
19168
19179
  {
@@ -19225,7 +19236,21 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19225
19236
  children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { d: "M15 18l-6-6 6-6" }) })
19226
19237
  }
19227
19238
  ),
19228
- galleryImages.map((image, idx) => /* @__PURE__ */ jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19239
+ showVideo && videoUrl ? /* @__PURE__ */ jsx(GalleryVideoWrap, { children: /* @__PURE__ */ jsx(
19240
+ "video",
19241
+ {
19242
+ autoPlay: true,
19243
+ loop: true,
19244
+ playsInline: true,
19245
+ muted: true,
19246
+ controls: true,
19247
+ onVolumeChange: (e) => {
19248
+ if (!e.currentTarget.muted) e.currentTarget.muted = true;
19249
+ },
19250
+ style: { objectFit: "contain", borderRadius: 12, boxShadow: "0 30px 80px rgba(0,0,0,0.55)" },
19251
+ children: /* @__PURE__ */ jsx("source", { src: videoUrl, type: "video/mp4" })
19252
+ }
19253
+ ) }) : galleryImages.map((image, idx) => /* @__PURE__ */ jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19229
19254
  galleryImages[currentIndex]?.hotspots?.map((hotspot) => /* @__PURE__ */ jsx(
19230
19255
  Hotspot,
19231
19256
  {
@@ -19270,11 +19295,83 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19270
19295
  ]
19271
19296
  }
19272
19297
  ),
19273
- /* @__PURE__ */ jsx(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: galleryImages.map((image, idx) => /* @__PURE__ */ jsx(Thumbnail3, { $active: idx === currentIndex, onClick: () => setCurrentIndex(idx), "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsx("img", { src: image.src, alt: image.alt }) }, image.alt)) })
19298
+ /* @__PURE__ */ jsxs(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: [
19299
+ galleryImages.map((image, idx) => /* @__PURE__ */ jsx(Thumbnail3, { $active: !showVideo && idx === currentIndex, onClick: () => {
19300
+ setShowVideo(false);
19301
+ setCurrentIndex(idx);
19302
+ }, "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsx("img", { src: image.src, alt: image.alt }) }, image.alt)),
19303
+ videoUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
19304
+ /* @__PURE__ */ jsx(ThumbnailDivider, {}),
19305
+ /* @__PURE__ */ jsxs(VideoBtn, { $active: showVideo, onClick: () => setShowVideo(true), children: [
19306
+ /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
19307
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "4", width: "14", height: "16", rx: "2" }),
19308
+ /* @__PURE__ */ jsx("path", { d: "M16 8l6-4v16l-6-4z" })
19309
+ ] }),
19310
+ "Video"
19311
+ ] })
19312
+ ] })
19313
+ ] })
19274
19314
  ]
19275
19315
  }
19276
19316
  );
19277
19317
  }
19318
+ var GalleryVideoWrap = styled10.div`
19319
+ position: absolute;
19320
+ inset: 0;
19321
+ display: flex;
19322
+ align-items: center;
19323
+ justify-content: center;
19324
+ padding: 20px 20px 160px;
19325
+
19326
+ video {
19327
+ max-width: 90%;
19328
+ max-height: 100%;
19329
+ }
19330
+
19331
+ /* Property videos stay permanently muted — remove the volume + mute buttons
19332
+ from the native player (Chrome/Edge/Safari/iOS/Android). Firefox ignores
19333
+ these and is covered by the onVolumeChange force-mute fallback. */
19334
+ video::-webkit-media-controls-mute-button,
19335
+ video::-webkit-media-controls-volume-slider,
19336
+ video::-webkit-media-controls-volume-control-container {
19337
+ display: none !important;
19338
+ }
19339
+
19340
+ @media (max-width: 768px) {
19341
+ padding: 10px 0 130px;
19342
+
19343
+ video {
19344
+ max-width: 100%;
19345
+ }
19346
+ }
19347
+ `;
19348
+ var ThumbnailDivider = styled10.div`
19349
+ width: 1px;
19350
+ height: 48px;
19351
+ background: rgba(255, 255, 255, 0.15);
19352
+ flex-shrink: 0;
19353
+ align-self: center;
19354
+ `;
19355
+ var VideoBtn = styled10.button`
19356
+ display: flex;
19357
+ align-items: center;
19358
+ gap: 6px;
19359
+ padding: 8px 14px;
19360
+ border-radius: 8px;
19361
+ border: 1px solid ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255, 255, 255, 0.2)"};
19362
+ background: ${(p) => p.$active ? "rgba(240, 185, 11, 0.15)" : "rgba(255, 255, 255, 0.08)"};
19363
+ color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "#fff"};
19364
+ font-size: 0.82rem;
19365
+ font-weight: 500;
19366
+ cursor: pointer;
19367
+ flex-shrink: 0;
19368
+ transition: all 0.2s ease;
19369
+
19370
+ &:hover {
19371
+ background: rgba(255, 255, 255, 0.15);
19372
+ border-color: rgba(255, 255, 255, 0.3);
19373
+ }
19374
+ `;
19278
19375
  var slideIn2 = keyframes`
19279
19376
  from { transform: translateX(110%); opacity: 0; }
19280
19377
  to { transform: translateX(0); opacity: 1; }