makes SvelteScroller, updates docs

This commit is contained in:
MinamiFunakoshiTR 2025-05-20 12:26:03 -07:00
parent ee205b81c1
commit 27a89998a0
Failed to extract signature
4 changed files with 80 additions and 385 deletions

View file

@ -117,7 +117,7 @@
top = 0,
bottom = 1,
threshold = 0.5,
query = 'section',
query = 'div.step-foreground-container',
parallax = false,
backgroundSnippet,
foregroundSnippet,

View file

@ -6,274 +6,70 @@ import * as SvelteScrollerStories from './SvelteScroller.stories.svelte';
# SvelteScroller
The `SvelteScroller` component creates a basic scrollytelling graphic with layout options.
The `SvelteScroller` component is the base Scroller component that powers the [`Scroller` component](?path=/story/components-graphics-scroller--docs). It is a Svelte 5 version of the [svelte-scroller](https://github.com/sveltejs/svelte-scroller).
> This component is designed to handle most common layouts for scrollytelling. To make something more complex, customise [ScrollerBase](https://github.com/reuters-graphics/graphics-components/blob/main/src/components/Scroller/ScrollerBase/index.svelte), which is a Svelte 5 version of the [svelte-scroller](https://github.com/sveltejs/svelte-scroller).
This component allows for customisation that the [`Scroller` component](?path=/story/components-graphics-scroller--docs) can't handle.
[Demo](?path=/story/components-graphics-scroller--demo)
> **Important❗:** Make sure each foreground step's container is a div with the class `step-foreground-container`. If you're modifying the class, pass the appropriate selector to the `query` prop.
```svelte
<script>
import { Scroller } from '@reuters-graphics/graphics-components';
import MyBackground from './MyBackground.svelte'; // Your own background component
// Array of step objects that define the steps in your scroller.
const steps = [
{
background: MyBackground,
backgroundProps: { colour: 'red' }, // Optional props for your background component
foreground: '#### Step 1\n\nLorem ipsum red',
altText: 'Red background',
},
{
background: MyBackground,
backgroundProps: { colour: 'blue' },
foreground: '#### Step 2\n\nLorem ipsum blue',
altText: 'Blue background',
},
{
background: MyBackground,
backgroundProps: { colour: 'green' },
foreground: '#### Step 3\n\nLorem ipsum green',
altText: 'Green background',
},
];
</script>
<Scroller {steps} foregroundPosition="middle" backgroundWidth="fluid" />
```
## Using with ArchieML and ai2svelte
[Demo](?path=/story/components-graphics-scroller--archie-ml)
In your graphics kit project, import your ai2svelte graphics in `App.svelte` and add them to the `aiCharts` object:
[Demo](?path=/story/components-graphics-sveltescroller--demo)
```svelte
<!-- App.svelte -->
<script>
import AiMap1 from './ai2svelte/my-map-1.svelte';
import AiMap2 from './ai2svelte/my-map-2.svelte';
import AiMap3 from './ai2svelte/my-map-3.svelte';
import { SvelteScroller } from '@reuters-graphics/graphics-components';
import content from '$locales/en/content.json';
// Graphics kit only
import { assets } from '$app/paths'; // 👈 If using in the graphics kit...
import { truthy } from '$utils/propValidators'; // 👈 If using in the graphics kit...
const aiCharts = {
AiMap1,
AiMap2,
AiMap3,
// Other charts...
};
</script>
```
Then add the following structure to your ArchieML Doc, making sure that the names of your charts in the `aiCharts` object match the names of each step's `background` in the ArchieML doc:
```yaml
# ArchieML doc
[blocks]
type: ai-scroller
id: my-map-scroller
width: fluid
foregroundPosition: right
stackBackground: true
# Array of step objects
[.steps]
background: AiMap1
foreground: #### Step 1
Here's where something happend.
:end
altText: A map showing the Upper West side in New York City.
Can add paragraphs of alt text if you want to break up sentences.
:end
background: AiMap2
foreground: #### Step 2
Something happened on some street...
:end
altText: The same map now highlights 98th Street.
:end
background: AiMap3
foreground: #### Step 3
... and now there are multiple protests.
:end
altText: The same map now highlights three locations near 98th Street where something particulary important happened.
:end
[]
[]
```
Then parse the relevant ArchieML block object before passing to the `Scroller` component.
```svelte
<!-- App.svelte -->
{#each content.blocks as block}
{#if block.type === 'ai-scroller'}
<Scroller
id={block.id}
backgroundWidth={block.width}
foregroundPosition={block.foregroundPosition}
stackBackground={truthy(block.stackBackground)}
steps={block.steps.map((step) => ({
background: aiCharts[step.background],
backgroundProps: { assetsPath: assets || '/' },
foreground: step.foreground,
altText: step.altText,
}))}
/>
{/if}
{/each}
```
> **Note:** Some props, like `stackBackground`, expect boolean values. If you're using the graphics kit, use the `truthy()` util function to convert a string value to a boolean.
> **Note:** In the graphics kit, the image source paths in ai2svelte components have to be fixed by passing `assets` to each step object, like in the example above.
## Custom foreground
[Demo](?path=/story/components-graphics-scroller--custom-foreground)
Instead of just text, you can use components as foregrounds, and optionally pass props to it.
If you're customising your own foreground component, remember to add alt text that describes the background graphic.
```svelte
<script>
import MyBackground from './MyBackground.svelte'; // Your own background component
import MyInteractiveForeground from './MyInteractiveForeground.svelte'; // Your custom foreground component
const steps = [
{
background: MyBackground,
backgroundProps: { colour: 'red' }, // Props for your background component, if needed
foreground: MyInteractiveForeground, // Custom foreground component
},
{
background: MyBackground,
backgroundProps: { colour: 'blue' },
foreground: '#### Step 2\n\nLorem ipsum blue', // You can still add a markdown string as foreground; you can mix and match
},
{
background: MyBackground,
backgroundProps: { colour: 'green' },
foreground: MyInteractiveForeground,
foregroundProps: { count: 100 }, // Props for your custom foreground component, if needed
},
];
// Optional: Bind your own `index`, `progress`, and other bindable variables to use them in your code.
let myIndex = $state(0);
let myProgress = $state(0);
</script>
<Scroller {steps} />
<SvelteScroller
bind:index={myIndex}
bind:progress={myProgress}
query="div.step-foreground-container"
>
{#snippet backgroundSnippet()}
<!-- Add custom backgroud as a snippet -->
<div class="custom-background">
<p>
This is the background content. It will stay fixed in place while the
foreground scrolls over the top.
</p>
</div>
{/snippet}
{#snippet foregroundSnippet()}
<!-- Add custom foreground as a snippet -->
<div class="step-foreground-container flex items-center justify-center">
<p>Index {myIndex}: This is the first section.</p>
</div>
<div class="step-foreground-container flex items-center justify-center">
<p>Index {myIndex}: This is the second section.</p>
</div>
<div class="step-foreground-container flex items-center justify-center">
<p>Index {myIndex}: This is the third section.</p>
</div>
{/snippet}
</SvelteScroller>
```
## Custom foreground with ArchieML
To add your own styling, you can write styles in a global SCSS stylesheet:
[Demo](?path=/story/components-graphics-scroller--customforeground-archie-ml)
```scss
// global.scss
.custom-background {
padding: 20px;
border-radius: 5px;
height: 100vh;
}
You can use custom foreground components with ArchieML with a few additional steps.
.step-foreground-container {
height: 100vh;
p {
padding: 1em;
background-color: rgba(162, 220, 231, 0.5);
}
}
In your graphics kit project's `App.svelte`, import your custom foregroud components and add them to a `foregroundComponents` object, just as you import ai2svelte background graphics and add them to the `aiCharts` object:
```svelte
<!-- App.svelte -->
<script>
import content from '$locales/en/content.json';
// Background ai2svelte graphics
import AiMap1 from './ai2svelte/my-map-1.svelte';
import AiMap2 from './ai2svelte/my-map-2.svelte';
import AiMap3 from './ai2svelte/my-map-3.svelte';
// Foreground components, which can be ai2svelte or not.
import Foreground1 from './ai2svelte/my-foreground-1.svelte';
// Graphics kit only
import { assets } from '$app/paths'; // 👈 If using in the graphics kit...
import { truthy } from '$utils/propValidators'; // 👈 If using in the graphics kit...
// Background ai2svelte graphics components
const aiCharts = {
AiMap1,
AiMap2,
AiMap3,
// Other charts...
};
// Foreground components
const foregroundComponents = {
Foreground1,
// Other components...
};
</script>
```
Then add the following structure to your ArchieML Doc, making sure that the names of your charts in the `aiCharts` and `foregroundComponents` objects match the names of each step's `background` and `foreground` in the ArchieML doc:
```yaml
# ArchieML doc
[blocks]
type: ai-scroller
id: my-map-scroller
foregroundPosition: left
stackBackground: true
# Array of step objects
[.steps]
background: AiMap1
# You can still use a markdown string even if other step/s use a custom foreground component
foreground: #### Step 1
Here's where something happend.
:end
altText: A map showing the Upper West side in New York City.
:end
background: AiMap2
foreground: Foreground1 # The name of your custom foreground component
altText: The same map now highlights 98th Street.
:end
background: AiMap3
foreground: #### Step 3
... and now there are multiple protests.
:end
altText: The same map now highlights three locations near 98th Street where something particulary important happened.
:end
[]
[]
```
Then parse the relevant ArchieML block object before passing to the `Scroller` component.
```svelte
<!-- App.svelte -->
{#each content.blocks as block}
{#if block.type === 'ai-scroller'}
<Scroller
id={block.id}
backgroundWidth={block.width}
foregroundPosition={block.foregroundPosition}
stackBackground={truthy(block.stackBackground)}
steps={block.steps.map((step) => ({
background: aiCharts[step.background],
backgroundProps: { assetsPath: assets || '/' },
foreground: foregroundComponents[step.foreground] || step.foreground,
foregroundProps: { assetsPath: assets || '/' },
altText: step.altText,
}))}
/>
{/if}
{/each}
```
> **Note:** You only need to pass `foregroundProps: { assetsPath: assets || '/' }` in the graphics kit if your foreground components are ai2svelte graphicss.

View file

@ -5,155 +5,54 @@
const { Story } = defineMeta({
title: 'Components/Graphics/SvelteScroller',
component: SvelteScroller,
argTypes: {
steps: { control: false },
backgroundWidth: {
control: 'select',
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
},
foregroundPosition: {
control: 'select',
options: ['middle', 'left', 'right', 'left opposite', 'right opposite'],
},
embeddedLayout: {
control: 'select',
options: ['fb', 'bf'],
},
},
});
</script>
<script lang="ts">
import MyBackground from './demo/components/basic/Step.svelte';
import MyInteractiveForeground from './demo/components/basic/InteractiveForeground.svelte';
// ai2svelte backgrounds
import AiMap1 from './demo/components/ai2svelte/ai-scroller-1.svelte';
import AiMap2 from './demo/components/ai2svelte/ai-scroller-2.svelte';
import AiMap3 from './demo/components/ai2svelte/ai-scroller-3.svelte';
// ai2svelte foreground
import AiForeground from './demo/components/ai2svelte/ai-foreground.svelte';
const aiCharts = {
AiMap1,
AiMap2,
AiMap3,
};
const foregroundComponents = {
AiForeground,
};
const docBlock = {
foregroundPosition: 'right',
id: 'my-scroller',
stackBackground: 'true',
steps: [
{
background: aiCharts.AiMap1,
foreground: "#### Step 1\n\nHere's where something happend.",
altText: 'A map showing the Upper West side in New York City.',
},
{
background: aiCharts.AiMap2,
foreground: '#### Step 2\n\nSomething happened on some street...',
altText: 'The same map now highlights 98th Street.',
},
{
background: aiCharts.AiMap3,
foreground: '#### Step 3\n\n... and now there are multiple protests.',
altText:
'The same map now highlights three locations near 98th Street where something particulary important happened.',
},
],
} as const;
const docBlockCustomForeground = {
foregroundPosition: 'left',
id: 'my-scroller',
stackBackground: 'true',
steps: [
{
background: aiCharts.AiMap1,
foreground: "#### Step 1\n\nHere's where something happend.",
altText: 'A map showing the Upper West side in New York City.',
},
{
background: aiCharts.AiMap2,
foreground: foregroundComponents.AiForeground,
altText: 'The same map now highlights 98th Street.',
},
{
background: aiCharts.AiMap3,
foreground: '#### Step 3\n\n... and now there are multiple protests.',
altText:
'The same map now highlights three locations near 98th Street where something particulary important happened.',
},
],
} as const;
let myIndex = $state(0);
let myProgress = $state(0);
</script>
<Story name="Demo">
<SvelteScroller>
{#snippet backgroundSnippet(index)}
<SvelteScroller
bind:index={myIndex}
bind:progress={myProgress}
query="div.step-foreground-container"
>
{#snippet backgroundSnippet()}
<div class="custom-background">
<p>
This is the background content. It will stay fixed in place while the
foreground scrolls over the top.
</p>
<p>Section {index + 1} is currently active.</p>
</div>
{/snippet}
{#snippet foregroundSnippet(index)}
<div class="foreground">Index {index}: This is the first section.</div>
<div class="foreground">Index {index}: This is the second section.</div>
<div class="foreground">Index {index}: This is the third section.</div>
{#snippet foregroundSnippet()}
<div class="step-foreground-container flex items-center justify-center">
<p>Index {myIndex}: This is the first section.</p>
</div>
<div class="step-foreground-container flex items-center justify-center">
<p>Index {myIndex}: This is the second section.</p>
</div>
<div class="step-foreground-container flex items-center justify-center">
<p>Index {myIndex}: This is the third section.</p>
</div>
{/snippet}
</SvelteScroller>
</Story>
<!--
<Story name="ArchieML and ai2svelte" exportName="ArchieML">
<SvelteScroller
id={docBlock.id}
foregroundPosition={docBlock.foregroundPosition}
stackBackground={docBlock.stackBackground === 'true'}
steps={docBlock.steps.map((step) => ({
background: step.background,
foreground: step.foreground,
altText: step.altText,
}))}
/>
</Story> -->
<!-- <Story
name="Custom foreground with ArchiemL"
exportName="CustomforegroundArchieML"
>
<SvelteScroller
id={docBlockCustomForeground.id}
foregroundPosition={docBlockCustomForeground.foregroundPosition}
stackBackground={docBlockCustomForeground.stackBackground === 'true'}
steps={docBlockCustomForeground.steps.map((step) => ({
background: step.background,
foreground: step.foreground,
altText: step.altText,
}))}
/>
</Story> -->
<style lang="scss">
.custom-background {
padding: 20px;
border-radius: 5px;
height: 100vh;
}
.foreground {
height: 80vh;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 1em;
margin: 0 0 2em 0;
.step-foreground-container {
height: 100vh;
p {
padding: 1em;
background-color: rgba(162, 220, 231, 0.5);
}
}
</style>

View file

@ -117,7 +117,7 @@
top = 0,
bottom = 1,
threshold = 0.5,
query = 'section',
query = 'div.step-foreground-container',
parallax = false,
backgroundSnippet,
foregroundSnippet,
@ -211,7 +211,7 @@
}
</script>
<svelte:window bind:setinnerHeight={wh} />
<svelte:window bind:innerHeight={wh} />
<svelte-scroller-outer bind:this={outer}>
<svelte-scroller-background-container
@ -219,12 +219,12 @@
style="{style}{widthStyle}"
>
<svelte-scroller-background bind:this={background}>
{@render backgroundSnippet(index)}
{@render backgroundSnippet()}
</svelte-scroller-background>
</svelte-scroller-background-container>
<svelte-scroller-foreground bind:this={foreground}>
{@render foregroundSnippet(index)}
{@render foregroundSnippet()}
</svelte-scroller-foreground>
</svelte-scroller-outer>