/** * Entry point for the MasonryGallery React component. * * Looks for every element with [data-react-masonry-gallery] on the page and * mounts a MasonryGallery instance into it. All configuration is passed via * data attributes so the Blade view does not need to know anything about React. * * Expected data attributes on the mount element: * data-artworks JSON array of artwork objects (required) * data-gallery-type e.g. "trending", "for-you" (default: "discover") * data-cursor-endpoint URL for cursor-based feeds (optional) * data-next-cursor Initial cursor token (optional) * data-next-page-url Initial "next page" URL (optional) * data-limit Items per page (default: 40) */ import React from 'react'; import { createRoot } from 'react-dom/client'; import MasonryGallery from './components/gallery/MasonryGallery'; function mountAll() { document .querySelectorAll('[data-react-masonry-gallery]') .forEach((container) => { // Already mounted by a previous call (e.g. HMR) if (container.dataset.reactMounted) return; container.dataset.reactMounted = '1'; let artworks = []; try { artworks = JSON.parse(container.dataset.artworks || '[]'); } catch { console.warn('[MasonryGallery] Could not parse data-artworks JSON'); } const props = { artworks, galleryType: container.dataset.galleryType || 'discover', cursorEndpoint: container.dataset.cursorEndpoint || null, initialNextCursor: container.dataset.nextCursor || null, initialNextPageUrl: container.dataset.nextPageUrl || null, limit: parseInt(container.dataset.limit || '40', 10), rankApiEndpoint: container.dataset.rankApiEndpoint || null, rankType: container.dataset.rankType || null, }; createRoot(container).render(); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', mountAll); } else { mountAll(); }