A JavaScript ki van kapcsolva. Az alábbi kód a teljes példa.
App.jsximport { useState } from 'react';
import {
MotionHighlight,
MotionPager,
MotionSheet,
MotionZoom,
motionTokens,
} from '@kkwaenggwari/animated-chorus';
import './styles.css';
function scene(sky, land, sun) {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 260">
<rect width="400" height="260" fill="${sky}"/>
<circle cx="300" cy="80" r="34" fill="${sun}"/>
<path d="M0 190 Q120 110 220 180 T400 150 V260 H0Z" fill="${land}"/>
<path d="M0 225 Q150 170 260 215 T400 205 V260 H0Z" fill="#0e2e24"/>
</svg>`;
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
}
const photos = [
{
id: 'dawn',
title: 'Dawn ridge',
place: 'North trail',
src: scene('#eaf6f0', '#43c58f', '#ffffff'),
},
{
id: 'forest',
title: 'Deep forest',
place: 'Valley road',
src: scene('#176b52', '#2e8f6d', '#eaf6f0'),
},
{
id: 'meadow',
title: 'Meadow',
place: 'South field',
src: scene('#cfe8dc', '#176b52', '#f4faf7'),
},
{
id: 'night',
title: 'Night hills',
place: 'Lookout',
src: scene('#0e2e24', '#176b52', '#43c58f'),
},
];
export default function App({ gap = 12, spring = 'default' }) {
const [page, setPage] = useState(0);
const [details, setDetails] = useState(false);
const transition = motionTokens.spring[spring];
const photo = photos[page];
return (
<div className="feed">
<MotionPager
items={photos}
index={page}
onIndexChange={setPage}
gap={gap}
transition={transition}
renderItem={(item) => (
<MotionZoom
src={item.src}
width={400}
height={260}
className="photo"
transition={transition}
/>
)}
/>
<div className="bar">
<MotionHighlight
items={photos}
value={photo.id}
onValueChange={(key) => setPage(photos.findIndex((item) => item.id === key))}
transition={transition}
className="thumbs"
itemClassName="thumb"
highlightClassName="thumb-on"
renderItem={(item) => <img src={item.src} width={48} height={32} />}
/>
<button className="info" onClick={() => setDetails(true)}>
Details
</button>
</div>
<MotionSheet open={details} onOpenChange={setDetails} className="sheet">
<span className="handle" />
<h2>{photo.title}</h2>
<p>
{photo.place} · photo {page + 1} of {photos.length}
</p>
<button onClick={() => setDetails(false)}>Close</button>
</MotionSheet>
</div>
);
}
styles.css.feed {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 12px;
max-width: 560px;
min-height: 420px;
margin: 0 auto;
padding: 24px 20px;
align-content: start;
}
.photo {
display: block;
width: 100%;
height: auto;
aspect-ratio: 400 / 260;
border-radius: 18px;
object-fit: cover;
cursor: zoom-in;
user-select: none;
-webkit-user-drag: none;
}
.bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.thumbs {
display: flex;
min-width: 0;
gap: 6px;
}
.thumb {
flex: 0 1 auto;
min-width: 0;
padding: 3px;
border: 0;
border-radius: 10px;
background: none;
cursor: pointer;
}
.thumb img {
display: block;
max-width: 100%;
height: auto;
border-radius: 7px;
}
.thumb-on {
border-radius: 10px;
box-shadow: inset 0 0 0 2px #176b52;
}
.info {
flex: none;
min-height: 44px;
padding: 0 18px;
border: 1px solid #cfe8dc;
border-radius: 999px;
background: #fff;
font:
600 14px system-ui,
sans-serif;
cursor: pointer;
}
.sheet {
gap: 6px;
padding: 12px 22px 22px;
border-radius: 22px 22px 0 0;
background: #fff;
box-shadow: 0 -8px 30px rgba(14, 46, 36, 0.18);
}
.handle {
justify-self: center;
width: 44px;
height: 5px;
border-radius: 3px;
background: #cfe8dc;
}
.sheet h2 {
margin: 6px 0 0;
font:
750 22px system-ui,
sans-serif;
color: #0e2e24;
}
.sheet p {
margin: 0;
color: #5b616b;
}
.sheet button {
min-height: 46px;
margin-top: 12px;
border: 0;
border-radius: 999px;
background: #176b52;
color: #fff;
font:
650 15px system-ui,
sans-serif;
cursor: pointer;
}