Skip to content

Commit

Permalink
feat: create useCSSTransition() (#7876)
Browse files Browse the repository at this point in the history
По аналогии `useCSSKeyframesAnimationController`, а также на основе компонента `CSSTransition` из [react-transitiong-group](https://reactcommunity.org/react-transition-group/css-transition) создал хук, чтобы можно было управлять переходами монтирования/размонитрования
  • Loading branch information
inomdzhon authored Nov 5, 2024
1 parent e985501 commit 5640748
Show file tree
Hide file tree
Showing 5 changed files with 862 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/vkui/src/lib/animation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ export { REDUCE_MOTION_MEDIA_QUERY, useReducedMotion } from './useReducedMotion'
export { rubberbandIfOutOfBounds } from './rubberbandIfOutOfBounds';
export { animationFadeClassNames } from './fades';
export { transformOriginClassNames } from './transformOrigin';
export {
type UseCSSTransitionState,
type UseCSSTransitionOptions,
type UseCSSTransition,
useCSSTransition,
} from './useCSSTransition';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.host {
--css-transition-duration: 1s;

inline-size: 50vh;
block-size: 50vh;
background-color: var(--vkui--color_background_accent);
border-radius: 8px;
}

.appear {
opacity: 0;
}

.appearing {
opacity: 1;
transition: opacity var(--css-transition-duration) ease-in-out;
}

.appeared {
opacity: 1;
}

.enter {
opacity: 0;
transform: translateY(-25%) rotate(25deg);
transform-origin: center center;
}

.entering {
opacity: 1;
transform: translateY(0) rotate(0deg);
transform-origin: center center;
transition:
transform var(--css-transition-duration) ease-in-out,
opacity var(--css-transition-duration) ease-in-out;
}

.entered {
opacity: 1;
}

.exit {
opacity: 1;
transform: translateY(0) rotate(0deg);
}

.exiting {
opacity: 0;
transform: translateY(-25%) rotate(25deg);
transform-origin: center center;
transition:
transform var(--css-transition-duration) ease-in-out,
opacity var(--css-transition-duration) ease-in-out;
}

.exited {
opacity: 0;
transform: translateY(-25%) rotate(25deg);
}
172 changes: 172 additions & 0 deletions packages/vkui/src/lib/animation/useCSSTransition.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/* eslint-disable no-console, import/no-default-export */
'use client';

import { type Meta, type StoryObj } from '@storybook/react';
import { classNames } from '@vkontakte/vkjs';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import type { CSSCustomProperties } from '../../types';
import {
useCSSTransition,
type UseCSSTransitionOptions,
type UseCSSTransitionState,
} from './useCSSTransition';
import styles from './useCSSTransition.stories.module.css';

interface DemoProps extends UseCSSTransitionOptions {
in: boolean;
duration: number;
}

const story: Meta<DemoProps> = {
title: 'DevTools/useCSSTransition',
tags: ['test'], // скрываем из публичной документации, т.к. хук внутренний
component: () => <div />,
parameters: { ...CanvasFullLayout, ...DisableCartesianParam },
argTypes: {
in: { control: { type: 'boolean' } },
enableAppear: { control: { type: 'boolean' } },
enableEnter: { control: { type: 'boolean' } },
enableExit: { control: { type: 'boolean' } },
duration: {
control: {
type: 'number',
},
table: {
defaultValue: {
summary: '⚠️ Это параметр данного Story',
},
},
},
},
args: {
duration: 1,
in: true,
enableAppear: true,
enableEnter: true,
enableExit: true,
onEnter(appear) {
console.log('onEnter', appear);
},
onEntering(appear) {
console.log('onEntering', appear);
},
onEntered(propertyName, appear) {
console.log('onEntered', propertyName, appear);
},
onExit() {
console.log('onExit');
},
onExiting() {
console.log('onExiting');
},
onExited(propertyName) {
console.log('onExited', propertyName);
},
},
};

export default story;

const transitionClassNames = {
appear: styles.appear,
appearing: styles.appearing,
appeared: styles.appeared,
enter: styles.enter,
entering: styles.entering,
entered: styles.entered,
exit: styles.exit,
exiting: styles.exiting,
exited: styles.exited,
};

export const WithClassNameAttribute: StoryObj<DemoProps> = {
render: function Render({ in: inProp, duration, ...restProps }) {
const [state, { ref, onTransitionEnd }] = useCSSTransition<HTMLDivElement>(inProp, restProps);

if (state === 'exited') {
return <div />;
}

return (
<div
ref={ref}
className={classNames(styles.host, transitionClassNames[state])}
style={
duration
? ({ '--css-transition-duration': `${duration}s` } as unknown as CSSCustomProperties)
: undefined
}
onTransitionEnd={onTransitionEnd}
/>
);
},
};

const getTransition = (state: UseCSSTransitionState, duration = 1) =>
({
appear: {
opacity: 0,
},

appearing: {
opacity: 1,
transition: `opacity ${duration}s ease-in-out`,
},

appeared: {
opacity: 1,
},

enter: {
opacity: 0,
transform: 'translateY(-25%) rotate(25deg)',
transformOrigin: 'center center',
},

entering: {
opacity: 1,
transform: 'translateY(0) rotate(0deg)',
transformOrigin: 'center center',
transition: `transform ${duration}s ease-in-out, opacity ${duration}s ease-in-out`,
},

entered: {
opacity: 1,
},

exit: {
opacity: 1,
transform: 'translateY(0) rotate(0deg)',
},

exiting: {
opacity: 0,
transform: 'translateY(-25%) rotate(25deg)',
transformOrigin: 'center center',
transition: `transform ${duration}s ease-in-out, opacity ${duration}s ease-in-out`,
},

exited: {
opacity: 0,
transform: 'translateY(-25%) rotate(25deg)',
},
})[state];

export const WithStyleAttribute: StoryObj<DemoProps> = {
render: function Render({ in: inProp, duration, ...restProps }) {
const [state, { ref, onTransitionEnd }] = useCSSTransition<HTMLDivElement>(inProp, restProps);

if (state === 'exited') {
return <div />;
}

return (
<div
ref={ref}
className={styles.host}
style={getTransition(state, duration)}
onTransitionEnd={onTransitionEnd}
/>
);
},
};
Loading

0 comments on commit 5640748

Please sign in to comment.