@loafmarkets/ui 0.1.402 → 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
@@ -18906,13 +18906,14 @@ var CloseButton3 = styled10__default.default.button`
18906
18906
  var MainImageContainer = styled10__default.default.div`
18907
18907
  position: relative;
18908
18908
  flex: 1;
18909
+ min-height: 0;
18909
18910
  display: flex;
18910
18911
  align-items: center;
18911
18912
  justify-content: center;
18912
- padding: 80px 20px 140px;
18913
+ padding: 20px 20px 160px;
18913
18914
 
18914
18915
  @media (max-width: 768px) {
18915
- padding: 60px 0 110px;
18916
+ padding: 10px 0 130px;
18916
18917
  }
18917
18918
  `;
18918
18919
  var MainImage = styled10__default.default.img`
@@ -18924,8 +18925,11 @@ var MainImage = styled10__default.default.img`
18924
18925
  opacity: ${(p) => p.$visible ? 1 : 0};
18925
18926
  pointer-events: ${(p) => p.$visible ? "auto" : "none"};
18926
18927
  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). */
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"). */
18929
18933
  position: absolute;
18930
18934
  top: 24px;
18931
18935
  left: 24px;
@@ -19119,9 +19123,11 @@ function PropertyPhotoGallery({
19119
19123
  isOpen,
19120
19124
  onClose,
19121
19125
  startIndex = 0,
19126
+ startWithVideo = false,
19122
19127
  title = "28 Derby Street, Vaucluse",
19123
19128
  subtitle = "Elara \u2014 Luxury Harbour Residence",
19124
- images
19129
+ images,
19130
+ videoUrl
19125
19131
  }) {
19126
19132
  const galleryImages = images?.length ? images : DEFAULT_IMAGES;
19127
19133
  if (!isOpen) return null;
@@ -19130,16 +19136,19 @@ function PropertyPhotoGallery({
19130
19136
  {
19131
19137
  galleryImages,
19132
19138
  startIndex,
19139
+ startWithVideo,
19133
19140
  title,
19134
19141
  subtitle,
19135
- onClose
19142
+ onClose,
19143
+ videoUrl
19136
19144
  },
19137
- `${startIndex}-${galleryImages.length}`
19145
+ `${startIndex}-${galleryImages.length}-${startWithVideo}`
19138
19146
  );
19139
19147
  }
19140
- function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose }) {
19148
+ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
19141
19149
  const [currentIndex, setCurrentIndex] = React5.useState(() => clampIndex(startIndex, galleryImages.length));
19142
19150
  const [activeHotspotId, setActiveHotspotId] = React5.useState(null);
19151
+ const [showVideo, setShowVideo] = React5.useState(startWithVideo ?? false);
19143
19152
  const sliderRef = React5.useRef(null);
19144
19153
  const [isDragging, setIsDragging] = React5.useState(false);
19145
19154
  const wasDraggingRef = React5.useRef(false);
@@ -19189,7 +19198,7 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19189
19198
  ] }),
19190
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" }) }) })
19191
19200
  ] }),
19192
- /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19201
+ !showVideo && /* @__PURE__ */ jsxRuntime.jsxs(SliderContainer, { onClick: (event) => event.stopPropagation(), children: [
19193
19202
  /* @__PURE__ */ jsxRuntime.jsxs(
19194
19203
  SliderTrack,
19195
19204
  {
@@ -19252,7 +19261,21 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19252
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" }) })
19253
19262
  }
19254
19263
  ),
19255
- 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)),
19256
19279
  galleryImages[currentIndex]?.hotspots?.map((hotspot) => /* @__PURE__ */ jsxRuntime.jsx(
19257
19280
  Hotspot,
19258
19281
  {
@@ -19297,11 +19320,83 @@ function GalleryContent({ galleryImages, startIndex, title, subtitle, onClose })
19297
19320
  ]
19298
19321
  }
19299
19322
  ),
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)) })
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
+ ] })
19301
19339
  ]
19302
19340
  }
19303
19341
  );
19304
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
+ `;
19305
19400
  var slideIn2 = styled10.keyframes`
19306
19401
  from { transform: translateX(110%); opacity: 0; }
19307
19402
  to { transform: translateX(0); opacity: 1; }