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
This commit is contained in:
53
resources/js/entry-masonry-gallery.jsx
Normal file
53
resources/js/entry-masonry-gallery.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
Reference in New Issue
Block a user