fixes gallery
This commit is contained in:
@@ -49,12 +49,15 @@ function renderMarkdownSafe(text) {
|
||||
export default function ArtworkDescription({ artwork }) {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const content = (artwork?.description || '').trim()
|
||||
|
||||
if (content.length === 0) return null
|
||||
|
||||
const collapsed = content.length > COLLAPSE_AT && !expanded
|
||||
const visibleText = collapsed ? `${content.slice(0, COLLAPSE_AT)}…` : content
|
||||
const rendered = useMemo(() => renderMarkdownSafe(visibleText), [visibleText])
|
||||
// useMemo must always be called (Rules of Hooks) — guard inside the callback
|
||||
const rendered = useMemo(
|
||||
() => (content.length > 0 ? renderMarkdownSafe(visibleText) : null),
|
||||
[content, visibleText],
|
||||
)
|
||||
|
||||
if (content.length === 0) return null
|
||||
|
||||
return (
|
||||
<section className="rounded-xl bg-panel p-5 shadow-lg shadow-deep/30">
|
||||
|
||||
@@ -24,86 +24,97 @@ export default function ArtworkHero({ artwork, presentMd, presentLg, presentXl,
|
||||
<figure className="w-full">
|
||||
<div className="relative mx-auto w-full max-w-[1280px]">
|
||||
|
||||
{/* Outer flex row: left arrow | image | right arrow */}
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
{hasRealArtworkImage && (
|
||||
<div className="absolute inset-0 -z-10" />
|
||||
)}
|
||||
{/* Prev arrow — outside the picture */}
|
||||
<div className="flex w-12 shrink-0 justify-center">
|
||||
{hasPrev && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Previous artwork"
|
||||
onClick={() => onPrev?.()}
|
||||
className="flex h-11 w-11 items-center justify-center rounded-full bg-white/10 text-white/70 ring-1 ring-white/15 shadow-lg hover:bg-white/20 hover:text-white focus:bg-white/20 focus:text-white transition-colors duration-150"
|
||||
>
|
||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`relative w-full aspect-video overflow-hidden ${onOpenViewer ? 'cursor-zoom-in' : ''}`}
|
||||
onClick={onOpenViewer}
|
||||
role={onOpenViewer ? 'button' : undefined}
|
||||
aria-label={onOpenViewer ? 'View fullscreen' : undefined}
|
||||
tabIndex={onOpenViewer ? 0 : undefined}
|
||||
onKeyDown={onOpenViewer ? (e) => e.key === 'Enter' && onOpenViewer() : undefined}
|
||||
>
|
||||
<img
|
||||
src={md}
|
||||
alt={artwork?.title ?? 'Artwork'}
|
||||
className="absolute inset-0 h-full w-full object-contain"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
{/* Image area */}
|
||||
<div className="relative min-w-0 flex-1">
|
||||
{hasRealArtworkImage && (
|
||||
<div className="absolute inset-0 -z-10" />
|
||||
)}
|
||||
|
||||
<img
|
||||
src={lg}
|
||||
srcSet={srcSet}
|
||||
sizes="(min-width: 1280px) 1280px, (min-width: 768px) 90vw, 100vw"
|
||||
alt={artwork?.title ?? 'Artwork'}
|
||||
className={`absolute inset-0 h-full w-full object-contain transition-opacity duration-500 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
onLoad={() => setIsLoaded(true)}
|
||||
onError={(event) => {
|
||||
event.currentTarget.src = FALLBACK_LG
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Prev arrow */}
|
||||
{hasPrev && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Previous artwork"
|
||||
onClick={(e) => { e.stopPropagation(); onPrev?.(); }}
|
||||
className="absolute left-3 top-1/2 -translate-y-1/2 z-10 flex h-11 w-11 items-center justify-center rounded-full bg-black/50 text-white/70 backdrop-blur-sm ring-1 ring-white/15 shadow-lg opacity-50 hover:opacity-100 focus:opacity-100 transition-opacity duration-150"
|
||||
<div
|
||||
className={`relative w-full aspect-video overflow-hidden ${onOpenViewer ? 'cursor-zoom-in' : ''}`}
|
||||
onClick={onOpenViewer}
|
||||
role={onOpenViewer ? 'button' : undefined}
|
||||
aria-label={onOpenViewer ? 'View fullscreen' : undefined}
|
||||
tabIndex={onOpenViewer ? 0 : undefined}
|
||||
onKeyDown={onOpenViewer ? (e) => e.key === 'Enter' && onOpenViewer() : undefined}
|
||||
>
|
||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
<img
|
||||
src={md}
|
||||
alt={artwork?.title ?? 'Artwork'}
|
||||
className="absolute inset-0 h-full w-full object-contain"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
{/* Next arrow */}
|
||||
{hasNext && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Next artwork"
|
||||
onClick={(e) => { e.stopPropagation(); onNext?.(); }}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 z-10 flex h-11 w-11 items-center justify-center rounded-full bg-black/50 text-white/70 backdrop-blur-sm ring-1 ring-white/15 shadow-lg opacity-50 hover:opacity-100 focus:opacity-100 transition-opacity duration-150"
|
||||
>
|
||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
<img
|
||||
src={lg}
|
||||
srcSet={srcSet}
|
||||
sizes="(min-width: 1280px) 1280px, (min-width: 768px) 90vw, 100vw"
|
||||
alt={artwork?.title ?? 'Artwork'}
|
||||
className={`absolute inset-0 h-full w-full object-contain transition-opacity duration-500 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
onLoad={() => setIsLoaded(true)}
|
||||
onError={(event) => {
|
||||
event.currentTarget.src = FALLBACK_LG
|
||||
}}
|
||||
/>
|
||||
|
||||
{onOpenViewer && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="View fullscreen"
|
||||
onClick={(e) => { e.stopPropagation(); onOpenViewer(); }}
|
||||
className="absolute bottom-3 right-3 flex h-9 w-9 items-center justify-center rounded-full bg-black/50 text-white/80 backdrop-blur-sm ring-1 ring-white/15 opacity-0 hover:opacity-100 focus:opacity-100 [div:hover_&]:opacity-100 transition-opacity duration-150 shadow-lg"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasRealArtworkImage && (
|
||||
<div className="pointer-events-none absolute inset-x-8 -bottom-5 h-10 rounded-full bg-accent/25 blur-2xl" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Next arrow — outside the picture */}
|
||||
<div className="flex w-12 shrink-0 justify-center">
|
||||
{hasNext && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Next artwork"
|
||||
onClick={() => onNext?.()}
|
||||
className="flex h-11 w-11 items-center justify-center rounded-full bg-white/10 text-white/70 ring-1 ring-white/15 shadow-lg hover:bg-white/20 hover:text-white focus:bg-white/20 focus:text-white transition-colors duration-150"
|
||||
>
|
||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{onOpenViewer && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="View fullscreen"
|
||||
onClick={(e) => { e.stopPropagation(); onOpenViewer(); }}
|
||||
className="absolute bottom-3 right-3 flex h-9 w-9 items-center justify-center rounded-full bg-black/50 text-white/80 backdrop-blur-sm ring-1 ring-white/15 opacity-0 hover:opacity-100 focus:opacity-100 [div:hover_&]:opacity-100 transition-opacity duration-150 shadow-lg"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasRealArtworkImage && (
|
||||
<div className="pointer-events-none absolute inset-x-8 -bottom-5 h-10 rounded-full bg-accent/25 blur-2xl" />
|
||||
)}
|
||||
</div>
|
||||
</figure>
|
||||
)
|
||||
|
||||
@@ -4,14 +4,10 @@ export default function ArtworkTags({ artwork }) {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
const tags = useMemo(() => {
|
||||
const primaryCategorySlug = artwork?.categories?.[0]?.slug || 'all'
|
||||
|
||||
const categories = (artwork?.categories || []).map((category) => ({
|
||||
key: `cat-${category.id || category.slug}`,
|
||||
label: category.name,
|
||||
href: category.content_type_slug && category.slug
|
||||
? `/browse/${category.content_type_slug}/${category.slug}`
|
||||
: `/browse/${category.slug || ''}`,
|
||||
href: category.url || `/${category.content_type_slug}/${category.slug}`,
|
||||
}))
|
||||
|
||||
const artworkTags = (artwork?.tags || []).map((tag) => ({
|
||||
|
||||
@@ -91,9 +91,12 @@
|
||||
return (int) $fallback;
|
||||
};
|
||||
|
||||
$imgWidth = max(1, $resolveDimension($art->width ?? null, 'width', 800));
|
||||
$imgHeight = max(1, $resolveDimension($art->height ?? null, 'height', 600));
|
||||
$imgAspectRatio = $imgWidth . ' / ' . $imgHeight;
|
||||
// Use stored dimensions when available; otherwise leave ratio unconstrained
|
||||
// so the thumbnail displays at its natural proportions (no 1:1 or 16:9 forcing).
|
||||
$hasDimensions = ($art->width ?? 0) > 0 && ($art->height ?? 0) > 0;
|
||||
$imgWidth = $hasDimensions ? max(1, $resolveDimension($art->width, 'width', 0)) : null;
|
||||
$imgHeight = $hasDimensions ? max(1, $resolveDimension($art->height, 'height', 0)) : null;
|
||||
$imgAspectRatio = $hasDimensions ? ($imgWidth . ' / ' . $imgHeight) : null;
|
||||
|
||||
$contentUrl = $imgSrc;
|
||||
$cardUrl = (string) ($art->url ?? '');
|
||||
@@ -134,8 +137,8 @@
|
||||
<div class="absolute left-3 top-3 z-30 rounded-md bg-black/55 px-2 py-1 text-xs text-white backdrop-blur-sm">{{ $category }}</div>
|
||||
@endif
|
||||
|
||||
<div class="nova-card-media relative overflow-hidden bg-neutral-900" style="aspect-ratio: {{ $imgAspectRatio }};">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-white/10 via-white/5 to-transparent"></div>
|
||||
<div class="nova-card-media relative overflow-hidden bg-neutral-900"@if($imgAspectRatio) style="aspect-ratio: {{ $imgAspectRatio }};"@endif>
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-white/10 via-white/5 to-transparent pointer-events-none"></div>
|
||||
<picture>
|
||||
<source srcset="{{ $imgAvifSrcset }}" type="image/avif">
|
||||
<source srcset="{{ $imgWebpSrcset }}" type="image/webp">
|
||||
@@ -148,9 +151,9 @@
|
||||
@if($fetchpriority) fetchpriority="{{ $fetchpriority }}" @endif
|
||||
@if($loading !== 'eager') data-blur-preview @endif
|
||||
alt="{{ e($title) }}"
|
||||
width="{{ $imgWidth }}"
|
||||
height="{{ $imgHeight }}"
|
||||
class="h-full w-full object-cover transition-[transform,filter] duration-300 ease-out group-hover:scale-[1.04]"
|
||||
@if($imgWidth) width="{{ $imgWidth }}" @endif
|
||||
@if($imgHeight) height="{{ $imgHeight }}" @endif
|
||||
class="{{ $imgAspectRatio ? 'h-full w-full object-cover' : 'w-full h-auto' }} transition-[transform,filter] duration-300 ease-out group-hover:scale-[1.04]"
|
||||
itemprop="thumbnailUrl"
|
||||
/>
|
||||
</picture>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
<div class="relative">
|
||||
<button class="hover:text-white inline-flex items-center gap-1" data-dd="cats">
|
||||
Categories
|
||||
Explore
|
||||
<svg class="w-4 h-4 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2">
|
||||
<path d="M6 9l6 6 6-6" />
|
||||
@@ -57,12 +57,12 @@
|
||||
</button>
|
||||
<div id="dd-cats"
|
||||
class="hidden absolute left-0 mt-2 w-64 rounded-lg bg-panel border border-panel shadow-sb overflow-hidden">
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/browse">All Artworks</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/photography">Photography</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/wallpapers">Wallpapers</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/skins">Skins</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/other">Other</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/featured-artworks">Featured Artwork</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/browse"><i class="fa-solid fa-border-all mr-3 text-sb-muted"></i>All Artworks</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/photography"><i class="fa-solid fa-camera mr-3 text-sb-muted"></i>Photography</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/wallpapers"><i class="fa-solid fa-desktop mr-3 text-sb-muted"></i>Wallpapers</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/skins"><i class="fa-solid fa-layer-group mr-3 text-sb-muted"></i>Skins</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/other"><i class="fa-solid fa-folder-open mr-3 text-sb-muted"></i>Other</a>
|
||||
<a class="block px-4 py-2 text-sm hover:bg-white/5" href="/featured-artworks"><i class="fa-solid fa-star mr-3 text-sb-muted"></i>Featured Artwork</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
6
resources/views/legacy/[#6223] Red Cloud XP.txt
Normal file
6
resources/views/legacy/[#6223] Red Cloud XP.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
[#6223] Red Cloud XP
|
||||
→ windows-logo, retro-computing, red-dominant-colour, dark-mood, pixelated-graphics, digital-art, grunge-texture, office-shortcuts-menu, 90s-aesthetic, chromatic-aberration, high-contrast, textured-background
|
||||
[#6225] Helping Hand zoomers (part 1)
|
||||
→ desktop screenshot, computer icons, windows interface, digital-art, blue-grey tones, flat design, minimalist-style, iconography, organized layout, technical illustration, screen capture, system icons
|
||||
[#6226] Helping Hand zoomers (part 2)
|
||||
PS D:\Sites\Skinbase26>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
{{-- Latest uploads grid — use same Nova gallery layout as /browse --}}
|
||||
<section class="px-6 pb-10 pt-6 md:px-10" data-nova-gallery data-gallery-type="home-uploads">
|
||||
<div class="{{ ($gridV2 ?? false) ? 'gallery' : 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6' }}" data-gallery-grid>
|
||||
<div class="{{ ($gridV2 ?? false) ? 'gallery' : 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6' }}" data-gallery-grid>
|
||||
@forelse($latestUploads as $upload)
|
||||
<x-artwork-card :art="$upload" />
|
||||
@empty
|
||||
@@ -53,10 +53,10 @@
|
||||
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (min-width: 2600px) {
|
||||
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
||||
}
|
||||
[data-nova-gallery].is-enhanced [data-gallery-grid] > .nova-card { margin: 0 !important; }
|
||||
[data-nova-gallery].is-enhanced [data-gallery-pagination] { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user