@reuters-graphics/graphics-components 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,16 +9,25 @@
9
9
  }
10
10
 
11
11
  let { index, steps, preload = 1, stackBackground = true }: Props = $props();
12
+
13
+ function showStep(i: number) {
14
+ if (preload === 0) return true;
15
+ if (stackBackground) return i >= 0;
16
+ return i >= index - preload && i <= index + preload;
17
+ }
18
+
19
+ function isVisible(i: number) {
20
+ if (stackBackground) return i <= index;
21
+ return i === index;
22
+ }
12
23
  </script>
13
24
 
14
25
  {#each steps as step, i}
15
- <!-- Load the step(s) before and after the active one, only -->
16
- <!-- Unless stackBackground is true. If so, keep all steps before the current one loaded. -->
17
- {#if preload === 0 || (i >= (stackBackground ? 0 : index - preload) && i <= index + preload)}
26
+ {#if showStep(i)}
18
27
  <div
19
- class="step-background step-{i + 1} w-full absolute"
20
- class:visible={stackBackground ? i <= index : i === index}
21
- class:invisible={stackBackground ? i > index : i !== index}
28
+ class={`step step-${i + 1} w-full absolute`}
29
+ class:visible={isVisible(i)}
30
+ class:invisible={!isVisible(i)}
22
31
  >
23
32
  <step.background {...step.backgroundProps || {}}></step.background>
24
33
  </div>
@@ -8,7 +8,7 @@ import * as ScrollerStories from './Scroller.stories.svelte';
8
8
 
9
9
  The `Scroller` component creates a basic scrollytelling graphic with layout options.
10
10
 
11
- > 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).
11
+ This component is designed to handle most common layouts for scrollytelling. To make something more complex, customise [ScrollerBase](?path=/story/components-graphics-scrollerbase--docs), which is a Svelte 5 version of the [svelte-scroller](https://github.com/sveltejs/svelte-scroller).
12
12
 
13
13
  [Demo](?path=/story/components-graphics-scroller--demo)
14
14
 
@@ -1,6 +1,6 @@
1
1
  <!-- @component `Scroller` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-graphics-scroller--docs) -->
2
2
  <script lang="ts">
3
- import ScrollerBase from './ScrollerBase/index.svelte';
3
+ import ScrollerBase from '../ScrollerBase/ScrollerBase.svelte';
4
4
  import Background from './Background.svelte';
5
5
  import Foreground from './Foreground.svelte';
6
6
  import Embedded from './Embedded/index.svelte';
@@ -0,0 +1,82 @@
1
+ import { Meta } from '@storybook/blocks';
2
+
3
+ import * as ScrollerBaseStories from './ScrollerBase.stories.svelte';
4
+
5
+ <Meta of={ScrollerBaseStories} />
6
+
7
+ # ScrollerBase
8
+
9
+ 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.
10
+
11
+ `ScrollerBase` is a Svelte 5 version of the [svelte-scroller](https://github.com/sveltejs/svelte-scroller).
12
+
13
+ > **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.
14
+
15
+ [Demo](?path=/story/components-graphics-scrollerbase--demo)
16
+
17
+ ```svelte
18
+ <script lang="ts">
19
+ import { ScrollerBase } from '@reuters-graphics/graphics-components';
20
+
21
+ // Optional: Bind your own variables to use them in your code.
22
+ let count = $state(1);
23
+ let index = $state(0);
24
+ let offset = $state(0);
25
+ let progress = $state(0);
26
+ let top = $state(0.1);
27
+ let threshold = $state(0.5);
28
+ let bottom = $state(0.9);
29
+ </script>
30
+
31
+ <ScrollerBase
32
+ {top}
33
+ {threshold}
34
+ {bottom}
35
+ bind:count
36
+ bind:index
37
+ bind:offset
38
+ bind:progress
39
+ query="div.step-foreground-container"
40
+ >
41
+ {#snippet backgroundSnippet()}
42
+ <!-- Add custom background HTML or component -->
43
+ <p class="mb-0">
44
+ Current step: <strong>{index + 1}/{count}</strong>
45
+ </p>
46
+ <progress class="mb-4" value={(index + 1) / count}></progress>
47
+
48
+ <p class="mb-0">Offset in current step</p>
49
+ <progress class="mb-4" value={offset}></progress>
50
+
51
+ <p class="mb-0">Total progress</p>
52
+ <progress class="mb-4" value={progress}></progress>
53
+ {/snippet}
54
+ {#snippet foregroundSnippet()}
55
+ <!-- Add custom foreground HTML or component -->
56
+ <div class="step-foreground-container">Step 1</div>
57
+ <div class="step-foreground-container">Step 2</div>
58
+ <div class="step-foreground-container">Step 3</div>
59
+ <div class="step-foreground-container">Step 4</div>
60
+ <div class="step-foreground-container">Step 5</div>
61
+ {/snippet}
62
+ </ScrollerBase>
63
+
64
+ <style lang="scss">
65
+ @use '@reuters-graphics/graphics-components/dist/scss/mixins' as mixins;
66
+
67
+ .scroller-demo-container {
68
+ width: mixins.$column-width-normal;
69
+ margin: auto;
70
+ }
71
+
72
+ .step-foreground-container {
73
+ height: 100vh;
74
+ width: 50%;
75
+ background-color: rgba(0, 0, 0, 0.2);
76
+ padding: 1em;
77
+ margin: 0 0 2em 0;
78
+ position: relative;
79
+ left: 50%;
80
+ }
81
+ </style>
82
+ ```
@@ -0,0 +1,12 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from '@storybook/addon-svelte-csf';
3
+ import ScrollerBase from './ScrollerBase.svelte';
4
+ import ScrollerDemo from './demo/ScrollerDemo.svelte';
5
+
6
+ const { Story } = defineMeta({
7
+ title: 'Components/Graphics/ScrollerBase',
8
+ component: ScrollerBase,
9
+ });
10
+ </script>
11
+
12
+ <Story name="Demo"><ScrollerDemo /></Story>
@@ -0,0 +1,19 @@
1
+ import ScrollerBase from './ScrollerBase.svelte';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const ScrollerBase: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type ScrollerBase = InstanceType<typeof ScrollerBase>;
19
+ export default ScrollerBase;
@@ -117,7 +117,7 @@
117
117
  top = 0,
118
118
  bottom = 1,
119
119
  threshold = 0.5,
120
- query = 'section',
120
+ query = 'div.step-foreground-container',
121
121
  parallax = false,
122
122
  backgroundSnippet,
123
123
  foregroundSnippet,
@@ -27,6 +27,6 @@ interface Props {
27
27
  /** Whether the foreground is visible */
28
28
  visible?: boolean;
29
29
  }
30
- declare const Index: import("svelte").Component<Props, {}, "progress" | "offset" | "index" | "count" | "visible">;
31
- type Index = ReturnType<typeof Index>;
32
- export default Index;
30
+ declare const ScrollerBase: import("svelte").Component<Props, {}, "progress" | "offset" | "index" | "count" | "visible">;
31
+ type ScrollerBase = ReturnType<typeof ScrollerBase>;
32
+ export default ScrollerBase;
@@ -0,0 +1,96 @@
1
+ <script lang="ts">
2
+ export let value = 0;
3
+ export let label = 'label';
4
+
5
+ function drag(node: HTMLElement) {
6
+ function handleMousedown() {
7
+ function handleMousemove(event: MouseEvent) {
8
+ event.preventDefault();
9
+
10
+ node.dispatchEvent(
11
+ new CustomEvent('drag', {
12
+ detail: {
13
+ value: event.clientY / window.innerHeight,
14
+ },
15
+ })
16
+ );
17
+ }
18
+
19
+ function handleMouseup() {
20
+ window.removeEventListener('mousemove', handleMousemove);
21
+ window.removeEventListener('mouseup', handleMouseup);
22
+ }
23
+
24
+ window.addEventListener('mousemove', handleMousemove);
25
+ window.addEventListener('mouseup', handleMouseup);
26
+ }
27
+
28
+ node.addEventListener('mousedown', handleMousedown);
29
+
30
+ return {
31
+ destroy() {
32
+ node.removeEventListener('mousedown', handleMousedown);
33
+ },
34
+ };
35
+ }
36
+
37
+ // Round to 2 decimal places
38
+ function round(value: number): number {
39
+ return Math.round(value * 100) / 100;
40
+ }
41
+ </script>
42
+
43
+ <div
44
+ class="label"
45
+ style="top: {value * 100}%"
46
+ use:drag
47
+ ondrag={(event: DragEvent) => {
48
+ const customEvent = event as unknown as { detail: { value: number } };
49
+ value = customEvent.detail.value;
50
+ }}
51
+ role="slider"
52
+ aria-valuemin="0"
53
+ aria-valuemax="1"
54
+ aria-valuenow={value}
55
+ tabindex="0"
56
+ >
57
+ <div class="drag-target"></div>
58
+ <hr />
59
+ <p>{label}: {round(value)}</p>
60
+ </div>
61
+
62
+ <style>/* Generated from
63
+ https://utopia.fyi/space/calculator/?c=320,18,1.125,1280,21,1.25,7,3,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12
64
+ */
65
+ /* Generated from
66
+ https://utopia.fyi/space/calculator/?c=320,18,1.125,1280,21,1.25,7,3,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12
67
+ */
68
+ /* Scales by 1.125 */
69
+ .label {
70
+ position: fixed;
71
+ top: 0;
72
+ right: 0;
73
+ width: 150px;
74
+ height: 0;
75
+ cursor: ns-resize;
76
+ }
77
+ .label .drag-target {
78
+ position: absolute;
79
+ height: 20px;
80
+ top: -10px;
81
+ }
82
+ .label hr {
83
+ position: absolute;
84
+ top: 0;
85
+ width: 100%;
86
+ height: 2px;
87
+ background: red;
88
+ border: none;
89
+ margin: 0;
90
+ }
91
+ .label p {
92
+ position: absolute;
93
+ font-family: var(--theme-font-family-sans-serif);
94
+ font-weight: 500;
95
+ font-size: var(--theme-font-size-sm);
96
+ }</style>
@@ -0,0 +1,21 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: Props & {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const DraggableLabel: $$__sveltets_2_IsomorphicComponent<{
15
+ value?: number;
16
+ label?: string;
17
+ }, {
18
+ [evt: string]: CustomEvent<any>;
19
+ }, {}, {}, string>;
20
+ type DraggableLabel = InstanceType<typeof DraggableLabel>;
21
+ export default DraggableLabel;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import ScrollerBase from '../ScrollerBase.svelte';
3
+ import DraggableLabel from './DraggableLabel.svelte';
4
+ import BodyText from '../../BodyText/BodyText.svelte';
5
+
6
+ let count = $state(1);
7
+ let index = $state(0);
8
+ let offset = $state(0);
9
+ let progress = $state(0);
10
+ let top = $state(0.1);
11
+ let threshold = $state(0.5);
12
+ let bottom = $state(0.9);
13
+
14
+ const text =
15
+ 'Read the documentation on the props `progress`, `top`, `threshold`, `bottom` under **Controls** to understand how they work. \n\nAdjust the red sliders on the right to see how changes in these values affect scrolling behaviour.';
16
+ </script>
17
+
18
+ <BodyText {text} />
19
+
20
+ <div class="scroller-demo-container">
21
+ <ScrollerBase
22
+ {top}
23
+ {threshold}
24
+ {bottom}
25
+ bind:count
26
+ bind:index
27
+ bind:offset
28
+ bind:progress
29
+ query="div.step-foreground-container"
30
+ >
31
+ {#snippet backgroundSnippet()}
32
+ <p class="mb-0">
33
+ Current step: <strong>{index + 1}/{count}</strong>
34
+ </p>
35
+ <progress class="mb-4" value={(index + 1) / count}></progress>
36
+
37
+ <p class="mb-0">Offset in current step</p>
38
+ <progress class="mb-4" value={offset}></progress>
39
+
40
+ <p class="mb-0">Total progress</p>
41
+ <progress class="mb-4" value={progress}></progress>
42
+ {/snippet}
43
+ {#snippet foregroundSnippet()}
44
+ <div class="step-foreground-container font-medium">Step 1</div>
45
+ <div class="step-foreground-container font-medium">Step 2</div>
46
+ <div class="step-foreground-container font-medium">Step 3</div>
47
+ <div class="step-foreground-container font-medium">Step 4</div>
48
+ <div class="step-foreground-container font-medium">Step 5</div>
49
+ {/snippet}
50
+ </ScrollerBase>
51
+ </div>
52
+
53
+ <BodyText
54
+ 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.
55
+ "
56
+ />
57
+
58
+ <DraggableLabel bind:value={top} label="top" />
59
+ <DraggableLabel bind:value={threshold} label="threshold" />
60
+ <DraggableLabel bind:value={bottom} label="bottom" />
61
+
62
+ <style>/* Generated from
63
+ https://utopia.fyi/space/calculator/?c=320,18,1.125,1280,21,1.25,7,3,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12
64
+ */
65
+ /* Generated from
66
+ https://utopia.fyi/space/calculator/?c=320,18,1.125,1280,21,1.25,7,3,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12
67
+ */
68
+ /* Scales by 1.125 */
69
+ .scroller-demo-container {
70
+ width: 660px;
71
+ margin: auto;
72
+ }
73
+
74
+ .step-foreground-container {
75
+ height: 100vh;
76
+ width: 50%;
77
+ background-color: rgba(0, 0, 0, 0.2);
78
+ padding: 1em;
79
+ margin: 0 0 2em 0;
80
+ position: relative;
81
+ left: 50%;
82
+ }</style>
@@ -0,0 +1,3 @@
1
+ declare const ScrollerDemo: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ScrollerDemo = ReturnType<typeof ScrollerDemo>;
3
+ export default ScrollerDemo;
@@ -44,4 +44,5 @@ export interface Theme {
44
44
  export interface CustomTheme {
45
45
  colour?: Partial<Colour>;
46
46
  font?: Partial<CustomFont>;
47
+ [customProperty: string]: unknown;
47
48
  }
@@ -14,7 +14,7 @@
14
14
  import mergeThemes from './utils/merge.js';
15
15
 
16
16
  // Types
17
- import type { CustomTheme } from './@types/component';
17
+ import type { CustomTheme, Theme } from './@types/component';
18
18
  import type { Snippet } from 'svelte';
19
19
  type Base = 'light' | 'dark';
20
20
 
@@ -22,7 +22,7 @@
22
22
  /** Custom theme object. Can be a partial theme with just
23
23
  * what you want to change.
24
24
  */
25
- theme?: CustomTheme;
25
+ theme?: CustomTheme | Theme;
26
26
  /**
27
27
  * Base theme is one of `light` or `dark` and will be merged
28
28
  * with your custom theme to fill in any values you don't
@@ -2,17 +2,17 @@
2
2
  * Pre-made themes you can import.
3
3
  */
4
4
  export declare const themes: {
5
- light: import("./@types/component").Theme;
6
- dark: import("./@types/component").Theme;
5
+ light: Theme;
6
+ dark: Theme;
7
7
  };
8
- import type { CustomTheme } from './@types/component';
8
+ import type { CustomTheme, Theme } from './@types/component';
9
9
  import type { Snippet } from 'svelte';
10
10
  type Base = 'light' | 'dark';
11
11
  interface Props {
12
12
  /** Custom theme object. Can be a partial theme with just
13
13
  * what you want to change.
14
14
  */
15
- theme?: CustomTheme;
15
+ theme?: CustomTheme | Theme;
16
16
  /**
17
17
  * Base theme is one of `light` or `dark` and will be merged
18
18
  * with your custom theme to fill in any values you don't
package/dist/index.d.ts CHANGED
@@ -34,6 +34,7 @@ export { default as SiteFooter } from './components/SiteFooter/SiteFooter.svelte
34
34
  export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte';
35
35
  export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
36
36
  export { default as Spinner } from './components/Spinner/Spinner.svelte';
37
+ export { default as ScrollerBase } from './components/ScrollerBase/ScrollerBase.svelte';
37
38
  export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
38
39
  export { default as Table } from './components/Table/Table.svelte';
39
40
  export { default as Theme, themes } from './components/Theme/Theme.svelte';
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ export { default as SiteFooter } from './components/SiteFooter/SiteFooter.svelte
36
36
  export { default as SiteHeader } from './components/SiteHeader/SiteHeader.svelte';
37
37
  export { default as SiteHeadline } from './components/SiteHeadline/SiteHeadline.svelte';
38
38
  export { default as Spinner } from './components/Spinner/Spinner.svelte';
39
+ export { default as ScrollerBase } from './components/ScrollerBase/ScrollerBase.svelte';
39
40
  export { default as SponsorshipAd } from './components/AdSlot/SponsorshipAd.svelte';
40
41
  export { default as Table } from './components/Table/Table.svelte';
41
42
  export { default as Theme, themes } from './components/Theme/Theme.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reuters-graphics/graphics-components",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "homepage": "https://reuters-graphics.github.io/graphics-components",