promptslide 0.2.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/dist/index.d.ts +377 -0
- package/dist/index.js +963 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
- package/src/commands/build.mjs +73 -0
- package/src/commands/create.mjs +197 -0
- package/src/commands/preview.mjs +22 -0
- package/src/commands/studio.mjs +27 -0
- package/src/core/animated.tsx +153 -0
- package/src/core/animation-config.ts +98 -0
- package/src/core/animation-context.tsx +54 -0
- package/src/core/index.ts +73 -0
- package/src/core/layouts/shared-footer.tsx +43 -0
- package/src/core/morph.tsx +153 -0
- package/src/core/slide-deck.tsx +430 -0
- package/src/core/slide-error-boundary.tsx +50 -0
- package/src/core/theme-context.tsx +48 -0
- package/src/core/transitions.ts +200 -0
- package/src/core/types.ts +136 -0
- package/src/core/use-slide-navigation.ts +142 -0
- package/src/core/utils.ts +8 -0
- package/src/index.mjs +70 -0
- package/src/utils/ansi.mjs +5 -0
- package/src/utils/colors.mjs +44 -0
- package/src/utils/prompts.mjs +50 -0
- package/src/utils/tsconfig.mjs +35 -0
- package/src/vite/config.mjs +40 -0
- package/src/vite/plugin.mjs +66 -0
- package/templates/default/AGENTS.md +453 -0
- package/templates/default/README.md +35 -0
- package/templates/default/package.json +26 -0
- package/templates/default/public/logo.svg +7 -0
- package/templates/default/src/App.tsx +11 -0
- package/templates/default/src/deck-config.ts +8 -0
- package/templates/default/src/globals.css +157 -0
- package/templates/default/src/layouts/slide-layout-centered.tsx +59 -0
- package/templates/default/src/slides/slide-example.tsx +53 -0
- package/templates/default/src/slides/slide-title.tsx +27 -0
- package/templates/default/src/theme.ts +8 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { motion, Variants } from "framer-motion"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ELEMENT_SLIDE_DISTANCE,
|
|
5
|
+
SPRING_SNAPPY,
|
|
6
|
+
STAGGER_DELAY,
|
|
7
|
+
STEP_ANIMATION_DURATION
|
|
8
|
+
} from "./animation-config"
|
|
9
|
+
import { useAnimationContext } from "./animation-context"
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// ANIMATION TYPES & VARIANTS
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export type AnimationType =
|
|
16
|
+
| "fade"
|
|
17
|
+
| "slide-up"
|
|
18
|
+
| "slide-down"
|
|
19
|
+
| "slide-left"
|
|
20
|
+
| "slide-right"
|
|
21
|
+
| "scale"
|
|
22
|
+
|
|
23
|
+
const animationVariants: Record<AnimationType, Variants> = {
|
|
24
|
+
fade: {
|
|
25
|
+
hidden: { opacity: 0 },
|
|
26
|
+
visible: { opacity: 1 }
|
|
27
|
+
},
|
|
28
|
+
"slide-up": {
|
|
29
|
+
hidden: { opacity: 0, y: ELEMENT_SLIDE_DISTANCE },
|
|
30
|
+
visible: { opacity: 1, y: 0 }
|
|
31
|
+
},
|
|
32
|
+
"slide-down": {
|
|
33
|
+
hidden: { opacity: 0, y: -ELEMENT_SLIDE_DISTANCE },
|
|
34
|
+
visible: { opacity: 1, y: 0 }
|
|
35
|
+
},
|
|
36
|
+
"slide-left": {
|
|
37
|
+
hidden: { opacity: 0, x: ELEMENT_SLIDE_DISTANCE },
|
|
38
|
+
visible: { opacity: 1, x: 0 }
|
|
39
|
+
},
|
|
40
|
+
"slide-right": {
|
|
41
|
+
hidden: { opacity: 0, x: -ELEMENT_SLIDE_DISTANCE },
|
|
42
|
+
visible: { opacity: 1, x: 0 }
|
|
43
|
+
},
|
|
44
|
+
scale: {
|
|
45
|
+
hidden: { opacity: 0, scale: 0.8 },
|
|
46
|
+
visible: { opacity: 1, scale: 1 }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface AnimatedProps {
|
|
51
|
+
/** Which step reveals this content (1-indexed) */
|
|
52
|
+
step: number
|
|
53
|
+
/** Animation type */
|
|
54
|
+
animation?: AnimationType
|
|
55
|
+
/** Animation duration in seconds */
|
|
56
|
+
duration?: number
|
|
57
|
+
/** Delay after trigger in seconds */
|
|
58
|
+
delay?: number
|
|
59
|
+
/** Custom className */
|
|
60
|
+
className?: string
|
|
61
|
+
children: React.ReactNode
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Animated element that appears at a specific animation step.
|
|
66
|
+
*
|
|
67
|
+
* The step count is now declared in slide config rather than discovered
|
|
68
|
+
* at runtime, so this component simply consumes the context without
|
|
69
|
+
* needing to register itself.
|
|
70
|
+
*/
|
|
71
|
+
export function Animated({
|
|
72
|
+
step,
|
|
73
|
+
animation = "slide-up",
|
|
74
|
+
duration = STEP_ANIMATION_DURATION,
|
|
75
|
+
delay = 0,
|
|
76
|
+
className,
|
|
77
|
+
children
|
|
78
|
+
}: AnimatedProps) {
|
|
79
|
+
const { currentStep, showAllAnimations } = useAnimationContext()
|
|
80
|
+
|
|
81
|
+
// Show all animations when navigating backward, otherwise check step
|
|
82
|
+
const isVisible = showAllAnimations || currentStep >= step
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<motion.div
|
|
86
|
+
initial="hidden"
|
|
87
|
+
animate={isVisible ? "visible" : "hidden"}
|
|
88
|
+
variants={animationVariants[animation]}
|
|
89
|
+
transition={{
|
|
90
|
+
...SPRING_SNAPPY,
|
|
91
|
+
duration,
|
|
92
|
+
delay: isVisible ? delay : 0
|
|
93
|
+
}}
|
|
94
|
+
className={className}
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
</motion.div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Wrapper for staggering multiple children.
|
|
103
|
+
* Each direct child will be animated in sequence.
|
|
104
|
+
*/
|
|
105
|
+
interface AnimatedGroupProps {
|
|
106
|
+
/** Starting step for the first child */
|
|
107
|
+
startStep: number
|
|
108
|
+
/** Animation type for all children */
|
|
109
|
+
animation?: AnimationType
|
|
110
|
+
/** Delay between each child in seconds */
|
|
111
|
+
staggerDelay?: number
|
|
112
|
+
className?: string
|
|
113
|
+
children: React.ReactNode
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function AnimatedGroup({
|
|
117
|
+
startStep,
|
|
118
|
+
animation = "slide-up",
|
|
119
|
+
staggerDelay = STAGGER_DELAY,
|
|
120
|
+
className,
|
|
121
|
+
children
|
|
122
|
+
}: AnimatedGroupProps) {
|
|
123
|
+
const { currentStep, showAllAnimations } = useAnimationContext()
|
|
124
|
+
|
|
125
|
+
const childArray = Array.isArray(children) ? children : [children]
|
|
126
|
+
|
|
127
|
+
// Show all animations when navigating backward, otherwise check step
|
|
128
|
+
const isVisible = showAllAnimations || currentStep >= startStep
|
|
129
|
+
|
|
130
|
+
const containerVariants: Variants = {
|
|
131
|
+
hidden: {},
|
|
132
|
+
visible: {
|
|
133
|
+
transition: {
|
|
134
|
+
staggerChildren: staggerDelay
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<motion.div
|
|
141
|
+
initial="hidden"
|
|
142
|
+
animate={isVisible ? "visible" : "hidden"}
|
|
143
|
+
variants={containerVariants}
|
|
144
|
+
className={className}
|
|
145
|
+
>
|
|
146
|
+
{childArray.map((child, index) => (
|
|
147
|
+
<motion.div key={index} variants={animationVariants[animation]}>
|
|
148
|
+
{child}
|
|
149
|
+
</motion.div>
|
|
150
|
+
))}
|
|
151
|
+
</motion.div>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized animation configuration for the slide deck framework.
|
|
3
|
+
* All animation timings, easings, and spring configs are defined here.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// TIMING CONSTANTS
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
/** Duration for slide-to-slide transitions (seconds) */
|
|
11
|
+
export const SLIDE_TRANSITION_DURATION = 0.3
|
|
12
|
+
|
|
13
|
+
/** Duration for morph/layout animations between slides (seconds) */
|
|
14
|
+
export const MORPH_DURATION = 0.8
|
|
15
|
+
|
|
16
|
+
/** Duration for within-slide step animations (seconds) */
|
|
17
|
+
export const STEP_ANIMATION_DURATION = 0.4
|
|
18
|
+
|
|
19
|
+
/** Default stagger delay for grouped animations (seconds) */
|
|
20
|
+
export const STAGGER_DELAY = 0.1
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// EASING PRESETS
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
export const EASE_DEFAULT = "easeInOut" as const
|
|
27
|
+
export const EASE_OUT = "easeOut" as const
|
|
28
|
+
export const EASE_IN = "easeIn" as const
|
|
29
|
+
|
|
30
|
+
/** Smooth ease-in-out curve for morph animations (cubic bezier) */
|
|
31
|
+
export const EASE_MORPH = [0.4, 0, 0.2, 1] as const
|
|
32
|
+
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// SPRING CONFIGURATIONS
|
|
35
|
+
// =============================================================================
|
|
36
|
+
|
|
37
|
+
/** Spring config for snappy, responsive animations */
|
|
38
|
+
export const SPRING_SNAPPY = {
|
|
39
|
+
type: "spring" as const,
|
|
40
|
+
stiffness: 300,
|
|
41
|
+
damping: 30
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Spring config for smooth, gentle animations */
|
|
45
|
+
export const SPRING_SMOOTH = {
|
|
46
|
+
type: "spring" as const,
|
|
47
|
+
stiffness: 200,
|
|
48
|
+
damping: 25
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Spring config for bouncy animations */
|
|
52
|
+
export const SPRING_BOUNCY = {
|
|
53
|
+
type: "spring" as const,
|
|
54
|
+
stiffness: 400,
|
|
55
|
+
damping: 20
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// TRANSITION PRESETS
|
|
60
|
+
// =============================================================================
|
|
61
|
+
|
|
62
|
+
/** Standard transition for slide transitions */
|
|
63
|
+
export const SLIDE_TRANSITION = {
|
|
64
|
+
duration: SLIDE_TRANSITION_DURATION,
|
|
65
|
+
ease: EASE_DEFAULT
|
|
66
|
+
} as const
|
|
67
|
+
|
|
68
|
+
/** Standard transition for morph animations (smooth ease-in-out) */
|
|
69
|
+
export const MORPH_TRANSITION = {
|
|
70
|
+
duration: MORPH_DURATION,
|
|
71
|
+
ease: EASE_MORPH
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Standard transition for step animations (spring-based, duration computed from stiffness/damping) */
|
|
75
|
+
export const STEP_TRANSITION = {
|
|
76
|
+
...SPRING_SNAPPY
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// DISTANCE CONSTANTS (pixels)
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
/** Distance for slide animations */
|
|
84
|
+
export const SLIDE_DISTANCE = 100
|
|
85
|
+
|
|
86
|
+
/** Distance for within-slide element animations */
|
|
87
|
+
export const ELEMENT_SLIDE_DISTANCE = 30
|
|
88
|
+
|
|
89
|
+
// =============================================================================
|
|
90
|
+
// SLIDE DIMENSIONS
|
|
91
|
+
// =============================================================================
|
|
92
|
+
|
|
93
|
+
/** Standard slide dimensions (16:9 aspect ratio) */
|
|
94
|
+
export const SLIDE_DIMENSIONS = {
|
|
95
|
+
width: 1280,
|
|
96
|
+
height: 720,
|
|
97
|
+
aspectRatio: 16 / 9
|
|
98
|
+
} as const
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo } from "react"
|
|
2
|
+
|
|
3
|
+
interface AnimationContextValue {
|
|
4
|
+
/** Current animation step (0 = no animations shown yet) */
|
|
5
|
+
currentStep: number
|
|
6
|
+
/** Total animation steps in this slide (declared in slide config) */
|
|
7
|
+
totalSteps: number
|
|
8
|
+
/** When true, all animation steps should be visible (used for backward navigation) */
|
|
9
|
+
showAllAnimations: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const AnimationContext = createContext<AnimationContextValue>({
|
|
13
|
+
currentStep: 0,
|
|
14
|
+
totalSteps: 0,
|
|
15
|
+
showAllAnimations: false
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
interface AnimationProviderProps {
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
/** Current animation step (0 = no animations shown yet) */
|
|
21
|
+
currentStep: number
|
|
22
|
+
/** Total animation steps declared in slide config */
|
|
23
|
+
totalSteps: number
|
|
24
|
+
/** When true, show all animations regardless of currentStep (for backward navigation) */
|
|
25
|
+
showAllAnimations?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Provides animation context to child components.
|
|
30
|
+
*
|
|
31
|
+
* The totalSteps is now passed from parent (declared in slide config)
|
|
32
|
+
* rather than discovered at runtime, eliminating race conditions.
|
|
33
|
+
*/
|
|
34
|
+
export function AnimationProvider({
|
|
35
|
+
children,
|
|
36
|
+
currentStep,
|
|
37
|
+
totalSteps,
|
|
38
|
+
showAllAnimations = false
|
|
39
|
+
}: AnimationProviderProps) {
|
|
40
|
+
const value = useMemo(
|
|
41
|
+
() => ({
|
|
42
|
+
currentStep,
|
|
43
|
+
totalSteps,
|
|
44
|
+
showAllAnimations
|
|
45
|
+
}),
|
|
46
|
+
[currentStep, totalSteps, showAllAnimations]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return <AnimationContext.Provider value={value}>{children}</AnimationContext.Provider>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function useAnimationContext() {
|
|
53
|
+
return useContext(AnimationContext)
|
|
54
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export type {
|
|
3
|
+
SlideProps,
|
|
4
|
+
SlideComponent,
|
|
5
|
+
SlideConfig,
|
|
6
|
+
NavigationDirection
|
|
7
|
+
} from "./types"
|
|
8
|
+
|
|
9
|
+
// Animation Config
|
|
10
|
+
export {
|
|
11
|
+
SLIDE_TRANSITION_DURATION,
|
|
12
|
+
MORPH_DURATION,
|
|
13
|
+
STEP_ANIMATION_DURATION,
|
|
14
|
+
STAGGER_DELAY,
|
|
15
|
+
EASE_DEFAULT,
|
|
16
|
+
EASE_OUT,
|
|
17
|
+
EASE_IN,
|
|
18
|
+
EASE_MORPH,
|
|
19
|
+
SPRING_SNAPPY,
|
|
20
|
+
SPRING_SMOOTH,
|
|
21
|
+
SPRING_BOUNCY,
|
|
22
|
+
SLIDE_TRANSITION,
|
|
23
|
+
MORPH_TRANSITION,
|
|
24
|
+
STEP_TRANSITION,
|
|
25
|
+
SLIDE_DISTANCE,
|
|
26
|
+
ELEMENT_SLIDE_DISTANCE,
|
|
27
|
+
SLIDE_DIMENSIONS
|
|
28
|
+
} from "./animation-config"
|
|
29
|
+
|
|
30
|
+
// Animation Context
|
|
31
|
+
export { AnimationProvider, useAnimationContext } from "./animation-context"
|
|
32
|
+
|
|
33
|
+
// Animated Components
|
|
34
|
+
export { Animated, AnimatedGroup } from "./animated"
|
|
35
|
+
export type { AnimationType } from "./animated"
|
|
36
|
+
|
|
37
|
+
// Transitions
|
|
38
|
+
export {
|
|
39
|
+
SLIDE_VARIANTS,
|
|
40
|
+
createDirectionalVariants,
|
|
41
|
+
directionalSlideX,
|
|
42
|
+
directionalSlideY,
|
|
43
|
+
getSlideVariants,
|
|
44
|
+
getSlideTransition,
|
|
45
|
+
DEFAULT_SLIDE_TRANSITION
|
|
46
|
+
} from "./transitions"
|
|
47
|
+
export type { SlideTransitionType, SlideTransitionConfig } from "./transitions"
|
|
48
|
+
|
|
49
|
+
// Morph
|
|
50
|
+
export { Morph, MorphGroup, MorphItem, MorphText } from "./morph"
|
|
51
|
+
|
|
52
|
+
// Navigation Hook
|
|
53
|
+
export { useSlideNavigation } from "./use-slide-navigation"
|
|
54
|
+
export type {
|
|
55
|
+
UseSlideNavigationOptions,
|
|
56
|
+
UseSlideNavigationReturn
|
|
57
|
+
} from "./use-slide-navigation"
|
|
58
|
+
|
|
59
|
+
// Theme
|
|
60
|
+
export { SlideThemeProvider, useTheme } from "./theme-context"
|
|
61
|
+
export type { ThemeConfig } from "./types"
|
|
62
|
+
|
|
63
|
+
// Shared Footer
|
|
64
|
+
export { SlideFooter } from "./layouts/shared-footer"
|
|
65
|
+
|
|
66
|
+
// SlideDeck
|
|
67
|
+
export { SlideDeck } from "./slide-deck"
|
|
68
|
+
|
|
69
|
+
// Error Boundary
|
|
70
|
+
export { SlideErrorBoundary } from "./slide-error-boundary"
|
|
71
|
+
|
|
72
|
+
// Utils
|
|
73
|
+
export { cn } from "./utils"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useTheme } from "../theme-context"
|
|
2
|
+
|
|
3
|
+
interface SlideFooterProps {
|
|
4
|
+
slideNumber: number
|
|
5
|
+
totalSlides: number
|
|
6
|
+
/** Use light text/logo for dark slide backgrounds */
|
|
7
|
+
variant?: "default" | "light"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SlideFooter({ slideNumber, totalSlides, variant = "default" }: SlideFooterProps) {
|
|
11
|
+
const theme = useTheme()
|
|
12
|
+
if (!theme) return null
|
|
13
|
+
|
|
14
|
+
const logoUrl = variant === "light"
|
|
15
|
+
? (theme.logo?.fullLight ?? theme.logo?.full)
|
|
16
|
+
: theme.logo?.full
|
|
17
|
+
|
|
18
|
+
const textClass = variant === "light"
|
|
19
|
+
? "text-white/70"
|
|
20
|
+
: "text-muted-foreground"
|
|
21
|
+
|
|
22
|
+
const nameClass = variant === "light"
|
|
23
|
+
? "text-white font-semibold"
|
|
24
|
+
: "text-foreground font-semibold"
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className={`mt-4 flex shrink-0 items-center justify-between text-sm ${textClass}`}>
|
|
28
|
+
<div className={`flex items-center gap-3 tracking-tight ${nameClass}`}>
|
|
29
|
+
{logoUrl && (
|
|
30
|
+
<img
|
|
31
|
+
src={logoUrl}
|
|
32
|
+
alt={`${theme.name} Logo`}
|
|
33
|
+
className="h-8 w-auto"
|
|
34
|
+
/>
|
|
35
|
+
)}
|
|
36
|
+
<span className="text-lg">{theme.name}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="font-mono">
|
|
39
|
+
{slideNumber} / {totalSlides}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { Transition } from "framer-motion"
|
|
2
|
+
|
|
3
|
+
import { motion } from "framer-motion"
|
|
4
|
+
|
|
5
|
+
import { MORPH_TRANSITION } from "./animation-config"
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// TYPES
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
interface MorphProps {
|
|
12
|
+
layoutId: string
|
|
13
|
+
transition?: Transition
|
|
14
|
+
className?: string
|
|
15
|
+
children: React.ReactNode
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface MorphGroupProps {
|
|
19
|
+
groupId: string
|
|
20
|
+
transition?: Transition
|
|
21
|
+
className?: string
|
|
22
|
+
children: React.ReactNode
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// MORPH COMPONENT
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Wrapper component that enables morph/shared-layout animations between slides.
|
|
31
|
+
*
|
|
32
|
+
* Usage:
|
|
33
|
+
* ```tsx
|
|
34
|
+
* // Slide 1 - Large version
|
|
35
|
+
* <Morph layoutId="hero-title">
|
|
36
|
+
* <h1 className="text-6xl">Title</h1>
|
|
37
|
+
* </Morph>
|
|
38
|
+
*
|
|
39
|
+
* // Slide 2 - Small version (same layoutId = morphs between them)
|
|
40
|
+
* <Morph layoutId="hero-title">
|
|
41
|
+
* <h1 className="text-2xl">Title</h1>
|
|
42
|
+
* </Morph>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function Morph({
|
|
46
|
+
layoutId,
|
|
47
|
+
transition = MORPH_TRANSITION,
|
|
48
|
+
className,
|
|
49
|
+
children
|
|
50
|
+
}: MorphProps) {
|
|
51
|
+
return (
|
|
52
|
+
<motion.div
|
|
53
|
+
layout
|
|
54
|
+
layoutId={layoutId}
|
|
55
|
+
initial={false}
|
|
56
|
+
transition={transition}
|
|
57
|
+
className={className}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</motion.div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// =============================================================================
|
|
65
|
+
// MORPH GROUP COMPONENT
|
|
66
|
+
// =============================================================================
|
|
67
|
+
|
|
68
|
+
export function MorphGroup({
|
|
69
|
+
groupId,
|
|
70
|
+
transition = MORPH_TRANSITION,
|
|
71
|
+
className,
|
|
72
|
+
children
|
|
73
|
+
}: MorphGroupProps) {
|
|
74
|
+
return (
|
|
75
|
+
<motion.div
|
|
76
|
+
layout
|
|
77
|
+
layoutId={groupId}
|
|
78
|
+
initial={false}
|
|
79
|
+
transition={transition}
|
|
80
|
+
className={className}
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
</motion.div>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// MORPH ITEM (for use within MorphGroup pattern)
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
interface MorphItemProps {
|
|
92
|
+
id: string
|
|
93
|
+
prefix?: string
|
|
94
|
+
transition?: Transition
|
|
95
|
+
className?: string
|
|
96
|
+
children: React.ReactNode
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function MorphItem({
|
|
100
|
+
id,
|
|
101
|
+
prefix,
|
|
102
|
+
transition = MORPH_TRANSITION,
|
|
103
|
+
className,
|
|
104
|
+
children
|
|
105
|
+
}: MorphItemProps) {
|
|
106
|
+
const layoutId = prefix ? `${prefix}-${id}` : id
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<motion.div
|
|
110
|
+
layout
|
|
111
|
+
layoutId={layoutId}
|
|
112
|
+
initial={false}
|
|
113
|
+
transition={transition}
|
|
114
|
+
className={className}
|
|
115
|
+
>
|
|
116
|
+
{children}
|
|
117
|
+
</motion.div>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// MORPH TEXT (specialized for text that changes size)
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
interface MorphTextProps {
|
|
126
|
+
layoutId: string
|
|
127
|
+
as?: "h1" | "h2" | "h3" | "h4" | "p" | "span"
|
|
128
|
+
transition?: Transition
|
|
129
|
+
className?: string
|
|
130
|
+
children: React.ReactNode
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function MorphText({
|
|
134
|
+
layoutId,
|
|
135
|
+
as: Component = "span",
|
|
136
|
+
transition = MORPH_TRANSITION,
|
|
137
|
+
className,
|
|
138
|
+
children
|
|
139
|
+
}: MorphTextProps) {
|
|
140
|
+
const MotionComponent = motion[Component]
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<MotionComponent
|
|
144
|
+
layout
|
|
145
|
+
layoutId={layoutId}
|
|
146
|
+
initial={false}
|
|
147
|
+
transition={transition}
|
|
148
|
+
className={className}
|
|
149
|
+
>
|
|
150
|
+
{children}
|
|
151
|
+
</MotionComponent>
|
|
152
|
+
)
|
|
153
|
+
}
|