updates visible

This commit is contained in:
MinamiFunakoshiTR 2025-03-27 10:23:20 -07:00
parent fd5df38dcf
commit b7ca532195
Failed to extract signature
4 changed files with 72 additions and 73 deletions

View file

@ -0,0 +1,31 @@
import { Meta, Canvas } from '@storybook/blocks';
import * as VisibleStories from './Visible.stories.svelte';
<Meta of={VisibleStories} />
# Visible
The `Visible` component wraps around other components or HTML elements and uses the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to determine if they are visible on the page.
This is useful for lazy loading elements, especially expensive media files or components that fetch lots of data. You can also use it trigger animations or play media once a reader scrolls a component into view.
By default, `visible` will switch between `false` and `true` whenever the element is in our out of view. To trigger this just once, set the prop `once` to `true`. This is useful for loading expensive media when they first come into view and then keeping them around once they're loaded.
> **Note:** Don't use this for content that's "above the fold" at the top of the page. That'll just slow down the first load of important visible content.
```svelte
<script>
import { Visible } from '@reuters-graphics/graphics-components';
</script>
<Visible let:visible>
{#if visible}
<p>Visible!</p>
{:else}
<p>Not yet visible.</p>
{/if}
</Visible>
```
<Canvas of={VisibleStories.Demo} />

View file

@ -1,36 +1,22 @@
<script module lang="ts">
// @ts-ignore raw
import componentDocs from './stories/docs/component.md?raw';
// @ts-ignore raw
import defaultSnippet from './stories/snippets/default.svelte?raw';
import { defineMeta } from '@storybook/addon-svelte-csf';
import Visible from './Visible.svelte';
import { withSource, withComponentDocs } from '$lib/docs/utils/withParams.js';
export const meta = {
const { Story } = defineMeta({
title: 'Components/Utilities/Visible',
component: Visible,
...withComponentDocs(componentDocs),
};
});
</script>
<script>
import { Template, Story } from '@storybook/addon-svelte-csf';
</script>
<Template >
{#snippet children({ args })}
<Visible {...args} >
{#snippet children({ visible })}
{#if visible}
<p>Visible!</p>
{:else}
<p>Not yet visible.</p>
{/if}
{/snippet}
</Visible>
{/snippet}
</Template>
<Story name="Default" {...withSource({ svelte: defaultSnippet })} />
<Story name="Demo" tags={['!autodocs', '!dev']}>
<Visible>
{#snippet children(visible)}
{#if visible}
<p>Visible!</p>
{:else}
<p>Not yet visible.</p>
{/if}
{/snippet}
</Visible>
</Story>

View file

@ -1,18 +1,13 @@
<!-- @component `Visible` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-utilities-visible--docs) -->
<script lang="ts">
import { onMount } from 'svelte';
import { onMount, type Snippet } from 'svelte';
interface Props {
/**
* Whether to change visibility just once.
*
* Useful for loading expensive images or other media and then keeping them around once they're first loaded.
*/
* Whether to change visibility just once.
*
* Useful for loading expensive images or other media and then keeping them around once they're first loaded.
*/
once?: boolean;
/** Set Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#rootmargin) `top`. */
top?: number;
@ -24,7 +19,7 @@
right?: number;
/** Set the Intersection Observer [threshold](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#threshold). */
threshold?: number;
children?: import('svelte').Snippet<[any]>;
children?: Snippet<[boolean]>;
}
let {
@ -34,19 +29,20 @@
left = 0,
right = 0,
threshold = 0,
children
children,
}: Props = $props();
let visible = $state(false);
let container: HTMLElement = $state();
let container: HTMLElement | undefined = $state(undefined);
onMount(() => {
if (typeof IntersectionObserver !== 'undefined') {
const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`;
const observer = new IntersectionObserver(
(entries) => {
visible = entries[0].isIntersecting;
if (visible && once) {
if (visible && once && container) {
observer.unobserve(container);
}
},
@ -55,16 +51,20 @@
threshold,
}
);
observer.observe(container);
return () => observer.unobserve(container);
if (container) observer.observe(container);
return () => {
if (container) observer.observe(container);
};
}
function handler() {
const bcr = container.getBoundingClientRect();
visible =
bcr.bottom + bottom > 0 &&
bcr.right + right > 0 &&
bcr.top - top < window.innerHeight &&
bcr.left - left < window.innerWidth;
if (container) {
const bcr = container.getBoundingClientRect();
visible =
bcr.bottom + bottom > 0 &&
bcr.right + right > 0 &&
bcr.top - top < window.innerHeight &&
bcr.left - left < window.innerWidth;
}
if (visible && once) {
window.removeEventListener('scroll', handler);
}
@ -74,7 +74,8 @@
});
</script>
<div bind:this="{container}">
<!-- An element or component -->
{@render children?.({ visible, })}
<div bind:this={container}>
{#if children}
{@render children(visible)}
{/if}
</div>

View file

@ -1,19 +0,0 @@
Wrap components or other HTML elements to determine if they are visible on the page using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
This is really useful for lazy loading elements, especially expensive media files or components that fetch lots of data. You can also use it trigger animations or play media once a reader scrolls a component into view.
> **Pro tip:** Don't use this for content that's "above the fold" at the top of the page. That'll just slow down the first load of important visible content.
```svelte
<script>
import { Visible } from '@reuters-graphics/graphics-components';
</script>
<Visible let:visible>
{#if visible}
<p>Visible!</p>
{:else}
<p>Not yet visible.</p>
{/if}
</Visible>
```