-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ability to direct page logic via data attr in order to better sup…
…port infinite carousels (#27)
- Loading branch information
1 parent
a7108cb
commit d02829a
Showing
4 changed files
with
511 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
.root { | ||
position: relative; | ||
margin: 0 -1rem; /* bust out of storybook margin (to demonstrate full bleed carousel) */ | ||
} | ||
|
||
.y.root { | ||
margin: -1rem 0; | ||
height: 100vh; | ||
width: 300px; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
.scroll { | ||
position: relative; | ||
display: flex; | ||
overflow: auto; | ||
scroll-snap-type: x mandatory; | ||
-ms-overflow-style: none; | ||
scrollbar-width: none; | ||
overscroll-behavior: contain; | ||
scroll-padding: 0 16px; | ||
padding: 0 16px; | ||
} | ||
|
||
.scroll::-webkit-scrollbar { | ||
display: none; | ||
} | ||
|
||
.y .scroll { | ||
display: block; | ||
scroll-snap-type: y mandatory; | ||
scroll-padding: 16px 0; | ||
padding: 16px 0; | ||
} | ||
|
||
.item { | ||
font-family: Futura, Trebuchet MS, Arial, sans-serif; | ||
font-size: 125px; | ||
line-height: 1; | ||
width: 300px; | ||
height: 300px; | ||
max-width: 100%; | ||
flex-shrink: 0; | ||
color: white; | ||
display: flex; | ||
justify-content: end; | ||
align-items: end; | ||
padding: 16px 20px; | ||
text-transform: uppercase; | ||
text-shadow: 6px 6px 0px rgba(0, 0, 0, 0.2); | ||
margin-right: 0.6rem; | ||
overflow: hidden; | ||
} | ||
|
||
.scrollMargin .item:nth-child(9) { | ||
scroll-margin-left: 200px; | ||
background: black !important; | ||
} | ||
|
||
.item:last-child { | ||
margin-right: 0; | ||
} | ||
|
||
.y .item { | ||
margin-right: 0; | ||
margin-bottom: 0.6rem; | ||
} | ||
|
||
.y .item:last-child { | ||
margin-bottom: 0; | ||
} | ||
|
||
.pageIndicator { | ||
font-family: Futura, Trebuchet MS, Arial, sans-serif; | ||
font-weight: bold; | ||
font-size: 14px; | ||
position: absolute; | ||
top: 10px; | ||
right: 10px; | ||
padding: 10px 12px; | ||
background: rgba(255, 255, 255, 0.5); | ||
pointer-events: none; | ||
border-radius: 5px; | ||
color: #374151; | ||
} | ||
|
||
.controls { | ||
margin: 1rem 0; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
color: #374151; | ||
padding: 0 1rem; | ||
} | ||
|
||
.prevButton, | ||
.nextButton { | ||
font-size: 18px; | ||
transition: opacity 100ms ease-out; | ||
} | ||
|
||
.prevButton[disabled], | ||
.nextButton[disabled] { | ||
opacity: 0.4; | ||
} | ||
|
||
.pagination { | ||
display: flex; | ||
flex-wrap: wrap; | ||
justify-content: center; | ||
margin: 0 10px; | ||
} | ||
|
||
.paginationItem { | ||
display: flex; | ||
justify-content: center; | ||
} | ||
|
||
.paginationButton { | ||
display: block; | ||
text-indent: -99999px; | ||
overflow: hidden; | ||
background: #374151; | ||
width: 12px; | ||
height: 12px; | ||
border-radius: 50%; | ||
margin: 5px; | ||
transition: opacity 100ms ease-out; | ||
} | ||
|
||
.paginationItemActive .paginationButton { | ||
opacity: 0.3; | ||
} | ||
|
||
@media only screen and (max-width: 480px) { | ||
.item { | ||
width: 280px; | ||
height: 280px; | ||
} | ||
|
||
.pagination { | ||
margin: 0 8px; | ||
} | ||
|
||
.prevButton, | ||
.nextButton { | ||
font-size: 15px; | ||
} | ||
|
||
.paginationButton { | ||
width: 9px; | ||
height: 9px; | ||
margin: 4px; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import React, { useLayoutEffect, useRef, useState } from 'react'; | ||
import { | ||
InfiniteCarousel, | ||
InfiniteCarouselItem, | ||
InfiniteCarouselRef | ||
} from './infinite-carousel'; | ||
import { Button } from './lib/button'; | ||
import { Select } from './lib/select'; | ||
|
||
export default { | ||
title: 'Infinite Carousel', | ||
component: InfiniteCarousel | ||
}; | ||
|
||
export const Default = () => { | ||
const items = Array.from({ length: 18 }).map((_, index) => ({ | ||
id: index, | ||
index | ||
})); | ||
return ( | ||
<InfiniteCarousel | ||
items={items} | ||
renderItem={({ item, index, isSnapPoint, shouldSnap }) => ( | ||
<InfiniteCarouselItem | ||
key={index} | ||
isSnapPoint={isSnapPoint} | ||
shouldSnap={shouldSnap} | ||
bgColor={getColor(item.index)} | ||
> | ||
{item.index + 1} | ||
</InfiniteCarouselItem> | ||
)} | ||
/> | ||
); | ||
}; | ||
|
||
export const VariableWidth = () => { | ||
const items = [ | ||
110, 300, 500, 120, 250, 300, 500, 400, 180, 300, 350, 700, 400, 230, 300 | ||
].map((width, index) => ({ id: index, index, width })); | ||
return ( | ||
<InfiniteCarousel | ||
items={items} | ||
renderItem={({ item, index, isSnapPoint, shouldSnap }) => ( | ||
<InfiniteCarouselItem | ||
key={index} | ||
isSnapPoint={isSnapPoint} | ||
shouldSnap={shouldSnap} | ||
bgColor={getColor(item.index)} | ||
width={item.width} | ||
> | ||
{item.index + 1} | ||
</InfiniteCarouselItem> | ||
)} | ||
/> | ||
); | ||
}; | ||
|
||
export const VerticalAxis = () => { | ||
const items = Array.from({ length: 18 }).map((_, index) => ({ | ||
id: index, | ||
index | ||
})); | ||
return ( | ||
<InfiniteCarousel | ||
axis="y" | ||
items={items} | ||
renderItem={({ item, index, isSnapPoint, shouldSnap }) => ( | ||
<InfiniteCarouselItem | ||
key={index} | ||
isSnapPoint={isSnapPoint} | ||
shouldSnap={shouldSnap} | ||
bgColor={getColor(item.index)} | ||
> | ||
{item.index + 1} | ||
</InfiniteCarouselItem> | ||
)} | ||
/> | ||
); | ||
}; | ||
|
||
export const DynamicItems = () => { | ||
const carouselRef = useRef<InfiniteCarouselRef>(null); | ||
const [items, setItems] = useState(() => | ||
Array.from({ length: 6 }).map((_, index) => ({ id: index, index })) | ||
); | ||
const addItem = () => { | ||
setItems((prev) => [...prev, { id: prev.length, index: prev.length }]); | ||
}; | ||
const removeItem = () => { | ||
setItems((prev) => prev.slice(0, -1)); | ||
}; | ||
useLayoutEffect(() => { | ||
if (!carouselRef.current) { | ||
return; | ||
} | ||
carouselRef.current.refresh(); | ||
}, [items]); | ||
return ( | ||
<> | ||
<div style={{ display: 'flex', gap: '10px', margin: '0 0 10px' }}> | ||
<Button onClick={() => removeItem()}>Remove Item</Button> | ||
<Button onClick={() => addItem()}>Add Item</Button> | ||
</div> | ||
<InfiniteCarousel | ||
ref={carouselRef} | ||
items={items} | ||
renderItem={({ item, index, isSnapPoint, shouldSnap }) => ( | ||
<InfiniteCarouselItem | ||
key={index} | ||
isSnapPoint={isSnapPoint} | ||
shouldSnap={shouldSnap} | ||
bgColor={getColor(item.index)} | ||
> | ||
{item.index + 1} | ||
</InfiniteCarouselItem> | ||
)} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
export const ScrollBehavior = () => { | ||
const scrollBehaviors: ScrollBehavior[] = ['smooth', 'instant', 'auto']; | ||
const [scrollBehavior, setScrollBehavior] = useState(scrollBehaviors[0]); | ||
const items = Array.from({ length: 18 }).map((_, index) => ({ | ||
id: index, | ||
index | ||
})); | ||
return ( | ||
<> | ||
<div style={{ margin: '0 0 10px' }}> | ||
<Select | ||
onChange={(e) => { | ||
setScrollBehavior(e.target.value as ScrollBehavior); | ||
}} | ||
value={scrollBehavior} | ||
> | ||
{scrollBehaviors.map((value) => ( | ||
<option key={value} value={value}> | ||
{value.slice(0, 1).toUpperCase() + value.slice(1)} | ||
</option> | ||
))} | ||
</Select> | ||
</div> | ||
<InfiniteCarousel | ||
scrollBehavior={scrollBehavior} | ||
items={items} | ||
renderItem={({ item, index, isSnapPoint, shouldSnap }) => ( | ||
<InfiniteCarouselItem | ||
key={index} | ||
isSnapPoint={isSnapPoint} | ||
shouldSnap={shouldSnap} | ||
bgColor={getColor(item.index)} | ||
> | ||
{item.index + 1} | ||
</InfiniteCarouselItem> | ||
)} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
/* Utils */ | ||
|
||
const getColor = (i: number) => { | ||
return `hsl(-${i * 12} 100% 50%)`; | ||
}; |
Oops, something went wrong.