wip; improve carousel docs

pull/1213/head
Cory LaViska 2023-02-23 16:50:18 -05:00
rodzic a526e8a956
commit c69db4919b
3 zmienionych plików z 346 dodań i 510 usunięć

Wyświetl plik

@ -2,10 +2,8 @@
[component-header:sl-carousel] [component-header:sl-carousel]
Carousels consist of optional navigation arrows to go backwards and forwards, as well as optional pagination indicators.
```html preview ```html preview
<sl-carousel loop pagination navigation autoplay mouse-dragging> <sl-carousel pagination navigation mouse-dragging loop>
<sl-carousel-item> <sl-carousel-item>
<img <img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash" alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
@ -37,242 +35,276 @@ Carousels consist of optional navigation arrows to go backwards and forwards, as
/> />
</sl-carousel-item> </sl-carousel-item>
</sl-carousel> </sl-carousel>
<sl-divider></sl-divider>
<div class="carousel-options">
<div class="carousel-options">
<sl-switch checked name="loop"> Loop </sl-switch>
<sl-switch checked name="navigation"> Show navigation </sl-switch>
<sl-switch checked name="pagination"> Show pagination </sl-switch>
<sl-switch checked name="autoplay"> Autoplay (3s) </sl-switch>
<sl-switch checked name="mouseDragging"> Mouse dragging </sl-switch>
</div>
<div class="carousel-options">
<sl-input type="number" label="Slides per page" name="slidesPerPage" value="1"></sl-input>
<sl-input type="number" label="Slides per move" name="slidesPerMove" value="1"></sl-input>
<sl-select label="Orientation" name="orientation" value="horizontal">
<sl-menu-item value="horizontal">Horizontal</sl-menu-item>
<sl-menu-item value="vertical">Vertical</sl-menu-item>
</sl-select>
</div>
</div>
<style>
sl-carousel {
--aspect-ratio: 3 / 2;
}
.carousel-options {
display: flex;
flex-wrap: wrap;
align-items: end;
gap: 1rem;
}
</style>
<script>
(() => {
const options = document.querySelector('.carousel-options');
const carousel = document.querySelector('sl-carousel');
const loop = options.querySelector('sl-switch[name="loop"]');
const navigation = options.querySelector('sl-switch[name="navigation"]');
const pagination = options.querySelector('sl-switch[name="pagination"]');
const autoplay = options.querySelector('sl-switch[name="autoplay"]');
const mouseDragging = options.querySelector('sl-switch[name="mouseDragging"]');
const slidesPerMove = options.querySelector('sl-input[name="slidesPerMove"]');
const slidesPerPage = options.querySelector('sl-input[name="slidesPerPage"]');
const orientation = options.querySelector('sl-select[name="orientation"]');
loop.addEventListener('sl-change', () => (carousel.loop = loop.checked));
navigation.addEventListener('sl-change', () => (carousel.navigation = navigation.checked));
pagination.addEventListener('sl-change', () => (carousel.pagination = pagination.checked));
autoplay.addEventListener('sl-change', () => (carousel.autoplay = autoplay.checked));
slidesPerMove.addEventListener('sl-change', () => (carousel.slidesPerMove = slidesPerMove.valueAsNumber));
slidesPerPage.addEventListener('sl-change', () => (carousel.slidesPerPage = slidesPerPage.valueAsNumber));
orientation.addEventListener('sl-change', () => (carousel.orientation = orientation.value));
mouseDragging.addEventListener('sl-change', () => (carousel.mouseDragging = mouseDragging.checked));
document.addEventListener('sl-slide-change', e => {
console.log('Slide changed:', e.detail);
});
})();
</script>
``` ```
```jsx react ```jsx react
import { useReducer } from 'react'; import { SlCarousel, SlCarouselItem } from '@shoelace-style/shoelace/dist/react';
import {
SlCarousel,
SlCarouselItem,
SlSwitch,
SlInput,
SlSelect,
SlMenuItem
} from '@shoelace-style/shoelace/dist/react';
const css = ` const App = () => (
sl-carousel { <>
--aspect-ratio: 3 / 2; <SlCarousel pagination mouse-dragging>
} <SlCarouselItem>
<img
.carousel-options { alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
display: flex; src="/assets/examples/carousel/mountains.jpg"
flex-wrap: wrap; />
align-items: end; </SlCarouselItem>
gap: 1rem; <SlCarouselItem>
} <img
`; alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
const App = () => { />
const [state, updateState] = useReducer( </SlCarouselItem>
(state, event) => { <SlCarouselItem>
console.log(event); <img
}, alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
{ src="/assets/examples/carousel/sunset.jpg"
loop: true, />
pagination: true, </SlCarouselItem>
navigation: true, <SlCarouselItem>
autoplay: true, <img
mouseDragging: true, alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
slidesPerPage: 1, src="/assets/examples/carousel/field.jpg"
slidesPerMove: 1, />
orientation: 'horizontal' </SlCarouselItem>
} <SlCarouselItem>
); <img
return ( alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
<> src="/assets/examples/carousel/valley.jpg"
<SlCarousel />
loop={state.loop} </SlCarouselItem>
pagination={state.pagination} </SlCarousel>
navigation={state.navigation} </>
autoplay={state.autoplay} );
mouseDragging={state.mouseDragging}
>
<SlCarouselItem>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</SlCarouselItem>
</SlCarousel>
<SlDivider></SlDivider>
<div className="carousel-options">
<div className="carousel-options">
<SlSwitch checked name="loop" value={state.loop} onSlChange={updateState}>
Loop
</SlSwitch>
<SlSwitch checked name="navigation" value={state.navigation} onSlChange={updateState}>
Show navigation
</SlSwitch>
<SlSwitch checked name="pagination" value={state.pagination} onSlChange={updateState}>
Show pagination
</SlSwitch>
<SlSwitch checked name="autoplay" value={state.autoplay} onSlChange={updateState}>
Autoplay (3s)
</SlSwitch>
<SlSwitch checked name="mouseDragging" value={state.mouseDragging} onSlChange={updateState}>
Mouse dragging
</SlSwitch>
</div>
<div className="carousel-options">
<SlInput
type="number"
label="Slides per page"
name="slidesPerPage"
value={state.slidesPerPage}
onSlChange={updateState}
/>
<SlInput
type="number"
label="Slides per move"
name="slidesPerMove"
value={state.slidesPerMove}
onSlChange={updateState}
/>
<SlSelect
label="Orientation"
name="orientation"
value="horizontal"
value={state.orientation}
onSlChange={updateState}
>
<SlMenuItem value="horizontal">Horizontal</SlMenuItem>
<SlMenuItem value="vertical">Vertical</SlMenuItem>
</SlSelect>
</div>
</div>
<style>{css}</style>
</>
);
};
``` ```
<script>
(() => {
const options = document.querySelector('.carousel-options');
const carousel = document.querySelector('sl-carousel');
const loop = options.querySelector('sl-switch[name="loop"]');
const navigation = options.querySelector('sl-switch[name="navigation"]');
const pagination = options.querySelector('sl-switch[name="pagination"]');
const autoplay = options.querySelector('sl-switch[name="autoplay"]');
const mouseDragging = options.querySelector('sl-switch[name="mouseDragging"]');
const slidesPerMove = options.querySelector('sl-input[name="slidesPerMove"]');
const slidesPerPage = options.querySelector('sl-input[name="slidesPerPage"]');
const orientation = options.querySelector('sl-select[name="orientation"]');
loop.addEventListener('sl-change', () => (carousel.loop = loop.checked));
navigation.addEventListener('sl-change', () => (carousel.navigation = navigation.checked));
pagination.addEventListener('sl-change', () => (carousel.pagination = pagination.checked));
autoplay.addEventListener('sl-change', () => (carousel.autoplay = autoplay.checked));
slidesPerMove.addEventListener('sl-change', () => (carousel.slidesPerMove = slidesPerMove.valueAsNumber));
slidesPerPage.addEventListener('sl-change', () => (carousel.slidesPerPage = slidesPerPage.valueAsNumber));
orientation.addEventListener('sl-change', () => (carousel.orientation = orientation.value));
mouseDragging.addEventListener('sl-change', () => (carousel.mouseDragging = mouseDragging.checked));
document.addEventListener('sl-slide-change', e => {
console.log('Slide changed:', e.detail);
});
})();
</script>
## Examples ## Examples
### Multiple slides per view ### Pagination
Setting the attribute `slides-per-view` is it possible to specify how many items are shown at a given time. Use the `pagination` attribute to show the total number of slides and the current slide as a set of interactive dots.
Using this feature, it may be also useful to advance multiple slides at once, even though not strictly necessary.
This can be done by using the `slides-per-move` attribute.
```html preview ```html preview
<sl-carousel class="multi-carousel" loop navigation pagination slides-per-page="3" slides-per-move="3"> <sl-carousel pagination>
<sl-carousel-item style="background: #204ed8;">Slide 1</sl-carousel-item> <sl-carousel-item>
<sl-carousel-item style="background: #be133d;">Slide 2</sl-carousel-item> <img
<sl-carousel-item style="background: #6e28d9;">Slide 3</sl-carousel-item> alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
<sl-carousel-item style="background: #c2420d;">Slide 4</sl-carousel-item> src="/assets/examples/carousel/mountains.jpg"
<sl-carousel-item style="background: #4d7c0f;">Slide 5</sl-carousel-item> />
<sl-carousel-item style="background: #4338cb;">Slide 6</sl-carousel-item> </sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
</sl-carousel>
```
### Navigation
Use the `navigation` attribute to show previous and next buttons.
```html preview
<sl-carousel navigation>
<sl-carousel-item>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
</sl-carousel>
```
### Looping
By default, the carousel will not advanced beyond the first and last slides. You can change this behavior and force the carousel to "wrap" with the `loop` attribute.
```html preview
<sl-carousel loop navigation pagination>
<sl-carousel-item>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
</sl-carousel>
```
### Autoplay
The carousel will automatically advance when the `autoplay` attribute is used. To change how long a slide is shown before advancing, set `autoplay-interval` to the desired number of milliseconds. For best results, use the `loop` attribute when autoplay is enabled. Note that autoplay will pause while the user interacts with the carousel.
```html preview
<sl-carousel autoplay loop pagination>
<sl-carousel-item>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
</sl-carousel>
```
### Mouse Dragging
The carousel uses [scroll snap](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap) to position slides at various snap positions. This allows users to scroll through the slides very naturally, especially on touch devices. Unfortunately, desktop users won't be able to click and drag with a mouse, which can feel unnatural. Adding the `mouse-dragging` attribute can help with this.
This example is best demonstrated using a mouse. Try clicking and dragging the slide to move it. Then toggle the switch and try again.
```html preview
<div class="mouse-dragging">
<sl-carousel pagination>
<sl-carousel-item>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
</sl-carousel>
<sl-divider></sl-divider>
<sl-switch>Enable mouse dragging</sl-switch>
</div>
<script>
const container = document.querySelector('.mouse-dragging');
const carousel = container.querySelector('sl-carousel');
const toggle = container.querySelector('sl-switch');
toggle.addEventListener('sl-change', () => {
carousel.toggleAttribute('mouse-dragging', toggle.checked);
});
</script>
```
### Multiple Slides Per View
The `slides-per-view` attribute makes it possible to display multiple slides at a time. You can also use the `slides-per-move` attribute to advance more than once slide at a time, if desired.
```html preview
<sl-carousel class="multi-carousel" navigation pagination slides-per-page="2" slides-per-move="2">
<sl-carousel-item style="background: var(--sl-color-red-200);">Slide 1</sl-carousel-item>
<sl-carousel-item style="background: var(--sl-color-orange-200);">Slide 2</sl-carousel-item>
<sl-carousel-item style="background: var(--sl-color-yellow-200);">Slide 3</sl-carousel-item>
<sl-carousel-item style="background: var(--sl-color-green-200);">Slide 4</sl-carousel-item>
<sl-carousel-item style="background: var(--sl-color-blue-200);">Slide 5</sl-carousel-item>
<sl-carousel-item style="background: var(--sl-color-violet-200);">Slide 6</sl-carousel-item>
</sl-carousel> </sl-carousel>
``` ```
@ -281,66 +313,73 @@ import { SlCarousel, SlCarouselItem } from '@shoelace-style/shoelace/dist/react'
const App = () => ( const App = () => (
<SlCarousel class="multi-carousel" loop navigation pagination slides-per-page="3" slides-per-move="3"> <SlCarousel class="multi-carousel" loop navigation pagination slides-per-page="3" slides-per-move="3">
<SlCarouselItem style="background: #204ed8;">Slide 1</SlCarouselItem> <SlCarouselItem style="background: var(--sl-color-red-200);">Slide 1</SlCarouselItem>
<SlCarouselItem style="background: #be133d;">Slide 2</SlCarouselItem> <SlCarouselItem style="background: var(--sl-color-orange-200);">Slide 2</SlCarouselItem>
<SlCarouselItem style="background: #6e28d9;">Slide 3</SlCarouselItem> <SlCarouselItem style="background: var(--sl-color-yellow-200);">Slide 3</SlCarouselItem>
<SlCarouselItem style="background: #c2420d;">Slide 4</SlCarouselItem> <SlCarouselItem style="background: var(--sl-color-green-200);">Slide 4</SlCarouselItem>
<SlCarouselItem style="background: #4d7c0f;">Slide 5</SlCarouselItem> <SlCarouselItem style="background: var(--sl-color-blue-200);">Slide 5</SlCarouselItem>
<SlCarouselItem style="background: #4338cb;">Slide 6</SlCarouselItem> <SlCarouselItem style="background: var(--sl-color-violet-200);">Slide 6</SlCarouselItem>
</SlCarousel> </SlCarousel>
); );
``` ```
### Adding/removing slides ### Adding and Removing Slides
The content of the carousel can be changed by either appending or removing items, the carousel will update itself automatically. The content of the carousel can be changed by appending or removing carousel items. The carousel will update itself automatically.
```html preview ```html preview
<sl-carousel class="dynamic-carousel" pagination navigation> <sl-carousel class="dynamic-carousel" pagination navigation>
<sl-carousel-item style="background: #204ed8;">Slide 1</sl-carousel-item> <sl-carousel-item style="background: var(--sl-color-red-200)">Slide 1</sl-carousel-item>
<sl-carousel-item style="background: #be133d;">Slide 2</sl-carousel-item> <sl-carousel-item style="background: var(--sl-color-orange-200)">Slide 2</sl-carousel-item>
<sl-carousel-item style="background: #6e28d9;">Slide 3</sl-carousel-item> <sl-carousel-item style="background: var(--sl-color-yellow-200)">Slide 3</sl-carousel-item>
</sl-carousel> </sl-carousel>
<sl-divider></sl-divider>
<div class="carousel-options"> <div class="carousel-options">
<sl-button id="dynamic-add">Add slide</sl-button> <sl-button id="dynamic-add">Add slide</sl-button>
<sl-button id="dynamic-remove">Remove slide</sl-button> <sl-button id="dynamic-remove">Remove slide</sl-button>
</div> </div>
<style> <style>
.dynamic-carousel { .dynamic-carousel {
--aspect-ratio: 3 / 2; --aspect-ratio: 3 / 2;
} }
.dynamic-carousel ~ .carousel-options {
display: flex;
justify-content: center;
gap: var(--sl-spacing-x-small);
margin-top: var(--sl-spacing-large);
}
.dynamic-carousel sl-carousel-item { .dynamic-carousel sl-carousel-item {
flex: 0 0 100%; flex: 0 0 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: white;
font-size: var(--sl-font-size-2x-large); font-size: var(--sl-font-size-2x-large);
} }
</style> </style>
<script> <script>
(() => { (() => {
const dynamicCarousel = document.querySelector('.dynamic-carousel'); const dynamicCarousel = document.querySelector('.dynamic-carousel');
const dynamicAdd = document.querySelector('#dynamic-add'); const dynamicAdd = document.querySelector('#dynamic-add');
const dynamicRemove = document.querySelector('#dynamic-remove'); const dynamicRemove = document.querySelector('#dynamic-remove');
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet'];
const rnd = (min, max) => Math.round(Math.random() * (max - min)) + min; let colorIndex = 2;
const getRandomColor = () => `rgb(${rnd(50, 150)}, ${rnd(50, 150)}, ${rnd(50, 150)})`;
const addSlide = () => { const addSlide = () => {
const slide = document.createElement('sl-carousel-item'); const slide = document.createElement('sl-carousel-item');
const color = colors[++colorIndex % colors.length];
slide.innerText = `Slide ${dynamicCarousel.children.length + 1}`; slide.innerText = `Slide ${dynamicCarousel.children.length + 1}`;
slide.style.setProperty('background', getRandomColor()); slide.style.setProperty('background', `var(--sl-color-${color}-200)`);
dynamicCarousel.appendChild(slide); dynamicCarousel.appendChild(slide);
}; };
const removeSlide = () => { const removeSlide = () => {
const slide = dynamicCarousel.children[dynamicCarousel.children.length - 1]; const slide = dynamicCarousel.children[dynamicCarousel.children.length - 1];
slide.remove(); slide.remove();
colorIndex--;
}; };
dynamicAdd.addEventListener('click', addSlide); dynamicAdd.addEventListener('click', addSlide);
@ -358,6 +397,12 @@ const css = `
--aspect-ratio: 3 / 2; --aspect-ratio: 3 / 2;
} }
.dynamic-carousel ~ .carousel-options {
display: flex;
justify-content: center;
margin-top: var(--sl-spacing-large);
}
.dynamic-carousel sl-carousel-item { .dynamic-carousel sl-carousel-item {
flex: 0 0 100%; flex: 0 0 100%;
display: flex; display: flex;
@ -370,9 +415,7 @@ const css = `
const App = () => { const App = () => {
const [slides, setSlides] = useState(['#204ed8', '#be133d', '#6e28d9']); const [slides, setSlides] = useState(['#204ed8', '#be133d', '#6e28d9']);
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet'];
const rnd = (min, max) => Math.round(Math.random() * (max - min)) + min;
const getRandomColor = () => `rgb(${rnd(50, 150)}, ${rnd(50, 150)}, ${rnd(50, 150)})`;
const addSlide = () => { const addSlide = () => {
setSlides([...slides, getRandomColor()]); setSlides([...slides, getRandomColor()]);
@ -386,28 +429,31 @@ const App = () => {
<> <>
<SlCarousel className="dynamic-carousel" pagination navigation> <SlCarousel className="dynamic-carousel" pagination navigation>
{slides.map((color, i) => ( {slides.map((color, i) => (
<SlCarouselItem style={{ background: color }}>Slide {i}</SlCarouselItem> <SlCarouselItem style={{ background: colors[i % colors.length }}>
Slide {i}
</SlCarouselItem>
))} ))}
</SlCarousel> </SlCarousel>
<SlDivider />
<div className="carousel-options"> <div className="carousel-options">
<SlButton onClick={addSlide}>Add slide</SlButton> <SlButton onClick={addSlide}>Add slide</SlButton>
<SlButton onClick={removeSlide}>Remove slide</SlButton> <SlButton onClick={removeSlide}>Remove slide</SlButton>
</div> </div>
<style>{css}</style> <style>{css}</style>
</> </>
); );
}; };
``` ```
### Vertical scrolling ### Vertical Scrolling
Setting the `orientation` attribute to `vertical`, will make the carousel laying out vertically, making it Setting the `orientation` attribute to `vertical`, will make the carousel laying out vertically, making it
possible for the user to scroll it up and down. In case of heterogeneous content, for example images of different sizes, possible for the user to scroll it up and down. In case of heterogeneous content, for example images of different sizes,
it's important to specify a predefined height to the carousel through CSS. it's important to specify a predefined height to the carousel through CSS.
```html preview ```html preview
<sl-carousel class="vertical" loop pagination orientation="vertical"> <sl-carousel class="vertical" pagination orientation="vertical">
<sl-carousel-item> <sl-carousel-item>
<img <img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash" alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
@ -520,12 +566,12 @@ const App = () => (
); );
``` ```
### Aspect ratio ### Aspect Ratio
Use the `--aspect-ratio` custom property to customize the size of viewport in order to make it match a particular aspect ratio. Use the `--aspect-ratio` custom property to customize the size of the carousel's viewport.
```html preview ```html preview
<sl-carousel class="aspect-ratio" navigation pagination style="--aspect-ratio: 3 / 2"> <sl-carousel class="aspect-ratio" navigation pagination style="--aspect-ratio: 3/2">
<sl-carousel-item> <sl-carousel-item>
<img <img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash" alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
@ -559,19 +605,20 @@ Use the `--aspect-ratio` custom property to customize the size of viewport in or
</sl-carousel> </sl-carousel>
<sl-divider></sl-divider> <sl-divider></sl-divider>
<sl-select label="Aspect ratio" name="aspect" value="3 / 2">
<sl-menu-item value="1 / 1">1 / 1</sl-menu-item> <sl-select label="Aspect ratio" name="aspect" value="3/2">
<sl-menu-item value="3 / 2">3 / 2</sl-menu-item> <sl-option value="1/1">1/1</sl-option>
<sl-menu-item value="16 / 9">16 / 9</sl-menu-item> <sl-option value="3/2">3/2</sl-option>
<sl-option value="16/9">16/9</sl-option>
</sl-select> </sl-select>
<script> <script>
(() => { (() => {
const carousel = document.querySelector('sl-carousel.aspect-ratio'); const carousel = document.querySelector('sl-carousel.aspect-ratio');
const aspect = document.querySelector('sl-select[name="aspect"]'); const aspectRatio = document.querySelector('sl-select[name="aspect"]');
aspect.addEventListener('sl-change', () => { aspectRatio.addEventListener('sl-change', () => {
carousel.style.setProperty('--aspect-ratio', aspect.value); carousel.style.setProperty('--aspect-ratio', aspectRatio.value);
}); });
})(); })();
</script> </script>
@ -579,7 +626,7 @@ Use the `--aspect-ratio` custom property to customize the size of viewport in or
```jsx react ```jsx react
import { useState } from 'react'; import { useState } from 'react';
import { SlCarousel, SlCarouselItem, SlDivider, SlSelect, SlMenuItem } from '@shoelace-style/shoelace/dist/react'; import { SlCarousel, SlCarouselItem, SlDivider, SlSelect, SlOption } from '@shoelace-style/shoelace/dist/react';
const App = () => { const App = () => {
const [aspectRatio, setAspectRatio] = useState('3/2'); const [aspectRatio, setAspectRatio] = useState('3/2');
@ -627,9 +674,9 @@ const App = () => {
value={aspectRatio} value={aspectRatio}
onSlChange={event => setAspectRatio(event.target.value)} onSlChange={event => setAspectRatio(event.target.value)}
> >
<SlMenuItem value="1 / 1">1 / 1</SlMenuItem> <SlOption value="1 / 1">1 / 1</SlOption>
<SlMenuItem value="3 / 2">3 / 2</SlMenuItem> <SlOption value="3 / 2">3 / 2</SlOption>
<SlMenuItem value="16 / 9">16 / 9</SlMenuItem> <SlOption value="16 / 9">16 / 9</SlOption>
</SlSelect> </SlSelect>
<style>{css}</style> <style>{css}</style>
@ -638,14 +685,12 @@ const App = () => {
}; };
``` ```
### Scroll hint ### Scroll Hint
Use `--scroll-padding` to add inline padding in horizontal carousels and block padding in vertical carousels. Use the `--scroll-hint` attribute to add inline padding in horizontal carousels and block padding in vertical carousels. Setting a padding will make the closest slides slightly visible, hinting that there are more items in the carousel.
Setting a padding, will make the closest slides visible, suggesting to the user that there are items that can
be scrolled.
```html preview ```html preview
<sl-carousel class="scroll-hint" navigation pagination style="--scroll-padding: calc(var(--slide-gap) + 10%);"> <sl-carousel class="scroll-hint" pagination style="--scroll-hint: 10%;">
<sl-carousel-item> <sl-carousel-item>
<img <img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash" alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
@ -677,20 +722,6 @@ be scrolled.
/> />
</sl-carousel-item> </sl-carousel-item>
</sl-carousel> </sl-carousel>
<sl-divider></sl-divider>
<sl-range label="Size (%)" name="scroll-hint" value="5" min="0" max="15"></sl-range>
<script>
(() => {
const carousel = document.querySelector('sl-carousel.scroll-hint');
const scrollHint = document.querySelector('sl-range[name="scroll-hint"]');
scrollHint.addEventListener('sl-input', () => {
carousel.style.setProperty('--scroll-padding', `calc(var(--slide-gap) + ${scrollHint.value}%)`);
});
})();
</script>
``` ```
```jsx react ```jsx react
@ -755,189 +786,9 @@ const App = () => {
}; };
``` ```
### Custom layout ### Gallery Example
The appearance of the carousel can be easily customized through its slots or `part` attributes. The carousel has a robust API that makes it possible to extend and customize. This example syncs the active slide with a set of thumbnails, effectively creating a gallery-style carousel.
```html preview
<sl-carousel class="custom-layout" navigation pagination>
<sl-carousel-item>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</sl-carousel-item>
<sl-carousel-item>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</sl-carousel-item>
<sl-icon name="arrow-right" slot="next-icon"></sl-icon>
<sl-icon name="arrow-left" slot="previous-icon"></sl-icon>
</sl-carousel>
<style>
.custom-layout::part(base) {
grid-template-areas:
'slides slides slides'
'slides slides slides';
}
.custom-layout::part(pagination) {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: var(--sl-spacing-large);
background: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 5%, rgba(0, 0, 0, 0.2) 75%, rgba(0, 0, 0, 0) 100%);
}
.custom-layout::part(pagination-item) {
height: 5px;
width: var(--sl-spacing-large);
border-radius: var(--sl-border-radius-pill);
background-color: #fff;
}
.custom-layout::part(pagination-item--active) {
background-color: var(--sl-color-primary-400);
width: var(--sl-spacing-x-large);
}
.custom-layout::part(navigation-button) {
margin: var(--sl-spacing-large);
border-radius: var(--sl-border-radius-circle);
font-weight: var(--sl-font-weight-bold);
color: var(--sl-color-neutral-1000);
background: var(--sl-color-neutral-0);
opacity: 0.6;
transition: var(--sl-transition-medium) opacity;
}
.custom-layout::part(navigation-button):focus,
.custom-layout::part(navigation-button):hover {
opacity: 1;
}
</style>
```
```jsx react
import { SlCarousel, SlCarouselItem } from '@shoelace-style/shoelace/dist/react';
const css = `
.custom-layout::part(base) {
grid-template-areas:
'slides slides slides'
'slides slides slides';
}
.custom-layout::part(pagination) {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: var(--sl-spacing-large);
background: linear-gradient(0deg, rgba(0, 0, 0, 0.8) 5%, rgba(0, 0, 0, 0.2) 75%, rgba(0, 0, 0, 0) 100%);
}
.custom-layout::part(pagination-item) {
height: 5px;
width: var(--sl-spacing-large);
border-radius: var(--sl-border-radius-pill);
background-color: #fff;
}
.custom-layout::part(pagination-item--active) {
background-color: var(--sl-color-primary-400);
width: var(--sl-spacing-x-large);
}
.custom-layout::part(navigation-button) {
margin: var(--sl-spacing-large);
border-radius: var(--sl-border-radius-circle);
font-weight: var(--sl-font-weight-bold);
color: var(--sl-color-neutral-1000);
background: var(--sl-color-neutral-0);
opacity: 0.6;
transition: var(--sl-transition-medium) opacity;
}
.custom-layout::part(navigation-button):focus,
.custom-layout::part(navigation-button):hover {
opacity: 1;
}
`;
const App = () => (
<>
<SlCarousel className="custom-layout" pagination navigation>
<SlCarouselItem>
<img
alt="The sun shines on the mountains and trees - Photo by Adam Kool on Unsplash"
src="/assets/examples/carousel/mountains.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A waterfall in the middle of a forest - Photo by Thomas Kelly on Unsplash"
src="/assets/examples/carousel/waterfall.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="The sun is setting over a lavender field - Photo by Leonard Cotte on Unsplash"
src="/assets/examples/carousel/sunset.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A field of grass with the sun setting in the background - Photo by Sapan Patel on Unsplash"
src="/assets/examples/carousel/field.jpg"
/>
</SlCarouselItem>
<SlCarouselItem>
<img
alt="A scenic view of a mountain with clouds rolling in - Photo by V2osk on Unsplash"
src="/assets/examples/carousel/valley.jpg"
/>
</SlCarouselItem>
<SlIcon name="arrow-right" slot="next-icon"></SlIcon>
<SlIcon name="arrow-left" slot="previous-icon"></SlIcon>
</SlCarousel>
<style>{css}</style>
</>
);
```
### Gallery example
The carousel has a set of API with which is possible to interact programmatically, for example it is possible to
use `next()` or `previous()` to go respectively to the next or the previous slide.
When the active slide is changed, the `sl-slide-change` event is emitted providing the `index` of the slide.
Using the API is possible to extend the carousel, for example by syncing the active slide with a set of thumbnails, like in the example below.
```html preview ```html preview
<sl-carousel class="carousel-thumbnails" navigation loop> <sl-carousel class="carousel-thumbnails" navigation loop>
@ -972,6 +823,7 @@ Using the API is possible to extend the carousel, for example by syncing the act
/> />
</sl-carousel-item> </sl-carousel-item>
</sl-carousel> </sl-carousel>
<div class="thumbnails"> <div class="thumbnails">
<div class="thumbnails__scroller"> <div class="thumbnails__scroller">
<img alt="Thumbnail Photo by 1" class="thumbnails__image active" src="/assets/examples/carousel/mountains.jpg" /> <img alt="Thumbnail Photo by 1" class="thumbnails__image active" src="/assets/examples/carousel/mountains.jpg" />
@ -981,6 +833,7 @@ Using the API is possible to extend the carousel, for example by syncing the act
<img alt="Thumbnail Photo by 5" class="thumbnails__image" src="/assets/examples/carousel/valley.jpg" /> <img alt="Thumbnail Photo by 5" class="thumbnails__image" src="/assets/examples/carousel/valley.jpg" />
</div> </div>
</div> </div>
<style> <style>
.carousel-thumbnails { .carousel-thumbnails {
--slide-aspect-ratio: 3 / 2; --slide-aspect-ratio: 3 / 2;
@ -1020,6 +873,7 @@ Using the API is possible to extend the carousel, for example by syncing the act
opacity: 1; opacity: 1;
} }
</style> </style>
<script> <script>
{ {
const carousel = document.querySelector('.carousel-thumbnails'); const carousel = document.querySelector('.carousel-thumbnails');

Wyświetl plik

@ -9,25 +9,20 @@ export default css`
--slide-gap: var(--sl-spacing-medium, 1rem); --slide-gap: var(--sl-spacing-medium, 1rem);
--aspect-ratio: 16 / 9; --aspect-ratio: 16 / 9;
--scroll-padding: 0px; --scroll-hint: 0px;
} }
.carousel { .carousel {
min-height: 100%;
min-width: 100%;
display: grid; display: grid;
gap: var(--sl-spacing-medium);
grid-template-columns: min-content 1fr min-content; grid-template-columns: min-content 1fr min-content;
grid-template-rows: 1fr min-content; grid-template-rows: 1fr min-content;
grid-template-areas: grid-template-areas:
'. slides .' '. slides .'
'. pagination .'; '. pagination .';
gap: var(--sl-spacing-medium);
align-items: center; align-items: center;
min-height: 100%;
min-width: 100%;
position: relative; position: relative;
} }
@ -40,19 +35,16 @@ export default css`
} }
.carousel__slides { .carousel__slides {
height: 100%;
width: 100%;
grid-area: slides; grid-area: slides;
display: grid; display: grid;
height: 100%;
width: 100%;
align-items: center; align-items: center;
justify-items: center; justify-items: center;
overflow: auto; overflow: auto;
overscroll-behavior-x: contain; overscroll-behavior-x: contain;
scrollbar-width: none; scrollbar-width: none;
aspect-ratio: calc(var(--aspect-ratio) * var(--slides-per-page)); aspect-ratio: calc(var(--aspect-ratio) * var(--slides-per-page));
--slide-size: calc((100% - (var(--slides-per-page) - 1) * var(--slide-gap)) / var(--slides-per-page)); --slide-size: calc((100% - (var(--slides-per-page) - 1) * var(--slide-gap)) / var(--slides-per-page));
@ -69,11 +61,9 @@ export default css`
grid-auto-columns: var(--slide-size); grid-auto-columns: var(--slide-size);
grid-auto-rows: 100%; grid-auto-rows: 100%;
column-gap: var(--slide-gap); column-gap: var(--slide-gap);
scroll-snap-type: x mandatory; scroll-snap-type: x mandatory;
scroll-padding-inline: var(--scroll-hint);
scroll-padding-inline: var(--scroll-padding); padding-inline: var(--scroll-hint);
padding-inline: var(--scroll-padding);
} }
.carousel__slides--vertical { .carousel__slides--vertical {
@ -81,11 +71,9 @@ export default css`
grid-auto-columns: 100%; grid-auto-columns: 100%;
grid-auto-rows: var(--slide-size); grid-auto-rows: var(--slide-size);
row-gap: var(--slide-gap); row-gap: var(--slide-gap);
scroll-snap-type: y mandatory; scroll-snap-type: y mandatory;
scroll-padding-block: var(--scroll-hint);
scroll-padding-block: var(--scroll-padding); padding-block: var(--scroll-hint);
padding-block: var(--scroll-padding);
} }
.carousel__slides--dragging, .carousel__slides--dragging,
@ -104,7 +92,6 @@ export default css`
.carousel__navigation { .carousel__navigation {
grid-area: navigation; grid-area: navigation;
display: contents; display: contents;
font-size: var(--sl-font-size-x-large); font-size: var(--sl-font-size-x-large);
} }

Wyświetl plik

@ -17,7 +17,7 @@ import styles from './carousel.styles';
import type { CSSResultGroup } from 'lit'; import type { CSSResultGroup } from 'lit';
/** /**
* @summary Carousels display an arbitrary number of slides along a horizontal or vertical axis. * @summary Carousels display an arbitrary number of content slides along a horizontal or vertical axis.
* *
* @since 2.0 * @since 2.0
* @status experimental * @status experimental
@ -42,7 +42,7 @@ import type { CSSResultGroup } from 'lit';
* *
* @cssproperty --slide-gap - The space between each slide. * @cssproperty --slide-gap - The space between each slide.
* @cssproperty --aspect-ratio - The aspect ratio of each slide. * @cssproperty --aspect-ratio - The aspect ratio of each slide.
* @cssproperty --scroll-padding - The amount of padding to apply to the scroll area. Useful to make adjacent slides * @cssproperty --scroll-hint - The amount of padding to apply to the scroll area. Useful to make adjacent slides
* visible. * visible.
*/ */
@customElement('sl-carousel') @customElement('sl-carousel')
@ -320,22 +320,17 @@ export default class SlCarousel extends ShoelaceElement {
${map(range(pagesCount), index => { ${map(range(pagesCount), index => {
const isActive = index === currentPage; const isActive = index === currentPage;
return html` return html`
<span role="presentation"> <button
<button part="pagination-item ${isActive ? 'pagination-item--active' : ''}"
@click="${() => this.goToSlide(index * slidesPerPage)}" class="${classMap({
aria-selected="${isActive ? 'true' : 'false'}" 'carousel__pagination-item': true,
aria-label="${this.localize.term('goToCarouselSlide', index + 1, pagesCount)}" 'carousel__pagination-item--active': isActive
role="tab" })}"
part=" aria-selected="${isActive ? 'true' : 'false'}"
pagination-item aria-label="${this.localize.term('goToCarouselSlide', index + 1, pagesCount)}"
${isActive ? 'pagination-item--active' : ''} role="tab"
" @click="${() => this.goToSlide(index * slidesPerPage)}"
class="${classMap({ ></button>
'carousel__pagination-item': true,
'carousel__pagination-item--active': isActive
})}"
></button>
</span>
`; `;
})} })}
</nav> </nav>