@loafmarkets/ui 0.1.402 → 0.1.404

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
@@ -706,6 +706,11 @@ type HeaderProps = {
706
706
  onOrdersNavigate?: () => void;
707
707
  onWalletNavigate?: () => void;
708
708
  onSettingsClick?: () => void;
709
+ /** Fired by the "Refer" CTA button + user-menu "Refer" item. When omitted, both
710
+ * fall back to `onSettingsClick` (so existing consumers are unchanged). Lets a
711
+ * consumer route Refer somewhere other than Settings (e.g. an invite modal)
712
+ * without DOM-level click interception. */
713
+ onReferClick?: () => void;
709
714
  portfolioSummary?: HeaderPortfolioData | null;
710
715
  /**
711
716
  * True while a brand-new user's test funds (100k mock USD) are being deposited
@@ -716,7 +721,7 @@ type HeaderProps = {
716
721
  /**
717
722
  * The bar's authed CTA button. Defaults to "deposit" (the funding flow) so
718
723
  * existing consumers are unchanged. "refer" swaps it for a Refer button that
719
- * opens the Settings popup (same `onSettingsClick` as the user-menu Refer item).
724
+ * fires `onReferClick` (falling back to `onSettingsClick` when not provided).
720
725
  */
721
726
  headerCta?: "deposit" | "refer";
722
727
  /** Show the "Refer" user-menu item. Off by default — referral is feature-flagged
@@ -1269,11 +1274,13 @@ type PropertyPhotoGalleryProps = {
1269
1274
  isOpen: boolean;
1270
1275
  onClose: () => void;
1271
1276
  startIndex?: number;
1277
+ startWithVideo?: boolean;
1272
1278
  images?: PropertyGalleryImage[];
1273
1279
  title?: string;
1274
1280
  subtitle?: string;
1281
+ videoUrl?: string;
1275
1282
  };
1276
- declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, title, subtitle, images, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1283
+ declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, startWithVideo, title, subtitle, images, videoUrl, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1277
1284
 
1278
1285
  type UseAdaptivePollingOptions = {
1279
1286
  enabled: boolean;
package/dist/index.d.ts CHANGED
@@ -706,6 +706,11 @@ type HeaderProps = {
706
706
  onOrdersNavigate?: () => void;
707
707
  onWalletNavigate?: () => void;
708
708
  onSettingsClick?: () => void;
709
+ /** Fired by the "Refer" CTA button + user-menu "Refer" item. When omitted, both
710
+ * fall back to `onSettingsClick` (so existing consumers are unchanged). Lets a
711
+ * consumer route Refer somewhere other than Settings (e.g. an invite modal)
712
+ * without DOM-level click interception. */
713
+ onReferClick?: () => void;
709
714
  portfolioSummary?: HeaderPortfolioData | null;
710
715
  /**
711
716
  * True while a brand-new user's test funds (100k mock USD) are being deposited
@@ -716,7 +721,7 @@ type HeaderProps = {
716
721
  /**
717
722
  * The bar's authed CTA button. Defaults to "deposit" (the funding flow) so
718
723
  * existing consumers are unchanged. "refer" swaps it for a Refer button that
719
- * opens the Settings popup (same `onSettingsClick` as the user-menu Refer item).
724
+ * fires `onReferClick` (falling back to `onSettingsClick` when not provided).
720
725
  */
721
726
  headerCta?: "deposit" | "refer";
722
727
  /** Show the "Refer" user-menu item. Off by default — referral is feature-flagged
@@ -1269,11 +1274,13 @@ type PropertyPhotoGalleryProps = {
1269
1274
  isOpen: boolean;
1270
1275
  onClose: () => void;
1271
1276
  startIndex?: number;
1277
+ startWithVideo?: boolean;
1272
1278
  images?: PropertyGalleryImage[];
1273
1279
  title?: string;
1274
1280
  subtitle?: string;
1281
+ videoUrl?: string;
1275
1282
  };
1276
- declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, title, subtitle, images, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1283
+ declare function PropertyPhotoGallery({ isOpen, onClose, startIndex, startWithVideo, title, subtitle, images, videoUrl, }: PropertyPhotoGalleryProps): react_jsx_runtime.JSX.Element | null;
1277
1284
 
1278
1285
  type UseAdaptivePollingOptions = {
1279
1286
  enabled: boolean;
package/dist/index.js CHANGED
@@ -5101,6 +5101,7 @@ var Header = ({
5101
5101
  onOrdersNavigate: _onOrdersNavigate,
5102
5102
  onWalletNavigate: _onWalletNavigate,
5103
5103
  onSettingsClick,
5104
+ onReferClick,
5104
5105
  portfolioSummary,
5105
5106
  fundsPending = false,
5106
5107
  transparentOnTop = false,
@@ -5539,7 +5540,7 @@ var Header = ({
5539
5540
  onClick: (event) => {
5540
5541
  event?.preventDefault();
5541
5542
  closeMenus();
5542
- onSettingsClick?.();
5543
+ (onReferClick ?? onSettingsClick)?.();
5543
5544
  },
5544
5545
  children: "Refer"
5545
5546
  }
@@ -5582,7 +5583,7 @@ var Header = ({
5582
5583
  onClick: (event) => {
5583
5584
  event?.preventDefault();
5584
5585
  closeMenus();
5585
- onSettingsClick?.();
5586
+ (onReferClick ?? onSettingsClick)?.();
5586
5587
  },
5587
5588
  children: /* @__PURE__ */ jsxRuntime.jsx(MenuItem, { children: "Refer" })
5588
5589
  }
