[ci] format

This commit is contained in:
matthewp 2023-07-19 19:21:13 +00:00 committed by astrobot-houston
parent 6a12fcecb0
commit 7a6ca236e8
12 changed files with 198 additions and 148 deletions

View file

@ -7,8 +7,9 @@ export interface Props {
const { fallback = 'animate' } = Astro.props as Props; const { fallback = 'animate' } = Astro.props as Props;
--- ---
<meta name="astro-view-transitions-enabled" content="true">
<meta name="astro-view-transitions-fallback" content={fallback}> <meta name="astro-view-transitions-enabled" content="true" />
<meta name="astro-view-transitions-fallback" content={fallback} />
<script> <script>
type Fallback = 'none' | 'animate' | 'swap'; type Fallback = 'none' | 'animate' | 'swap';
type Direction = 'forward' | 'back'; type Direction = 'forward' | 'back';
@ -17,22 +18,23 @@ const { fallback = 'animate' } = Astro.props as Props;
// you can figure it using an index. On pushState the index is incremented so you // you can figure it using an index. On pushState the index is incremented so you
// can use that to determine popstate if going forward or back. // can use that to determine popstate if going forward or back.
let currentHistoryIndex = history.state?.index || 0; let currentHistoryIndex = history.state?.index || 0;
if(!history.state) { if (!history.state) {
history.replaceState({index: currentHistoryIndex}, document.title); history.replaceState({ index: currentHistoryIndex }, document.title);
} }
const supportsViewTransitions = !!document.startViewTransition; const supportsViewTransitions = !!document.startViewTransition;
const transitionEnabledOnThisPage = () => !!document.querySelector('[name="astro-view-transitions-enabled"]'); const transitionEnabledOnThisPage = () =>
!!document.querySelector('[name="astro-view-transitions-enabled"]');
async function getHTML(href: string) { async function getHTML(href: string) {
const res = await fetch(href) const res = await fetch(href);
const html = await res.text(); const html = await res.text();
return { ok: res.ok, html }; return { ok: res.ok, html };
} }
function getFallback(): Fallback { function getFallback(): Fallback {
const el = document.querySelector('[name="astro-view-transitions-fallback"]'); const el = document.querySelector('[name="astro-view-transitions-fallback"]');
if(el) { if (el) {
return el.getAttribute('content') as Fallback; return el.getAttribute('content') as Fallback;
} }
return 'animate'; return 'animate';
@ -40,14 +42,14 @@ const { fallback = 'animate' } = Astro.props as Props;
const parser = new DOMParser(); const parser = new DOMParser();
async function updateDOM(dir: Direction, html: string, fallback?: Fallback) { async function updateDOM(dir: Direction, html: string, fallback?: Fallback) {
const doc = parser.parseFromString(html, 'text/html'); const doc = parser.parseFromString(html, 'text/html');
doc.documentElement.dataset.astroTransition = dir; doc.documentElement.dataset.astroTransition = dir;
const swap = () => document.documentElement.replaceWith(doc.documentElement); const swap = () => document.documentElement.replaceWith(doc.documentElement);
if(fallback === 'animate') { if (fallback === 'animate') {
let isAnimating = false; let isAnimating = false;
addEventListener('animationstart', () => isAnimating = true, { once: true }); addEventListener('animationstart', () => (isAnimating = true), { once: true });
// Trigger the animations // Trigger the animations
document.documentElement.dataset.astroTransitionFallback = 'old'; document.documentElement.dataset.astroTransitionFallback = 'old';
@ -61,17 +63,17 @@ const { fallback = 'animate' } = Astro.props as Props;
} else { } else {
swap(); swap();
} }
} }
async function navigate(dir: Direction, href: string) { async function navigate(dir: Direction, href: string) {
let finished: Promise<void>; let finished: Promise<void>;
const { html, ok } = await getHTML(href); const { html, ok } = await getHTML(href);
// If there is a problem fetching the new page, just do an MPA navigation to it. // If there is a problem fetching the new page, just do an MPA navigation to it.
if(!ok) { if (!ok) {
location.href = href; location.href = href;
return; return;
} }
if(supportsViewTransitions) { if (supportsViewTransitions) {
finished = document.startViewTransition(() => updateDOM(dir, html)).finished; finished = document.startViewTransition(() => updateDOM(dir, html)).finished;
} else { } else {
finished = updateDOM(dir, html, getFallback()); finished = updateDOM(dir, html, getFallback());
@ -83,12 +85,12 @@ const { fallback = 'animate' } = Astro.props as Props;
} }
} }
// Prefetching // Prefetching
function maybePrefetch(pathname: string) { function maybePrefetch(pathname: string) {
if(document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return; if (document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return;
if(navigator.connection){ if (navigator.connection) {
let conn = navigator.connection; let conn = navigator.connection;
if(conn.saveData || /(2|3)g/.test(conn.effectiveType || '')) return; if (conn.saveData || /(2|3)g/.test(conn.effectiveType || '')) return;
} }
let link = document.createElement('link'); let link = document.createElement('link');
link.setAttribute('rel', 'prefetch'); link.setAttribute('rel', 'prefetch');
@ -96,51 +98,60 @@ const { fallback = 'animate' } = Astro.props as Props;
document.head.append(link); document.head.append(link);
} }
if(supportsViewTransitions || getFallback() !== 'none') { if (supportsViewTransitions || getFallback() !== 'none') {
document.addEventListener('click', (ev) => { document.addEventListener('click', (ev) => {
let link = ev.target; let link = ev.target;
if(link instanceof Element && link.tagName !== 'A') { if (link instanceof Element && link.tagName !== 'A') {
link = link.closest('a'); link = link.closest('a');
}
// This check verifies that the click is happening on an anchor
// that is going to another page within the same origin. Basically it determines
// same-origin navigation, but omits special key combos for new tabs, etc.
if (link &&
link instanceof HTMLAnchorElement &&
link.href &&
(!link.target || link.target === '_self') &&
link.origin === location.origin &&
ev.button === 0 && // left clicks only
!ev.metaKey && // new tab (mac)
!ev.ctrlKey && // new tab (windows)
!ev.altKey && // download
!ev.shiftKey &&
!ev.defaultPrevented &&
transitionEnabledOnThisPage()
) {
ev.preventDefault();
navigate('forward', link.href);
currentHistoryIndex++;
history.pushState({index: currentHistoryIndex}, '', link.href);
}
});
window.addEventListener('popstate', () => {
if(!transitionEnabledOnThisPage()) return;
const nextIndex = history.state?.index ?? (currentHistoryIndex + 1);
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
navigate(direction, location.href);
currentHistoryIndex = nextIndex;
});
['mouseenter', 'touchstart', 'focus'].forEach(evName => {
document.addEventListener(evName, ev => {
if(ev.target instanceof HTMLAnchorElement) {
let el = ev.target;
if(el.origin === location.origin && el.pathname !== location.pathname && transitionEnabledOnThisPage()) {
maybePrefetch(el.pathname);
}
} }
}, { passive: true, capture: true }); // This check verifies that the click is happening on an anchor
}); // that is going to another page within the same origin. Basically it determines
// same-origin navigation, but omits special key combos for new tabs, etc.
if (
link &&
link instanceof HTMLAnchorElement &&
link.href &&
(!link.target || link.target === '_self') &&
link.origin === location.origin &&
ev.button === 0 && // left clicks only
!ev.metaKey && // new tab (mac)
!ev.ctrlKey && // new tab (windows)
!ev.altKey && // download
!ev.shiftKey &&
!ev.defaultPrevented &&
transitionEnabledOnThisPage()
) {
ev.preventDefault();
navigate('forward', link.href);
currentHistoryIndex++;
history.pushState({ index: currentHistoryIndex }, '', link.href);
}
});
window.addEventListener('popstate', () => {
if (!transitionEnabledOnThisPage()) return;
const nextIndex = history.state?.index ?? currentHistoryIndex + 1;
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
navigate(direction, location.href);
currentHistoryIndex = nextIndex;
});
['mouseenter', 'touchstart', 'focus'].forEach((evName) => {
document.addEventListener(
evName,
(ev) => {
if (ev.target instanceof HTMLAnchorElement) {
let el = ev.target;
if (
el.origin === location.origin &&
el.pathname !== location.pathname &&
transitionEnabledOnThisPage()
) {
maybePrefetch(el.pathname);
}
}
},
{ passive: true, capture: true }
);
});
} }
</script> </script>

View file

@ -8,25 +8,37 @@
} }
@keyframes astroFadeIn { @keyframes astroFadeIn {
from { opacity: 0; } from {
opacity: 0;
}
} }
@keyframes astroFadeOut { @keyframes astroFadeOut {
to { opacity: 0; } to {
opacity: 0;
}
} }
@keyframes astroSlideFromRight { @keyframes astroSlideFromRight {
from { transform: translateX(100%); } from {
transform: translateX(100%);
}
} }
@keyframes astroSlideFromLeft { @keyframes astroSlideFromLeft {
from { transform: translateX(-100%); } from {
transform: translateX(-100%);
}
} }
@keyframes astroSlideToRight { @keyframes astroSlideToRight {
to { transform: translateX(100%); } to {
transform: translateX(100%);
}
} }
@keyframes astroSlideToLeft { @keyframes astroSlideToLeft {
to { transform: translateX(-100%); } to {
transform: translateX(-100%);
}
} }

View file

@ -16,7 +16,7 @@ test.afterAll(async () => {
test.describe('View Transitions', () => { test.describe('View Transitions', () => {
test('Moving from page 1 to page 2', async ({ page, astro }) => { test('Moving from page 1 to page 2', async ({ page, astro }) => {
const loads = []; const loads = [];
page.addListener('load', p => { page.addListener('load', (p) => {
loads.push(p.title()); loads.push(p.title());
}); });
@ -35,7 +35,7 @@ test.describe('View Transitions', () => {
test('Back button is captured', async ({ page, astro }) => { test('Back button is captured', async ({ page, astro }) => {
const loads = []; const loads = [];
page.addListener('load', p => { page.addListener('load', (p) => {
loads.push(p.title()); loads.push(p.title());
}); });
@ -59,7 +59,7 @@ test.describe('View Transitions', () => {
test('Clicking on a link with nested content', async ({ page, astro }) => { test('Clicking on a link with nested content', async ({ page, astro }) => {
const loads = []; const loads = [];
page.addListener('load', p => { page.addListener('load', (p) => {
loads.push(p.title()); loads.push(p.title());
}); });
@ -76,9 +76,12 @@ test.describe('View Transitions', () => {
expect(loads.length, 'There should only be 1 page load').toEqual(1); expect(loads.length, 'There should only be 1 page load').toEqual(1);
}); });
test('Moving from a page without ViewTransitions triggers a full page navigation', async ({ page, astro }) => { test('Moving from a page without ViewTransitions triggers a full page navigation', async ({
page,
astro,
}) => {
const loads = []; const loads = [];
page.addListener('load', p => { page.addListener('load', (p) => {
loads.push(p.title()); loads.push(p.title());
}); });
@ -96,6 +99,9 @@ test.describe('View Transitions', () => {
p = page.locator('#two'); p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2'); await expect(p, 'should have content').toHaveText('Page 2');
expect(loads.length, 'There should be 2 page loads. The original, then going from 3 to 2').toEqual(2); expect(
loads.length,
'There should be 2 page loads. The original, then going from 3 to 2'
).toEqual(2);
}); });
}); });

View file

@ -56,10 +56,10 @@ export interface AstroBuiltinProps {
} }
export interface TransitionAnimation { export interface TransitionAnimation {
name: string; // The name of the keyframe name: string; // The name of the keyframe
delay?: number | string; delay?: number | string;
duration?: number | string; duration?: number | string;
easing?: string; easing?: string;
fillMode?: string; fillMode?: string;
direction?: string; direction?: string;
} }
@ -1270,7 +1270,7 @@ export interface AstroUserConfig {
* } * }
* ``` * ```
*/ */
viewTransitions?: boolean; viewTransitions?: boolean;
}; };
// Legacy options to be removed // Legacy options to be removed

View file

@ -233,7 +233,10 @@ export const AstroConfigSchema = z.object({
experimental: z experimental: z
.object({ .object({
assets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.assets), assets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.assets),
viewTransitions: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.viewTransitions), viewTransitions: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.viewTransitions),
}) })
.passthrough() .passthrough()
.refine( .refine(

View file

@ -11,6 +11,7 @@ import {
astroContentImportPlugin, astroContentImportPlugin,
astroContentVirtualModPlugin, astroContentVirtualModPlugin,
} from '../content/index.js'; } from '../content/index.js';
import astroTransitions from '../transitions/vite-plugin-transitions.js';
import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js'; import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js';
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js'; import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
import astroVitePlugin from '../vite-plugin-astro/index.js'; import astroVitePlugin from '../vite-plugin-astro/index.js';
@ -27,7 +28,6 @@ import astroScannerPlugin from '../vite-plugin-scanner/index.js';
import astroScriptsPlugin from '../vite-plugin-scripts/index.js'; import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js'; import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js';
import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.js'; import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.js';
import astroTransitions from '../transitions/vite-plugin-transitions.js';
import { joinPaths } from './path.js'; import { joinPaths } from './path.js';
interface CreateViteOptions { interface CreateViteOptions {

View file

@ -7,7 +7,11 @@ function validateArgs(args: unknown[]): args is Parameters<AstroComponentFactory
if (!args[0] || typeof args[0] !== 'object') return false; if (!args[0] || typeof args[0] !== 'object') return false;
return true; return true;
} }
function baseCreateComponent(cb: AstroComponentFactory, moduleId?: string, propagation?: PropagationHint): AstroComponentFactory { function baseCreateComponent(
cb: AstroComponentFactory,
moduleId?: string,
propagation?: PropagationHint
): AstroComponentFactory {
const name = moduleId?.split('/').pop()?.replace('.astro', '') ?? ''; const name = moduleId?.split('/').pop()?.replace('.astro', '') ?? '';
const fn = (...args: Parameters<AstroComponentFactory>) => { const fn = (...args: Parameters<AstroComponentFactory>) => {
if (!validateArgs(args)) { if (!validateArgs(args)) {
@ -40,7 +44,7 @@ function createComponentWithOptions(opts: CreateComponentOptions) {
export function createComponent( export function createComponent(
arg1: AstroComponentFactory | CreateComponentOptions, arg1: AstroComponentFactory | CreateComponentOptions,
moduleId?: string, moduleId?: string,
propagation?: PropagationHint, propagation?: PropagationHint
) { ) {
if (typeof arg1 === 'function') { if (typeof arg1 === 'function') {
return baseCreateComponent(arg1, moduleId, propagation); return baseCreateComponent(arg1, moduleId, propagation);

View file

@ -33,13 +33,13 @@ export {
stringifyChunk, stringifyChunk,
voidElementNames, voidElementNames,
} from './render/index.js'; } from './render/index.js';
export { renderTransition } from './transition.js';
export type { export type {
AstroComponentFactory, AstroComponentFactory,
AstroComponentInstance, AstroComponentInstance,
ComponentSlots, ComponentSlots,
RenderInstruction, RenderInstruction,
} from './render/index.js'; } from './render/index.js';
export { renderTransition } from './transition.js';
import { markHTMLString } from './escape.js'; import { markHTMLString } from './escape.js';
import { addAttribute, Renderer } from './render/index.js'; import { addAttribute, Renderer } from './render/index.js';

View file

@ -1,16 +1,16 @@
import type { import type {
SSRResult, SSRResult,
TransitionAnimation, TransitionAnimation,
TransitionDirectionalAnimations,
TransitionAnimationValue, TransitionAnimationValue,
TransitionDirectionalAnimations,
} from '../../@types/astro'; } from '../../@types/astro';
import { fade, slide } from '../../transitions/index.js';
import { markHTMLString } from './escape.js'; import { markHTMLString } from './escape.js';
import { slide, fade } from '../../transitions/index.js';
const transitionNameMap = new WeakMap<SSRResult, number>(); const transitionNameMap = new WeakMap<SSRResult, number>();
function incrementTransitionNumber(result: SSRResult) { function incrementTransitionNumber(result: SSRResult) {
let num = 1; let num = 1;
if(transitionNameMap.has(result)) { if (transitionNameMap.has(result)) {
num = transitionNameMap.get(result)! + 1; num = transitionNameMap.get(result)! + 1;
} }
transitionNameMap.set(result, num); transitionNameMap.set(result, num);
@ -21,9 +21,14 @@ function createTransitionScope(result: SSRResult, hash: string) {
const num = incrementTransitionNumber(result); const num = incrementTransitionNumber(result);
return `astro-${hash}-${num}`; return `astro-${hash}-${num}`;
} }
export function renderTransition(result: SSRResult, hash: string, animationName: TransitionAnimationValue | undefined, transitionName: string) { export function renderTransition(
result: SSRResult,
hash: string,
animationName: TransitionAnimationValue | undefined,
transitionName: string
) {
let animations: TransitionDirectionalAnimations | null = null; let animations: TransitionDirectionalAnimations | null = null;
switch(animationName) { switch (animationName) {
case 'fade': { case 'fade': {
animations = fade(); animations = fade();
break; break;
@ -33,7 +38,7 @@ export function renderTransition(result: SSRResult, hash: string, animationName:
break; break;
} }
default: { default: {
if(typeof animationName === 'object') { if (typeof animationName === 'object') {
animations = animationName; animations = animationName;
} }
} }
@ -42,16 +47,18 @@ export function renderTransition(result: SSRResult, hash: string, animationName:
const scope = createTransitionScope(result, hash); const scope = createTransitionScope(result, hash);
// Default transition name is the scope of the element, ie HASH-1 // Default transition name is the scope of the element, ie HASH-1
if(!transitionName) { if (!transitionName) {
transitionName = scope; transitionName = scope;
} }
const styles = markHTMLString(`<style>[data-astro-transition-scope="${scope}"] { const styles = markHTMLString(`<style>[data-astro-transition-scope="${scope}"] {
view-transition-name: ${transitionName}; view-transition-name: ${transitionName};
} }
${!animations ? `` : ${
// Regular animations !animations
` ? ``
: // Regular animations
`
::view-transition-old(${transitionName}) { ::view-transition-old(${transitionName}) {
${stringifyAnimation(animations.forwards.old)} ${stringifyAnimation(animations.forwards.old)}
} }
@ -79,8 +86,9 @@ export function renderTransition(result: SSRResult, hash: string, animationName:
[data-astro-transition=back][data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"] { [data-astro-transition=back][data-astro-transition-fallback=new] [data-astro-transition-scope="${scope}"] {
${stringifyAnimation(animations.backwards.new)} ${stringifyAnimation(animations.backwards.new)}
} }
`.trim()} `.trim()
</style>`) }
</style>`);
result._metadata.extraHead.push(styles); result._metadata.extraHead.push(styles);
@ -89,12 +97,12 @@ export function renderTransition(result: SSRResult, hash: string, animationName:
type AnimationBuilder = { type AnimationBuilder = {
toString(): string; toString(): string;
[key: string]: string[] | ((k: string) => string); [key: string]: string[] | ((k: string) => string);
} };
function addAnimationProperty(builder: AnimationBuilder, prop: string, value: string | number) { function addAnimationProperty(builder: AnimationBuilder, prop: string, value: string | number) {
let arr = builder[prop]; let arr = builder[prop];
if(Array.isArray(arr)) { if (Array.isArray(arr)) {
arr.push(value.toString()); arr.push(value.toString());
} else { } else {
builder[prop] = [value.toString()]; builder[prop] = [value.toString()];
@ -105,19 +113,19 @@ function animationBuilder(): AnimationBuilder {
return { return {
toString() { toString() {
let out = ''; let out = '';
for(let k in this) { for (let k in this) {
let value = this[k]; let value = this[k];
if(Array.isArray(value)) { if (Array.isArray(value)) {
out += `\n\t${k}: ${value.join(', ')};` out += `\n\t${k}: ${value.join(', ')};`;
} }
} }
return out; return out;
} },
}; };
} }
function stringifyAnimation(anim: TransitionAnimation | TransitionAnimation[]): string { function stringifyAnimation(anim: TransitionAnimation | TransitionAnimation[]): string {
if(Array.isArray(anim)) { if (Array.isArray(anim)) {
return stringifyAnimations(anim); return stringifyAnimations(anim);
} else { } else {
return stringifyAnimations([anim]); return stringifyAnimations([anim]);
@ -127,26 +135,26 @@ function stringifyAnimation(anim: TransitionAnimation | TransitionAnimation[]):
function stringifyAnimations(anims: TransitionAnimation[]): string { function stringifyAnimations(anims: TransitionAnimation[]): string {
const builder = animationBuilder(); const builder = animationBuilder();
for(const anim of anims) { for (const anim of anims) {
/*300ms cubic-bezier(0.4, 0, 0.2, 1) both astroSlideFromRight;*/ /*300ms cubic-bezier(0.4, 0, 0.2, 1) both astroSlideFromRight;*/
if(anim.duration) { if (anim.duration) {
addAnimationProperty(builder, 'animation-duration', toTimeValue(anim.duration)); addAnimationProperty(builder, 'animation-duration', toTimeValue(anim.duration));
} }
if(anim.easing) { if (anim.easing) {
addAnimationProperty(builder, 'animation-timing-function', anim.easing); addAnimationProperty(builder, 'animation-timing-function', anim.easing);
} }
if(anim.direction) { if (anim.direction) {
addAnimationProperty(builder, 'animation-direction', anim.direction); addAnimationProperty(builder, 'animation-direction', anim.direction);
} }
if(anim.delay) { if (anim.delay) {
addAnimationProperty(builder, 'animation-delay', anim.delay); addAnimationProperty(builder, 'animation-delay', anim.delay);
} }
if(anim.fillMode) { if (anim.fillMode) {
addAnimationProperty(builder, 'animation-fill-mode', anim.fillMode); addAnimationProperty(builder, 'animation-fill-mode', anim.fillMode);
} }
addAnimationProperty(builder, 'animation-name', anim.name); addAnimationProperty(builder, 'animation-name', anim.name);
} }
return builder.toString(); return builder.toString();
} }

View file

@ -1,45 +1,51 @@
import type { TransitionDirectionalAnimations, TransitionAnimationPair } from '../@types/astro'; import type { TransitionDirectionalAnimations } from '../@types/astro';
export function slide({ export function slide({
duration duration,
}: { }: {
duration?: string | number; duration?: string | number;
} = {}): TransitionDirectionalAnimations { } = {}): TransitionDirectionalAnimations {
return { return {
forwards: { forwards: {
old: [{ old: [
name: 'astroFadeOut', {
duration: duration ?? '90ms', name: 'astroFadeOut',
easing: 'cubic-bezier(0.4, 0, 1, 1)', duration: duration ?? '90ms',
fillMode: 'both' easing: 'cubic-bezier(0.4, 0, 1, 1)',
}, { fillMode: 'both',
name: 'astroSlideToLeft', },
duration: duration ?? '300ms', {
easing: 'cubic-bezier(0.4, 0, 0.2, 1)', name: 'astroSlideToLeft',
fillMode: 'both' duration: duration ?? '300ms',
}], easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
new: [{ fillMode: 'both',
name: 'astroFadeIn', },
duration: duration ?? '210ms', ],
easing: 'cubic-bezier(0, 0, 0.2, 1)', new: [
delay: '90ms', {
fillMode: 'both' name: 'astroFadeIn',
}, { duration: duration ?? '210ms',
name: 'astroSlideFromRight', easing: 'cubic-bezier(0, 0, 0.2, 1)',
duration: duration ?? '300ms', delay: '90ms',
easing: 'cubic-bezier(0.4, 0, 0.2, 1)', fillMode: 'both',
fillMode: 'both' },
}] {
name: 'astroSlideFromRight',
duration: duration ?? '300ms',
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
fillMode: 'both',
},
],
}, },
backwards: { backwards: {
old: [{ name: 'astroFadeOut' }, { name: 'astroSlideToRight' }], old: [{ name: 'astroFadeOut' }, { name: 'astroSlideToRight' }],
new: [{ name: 'astroFadeIn' }, { name: 'astroSlideFromLeft' }] new: [{ name: 'astroFadeIn' }, { name: 'astroSlideFromLeft' }],
} },
}; };
} }
export function fade({ export function fade({
duration duration,
}: { }: {
duration?: string | number; duration?: string | number;
} = {}): TransitionDirectionalAnimations { } = {}): TransitionDirectionalAnimations {
@ -55,7 +61,7 @@ export function fade({
duration: duration ?? '0.3s', duration: duration ?? '0.3s',
easing: 'linear', easing: 'linear',
fillMode: 'backwards', fillMode: 'backwards',
} },
} satisfies TransitionAnimationPair; } satisfies TransitionAnimationPair;
return { return {

View file

@ -1,12 +1,12 @@
import type { AstroConfig } from '../@types/astro';
import * as vite from 'vite'; import * as vite from 'vite';
import type { AstroConfig } from '../@types/astro';
import { AstroError } from '../core/errors/index.js'; import { AstroError } from '../core/errors/index.js';
const virtualModuleId = 'astro:transitions'; const virtualModuleId = 'astro:transitions';
const resolvedVirtualModuleId = '\0' + virtualModuleId; const resolvedVirtualModuleId = '\0' + virtualModuleId;
// The virtual module for the astro:transitions namespace // The virtual module for the astro:transitions namespace
export default function astroTransitions({ config }: { config: AstroConfig; }): vite.Plugin { export default function astroTransitions({ config }: { config: AstroConfig }): vite.Plugin {
return { return {
name: 'astro:transitions', name: 'astro:transitions',
async resolveId(id) { async resolveId(id) {
@ -16,7 +16,7 @@ export default function astroTransitions({ config }: { config: AstroConfig; }):
}, },
load(id) { load(id) {
if (id === resolvedVirtualModuleId) { if (id === resolvedVirtualModuleId) {
if(!config.experimental.viewTransitions) { if (!config.experimental.viewTransitions) {
throw new AstroError({ throw new AstroError({
title: 'Experimental View Transitions not enabled', title: 'Experimental View Transitions not enabled',
message: `View Transitions support is experimental. To enable update your config to include: message: `View Transitions support is experimental. To enable update your config to include:
@ -25,7 +25,7 @@ export default defineConfig({
experimental: { experimental: {
viewTransitions: true viewTransitions: true
} }
})` })`,
}); });
} }

View file

@ -12,7 +12,7 @@ describe('astro/src/core/compile', () => {
await cachedCompilation({ await cachedCompilation({
astroConfig: { astroConfig: {
root: pathToFileURL('/'), root: pathToFileURL('/'),
experimental: {} experimental: {},
}, },
viteConfig: await resolveConfig({ configFile: false }, 'serve'), viteConfig: await resolveConfig({ configFile: false }, 'serve'),
filename: '/src/pages/index.astro', filename: '/src/pages/index.astro',