Files
SkinbaseNova/resources/js/entry-masonry-gallery.jsx
Gregor Klevze 67ef79766c fix(gallery): fill tall portrait cards to full block width with object-cover crop
- ArtworkCard: add w-full to nova-card-media, use absolute inset-0 on img so
  object-cover fills the max-height capped box instead of collapsing the width
- MasonryGallery.css: add width:100% to media container, position img
  absolutely so top/bottom is cropped rather than leaving dark gaps
- Add React MasonryGallery + ArtworkCard components and entry point
- Add recommendation system: UserRecoProfile model/DTO/migration,
  SuggestedCreatorsController, SuggestedTagsController, Recommendation
  services, config/recommendations.php
- SimilarArtworksController, DiscoverController, HomepageService updates
- Update routes (api + web) and discover/for-you views
- Refresh favicon assets, update vite.config.js
2026-02-27 13:34:08 +01:00

54 lines
2.0 KiB
JavaScript

/**
* 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),
};
createRoot(container).render(<MasonryGallery {...props} />);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', mountAll);
} else {
mountAll();
}