-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Animate: refactor to TypeScript #49243
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import type { AnimateProps, GetAnimateOptions } from './types'; | ||
|
||
/** | ||
* @param type The animation type | ||
* @return Default origin | ||
*/ | ||
function getDefaultOrigin( type?: GetAnimateOptions[ 'type' ] ) { | ||
return type === 'appear' ? 'top' : 'left'; | ||
} | ||
|
||
/** | ||
* @param options | ||
* | ||
* @return ClassName that applies the animations | ||
*/ | ||
export function getAnimateClassName( options: GetAnimateOptions ) { | ||
if ( options.type === 'loading' ) { | ||
return classnames( 'components-animate__loading' ); | ||
} | ||
|
||
const { type, origin = getDefaultOrigin( type ) } = options; | ||
|
||
if ( type === 'appear' ) { | ||
const [ yAxis, xAxis = 'center' ] = origin.split( ' ' ); | ||
return classnames( 'components-animate__appear', { | ||
[ 'is-from-' + xAxis ]: xAxis !== 'center', | ||
[ 'is-from-' + yAxis ]: yAxis !== 'middle', | ||
} ); | ||
} | ||
|
||
if ( type === 'slide-in' ) { | ||
return classnames( | ||
'components-animate__slide-in', | ||
'is-from-' + origin | ||
); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
/** | ||
* Simple interface to introduce animations to components. | ||
* | ||
* ```jsx | ||
* import { Animate, Notice } from '@wordpress/components'; | ||
* | ||
* const MyAnimatedNotice = () => ( | ||
* <Animate type="slide-in" options={ { origin: 'top' } }> | ||
* { ( { className } ) => ( | ||
* <Notice className={ className } status="success"> | ||
* <p>Animation finished.</p> | ||
* </Notice> | ||
* ) } | ||
* </Animate> | ||
* ); | ||
* ``` | ||
*/ | ||
export function Animate( { type, options = {}, children }: AnimateProps ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It felt odd that we're allowing Anyway, this was like that before, and I've confirmed that the distributive conditional type support that use case correctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also felt odd to me, but then I saw that the Default Storybook example explicitly mentions that the lack of |
||
return children( { | ||
className: getAnimateClassName( { | ||
type, | ||
...options, | ||
} as GetAnimateOptions ), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This typecast is not ideal, but there wasn't an easy way around it. Given that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough 👍 |
||
} ); | ||
} | ||
|
||
export default Animate; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import type { ComponentMeta, ComponentStory } from '@storybook/react'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { Animate } from '..'; | ||
import Notice from '../../notice'; | ||
|
||
const meta: ComponentMeta< typeof Animate > = { | ||
title: 'Components/Animate', | ||
component: Animate, | ||
parameters: { | ||
controls: { expanded: true }, | ||
docs: { source: { state: 'open' } }, | ||
}, | ||
}; | ||
export default meta; | ||
|
||
const Template: ComponentStory< typeof Animate > = ( props ) => ( | ||
<Animate { ...props } /> | ||
); | ||
|
||
export const Default: ComponentStory< typeof Animate > = Template.bind( {} ); | ||
Default.args = { | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>{ `No default animation. Use one of type = "appear", "slide-in", or "loading".` }</p> | ||
</Notice> | ||
), | ||
}; | ||
|
||
export const AppearTopLeft: ComponentStory< typeof Animate > = Template.bind( | ||
{} | ||
); | ||
AppearTopLeft.args = { | ||
type: 'appear', | ||
options: { origin: 'top left' }, | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>Appear animation. Origin: top left.</p> | ||
</Notice> | ||
), | ||
}; | ||
export const AppearTopRight: ComponentStory< typeof Animate > = Template.bind( | ||
{} | ||
); | ||
AppearTopRight.args = { | ||
type: 'appear', | ||
options: { origin: 'top right' }, | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>Appear animation. Origin: top right.</p> | ||
</Notice> | ||
), | ||
}; | ||
export const AppearBottomLeft: ComponentStory< typeof Animate > = Template.bind( | ||
{} | ||
); | ||
AppearBottomLeft.args = { | ||
type: 'appear', | ||
options: { origin: 'bottom left' }, | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>Appear animation. Origin: bottom left.</p> | ||
</Notice> | ||
), | ||
}; | ||
export const AppearBottomRight: ComponentStory< typeof Animate > = | ||
Template.bind( {} ); | ||
AppearBottomRight.args = { | ||
type: 'appear', | ||
options: { origin: 'bottom right' }, | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>Appear animation. Origin: bottom right.</p> | ||
</Notice> | ||
), | ||
}; | ||
|
||
export const Loading: ComponentStory< typeof Animate > = Template.bind( {} ); | ||
Loading.args = { | ||
type: 'loading', | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>Loading animation.</p> | ||
</Notice> | ||
), | ||
}; | ||
|
||
export const SlideIn: ComponentStory< typeof Animate > = Template.bind( {} ); | ||
SlideIn.args = { | ||
type: 'slide-in', | ||
options: { origin: 'left' }, | ||
children: ( { className } ) => ( | ||
<Notice className={ className } status="success"> | ||
<p>Slide-in animation.</p> | ||
</Notice> | ||
), | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export type AppearOptions = { | ||
type: 'appear'; | ||
origin?: | ||
| 'top' | ||
| 'top left' | ||
| 'top right' | ||
| 'middle' | ||
| 'middle left' | ||
| 'middle right' | ||
| 'bottom' | ||
| 'bottom left' | ||
| 'bottom right'; | ||
}; | ||
type SlideInOptions = { type: 'slide-in'; origin?: 'left' | 'right' }; | ||
type LoadingOptions = { type: 'loading'; origin?: never }; | ||
type NoAnimationOptions = { type?: never; origin?: never }; | ||
|
||
export type GetAnimateOptions = | ||
| AppearOptions | ||
| SlideInOptions | ||
| LoadingOptions | ||
| NoAnimationOptions; | ||
|
||
// Create a new type that and distributes the `Pick` operator separately to | ||
// every individual type of a union, thus preserving that same union. | ||
type DistributiveTypeAndOptions< T extends { type?: any } > = T extends any | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chad1008 does this look familiar? |
||
? Pick< T, 'type' > & { options?: Omit< T, 'type' > } | ||
: never; | ||
|
||
export type AnimateProps = DistributiveTypeAndOptions< GetAnimateOptions > & { | ||
children: ( props: { className?: string } ) => JSX.Element; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that there shouldn't be runtime changes in this file