Merge pull request #248 from reuters-graphics/mf-photo-carousel
Updates PhotoCarousel
This commit is contained in:
commit
20ab459d57
6 changed files with 183 additions and 41 deletions
|
|
@ -50,17 +50,13 @@ export interface ScrollerStep {
|
|||
}
|
||||
|
||||
export interface PhotoCarouselImage {
|
||||
/**
|
||||
* Image src
|
||||
*/
|
||||
/** Image source */
|
||||
src: string;
|
||||
/**
|
||||
* Image alt text
|
||||
*/
|
||||
/** Image alt text */
|
||||
altText: string;
|
||||
/** Optional caption as a string */
|
||||
/** Optional caption */
|
||||
caption?: string;
|
||||
/** Optional credit as string */
|
||||
/** Optional credit */
|
||||
credit?: string;
|
||||
/** Optional object-fit rule */
|
||||
objectFit?: string;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ text: Bacon ipsum ...
|
|||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
<script>
|
||||
import { BodyText } from '@reuters-graphics/graphics-components';
|
||||
import content from '$locales/en/content.json';
|
||||
</script>
|
||||
|
||||
{#each content.blocks as block}
|
||||
{#if block.type === 'text'}
|
||||
<BodyText text={block.text} />
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import * as PhotoCarouselStories from './PhotoCarousel.stories.svelte';
|
|||
|
||||
# PhotoCarousel
|
||||
|
||||
The `PhotoCarousel` component creates a simple, accessible photo carousel with lazy-loading and mobile swipe built in.
|
||||
The `PhotoCarousel` component creates a simple and accessible photo carousel with built-in lazy-loading and mobile swipe.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
|
|
@ -21,7 +21,6 @@ The `PhotoCarousel` component creates a simple, accessible photo carousel with l
|
|||
objectFit: 'contain', // Optional
|
||||
objectPosition: '50% 50%', // Optional
|
||||
},
|
||||
// ...
|
||||
];
|
||||
</script>
|
||||
|
||||
|
|
@ -30,16 +29,82 @@ The `PhotoCarousel` component creates a simple, accessible photo carousel with l
|
|||
|
||||
<Canvas of={PhotoCarouselStories.Demo} />
|
||||
|
||||
## Custom text
|
||||
## Using with ArchieML docs
|
||||
|
||||
Use named slots to style your own custom credits and/or captions.
|
||||
With the Graphics Kit, you'll likely get your text value from an ArchieML doc...
|
||||
|
||||
```yaml
|
||||
# ArchieML doc
|
||||
[blocks]
|
||||
type: photo-carousel
|
||||
|
||||
# List of photo data
|
||||
[.images]
|
||||
# Photo 1
|
||||
src: 'images/myImage.jpg', # The source path can be a URL or a local path
|
||||
altText: 'A picture of...',
|
||||
caption: 'My caption...', // Optional
|
||||
credit: 'REUTERS/Jane Doe', // Optional
|
||||
objectFit: 'contain', // Optional
|
||||
objectPosition: '50% 50%', // Optional
|
||||
|
||||
# Photo 2
|
||||
src: 'images/myImage2.jpg',
|
||||
altText: 'A picture of...',
|
||||
caption: 'My caption...', // Optional
|
||||
credit: 'REUTERS/Jane Doe', // Optional
|
||||
objectFit: 'contain', // Optional
|
||||
objectPosition: '50% 50%', // Optional
|
||||
...
|
||||
[]
|
||||
[]
|
||||
```
|
||||
|
||||
... which you'll parse out of a ArchieML block object before passing to the `PhotoCarousel` component.
|
||||
|
||||
> **Note:** If you're using local images files stored in your Graphics Kit project, prefix `assets` to the image source path, as in the example below.
|
||||
|
||||
```svelte
|
||||
<PhotoCarousel {photos}>
|
||||
<p slot="credit" class="custom-credit" let:credit>{credit}</p>
|
||||
<p slot="caption" class="custom-caption" let:caption>{caption}</p>
|
||||
<!-- App.svelte -->
|
||||
<script>
|
||||
import { PhotoCarousel } from '@reuters-graphics/graphics-components';
|
||||
import { assets } from '$app/paths'; // 👈 If using in the Graphics Kit...
|
||||
|
||||
import content from '$locales/en/content.json';
|
||||
</script>
|
||||
|
||||
{#each content.blocks as block}
|
||||
{#if block.type === 'photo-carousel'}
|
||||
<PhotoCarousel
|
||||
images={block.images.map((img) => ({
|
||||
...img,
|
||||
src: `${assets}/${img.src}`,
|
||||
}))}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
```
|
||||
|
||||
<Canvas of={PhotoCarouselStories.Demo} />
|
||||
|
||||
## Custom text
|
||||
|
||||
To customise the credit and/or caption style, use the `credit` and `caption` [snippets](https://svelte.dev/docs/svelte/snippet) and pass `photo` as an argument.
|
||||
|
||||
```svelte
|
||||
<PhotoCarousel {images}>
|
||||
<!-- Pass `image` and use the `image.credit` string in the HTML -->
|
||||
{#snippet credit(image)}
|
||||
<p class="custom-credit">{image.credit}</p>
|
||||
{/snippet}
|
||||
|
||||
<!-- Pass `image` and use the `image.caption` string in the HTML -->
|
||||
{#snippet caption(image)}
|
||||
<p class="custom-caption">{image.caption}</p>
|
||||
{/snippet}
|
||||
</PhotoCarousel>
|
||||
|
||||
<!-- Customise credit and caption styles -->
|
||||
<style lang="scss">
|
||||
p {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -15,26 +15,25 @@
|
|||
</script>
|
||||
|
||||
<script>
|
||||
import photosJson from './demo/photos.json';
|
||||
// import { PhotoCarouselImage } from '../@types/global';
|
||||
import imagesJson from './demo/images.json';
|
||||
|
||||
const photos = photosJson.map((p) => ({ ...p, altText: p.caption }));
|
||||
const images = imagesJson.map((img) => ({ ...img, altText: img.caption }));
|
||||
</script>
|
||||
|
||||
<Story
|
||||
name="Demo"
|
||||
args={{
|
||||
photos,
|
||||
images,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story name="Custom text" exportName="CustomText">
|
||||
<PhotoCarousel {photos}>
|
||||
{#snippet credit(photo)}
|
||||
<p class="custom-credit">{photo.credit}</p>
|
||||
<PhotoCarousel {images}>
|
||||
{#snippet credit(image)}
|
||||
<p class="custom-credit">{image.credit}</p>
|
||||
{/snippet}
|
||||
{#snippet caption(photo)}
|
||||
<p class="custom-caption">{photo.caption}</p>
|
||||
{#snippet caption(image)}
|
||||
<p class="custom-caption">{image.caption}</p>
|
||||
{/snippet}
|
||||
</PhotoCarousel>
|
||||
</Story>
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@
|
|||
type ObjectFit = 'cover' | 'contain';
|
||||
|
||||
interface Props {
|
||||
/** Array of photos. */
|
||||
photos: PhotoCarouselImage[];
|
||||
/** Array of images. */
|
||||
images: PhotoCarouselImage[];
|
||||
/** Width of the component within the text well: normal, wide, wider, widest, fluid */
|
||||
width?: ContainerWidth;
|
||||
/**
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
textWidth = 'normal',
|
||||
id = '',
|
||||
cls = '',
|
||||
photos,
|
||||
images,
|
||||
maxHeight = 660,
|
||||
defaultImageObjectFit = 'cover',
|
||||
defaultImageObjectPosition = 'center center',
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
>
|
||||
<div class="image-container">
|
||||
<SplideTrack>
|
||||
{#each photos as photo}
|
||||
{#each images as image}
|
||||
<SplideSlide>
|
||||
<div class="photo-slide w-full h-full relative">
|
||||
<figure
|
||||
|
|
@ -112,21 +112,23 @@
|
|||
>
|
||||
<img
|
||||
class="w-full h-full fmy-0"
|
||||
data-splide-lazy={photo.src}
|
||||
alt={photo.altText}
|
||||
style:object-fit={photo.objectFit || defaultImageObjectFit}
|
||||
style:object-position={photo.objectPosition ||
|
||||
data-splide-lazy={image.src}
|
||||
alt={image.altText}
|
||||
style:object-fit={image.objectFit || defaultImageObjectFit}
|
||||
style:object-position={image.objectPosition ||
|
||||
defaultImageObjectPosition}
|
||||
/>
|
||||
{#if credit}
|
||||
<!-- Render custom credit snippet -->
|
||||
{@render credit(photo)}
|
||||
{:else if photo.credit}
|
||||
<!-- Render custom credit if credit snippet and string both exist -->
|
||||
{#if credit && image.credit}
|
||||
{@render credit(image)}
|
||||
|
||||
<!-- Otherwise, render with default credit style -->
|
||||
{:else if image.credit}
|
||||
<span
|
||||
class="credit absolute fmb-1 fml-1 leading-tighter font-note text-xxs"
|
||||
class:contain-fit={photo.objectFit === 'contain' ||
|
||||
class:contain-fit={image.objectFit === 'contain' ||
|
||||
defaultImageObjectFit === 'contain'}
|
||||
>{photo.credit}</span
|
||||
>{image.credit}</span
|
||||
>
|
||||
{/if}
|
||||
</figure>
|
||||
|
|
@ -135,15 +137,18 @@
|
|||
{/each}
|
||||
</SplideTrack>
|
||||
|
||||
{#if photos[activeImageIndex].caption}
|
||||
{@const activePhoto = photos[activeImageIndex]}
|
||||
{#if images[activeImageIndex].caption}
|
||||
{@const activePhoto = images[activeImageIndex]}
|
||||
<PaddingReset containerIsFluid={width === 'fluid'}>
|
||||
<Block width={textWidth}>
|
||||
<!-- Render custom caption if caption snippet and string both exist -->
|
||||
{#if caption && activePhoto.caption}
|
||||
{#key activeImageIndex}
|
||||
{@render caption(activePhoto)}
|
||||
{/key}
|
||||
{:else if typeof activePhoto.caption}
|
||||
|
||||
<!-- Otherwise, render with default caption style -->
|
||||
{:else if activePhoto.caption}
|
||||
{#key activeImageIndex}
|
||||
<p
|
||||
class="caption body-caption text-center"
|
||||
|
|
|
|||
72
src/components/PhotoCarousel/demo/images.json
Normal file
72
src/components/PhotoCarousel/demo/images.json
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
[
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T194630Z_544493697_UP1E.jpeg",
|
||||
"caption": "Spain's Sergio Busquets and Aymeric Laporte react before a Germany goal is disallowed following a VAR review.",
|
||||
"credit": "REUTERS/Molly Darlington"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T194619Z_2007900040_UP1.jpeg",
|
||||
"caption": "Spain's Sergio Busquets fouls Germany's Jamal Musiala before being shown yellow card.",
|
||||
"credit": "REUTERS/Kai Pfaffenbach"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T194619Z_635809122_UP1E.jpeg",
|
||||
"caption": "Spain's Sergio Busquets is shown a yellow card by referee Danny Desmond Makkelie.",
|
||||
"credit": "REUTERS/Albert Gea"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T191015Z_1293757566_UP1.jpeg",
|
||||
"caption": "Spain's Sergio Busquets in action with Germany's Thomas Muller.",
|
||||
"credit": "REUTERS/John Sibley"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T181411Z_1_MTZXEIBR0QNN.jpeg",
|
||||
"caption": "Spain fans inside the stadium before the match.",
|
||||
"credit": "REUTERS/Albert Gea"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T194827Z_345059331_UP1E.jpeg",
|
||||
"caption": "Spain's Gavi.",
|
||||
"credit": "REUTERS/Fabrizio Bensch"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T175149Z_1_MTZXEIBR0PMD.jpeg",
|
||||
"caption": "",
|
||||
"credit": "REUTERS/John Sibley"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T203232Z_890709671_UP1E.jpeg",
|
||||
"caption": "Spain's Alvaro Morata scores their first goal.",
|
||||
"credit": "REUTERS/Kai Pfaffenbach"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T203612Z_1399473226_UP1.jpeg",
|
||||
"caption": "Spain's Alvaro Morata celebrates scoring their first goal.",
|
||||
"credit": "REUTERS/Molly Darlington"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T204305Z_1795686896_UP1.jpeg",
|
||||
"caption": "Germany's Niclas Fullkrug scores their first goal.",
|
||||
"credit": "REUTERS/Molly Darlington"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T204528Z_151067034_UP1E.jpeg",
|
||||
"caption": "Germany's Niclas Fullkrug celebrates scoring their first goal.",
|
||||
"credit": "REUTERS/Molly Darlington"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T205041Z_2076149593_UP1.jpeg",
|
||||
"caption": "Spain coach Luis Enrique.",
|
||||
"credit": "REUTERS/John Sibley"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T205604Z_1468073277_UP1.jpeg",
|
||||
"caption": "Germany's Manuel Neuer applauds fans after the match.",
|
||||
"credit": "REUTERS/Kai Pfaffenbach"
|
||||
},
|
||||
{
|
||||
"src": "https://graphics.thomsonreuters.com/cdn/django-tools/media/graphics-gallery/galleries/world-cup-2022/spain-germany-11-27/2022-11-27T205854Z_408619749_UP1E.jpeg",
|
||||
"caption": "Spain players applaud fans after the match.",
|
||||
"credit": "REUTERS/Albert Gea"
|
||||
}
|
||||
]
|
||||
Loading…
Reference in a new issue