@@ -18906,13 +18907,14 @@ var CloseButton3 = styled10__default.default.button`
18906
18907
  var MainImageContainer = styled10__default.default.div`
18907
18908
  position: relative;
18908
18909
  flex: 1;
18910
+ min-height: 0;
18909
18911
  display: flex;
18910
18912
  align-items: center;
18911
18913
  justify-content: center;
18912
- padding: 80px 20px 140px;
18914
+ padding: 20px 20px 160px;
18913
18915
 
18914
18916
  @media (max-width: 768px) {
18915
- padding: 60px 0 110px;
18917
+ padding: 10px 0 130px;
18916
18918
  }
18917
18919
  `;
18918
18920
  var MainImage = styled10__default.default.img`
@@ -18924,8 +18926,11 @@ var MainImage = styled10__default.default.img`
18924
18926
  opacity: ${(p) => p.$visible ? 1 : 0};
18925
18927
  pointer-events: ${(p) => p.$visible ? "auto" : "none"};
18926
18928
  transition: opacity 300ms ease;
18927
- /* Inset box excludes the bottom thumbnail strip so margin:auto centres the image in the
18928
- space ABOVE the thumbnails (absolute ignores the parent's padding). */
18929
+ /* All slides share the same absolute box so switching only crossfades opacity
18930
+ no relative↔absolute size jump (the "grow then shrink" on navigation).
18931
+ The inset box excludes the bottom thumbnail strip so margin:auto centres the image
18932
+ in the space ABOVE the thumbnails (absolute positioning ignores the parent's padding,
18933
+ which otherwise let the image sink behind the thumbnails / "lean down"). */
18929
18934
  position: absolute;
18930
18935
  top: 24px;
18931
18936
  left: 24px;
@@ -19119,9 +19124,11 @@ function PropertyPhotoGallery({
19119
19124
  isOpen,
19120
19125
  onClose,
19121
19126
  startIndex = 0,
19127
+ startWithVideo = false,
19122
19128
  title = "28 Derby Street, Vaucluse",
19123
19129
  subtitle = "Elara \u2014 Luxury Harbour Residence",
19124
- images
19130
+ images,
19131
+ videoUrl
19125
19132
  }) {
19126
19133
  const galleryImages = images?.length ? images : DEFAULT_IMAGES;
19127
19134
  if (!isOpen) return null;
@@ -19130,16 +19137,19 @@ function PropertyPhotoGallery({
19130
19137
  {
19131
19138
  galleryImages,
19132
19139
  startIndex,
19140
+ startWithVideo,
19133
19141
  title,
19134
19142
  subtitle,
19135
- onClose
19143
+ onClose,
19144
+ videoUrl
19136
19145
  },
19137
- `${startIndex}-${galleryImages.length}`
19146
+ `${startIndex}-${galleryImages.length}-${startWithVideo}`
19138
19147
  );
19139
19148
  }
19140
- function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose }) {
19149
+ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
19141
19150
  const [currentIndex, setCurrentIndex] = React5.useState(() => clampIndex(startIndex, galleryImages.length));
19142
19151
  const [activeHotspotId, setActiveHotspotId] = React5.useState(null);
19152
+ const [showVideo, setShowVideo] = React5.useState(startWithVideo ?? false);
19143
19153
  const sliderRef = React5.useRef(null);
19144
19154
  const [isDragging, setIsDragging] = React5.useState(false);
19145
19155
  const wasDraggingRef = React5.useRef(false);
@@ -19189,7 +19199,7 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19189
19199
  ] }),
19190
19200
  /* @__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" }) }) })
19191
19201
  ] }),
19192
- /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19202
+ !showVideo && /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19193
19203
  /* @__PURE__ */ jsxRuntime.jsxs(
19194
19204
  SliderTrack,
19195
19205
  {
@@ -19252,7 +19262,21 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19252
19262
  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" }) })
19253
19263
  }
19254
19264
  ),
19255
- galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19265
+ showVideo && videoUrl ? /* @__PURE__ */ jsxRuntime.jsx(GalleryVideoWrap, { children: /* @__PURE__ */ jsxRuntime.jsx(
19266
+ "video",
19267
+ {
19268
+ autoPlay: true,
19269
+ loop: true,
19270
+ playsInline: true,
19271
+ muted: true,
19272
+ controls: true,
19273
+ onVolumeChange: (e) => {
19274
+ if (!e.currentTarget.muted) e.currentTarget.muted = true;
19275
+ },
19276
+ style: { objectFit: "contain", borderRadius: 12, boxShadow: "0 30px 80px rgba(0,0,0,0.55)" },
19277
+ children: /* @__PURE__ */ jsxRuntime.jsx("source", { src: videoUrl, type: "video/mp4" })
19278
+ }
19279
+ ) }) : galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(MainImage, { src: image.src, alt: image.alt, $visible: idx === currentIndex }, image.alt)),
19256
19280
  galleryImages[currentIndex]?.hotspots?.map((hotspot) => /* @__PURE__ */ jsxRuntime.jsx(
19257
19281
  Hotspot,
19258
19282
  {
@@ -19297,11 +19321,83 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19297
19321
  ]
19298
19322
  }
19299
19323
  ),
19300
- /* @__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)) })
19324
+ /* @__PURE__ */ jsxRuntime.jsxs(ThumbnailStrip, { onClick: (event) => event.stopPropagation(), children: [
19325
+ galleryImages.map((image, idx) => /* @__PURE__ */ jsxRuntime.jsx(Thumbnail3, { $active: !showVideo && idx === currentIndex, onClick: () => {
19326
+ setShowVideo(false);
19327
+ setCurrentIndex(idx);
19328
+ }, "aria-label": `Preview ${image.label}`, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.src, alt: image.alt }) }, image.alt)),
19329
+ videoUrl && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19330
+ /* @__PURE__ */ jsxRuntime.jsx(ThumbnailDivider, {}),
19331
+ /* @__PURE__ */ jsxRuntime.jsxs(VideoBtn, { $active: showVideo, onClick: () => setShowVideo(true), children: [
19332
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
19333
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "4", width: "14", height: "16", rx: "2" }),
19334
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 8l6-4v16l-6-4z" })
19335
+ ] }),
19336
+ "Video"
19337
+ ] })
19338
+ ] })
19339
+ ] })
19301
19340
  ]
19302
19341
  }
19303
19342
  );
19304
19343
  }
19344
+ var GalleryVideoWrap = styled10__default.default.div`
19345
+ position: absolute;
19346
+ inset: 0;
19347
+ display: flex;
19348
+ align-items: center;
19349
+ justify-content: center;
19350
+ padding: 20px 20px 160px;
19351
+
19352
+ video {
19353
+ max-width: 90%;
19354
+ max-height: 100%;
19355
+ }
19356
+
19357
+ /* Property videos stay permanently muted — remove the volume + mute buttons
19358
+ from the native player (Chrome/Edge/Safari/iOS/Android). Firefox ignores
19359
+ these and is covered by the onVolumeChange force-mute fallback. */
19360
+ video::-webkit-media-controls-mute-button,
19361
+ video::-webkit-media-controls-volume-slider,
19362
+ video::-webkit-media-controls-volume-control-container {
19363
+ display: none !important;
19364
+ }
19365
+
19366
+ @media (max-width: 768px) {
19367
+ padding: 10px 0 130px;
19368
+
19369
+ video {
19370
+ max-width: 100%;
19371
+ }
19372
+ }
19373
+ `;
19374
+ var ThumbnailDivider = styled10__default.default.div`
19375
+ width: 1px;
19376
+ height: 48px;
19377
+ background: rgba(255, 255, 255, 0.15);
19378
+ flex-shrink: 0;
19379
+ align-self: center;
19380
+ `;
19381
+ var VideoBtn = styled10__default.default.button`
19382
+ display: flex;
19383
+ align-items: center;
19384
+ gap: 6px;
19385
+ padding: 8px 14px;
19386
+ border-radius: 8px;
19387
+ border: 1px solid ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255, 255, 255, 0.2)"};
19388
+ background: ${(p) => p.$active ? "rgba(240, 185, 11, 0.15)" : "rgba(255, 255, 255, 0.08)"};
19389
+ color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "#fff"};
19390
+ font-size: 0.82rem;
19391
+ font-weight: 500;
19392
+ cursor: pointer;
19393
+ flex-shrink: 0;
19394
+ transition: all 0.2s ease;
19395
+
19396
+ &:hover {
19397
+ background: rgba(255, 255, 255, 0.15);
19398
+ border-color: rgba(255, 255, 255, 0.3);
19399
+ }
19400
+ `;
19305
19401
  var slideIn2 = styled10.keyframes`
19306
19402
  from { transform: translateX(110%); opacity: 0; }
19307
19403
  to { transform: translateX(0); opacity: 1; }