Basic 3-column
import { html, renderApp } from 'mates';
import { MasonryGrid } from 'mates-ui';
const PHOTOS = [
{ id: "1", src: "https://picsum.photos/id/1015/800/534", thumbSrc: "https://picsum.photos/id/1015/400/267", alt: "River canyon" },
{ id: "2", src: "https://picsum.photos/id/1018/800/450", thumbSrc: "https://picsum.photos/id/1018/400/225", alt: "Hills" },
{ id: "3", src: "https://picsum.photos/id/1025/800/600", thumbSrc: "https://picsum.photos/id/1025/400/300", alt: "Puppy" },
{ id: "4", src: "https://picsum.photos/id/1036/800/450", thumbSrc: "https://picsum.photos/id/1036/400/225", alt: "Architecture" },
{ id: "5", src: "https://picsum.photos/id/1044/800/534", thumbSrc: "https://picsum.photos/id/1044/400/267", alt: "Mountain lake" },
{ id: "6", src: "https://picsum.photos/id/1050/800/500", thumbSrc: "https://picsum.photos/id/1050/400/250", alt: "Forest path" },
{ id: "7", src: "https://picsum.photos/id/1060/800/534", thumbSrc: "https://picsum.photos/id/1060/400/267", alt: "Waterfall" },
{ id: "8", src: "https://picsum.photos/id/1074/800/450", thumbSrc: "https://picsum.photos/id/1074/400/225", alt: "Sunset" },
{ id: "9", src: "https://picsum.photos/id/1080/800/534", thumbSrc: "https://picsum.photos/id/1080/400/267", alt: "Mountains" },
{ id: "10", src: "https://picsum.photos/id/1082/800/450", thumbSrc: "https://picsum.photos/id/1082/400/225", alt: "Skyline" },
{ id: "11", src: "https://picsum.photos/id/1084/800/600", thumbSrc: "https://picsum.photos/id/1084/400/300", alt: "Autumn" },
{ id: "12", src: "https://picsum.photos/id/1086/800/534", thumbSrc: "https://picsum.photos/id/1086/400/267", alt: "Snow" },
];
const App = () => () => html`
<div style="max-width:48rem;width:100%;">
${MasonryGrid({ items: PHOTOS, columns: 3 })}
</div>
`;
renderApp(App, document.getElementById('app'));
Column variants
import { html, atom, renderApp } from 'mates';
import { MasonryGrid, FilledButton, OutlinedButton } from 'mates-ui';
const PHOTOS = Array.from({ length: 12 }, (_, i) => ({
id: String(i + 1),
src: "https://picsum.photos/seed/mc" + i + "/800/" + (400 + (i * 53) % 280),
thumbSrc: "https://picsum.photos/seed/mc" + i + "/400/300",
alt: "Photo " + (i + 1),
}));
const App = () => {
const cols = atom(3);
return () => html`
<x-col gap="var(--md-space-4)" style="max-width:48rem;width:100%;">
<x-row gap="var(--md-space-2)">
${[2, 3, 4].map((n) => cols() === n
? FilledButton(n + " columns", { size: "sm", onClick: () => cols.set(n) })
: OutlinedButton(n + " columns", { size: "sm", onClick: () => cols.set(n) })
)}
</x-row>
${MasonryGrid({ items: PHOTOS, columns: cols() })}
</x-col>
`;
};
renderApp(App, document.getElementById('app'));
Infinite scroll (simulated)
import { html, atom, isSSR, renderApp } from 'mates';
import { MasonryGrid, Text } from 'mates-ui';
const ALL = Array.from({ length: 12 }, (_, i) => ({
id: String(i + 1),
src: "https://picsum.photos/seed/mi" + i + "/800/" + (400 + (i * 53) % 280),
thumbSrc: "https://picsum.photos/seed/mi" + i + "/400/300",
alt: "Photo " + (i + 1),
}));
const App = () => {
const visibleCount = atom(6);
const loading = atom(false);
const onLoadMore = () => {
if (loading()) return;
loading.set(true);
setTimeout(() => { visibleCount.set(Math.min(ALL.length, visibleCount() + 3)); loading.set(false); }, 800);
};
return () => html`
<x-col gap="var(--md-space-3)" style="max-width:48rem;width:100%;">
${MasonryGrid({ items: ALL.slice(0, visibleCount()), columns: 3, hasMore: !isSSR() && visibleCount() < ALL.length, onLoadMore })}
${loading() ? Text("Loading…", { type: "body-sm", color: "muted" }) : visibleCount() >= ALL.length ? Text("All " + ALL.length + " photos loaded.", { type: "body-sm", color: "muted" }) : html``}
</x-col>
`;
};
renderApp(App, document.getElementById('app'));