Skip to content

Commit

Permalink
fix typing for action and move jsdoc to return value
Browse files Browse the repository at this point in the history
  • Loading branch information
paoloricciuti committed Sep 17, 2023
1 parent facb4f5 commit 70f9ad8
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 61 deletions.
5 changes: 5 additions & 0 deletions .changeset/mighty-pigs-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'sveltekit-view-transition': patch
---

fix typing for action and move jsdoc to return value
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,27 @@ Simplified `view-transition-api` for Sveltekit.

## Before we begin: what is a view transition?

I could go in thousands of details on what `view-transitions` are but what a better way of understanding them that from the words of Jake Archibald, the main mind behind them? Here's a [wonderful article](https://developer.chrome.com/docs/web-platform/view-transitions/) from him that explains what they are and how to use them in Vanilla JS.
Svelte has kinda spoiled us: we have built in animations and transitions so that we can add a little bit of micro-interactions to our websites and apps. Do you need to animate a list reordering? `flip` comes to the rescue. Is a div moving and morphing from one list to another? `crossfade` is your friend. The web platform must have seen how much svelte developers love those kind of things and has provided us with a brand new, fully progressive enhance-able api: **[the view transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)**. This api not only allows you to animate between two states of your application regardless of the fact that two elements are actually the same or not (something that before was only possible with third party libraries or very complex code) but allows you to animate elements that are **on different pages**!

SvelteKit provides a way to hook into the navigation to allow the developer to start a view transition before the navigation and they do so with a very low level primitive. Here's how you can enable view transitions in SvelteKit

```ts
onNavigate((navigation) => {
if (!document.startViewTransition) return;
return new Promise((resolve) => {
document.startViewTransition(async () => {
resolve();
await navigation.complete;
});
});
});
```

While not very complex, writing this snippets everywhere you need it can get quite tedious and sometimes based on how the `view-transition-api` works you might need to do other things like add classes or change the style of an element.

`sveltekit-view-transition` aim to ease the experience of writing easy and complex transitions in SvelteKit!

Before going in the details of how this library works and how to make use of it i want to leave here a [wonderful article](https://developer.chrome.com/docs/web-platform/view-transitions/) that explains what they are and how to use them in Vanilla JS from Jake Archibald, the main mind behind them.

## Installation

Expand Down
114 changes: 57 additions & 57 deletions src/lib/sveltekit-view-transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,6 @@ function run_all_events<T extends SveltekitViewTransitionEvents>(
}
}

/**
* This function is used to deregister from an event. A function that
* deregister from an event is also returned from the on function.
* @param event the event name you want to deregister from
* @param callback the callback reference you want to deregister
* @param autoWrap by default the off function is wrapped in afterNavigate so that you can
* avoid unnecessarily wrap it every time. If you need to avoid this behavior you can pass false.
*/
function off<T extends SveltekitViewTransitionEvents>(
event: T,
callback: (props: SveltekitViewTransitionEventsMap[T]) => void | Promise<void>,
Expand Down Expand Up @@ -142,19 +134,6 @@ function off<T extends SveltekitViewTransitionEvents>(
}
}

