adds svelte 5 version of the official scroller demo
This commit is contained in:
parent
217780d785
commit
a44163ded4
6 changed files with 224 additions and 95 deletions
|
|
@ -10,19 +10,18 @@
|
|||
|
||||
let { index, steps, preload = 1, stackBackground = true }: Props = $props();
|
||||
|
||||
function shouldShowStep(i:number) {
|
||||
function shouldShowStep(i: number) {
|
||||
if (preload === 0) return true;
|
||||
if (stackBackground) return i <= index;
|
||||
return i >= index - preload && i <= index + preload;
|
||||
}
|
||||
|
||||
function isStepVisible(i:number) {
|
||||
function isStepVisible(i: number) {
|
||||
if (stackBackground) return i <= index;
|
||||
return i === index;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
{#each steps as step, i}
|
||||
{#if shouldShowStep(i)}
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -6,68 +6,77 @@ import * as ScrollerBaseStories from './ScrollerBase.stories.svelte';
|
|||
|
||||
# ScrollerBase
|
||||
|
||||
The `ScrollerBase` component powers the [`Scroller` component](?path=/story/components-graphics-scroller--docs), which creates a basic storytelling graphic with preset layout options. The `ScrollerBase` component contains the bare minimum code necessary for a scrollytelling section, and allows for customisation beyond what the [`Scroller` component](?path=/story/components-graphics-scroller--docs) allows.
|
||||
The `ScrollerBase` component powers the [`Scroller` component](?path=/story/components-graphics-scroller--docs), which creates a basic storytelling graphic with preset layout options. `ScrollerBase` contains the bare minimum code necessary for a scrollytelling section, and allows for customisation beyond what the [`Scroller` component](?path=/story/components-graphics-scroller--docs) allows.
|
||||
|
||||
`ScrollerBase` is a Svelte 5 version of the [svelte-scroller](https://github.com/sveltejs/svelte-scroller).
|
||||
|
||||
> **Important❗:** Make sure the HTML element containing each foreground is a div with the class `step-foreground-container`. If you're modifying this to something else, pass the appropriate selector to the `query` prop.
|
||||
|
||||
[Demo](?path=/story/components-graphics-scrollerbase--demo)
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { ScrollerBase } from '@reuters-graphics/graphics-components';
|
||||
|
||||
// Optional: Bind your own `index`, `progress`, and other bindable variables to use them in your code.
|
||||
let myIndex = $state(0);
|
||||
let myProgress = $state(0);
|
||||
// Optional: Bind your own variables to use them in your code.
|
||||
let count = $state(1);
|
||||
let index = $state(0);
|
||||
let offset = $state(0);
|
||||
let progress = $state(0);
|
||||
let top = $state(0.1);
|
||||
let threshold = $state(0.5);
|
||||
let bottom = $state(0.9);
|
||||
</script>
|
||||
|
||||
<ScrollerBase
|
||||
bind:index={myIndex}
|
||||
bind:progress={myProgress}
|
||||
{top}
|
||||
{threshold}
|
||||
{bottom}
|
||||
bind:count
|
||||
bind:index
|
||||
bind:offset
|
||||
bind:progress
|
||||
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>
|
||||
<!-- Add custom background HTML or component -->
|
||||
<p class="mb-0">
|
||||
Current step: <strong>{index + 1}/{count}</strong>
|
||||
</p>
|
||||
<progress class="mb-4" value={(index + 1) / count}></progress>
|
||||
|
||||
<p class="mb-0">Offset in current step</p>
|
||||
<progress class="mb-4" value={offset}></progress>
|
||||
|
||||
<p class="mb-0">Total progress</p>
|
||||
<progress class="mb-4" value={progress}></progress>
|
||||
{/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>
|
||||
<!-- Add custom foreground HTML or component -->
|
||||
<div class="step-foreground-container">Step 1</div>
|
||||
<div class="step-foreground-container">Step 2</div>
|
||||
<div class="step-foreground-container">Step 3</div>
|
||||
<div class="step-foreground-container">Step 4</div>
|
||||
<div class="step-foreground-container">Step 5</div>
|
||||
{/snippet}
|
||||
</ScrollerBase>
|
||||
```
|
||||
|
||||
> **Important❗:** Make sure the HTML element containing each foreground is a div with the class `step-foreground-container`. If you're modifying this to something else, pass the appropriate selector to the `query` prop.
|
||||
<style lang="scss">
|
||||
@use '@reuters-graphics/graphics-components/dist/scss/mixins' as mixins;
|
||||
|
||||
To add your own styling, you can write styles in a global SCSS stylesheet:
|
||||
|
||||
```scss
|
||||
// global.scss
|
||||
.custom-background {
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.step-foreground-container {
|
||||
height: 100vh;
|
||||
p {
|
||||
padding: 1em;
|
||||
background-color: rgba(162, 220, 231, 0.5);
|
||||
.scroller-demo-container {
|
||||
width: mixins.$column-width-normal;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.step-foreground-container {
|
||||
height: 100vh;
|
||||
width: 50%;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 1em;
|
||||
margin: 0 0 2em 0;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script module lang="ts">
|
||||
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||||
import ScrollerBase from './ScrollerBase.svelte';
|
||||
import ScrollerDemo from './demo/ScrollerDemo.svelte';
|
||||
|
||||
const { Story } = defineMeta({
|
||||
title: 'Components/Graphics/ScrollerBase',
|
||||
|
|
@ -8,51 +9,4 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
let myIndex = $state(0);
|
||||
let myProgress = $state(0);
|
||||
</script>
|
||||
|
||||
<Story name="Demo">
|
||||
<ScrollerBase
|
||||
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>
|
||||
</div>
|
||||
{/snippet}
|
||||
{#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}
|
||||
</ScrollerBase>
|
||||
</Story>
|
||||
|
||||
<style lang="scss">
|
||||
.custom-background {
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.step-foreground-container {
|
||||
height: 100vh;
|
||||
p {
|
||||
padding: 1em;
|
||||
background-color: rgba(162, 220, 231, 0.5);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<Story name="Demo"><ScrollerDemo /></Story>
|
||||
|
|
|
|||
87
src/components/ScrollerBase/demo/DraggableLabel.svelte
Normal file
87
src/components/ScrollerBase/demo/DraggableLabel.svelte
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<script lang="ts">
|
||||
let { value = $bindable(0), label = 'label' } = $props();
|
||||
let isDragging = false;
|
||||
|
||||
function drag(node, callback) {
|
||||
function handleMousedown(event) {
|
||||
function handleMousemove(event) {
|
||||
event.preventDefault();
|
||||
|
||||
node.dispatchEvent(
|
||||
new CustomEvent('drag', {
|
||||
detail: {
|
||||
value: event.clientY / window.innerHeight,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function handleMouseup(event) {
|
||||
window.removeEventListener('mousemove', handleMousemove);
|
||||
window.removeEventListener('mouseup', handleMouseup);
|
||||
}
|
||||
|
||||
window.addEventListener('mousemove', handleMousemove);
|
||||
window.addEventListener('mouseup', handleMouseup);
|
||||
}
|
||||
|
||||
node.addEventListener('mousedown', handleMousedown);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
node.removeEventListener('mousedown', handleMousedown);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Round to 2 decimal places
|
||||
function round(value) {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="label"
|
||||
style="top: {value * 100}%"
|
||||
use:drag
|
||||
on:drag={(e) => (value = e.detail.value)}
|
||||
>
|
||||
<div class="drag-target"></div>
|
||||
<hr />
|
||||
<p>{label}: {round(value)}</p>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use '../../../scss/mixins' as mixins;
|
||||
|
||||
.label {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 150px;
|
||||
height: 0;
|
||||
cursor: ns-resize;
|
||||
|
||||
.drag-target {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
hr {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: red;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
p {
|
||||
position: absolute;
|
||||
@include mixins.font-sans;
|
||||
@include mixins.font-medium;
|
||||
@include mixins.text-sm;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
src/components/ScrollerBase/demo/ScrollerDemo.svelte
Normal file
80
src/components/ScrollerBase/demo/ScrollerDemo.svelte
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<script lang="ts">
|
||||
import ScrollerBase from '../ScrollerBase.svelte';
|
||||
import DraggableLabel from './DraggableLabel.svelte';
|
||||
import BodyText from '../../BodyText/BodyText.svelte';
|
||||
|
||||
let count = $state(1);
|
||||
let index = $state(0);
|
||||
let offset = $state(0);
|
||||
let progress = $state(0);
|
||||
let top = $state(0.1);
|
||||
let threshold = $state(0.5);
|
||||
let bottom = $state(0.9);
|
||||
</script>
|
||||
|
||||
<BodyText
|
||||
text="Read the documentation on the props `progress`, `top`, `threshold`, `bottom` under **Controls** to understand how they work."
|
||||
/>
|
||||
|
||||
<div class="scroller-demo-container">
|
||||
<ScrollerBase
|
||||
{top}
|
||||
{threshold}
|
||||
{bottom}
|
||||
bind:count
|
||||
bind:index
|
||||
bind:offset
|
||||
bind:progress
|
||||
query="div.step-foreground-container"
|
||||
>
|
||||
{#snippet backgroundSnippet()}
|
||||
<p class="mb-0">
|
||||
Current step: <strong>{index + 1}/{count}</strong>
|
||||
</p>
|
||||
<progress class="mb-4" value={(index + 1) / count}></progress>
|
||||
|
||||
<p class="mb-0">Offset in current step</p>
|
||||
<progress class="mb-4" value={offset}></progress>
|
||||
|
||||
<p class="mb-0">Total progress</p>
|
||||
<progress class="mb-4" value={progress}></progress>
|
||||
{/snippet}
|
||||
{#snippet foregroundSnippet()}
|
||||
<div class="step-foreground-container font-medium">Step 1</div>
|
||||
<div class="step-foreground-container font-medium">Step 2</div>
|
||||
<div class="step-foreground-container font-medium">Step 3</div>
|
||||
<div class="step-foreground-container font-medium">Step 4</div>
|
||||
<div class="step-foreground-container font-medium">Step 5</div>
|
||||
{/snippet}
|
||||
</ScrollerBase>
|
||||
</div>
|
||||
|
||||
<BodyText
|
||||
text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
"
|
||||
/>
|
||||
|
||||
<DraggableLabel bind:value={top} label="top" />
|
||||
<DraggableLabel bind:value={threshold} label="threshold" />
|
||||
<DraggableLabel bind:value={bottom} label="bottom" />
|
||||
|
||||
<style lang="scss">
|
||||
@use '../../../scss/mixins' as mixins;
|
||||
|
||||
.scroller-demo-container {
|
||||
width: mixins.$column-width-normal;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.step-foreground-container {
|
||||
height: 100vh;
|
||||
width: 50%;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 1em;
|
||||
margin: 0 0 2em 0;
|
||||
|
||||
// Make it align to the right
|
||||
position: relative;
|
||||
left: 50%;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in a new issue