Skip to content

Commit

Permalink
Merge pull request #34 from paoloricciuti/node-and-isinviewport
Browse files Browse the repository at this point in the history
feat: add node and isInViewport to props of action functions
  • Loading branch information
paoloricciuti authored Sep 28, 2023
2 parents 0396d2a + 6c32491 commit b2c4f99
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/soft-phones-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'sveltekit-view-transition': minor
---

provide node and isInViewport arguments to action functions
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ Let's take a look at each of these options in more detail:

The `view-transition-name` -- the only required parameter. It can be a `string` _or_ a `function` that takes a navigation object and returns a string, and will be applied to the target element during the transition. This is equivalent to setting the style property `view-transition-name` on an element.

Apart from the `navigation` object this function also receive the `HTMLElement` of the action (it's the property `node` inside the prop object) and a boolean that express if this element is actually in the viewport at the time of the transition (it's the property `isInViewport` inside the prop object). This allow you to easily skip a transition if the element is not in the viewport to avoid having elements that fly off the viewport (inspired by this [tweet by Ryan Florence](https://x.com/ryanflorence/status/1706686168837054754))

### `classes`

Either an array of strings, or a function that returns an array of strings. These classes will be applied as classnames to the root element during the transition.
Expand All @@ -141,6 +143,8 @@ To demonstrate this, let's assume we want to apply a unique transition to our he

This can be achieved by returning an array, i.e. `["back"]`, to our `classes` callback.

Apart from the `navigation` object this function also receive the `HTMLElement` of the action (it's the property `node` inside the prop object) and a boolean that express if this element is actually in the viewport at the time of the transition (it's the property `isInViewport` inside the prop object). This allow you to easily skip a transition if the element is not in the viewport to avoid having elements that fly off the viewport (inspired by this [tweet by Ryan Florence](https://x.com/ryanflorence/status/1706686168837054754))

```svelte
<script>
import { setupViewTransition } from 'sveltekit-view-transition';
Expand Down Expand Up @@ -242,6 +246,8 @@ By default, the transition name you provide will only be applied during the actu

`applyImmediately` is either a `boolean` or a `function` that take the `navigation` object _(please note that this is the navigation object from the previous page, so the `from` will be the page that is navigating to the current page, and the `to` will be the current page)_ and returns a boolean.

Apart from the `navigation` object this function also receive the `HTMLElement` of the action (it's the property `node` inside the prop object) and a boolean that express if this element is actually in the viewport at the time of the transition (it's the property `isInViewport` inside the prop object). This allow you to easily skip a transition if the element is not in the viewport to avoid having elements that fly off the viewport (inspired by this [tweet by Ryan Florence](https://x.com/ryanflorence/status/1706686168837054754))

Here's a simple example of this in action:

```svelte
Expand Down Expand Up @@ -288,6 +294,8 @@ As mentioned above, this can be either a `boolean` or a `function` that takes a

NB: the default is true so if you don't pass `shouldApply` the transition name will be applied every time.

Apart from the `navigation` object this function also receive the `HTMLElement` of the action (it's the property `node` inside the prop object) and a boolean that express if this element is actually in the viewport at the time of the transition (it's the property `isInViewport` inside the prop object). This allow you to easily skip a transition if the element is not in the viewport to avoid having elements that fly off the viewport (inspired by this [tweet by Ryan Florence](https://x.com/ryanflorence/status/1706686168837054754))

So, completing the example above:

```svelte
Expand Down
9 changes: 9 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
// Reexport your entry components here

export { setupViewTransition } from './sveltekit-view-transition';

export type {
OnOptions,
SveltekitViewTransitionEvents,
SveltekitViewTransitionEventsMap,
TransitionAction,
TransitionActionFunctionProps,
TransitionActionFunctions,
} from './sveltekit-view-transition';
39 changes: 28 additions & 11 deletions src/lib/sveltekit-view-transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ import { SetOfCallback } from './utils';
import { onDestroy } from 'svelte';
import { browser } from '$app/environment';

export type TransitionActionFunctionProps<TEvent extends keyof SveltekitViewTransitionEventsMap> =
SveltekitViewTransitionEventsMap[TEvent] & {
node: HTMLElement | SVGElement;
isInViewport: boolean;
};

export type TransitionAction = {
name:
| string
| ((props: SveltekitViewTransitionEventsMap['before-start-view-transition']) => string);
name: string | ((props: TransitionActionFunctionProps<'before-start-view-transition'>) => string);
classes?:
| string[]
| ((
props: SveltekitViewTransitionEventsMap['before-start-view-transition'],
props: TransitionActionFunctionProps<'before-start-view-transition'>,
) => string[] | undefined);
shouldApply?:
| boolean
| ((props: SveltekitViewTransitionEventsMap['before-start-view-transition']) => boolean);
| ((props: TransitionActionFunctionProps<'before-start-view-transition'>) => boolean);
applyImmediately?:
| boolean
| ((props: SveltekitViewTransitionEventsMap['after-navigation-complete']) => boolean);
| ((props: TransitionActionFunctionProps<'after-navigation-complete'>) => boolean);
};

export type TransitionActionFunctions = {
// eslint-disable-next-line @typescript-eslint/ban-types
[Key in keyof TransitionAction]-?: Extract<TransitionAction[Key], Function>;
};

export type OnOptions = {
Expand Down Expand Up @@ -229,15 +238,19 @@ function transition(node: HTMLElement | SVGElement, props: string | TransitionAc
on(
'after-navigation-complete',
(callback_props) => {
const { top } = node.getBoundingClientRect();
const is_in_viewport = top < window.innerHeight + window.scrollY;
const props_for_callback = { ...callback_props, node, isInViewport: is_in_viewport };
let apply_immediately = false;
if (props.applyImmediately != null) {
apply_immediately =
typeof props.applyImmediately === 'boolean'
? props.applyImmediately
: props.applyImmediately(callback_props);
: props.applyImmediately(props_for_callback);
}
if (apply_immediately) {
const name = typeof props.name === 'function' ? props.name(callback_props) : props.name;
const name =
typeof props.name === 'function' ? props.name(props_for_callback) : props.name;
node.style.setProperty('view-transition-name', name);
off_functions.push(
on(
Expand All @@ -257,14 +270,18 @@ function transition(node: HTMLElement | SVGElement, props: string | TransitionAc
'before-start-view-transition',
(callback_props) => {
let should_apply = true;
const { top } = node.getBoundingClientRect();
const is_in_viewport = top < window.innerHeight + window.scrollY;
const props_for_callback = { ...callback_props, node, isInViewport: is_in_viewport };
if (props.shouldApply != null) {
should_apply =
typeof props.shouldApply === 'boolean'
? props.shouldApply
: props.shouldApply(callback_props);
: props.shouldApply(props_for_callback);
}
if (should_apply) {
const name = typeof props.name === 'function' ? props.name(callback_props) : props.name;
const name =
typeof props.name === 'function' ? props.name(props_for_callback) : props.name;
node.style.setProperty('view-transition-name', name);
off_functions.push(
on(
Expand All @@ -282,7 +299,7 @@ function transition(node: HTMLElement | SVGElement, props: string | TransitionAc
if (props.classes) {
classes_to_add = Array.isArray(props.classes)
? props.classes
: props.classes(callback_props);
: props.classes(props_for_callback);
}
if (classes_to_add) {
document.documentElement.classList.add(...classes_to_add);
Expand Down

1 comment on commit b2c4f99

@vercel
Copy link

@vercel vercel bot commented on b2c4f99 Sep 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.