/**
* Function used to register a callback run during the onNavigate
* @param event the event name you want to register a callback for
* @param callback The callback you want to run
* @param options The options for the add listener
* @param options.registerDuringTransition if you want to register this callback even if a transition is running (if false
* it will still be registered as soon as the transition finishes)
* @param options.avoidWrapping by default the on function is wrapped in afterNavigate so that you can
* avoid unnecessarily wrap it every time. If you need to avoid this behavior you can pass true.
* @param options.autoClean wether the listener clean automatically after has been applied or it requires manual cleaning.
* it defaults to true
* @returns A function to deregister the callback
*/
function on<T extends SveltekitViewTransitionEvents>(
event: T,
callback: (props: SveltekitViewTransitionEventsMap[T]) => void,
Expand Down Expand Up @@ -199,18 +178,6 @@ function on<T extends SveltekitViewTransitionEvents>(
return return_value;
}

/**
* Function to call during component initialization to add a class or a series of classes
* during the navigation. This is useful to run a different types of transition when you are going
* back to a specific page.
*
* The classes will be automatically removed at the end of the transition.
*
* @param to_add either a list of class that will always be applied or a function that returns an array
* of strings. The function will get a navigation props as input to allow you to check the to, from, route id etc.
* @param autoWrap by default the classes function is wrapped in afterNavigate so that you can
* avoid unnecessarily wrap it every time. If you need to avoid this behavior you can pass false.
*/
function classes(
to_add:
| string[]
Expand Down Expand Up @@ -249,30 +216,7 @@ function classes(
);
}

/**
* The action to specify a transition name on a specific element. You can use it on an element
* passing a string to directly assign that specific string as view transition name.
*
* If you pass an object instead you can specify a series of options:
*
* - name: required, it's the transition name that will be applied it can be either a string or a function that return a string. The function takes a navigation
* object as input. This is useful if you need to apply different transition names depending on where the navigation is going.
* - classes: either an array of strings or a function that returns an array of string that will be applied
* during the transition. The function will take a navigation object
* - applyImmediately: by default when you specify a transition name it will only be applied when it's actually navigating
* following the suggestion from Jake Archibald himself (the creator of the view transition api) https://twitter.com/jaffathecake/status/1697541871847748098?s=20
* that you should not add transition names to everything but only to the elements involved in the transition. Sometimes tho you want to
* add a transition name immediately (for example when you are coming back from a detail page and you want to animate back an image in the list).
* 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 this page and the `to` will be this page) and returns a boolean.
* - shouldApply: as mentioned above this can be either a boolean or a function that takes a navigation object (this time the `from` is
* this page and the `to` is the page you are navigating to) and return a boolean. If the boolean is true the transition name will be applied.
* This is useful when you want to navigate from a list to a detail.
*
* @param node The element to apply the action to
* @param props either a string for a simple view transition name or a set of options
*/
function transition(node: HTMLElement, props: string | TransitionAction) {
function transition(node: HTMLElement | SVGElement, props: string | TransitionAction) {
if (typeof props === 'string') {
node.style.setProperty('view-transition-name', props);
return;
Expand Down Expand Up @@ -434,9 +378,65 @@ export function setupViewTransition() {
}

return {
/**
* Function used to register a callback run during the onNavigate
* @param event the event name you want to register a callback for
* @param callback The callback you want to run
* @param options The options for the add listener
* @param options.registerDuringTransition if you want to register this callback even if a transition is running (if false
* it will still be registered as soon as the transition finishes)
* @param options.avoidWrapping by default the on function is wrapped in afterNavigate so that you can
* avoid unnecessarily wrap it every time. If you need to avoid this behavior you can pass true.
* @param options.autoClean wether the listener clean automatically after has been applied or it requires manual cleaning.
* it defaults to true
* @returns A function to deregister the callback
*/
on,
/**
* This function is used to deregister from an event. A function that
* deregister from an event is also returned from the on function.
* @param event the event name you want to deregister from
* @param callback the callback reference you want to deregister
* @param autoWrap by default the off function is wrapped in afterNavigate so that you can
* avoid unnecessarily wrap it every time. If you need to avoid this behavior you can pass false.
*/
off,
/**
* The action to specify a transition name on a specific element. You can use it on an element
* passing a string to directly assign that specific string as view transition name.
*
* If you pass an object instead you can specify a series of options:
*
* - name: required, it's the transition name that will be applied it can be either a string or a function that return a string. The function takes a navigation
* object as input. This is useful if you need to apply different transition names depending on where the navigation is going.
* - classes: either an array of strings or a function that returns an array of string that will be applied
* during the transition. The function will take a navigation object
* - applyImmediately: by default when you specify a transition name it will only be applied when it's actually navigating
* following the suggestion from Jake Archibald himself (the creator of the view transition api) https://twitter.com/jaffathecake/status/1697541871847748098?s=20
* that you should not add transition names to everything but only to the elements involved in the transition. Sometimes tho you want to
* add a transition name immediately (for example when you are coming back from a detail page and you want to animate back an image in the list).
* 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 this page and the `to` will be this page) and returns a boolean.
* - shouldApply: as mentioned above this can be either a boolean or a function that takes a navigation object (this time the `from` is
* this page and the `to` is the page you are navigating to) and return a boolean. If the boolean is true the transition name will be applied.
* This is useful when you want to navigate from a list to a detail.
*
* @param node The element to apply the action to
* @param props either a string for a simple view transition name or a set of options
*/
transition,
/**
* Function to call during component initialization to add a class or a series of classes
* during the navigation. This is useful to run a different types of transition when you are going
* back to a specific page.
*
* The classes will be automatically removed at the end of the transition.
*
* @param to_add either a list of class that will always be applied or a function that returns an array
* of strings. The function will get a navigation props as input to allow you to check the to, from, route id etc.
* @param autoWrap by default the classes function is wrapped in afterNavigate so that you can
* avoid unnecessarily wrap it every time. If you need to avoid this behavior you can pass false.
*/
classes,
};
}
49 changes: 48 additions & 1 deletion src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,37 @@
<Mouse {clicking} />
</span>
</a>
<p>
<a class="flex items-center gap-2" href="https://twitter.com/paoloricciuti"
><svg
in:receive={{
key: 'github',
}}
out:send={{
key: 'github',
}}
use:transition={'github'}
aria-hidden="true"
viewBox="0 0 16 16"
version="1.1"
class="h-4 w-4"
fill="currentColor"
><path
fill-rule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
></path></svg
> sveltekit-view-transition</a
>
</p>
<p>
Made with 🧡 by <a class="underline" href="https://twitter.com/paoloricciuti"
>paoloricciuti</a
>
</p>
</main>
{:else}
<main class="absolute inset-0">
<header class="bg-slate-800 p-4">
<header class="flex justify-between bg-slate-800 p-4">
<h1
in:receive={{
key: 'title',
Expand All @@ -81,6 +108,26 @@
>
<code use:transition={'title'}>`sveltekit-view-transition`</code>
</h1>
<a href="https://twitter.com/paoloricciuti"
><svg
in:receive={{
key: 'github',
}}
out:send={{
key: 'github',
}}
use:transition={'github'}
aria-hidden="true"
viewBox="0 0 16 16"
version="1.1"
class="h-8 w-8"
fill="currentColor"
><path
fill-rule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
></path></svg
></a
>
</header>
<p class="p-5 text-2xl font-bold">Getting started</p>
<p class="px-5">
Expand Down
18 changes: 16 additions & 2 deletions src/routes/docs/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,23 @@
const { transition } = setupViewTransition();
</script>

<header class="sticky top-0 bg-slate-900 p-4">
<header class="sticky top-0 flex justify-between bg-slate-900 p-4">
<h1><code use:transition={'title'} class="text-2xl">`sveltekit-view-transition`</code></h1>
<a href="https://twitter.com/paoloricciuti"
><svg
use:transition={'github'}
aria-hidden="true"
viewBox="0 0 16 16"
version="1.1"
class="h-8 w-8"
fill="currentColor"
><path
fill-rule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
></path></svg
></a
>
</header>
<section class="prose prose-invert prose-h1:hidden m-auto overflow-y-auto p-4">
<section class="prose prose-invert m-auto overflow-y-auto p-4 prose-h1:hidden">
<Readme />
</section>

1 comment on commit 70f9ad8

@vercel
Copy link

@vercel vercel bot commented on 70f9ad8 Sep 17, 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.