@flight-framework/transitions 0.1.0
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.
- package/README.md +232 -0
- package/dist/adapters/react/index.d.ts +210 -0
- package/dist/adapters/react/index.js +261 -0
- package/dist/adapters/react/index.js.map +1 -0
- package/dist/adapters/solid/index.d.ts +5 -0
- package/dist/adapters/solid/index.js +9 -0
- package/dist/adapters/solid/index.js.map +1 -0
- package/dist/adapters/svelte/index.d.ts +5 -0
- package/dist/adapters/svelte/index.js +9 -0
- package/dist/adapters/svelte/index.js.map +1 -0
- package/dist/adapters/vue/index.d.ts +5 -0
- package/dist/adapters/vue/index.js +9 -0
- package/dist/adapters/vue/index.js.map +1 -0
- package/dist/chunk-4SF4GHDQ.js +252 -0
- package/dist/chunk-4SF4GHDQ.js.map +1 -0
- package/dist/chunk-7R3FXL3A.js +442 -0
- package/dist/chunk-7R3FXL3A.js.map +1 -0
- package/dist/chunk-BAILQEFB.js +136 -0
- package/dist/chunk-BAILQEFB.js.map +1 -0
- package/dist/chunk-ITLC6KJ4.js +138 -0
- package/dist/chunk-ITLC6KJ4.js.map +1 -0
- package/dist/chunk-JRRJMJDL.js +121 -0
- package/dist/chunk-JRRJMJDL.js.map +1 -0
- package/dist/chunk-UZUZC3MA.js +190 -0
- package/dist/chunk-UZUZC3MA.js.map +1 -0
- package/dist/chunk-W7HSR35B.js +3 -0
- package/dist/chunk-W7HSR35B.js.map +1 -0
- package/dist/chunk-WDXXYC7B.js +70 -0
- package/dist/chunk-WDXXYC7B.js.map +1 -0
- package/dist/chunk-XLVYHPII.js +3 -0
- package/dist/chunk-XLVYHPII.js.map +1 -0
- package/dist/chunk-ZBJ6FSAK.js +438 -0
- package/dist/chunk-ZBJ6FSAK.js.map +1 -0
- package/dist/component/index.d.ts +87 -0
- package/dist/component/index.js +5 -0
- package/dist/component/index.js.map +1 -0
- package/dist/config/index.d.ts +93 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/index.d.ts +107 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/index.d.ts +112 -0
- package/dist/layout/index.js +4 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/page/index.d.ts +87 -0
- package/dist/page/index.js +7 -0
- package/dist/page/index.js.map +1 -0
- package/dist/presets/index.d.ts +192 -0
- package/dist/presets/index.js +3 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/router/index.d.ts +104 -0
- package/dist/router/index.js +7 -0
- package/dist/router/index.js.map +1 -0
- package/dist/transition-manager-CuO0S_Yn.d.ts +62 -0
- package/dist/types-BT3SCjiY.d.ts +272 -0
- package/dist/view-transition-Hp-Q9vWJ.d.ts +97 -0
- package/package.json +110 -0
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# @flight-framework/transitions
|
|
2
|
+
|
|
3
|
+
Smooth page, layout, and component transitions for Flight Framework.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Native View Transitions API** - Uses browser-native API when available
|
|
8
|
+
- 🎨 **20+ Built-in Presets** - Fade, slide, scale, flip, morph, and more
|
|
9
|
+
- ⚡ **Zero Dependencies** - Core bundle ~3kb gzipped
|
|
10
|
+
- 🎯 **Framework Agnostic** - Works with React, Vue, Svelte, Solid, and more
|
|
11
|
+
- ♿ **Accessible** - Respects `prefers-reduced-motion` by default
|
|
12
|
+
- 📱 **SSR Safe** - Works with server-side rendering
|
|
13
|
+
- 🔧 **Highly Configurable** - Global config or per-page overrides
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @flight-framework/transitions
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Global Configuration
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// flight.config.ts
|
|
27
|
+
import { defineConfig } from '@flight-framework/core';
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
transitions: {
|
|
31
|
+
enabled: true,
|
|
32
|
+
pageTransition: 'fade',
|
|
33
|
+
duration: 200,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### React Usage
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// RootLayout.tsx
|
|
42
|
+
import { TransitionProvider, PageTransition } from '@flight-framework/transitions/react';
|
|
43
|
+
|
|
44
|
+
export default function RootLayout({ children }) {
|
|
45
|
+
return (
|
|
46
|
+
<TransitionProvider config={{ pageTransition: 'fade' }}>
|
|
47
|
+
<Header />
|
|
48
|
+
<PageTransition>
|
|
49
|
+
{children}
|
|
50
|
+
</PageTransition>
|
|
51
|
+
<Footer />
|
|
52
|
+
</TransitionProvider>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Page-Specific Transitions
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// routes/dashboard.page.tsx
|
|
61
|
+
import { definePageTransition } from '@flight-framework/transitions';
|
|
62
|
+
|
|
63
|
+
definePageTransition({
|
|
64
|
+
default: 'slide-left',
|
|
65
|
+
backTransition: 'slide-right',
|
|
66
|
+
duration: 250,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default function DashboardPage() {
|
|
70
|
+
return <div>Dashboard</div>;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Presets
|
|
75
|
+
|
|
76
|
+
### Fade
|
|
77
|
+
- `fade` - Simple opacity fade
|
|
78
|
+
- `fade-scale` - Fade with subtle scale
|
|
79
|
+
- `fade-blur` - Fade with blur effect
|
|
80
|
+
- `fade-slow` - Slow fade for emphasis
|
|
81
|
+
- `fade-fast` - Quick fade for snappy UI
|
|
82
|
+
|
|
83
|
+
### Slide
|
|
84
|
+
- `slide-left` - Slide in from left
|
|
85
|
+
- `slide-right` - Slide in from right
|
|
86
|
+
- `slide-up` - Slide in from top
|
|
87
|
+
- `slide-down` - Slide in from bottom
|
|
88
|
+
- `slide-fade` - Subtle offset slide
|
|
89
|
+
- `slide-scale` - iOS-like slide with scale
|
|
90
|
+
|
|
91
|
+
### Scale
|
|
92
|
+
- `scale` - Scale in/out
|
|
93
|
+
- `scale-up` - Zoom in effect
|
|
94
|
+
- `scale-down` - Zoom out effect
|
|
95
|
+
- `scale-fade` - Subtle scale with fade
|
|
96
|
+
- `pop` - Bouncy scale animation
|
|
97
|
+
- `expand` - Expand from center
|
|
98
|
+
|
|
99
|
+
### Special
|
|
100
|
+
- `flip-x` - 3D horizontal flip
|
|
101
|
+
- `flip-y` - 3D vertical flip
|
|
102
|
+
- `rotate` - Rotation with fade
|
|
103
|
+
- `morph` - Shared element morphing
|
|
104
|
+
- `wipe-left` - Reveal wipe effect
|
|
105
|
+
- `circle-reveal` - Circular reveal from center
|
|
106
|
+
- `none` - Disable transitions
|
|
107
|
+
|
|
108
|
+
## Custom Transitions
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { definePageTransition } from '@flight-framework/transitions';
|
|
112
|
+
|
|
113
|
+
definePageTransition({
|
|
114
|
+
default: {
|
|
115
|
+
name: 'my-custom',
|
|
116
|
+
leave: {
|
|
117
|
+
from: { opacity: '1', transform: 'scale(1)' },
|
|
118
|
+
to: { opacity: '0', transform: 'scale(0.9) rotate(5deg)' },
|
|
119
|
+
},
|
|
120
|
+
enter: {
|
|
121
|
+
from: { opacity: '0', transform: 'translateY(20px)' },
|
|
122
|
+
to: { opacity: '1', transform: 'translateY(0)' },
|
|
123
|
+
},
|
|
124
|
+
duration: { leave: 150, enter: 200 },
|
|
125
|
+
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Shared Element Transitions
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
// ProductList.tsx
|
|
134
|
+
<Link href={`/product/${id}`}>
|
|
135
|
+
<SharedElement name={`product-${id}`}>
|
|
136
|
+
<img src={product.image} alt={product.name} />
|
|
137
|
+
</SharedElement>
|
|
138
|
+
</Link>
|
|
139
|
+
|
|
140
|
+
// ProductDetail.tsx
|
|
141
|
+
<SharedElement name={`product-${id}`}>
|
|
142
|
+
<img src={product.image} alt={product.name} className="large" />
|
|
143
|
+
</SharedElement>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Programmatic Navigation
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { navigateWithTransition } from '@flight-framework/transitions/router';
|
|
150
|
+
|
|
151
|
+
// With default transition
|
|
152
|
+
await navigateWithTransition('/dashboard');
|
|
153
|
+
|
|
154
|
+
// With custom transition
|
|
155
|
+
await navigateWithTransition('/settings', {
|
|
156
|
+
transition: 'slide-left',
|
|
157
|
+
replace: false,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Disable transition
|
|
161
|
+
await navigateWithTransition('/login', {
|
|
162
|
+
transition: false,
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## React Hooks
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import {
|
|
170
|
+
useTransition,
|
|
171
|
+
useIsTransitioning,
|
|
172
|
+
useTransitionDirection,
|
|
173
|
+
useReducedMotion,
|
|
174
|
+
} from '@flight-framework/transitions/react';
|
|
175
|
+
|
|
176
|
+
function MyComponent() {
|
|
177
|
+
const { state, startTransition } = useTransition();
|
|
178
|
+
const isTransitioning = useIsTransitioning();
|
|
179
|
+
const direction = useTransitionDirection();
|
|
180
|
+
const prefersReduced = useReducedMotion();
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
{isTransitioning && <LoadingIndicator />}
|
|
185
|
+
<p>Direction: {direction}</p>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## CSS-Only Usage
|
|
192
|
+
|
|
193
|
+
For CSS-only transitions without JavaScript:
|
|
194
|
+
|
|
195
|
+
```css
|
|
196
|
+
/* Import base transitions */
|
|
197
|
+
@import '@flight-framework/transitions/css';
|
|
198
|
+
|
|
199
|
+
/* Import View Transitions API styles */
|
|
200
|
+
@import '@flight-framework/transitions/css/view-transitions';
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
<div class="flight-transition flight-transition-fade">
|
|
205
|
+
Content with transition
|
|
206
|
+
</div>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Configuration Options
|
|
210
|
+
|
|
211
|
+
| Option | Type | Default | Description |
|
|
212
|
+
|--------|------|---------|-------------|
|
|
213
|
+
| `enabled` | `boolean` | `true` | Enable/disable transitions globally |
|
|
214
|
+
| `viewTransitions` | `boolean` | `true` | Use native View Transitions API |
|
|
215
|
+
| `pageTransition` | `string \| object` | `'fade'` | Default page transition |
|
|
216
|
+
| `layoutTransition` | `string \| object` | `false` | Default layout transition |
|
|
217
|
+
| `duration` | `number` | `200` | Animation duration in ms |
|
|
218
|
+
| `easing` | `string` | `cubic-bezier(0.4, 0, 0.2, 1)` | Easing function |
|
|
219
|
+
| `reduceMotion` | `'respect-system' \| 'always-reduce' \| 'ignore'` | `'respect-system'` | Reduced motion handling |
|
|
220
|
+
| `crossDocument` | `boolean` | `false` | Enable MPA transitions |
|
|
221
|
+
|
|
222
|
+
## Browser Support
|
|
223
|
+
|
|
224
|
+
| Feature | Chrome | Firefox | Safari | Edge |
|
|
225
|
+
|---------|--------|---------|--------|------|
|
|
226
|
+
| View Transitions API | 111+ | ❌ (fallback) | 18.4+ | 111+ |
|
|
227
|
+
| CSS Fallback | ✅ | ✅ | ✅ | ✅ |
|
|
228
|
+
| Web Animations API | ✅ | ✅ | ✅ | ✅ |
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT © Flight Contributors
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import React, { FC, ReactNode, CSSProperties } from 'react';
|
|
2
|
+
import { i as TransitionsConfig, j as TransitionState, P as PageTransitionConfig, T as TransitionPreset, C as CustomTransition } from '../../types-BT3SCjiY.js';
|
|
3
|
+
import { T as TransitionManager } from '../../transition-manager-CuO0S_Yn.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @flight-framework/transitions - React Adapter
|
|
7
|
+
*
|
|
8
|
+
* React components and hooks for Flight Transitions.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface TransitionContextValue {
|
|
12
|
+
/** Current transition state */
|
|
13
|
+
state: TransitionState;
|
|
14
|
+
/** Transition manager instance */
|
|
15
|
+
manager: TransitionManager;
|
|
16
|
+
/** Whether transitions are enabled */
|
|
17
|
+
isEnabled: boolean;
|
|
18
|
+
/** Whether View Transitions API is supported */
|
|
19
|
+
isViewTransitionSupported: boolean;
|
|
20
|
+
/** Start a page transition programmatically */
|
|
21
|
+
startTransition: (to: string, config?: PageTransitionConfig) => Promise<void>;
|
|
22
|
+
/** Skip the current transition */
|
|
23
|
+
skipTransition: () => void;
|
|
24
|
+
}
|
|
25
|
+
interface TransitionProviderProps {
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
/** Initial transition configuration */
|
|
28
|
+
config?: TransitionsConfig;
|
|
29
|
+
/** Use a custom transition manager */
|
|
30
|
+
manager?: TransitionManager;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* TransitionProvider - Provides transition context to the app
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* // In your root layout
|
|
38
|
+
* import { TransitionProvider } from '@flight-framework/transitions/react';
|
|
39
|
+
*
|
|
40
|
+
* export default function RootLayout({ children }) {
|
|
41
|
+
* return (
|
|
42
|
+
* <TransitionProvider config={{ pageTransition: 'fade' }}>
|
|
43
|
+
* {children}
|
|
44
|
+
* </TransitionProvider>
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare const TransitionProvider: FC<TransitionProviderProps>;
|
|
50
|
+
/**
|
|
51
|
+
* useTransition - Access the transition context
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* function MyComponent() {
|
|
56
|
+
* const { state, startTransition } = useTransition();
|
|
57
|
+
*
|
|
58
|
+
* return (
|
|
59
|
+
* <div>
|
|
60
|
+
* {state.isTransitioning && <LoadingSpinner />}
|
|
61
|
+
* <button onClick={() => startTransition('/about')}>
|
|
62
|
+
* Go to About
|
|
63
|
+
* </button>
|
|
64
|
+
* </div>
|
|
65
|
+
* );
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function useTransition(): TransitionContextValue;
|
|
70
|
+
/**
|
|
71
|
+
* useTransitionState - Get just the transition state
|
|
72
|
+
*/
|
|
73
|
+
declare function useTransitionState(): TransitionState;
|
|
74
|
+
/**
|
|
75
|
+
* useIsTransitioning - Check if a transition is in progress
|
|
76
|
+
*/
|
|
77
|
+
declare function useIsTransitioning(): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* useTransitionDirection - Get the current transition direction
|
|
80
|
+
*/
|
|
81
|
+
declare function useTransitionDirection(): 'forward' | 'back' | 'auto';
|
|
82
|
+
/**
|
|
83
|
+
* useReducedMotion - Check if user prefers reduced motion
|
|
84
|
+
*/
|
|
85
|
+
declare function useReducedMotion(): boolean;
|
|
86
|
+
interface PageTransitionProps {
|
|
87
|
+
children: ReactNode;
|
|
88
|
+
/** Transition type (overrides context config) */
|
|
89
|
+
type?: TransitionPreset | CustomTransition;
|
|
90
|
+
/** Duration override */
|
|
91
|
+
duration?: number;
|
|
92
|
+
/** Mode for sequencing enter/leave */
|
|
93
|
+
mode?: 'in-out' | 'out-in' | 'simultaneous';
|
|
94
|
+
/** Callback when transition starts */
|
|
95
|
+
onStart?: () => void;
|
|
96
|
+
/** Callback when transition ends */
|
|
97
|
+
onEnd?: () => void;
|
|
98
|
+
/** CSS class for the wrapper */
|
|
99
|
+
className?: string;
|
|
100
|
+
/** Inline styles for the wrapper */
|
|
101
|
+
style?: CSSProperties;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* PageTransition - Wraps page content with transition animations
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* // In a layout
|
|
109
|
+
* export default function Layout({ children }) {
|
|
110
|
+
* return (
|
|
111
|
+
* <PageTransition type="slide-left">
|
|
112
|
+
* {children}
|
|
113
|
+
* </PageTransition>
|
|
114
|
+
* );
|
|
115
|
+
* }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare const PageTransition: FC<PageTransitionProps>;
|
|
119
|
+
interface TransitionElementProps {
|
|
120
|
+
children: ReactNode;
|
|
121
|
+
/** Show or hide the element */
|
|
122
|
+
show: boolean;
|
|
123
|
+
/** Transition type */
|
|
124
|
+
type?: TransitionPreset | CustomTransition;
|
|
125
|
+
/** Duration override */
|
|
126
|
+
duration?: number;
|
|
127
|
+
/** Animate on initial mount */
|
|
128
|
+
appear?: boolean;
|
|
129
|
+
/** Unmount when hidden */
|
|
130
|
+
unmountOnHide?: boolean;
|
|
131
|
+
/** Callbacks */
|
|
132
|
+
onEnter?: () => void;
|
|
133
|
+
onEnterComplete?: () => void;
|
|
134
|
+
onLeave?: () => void;
|
|
135
|
+
onLeaveComplete?: () => void;
|
|
136
|
+
/** Wrapper element tag name */
|
|
137
|
+
as?: 'div' | 'span' | 'section' | 'article' | 'main' | 'aside' | 'header' | 'footer' | 'nav';
|
|
138
|
+
/** CSS class */
|
|
139
|
+
className?: string;
|
|
140
|
+
/** Inline styles */
|
|
141
|
+
style?: CSSProperties;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Transition - Animate element enter/leave
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```tsx
|
|
148
|
+
* function Modal({ isOpen, onClose, children }) {
|
|
149
|
+
* return (
|
|
150
|
+
* <Transition show={isOpen} type="fade-scale">
|
|
151
|
+
* <div className="modal">
|
|
152
|
+
* {children}
|
|
153
|
+
* </div>
|
|
154
|
+
* </Transition>
|
|
155
|
+
* );
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare const Transition: FC<TransitionElementProps>;
|
|
160
|
+
interface SharedElementProps {
|
|
161
|
+
children: ReactNode;
|
|
162
|
+
/** Unique name for matching across pages */
|
|
163
|
+
name: string;
|
|
164
|
+
/** Wrapper element tag name */
|
|
165
|
+
as?: 'div' | 'span' | 'section' | 'article' | 'main' | 'aside' | 'header' | 'footer' | 'img';
|
|
166
|
+
/** CSS class */
|
|
167
|
+
className?: string;
|
|
168
|
+
/** Inline styles */
|
|
169
|
+
style?: CSSProperties;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* SharedElement - Mark an element for shared element transitions
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```tsx
|
|
176
|
+
* // On list page
|
|
177
|
+
* <SharedElement name={`product-${product.id}`}>
|
|
178
|
+
* <img src={product.image} />
|
|
179
|
+
* </SharedElement>
|
|
180
|
+
*
|
|
181
|
+
* // On detail page
|
|
182
|
+
* <SharedElement name={`product-${product.id}`}>
|
|
183
|
+
* <img src={product.image} />
|
|
184
|
+
* </SharedElement>
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare const SharedElement: FC<SharedElementProps>;
|
|
188
|
+
interface TransitionLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
189
|
+
/** Transition type for this navigation */
|
|
190
|
+
transition?: TransitionPreset | CustomTransition | false;
|
|
191
|
+
/** Replace instead of push */
|
|
192
|
+
replace?: boolean;
|
|
193
|
+
/** Prefetch on hover */
|
|
194
|
+
prefetch?: boolean;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* TransitionLink - Link component with transition support
|
|
198
|
+
*
|
|
199
|
+
* Use with @flight-framework/router or wrap your router's Link component.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```tsx
|
|
203
|
+
* <TransitionLink href="/about" transition="slide-left">
|
|
204
|
+
* About Us
|
|
205
|
+
* </TransitionLink>
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare const TransitionLink: FC<TransitionLinkProps>;
|
|
209
|
+
|
|
210
|
+
export { PageTransition, type PageTransitionProps, SharedElement, type SharedElementProps, Transition, type TransitionElementProps, TransitionLink, type TransitionLinkProps, TransitionProvider, type TransitionProviderProps, useIsTransitioning, useReducedMotion, useTransition, useTransitionDirection, useTransitionState };
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { createTransitionManager, getTransitionManager } from '../../chunk-7R3FXL3A.js';
|
|
2
|
+
import { prefersReducedMotion } from '../../chunk-4SF4GHDQ.js';
|
|
3
|
+
import { createContext, useRef, useState, useEffect, useMemo, useContext, createElement, useCallback } from 'react';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var TransitionContext = createContext(null);
|
|
7
|
+
var TransitionProvider = ({
|
|
8
|
+
children,
|
|
9
|
+
config,
|
|
10
|
+
manager: customManager
|
|
11
|
+
}) => {
|
|
12
|
+
const managerRef = useRef(null);
|
|
13
|
+
if (!managerRef.current) {
|
|
14
|
+
managerRef.current = customManager ?? createTransitionManager(config);
|
|
15
|
+
}
|
|
16
|
+
const manager = managerRef.current;
|
|
17
|
+
const [state, setState] = useState(manager.getState());
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const unsubscribe = manager.subscribe((newState) => {
|
|
20
|
+
setState(newState);
|
|
21
|
+
});
|
|
22
|
+
return unsubscribe;
|
|
23
|
+
}, [manager]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (config) {
|
|
26
|
+
manager.configure(config);
|
|
27
|
+
}
|
|
28
|
+
}, [config, manager]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
return () => {
|
|
31
|
+
if (!customManager) {
|
|
32
|
+
manager.destroy();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}, [manager, customManager]);
|
|
36
|
+
const value = useMemo(() => ({
|
|
37
|
+
state,
|
|
38
|
+
manager,
|
|
39
|
+
isEnabled: manager.isEnabled(),
|
|
40
|
+
isViewTransitionSupported: manager.isViewTransitionSupported(),
|
|
41
|
+
startTransition: (to, pageConfig) => manager.startPageTransition(to, pageConfig),
|
|
42
|
+
skipTransition: () => manager.skipTransition()
|
|
43
|
+
}), [state, manager]);
|
|
44
|
+
return /* @__PURE__ */ jsx(TransitionContext.Provider, { value, children });
|
|
45
|
+
};
|
|
46
|
+
function useTransition() {
|
|
47
|
+
const context = useContext(TransitionContext);
|
|
48
|
+
if (!context) {
|
|
49
|
+
const manager = getTransitionManager();
|
|
50
|
+
return {
|
|
51
|
+
state: manager.getState(),
|
|
52
|
+
manager,
|
|
53
|
+
isEnabled: manager.isEnabled(),
|
|
54
|
+
isViewTransitionSupported: manager.isViewTransitionSupported(),
|
|
55
|
+
startTransition: (to, config) => manager.startPageTransition(to, config),
|
|
56
|
+
skipTransition: () => manager.skipTransition()
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
61
|
+
function useTransitionState() {
|
|
62
|
+
const { state } = useTransition();
|
|
63
|
+
return state;
|
|
64
|
+
}
|
|
65
|
+
function useIsTransitioning() {
|
|
66
|
+
const { state } = useTransition();
|
|
67
|
+
return state.isTransitioning;
|
|
68
|
+
}
|
|
69
|
+
function useTransitionDirection() {
|
|
70
|
+
const { state } = useTransition();
|
|
71
|
+
return state.direction;
|
|
72
|
+
}
|
|
73
|
+
function useReducedMotion() {
|
|
74
|
+
const [reduced, setReduced] = useState(() => prefersReducedMotion());
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
77
|
+
const handler = (event) => {
|
|
78
|
+
setReduced(event.matches);
|
|
79
|
+
};
|
|
80
|
+
mediaQuery.addEventListener("change", handler);
|
|
81
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
82
|
+
}, []);
|
|
83
|
+
return reduced;
|
|
84
|
+
}
|
|
85
|
+
var PageTransition = ({
|
|
86
|
+
children,
|
|
87
|
+
type,
|
|
88
|
+
duration,
|
|
89
|
+
mode = "out-in",
|
|
90
|
+
onStart,
|
|
91
|
+
onEnd,
|
|
92
|
+
className,
|
|
93
|
+
style
|
|
94
|
+
}) => {
|
|
95
|
+
const { state } = useTransition();
|
|
96
|
+
const containerRef = useRef(null);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (state.isTransitioning && state.phase === "leaving") {
|
|
99
|
+
onStart?.();
|
|
100
|
+
}
|
|
101
|
+
if (!state.isTransitioning && state.phase === "idle") {
|
|
102
|
+
onEnd?.();
|
|
103
|
+
}
|
|
104
|
+
}, [state.isTransitioning, state.phase, onStart, onEnd]);
|
|
105
|
+
const transitionClass = useMemo(() => {
|
|
106
|
+
const classes = ["flight-page-transition"];
|
|
107
|
+
if (className) {
|
|
108
|
+
classes.push(className);
|
|
109
|
+
}
|
|
110
|
+
if (state.isTransitioning) {
|
|
111
|
+
classes.push("flight-transitioning");
|
|
112
|
+
classes.push(`flight-transition-${state.phase}`);
|
|
113
|
+
classes.push(`flight-direction-${state.direction}`);
|
|
114
|
+
}
|
|
115
|
+
return classes.join(" ");
|
|
116
|
+
}, [className, state.isTransitioning, state.phase, state.direction]);
|
|
117
|
+
const inlineStyle = useMemo(() => {
|
|
118
|
+
const styles = { ...style };
|
|
119
|
+
if (duration !== void 0) {
|
|
120
|
+
styles["--flight-transition-duration"] = `${duration}ms`;
|
|
121
|
+
}
|
|
122
|
+
if (type && typeof type === "string") {
|
|
123
|
+
styles["--flight-transition-type"] = type;
|
|
124
|
+
}
|
|
125
|
+
return styles;
|
|
126
|
+
}, [style, duration, type]);
|
|
127
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, className: transitionClass, style: inlineStyle, children });
|
|
128
|
+
};
|
|
129
|
+
var Transition = ({
|
|
130
|
+
children,
|
|
131
|
+
show,
|
|
132
|
+
type = "fade",
|
|
133
|
+
duration,
|
|
134
|
+
appear = false,
|
|
135
|
+
unmountOnHide = true,
|
|
136
|
+
onEnter,
|
|
137
|
+
onEnterComplete,
|
|
138
|
+
onLeave,
|
|
139
|
+
onLeaveComplete,
|
|
140
|
+
as: Component = "div",
|
|
141
|
+
className,
|
|
142
|
+
style
|
|
143
|
+
}) => {
|
|
144
|
+
const [shouldRender, setShouldRender] = useState(show);
|
|
145
|
+
const [phase, setPhase] = useState(
|
|
146
|
+
show ? "entered" : "left"
|
|
147
|
+
);
|
|
148
|
+
const isInitialMount = useRef(true);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const isMount = isInitialMount.current;
|
|
151
|
+
isInitialMount.current = false;
|
|
152
|
+
if (show) {
|
|
153
|
+
setShouldRender(true);
|
|
154
|
+
if (!isMount || appear) {
|
|
155
|
+
setPhase("entering");
|
|
156
|
+
onEnter?.();
|
|
157
|
+
const timer = setTimeout(() => {
|
|
158
|
+
setPhase("entered");
|
|
159
|
+
onEnterComplete?.();
|
|
160
|
+
}, duration ?? 200);
|
|
161
|
+
return () => clearTimeout(timer);
|
|
162
|
+
} else {
|
|
163
|
+
setPhase("entered");
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
setPhase("leaving");
|
|
167
|
+
onLeave?.();
|
|
168
|
+
const timer = setTimeout(() => {
|
|
169
|
+
setPhase("left");
|
|
170
|
+
onLeaveComplete?.();
|
|
171
|
+
if (unmountOnHide) {
|
|
172
|
+
setShouldRender(false);
|
|
173
|
+
}
|
|
174
|
+
}, duration ?? 200);
|
|
175
|
+
return () => clearTimeout(timer);
|
|
176
|
+
}
|
|
177
|
+
}, [show, appear, duration, unmountOnHide, onEnter, onEnterComplete, onLeave, onLeaveComplete]);
|
|
178
|
+
if (!shouldRender) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const transitionClass = [
|
|
182
|
+
"flight-transition",
|
|
183
|
+
`flight-transition-${phase}`,
|
|
184
|
+
typeof type === "string" ? `flight-transition-${type}` : void 0,
|
|
185
|
+
className
|
|
186
|
+
].filter(Boolean).join(" ");
|
|
187
|
+
const inlineStyle = {
|
|
188
|
+
...style
|
|
189
|
+
};
|
|
190
|
+
if (duration !== void 0) {
|
|
191
|
+
inlineStyle["--flight-transition-duration"] = `${duration}ms`;
|
|
192
|
+
}
|
|
193
|
+
return createElement(
|
|
194
|
+
Component,
|
|
195
|
+
{ className: transitionClass, style: inlineStyle },
|
|
196
|
+
children
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
var SharedElement = ({
|
|
200
|
+
children,
|
|
201
|
+
name,
|
|
202
|
+
as: Component = "div",
|
|
203
|
+
className,
|
|
204
|
+
style
|
|
205
|
+
}) => {
|
|
206
|
+
const ref = useRef(null);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
const element = ref.current;
|
|
209
|
+
if (element) {
|
|
210
|
+
element.style.viewTransitionName = name;
|
|
211
|
+
return () => {
|
|
212
|
+
element.style.viewTransitionName = "";
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}, [name]);
|
|
216
|
+
return createElement(
|
|
217
|
+
Component,
|
|
218
|
+
{
|
|
219
|
+
ref,
|
|
220
|
+
className,
|
|
221
|
+
style,
|
|
222
|
+
"data-transition-name": name
|
|
223
|
+
},
|
|
224
|
+
children
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
var TransitionLink = ({
|
|
228
|
+
children,
|
|
229
|
+
href,
|
|
230
|
+
transition,
|
|
231
|
+
replace = false,
|
|
232
|
+
prefetch = false,
|
|
233
|
+
onClick,
|
|
234
|
+
...props
|
|
235
|
+
}) => {
|
|
236
|
+
const { startTransition, manager } = useTransition();
|
|
237
|
+
const handleClick = useCallback(async (e) => {
|
|
238
|
+
onClick?.(e);
|
|
239
|
+
if (e.defaultPrevented) return;
|
|
240
|
+
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (href && !href.startsWith("/") && !href.startsWith(window.location.origin)) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
const targetPath = href?.startsWith("/") ? href : new URL(href ?? "", window.location.origin).pathname;
|
|
248
|
+
await startTransition(targetPath, transition !== void 0 ? { default: transition } : void 0);
|
|
249
|
+
if (replace) {
|
|
250
|
+
window.history.replaceState({}, "", targetPath);
|
|
251
|
+
} else {
|
|
252
|
+
window.history.pushState({}, "", targetPath);
|
|
253
|
+
}
|
|
254
|
+
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
255
|
+
}, [href, transition, replace, onClick, startTransition]);
|
|
256
|
+
return /* @__PURE__ */ jsx("a", { href, onClick: handleClick, ...props, children });
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export { PageTransition, SharedElement, Transition, TransitionLink, TransitionProvider, useIsTransitioning, useReducedMotion, useTransition, useTransitionDirection, useTransitionState };
|
|
260
|
+
//# sourceMappingURL=index.js.map
|
|
261
|
+
//# sourceMappingURL=index.js.map
|