1
This commit is contained in:
+9
@@ -0,0 +1,9 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019-present afc163
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
# @rc-component/motion
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![npm download][download-image]][download-url]
|
||||
[![build status][github-actions-image]][github-actions-url]
|
||||
[![Codecov][codecov-image]][codecov-url]
|
||||
[![bundle size][bundlephobia-image]][bundlephobia-url]
|
||||
[![dumi][dumi-image]][dumi-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/@rc-component/motion.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/@rc-component/motion
|
||||
[travis-image]: https://img.shields.io/travis/react-component/motion/master?style=flat-square
|
||||
[travis-url]: https://travis-ci.com/react-component/motion
|
||||
[github-actions-image]: https://github.com/react-component/motion/actions/workflows/main.yml/badge.svg
|
||||
[github-actions-url]: https://github.com/react-component/motion/actions/workflows/main.yml
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/motion/master.svg?style=flat-square
|
||||
[codecov-url]: https://app.codecov.io/gh/react-component/motion
|
||||
[david-url]: https://david-dm.org/react-component/motion
|
||||
[david-image]: https://david-dm.org/react-component/motion/status.svg?style=flat-square
|
||||
[david-dev-url]: https://david-dm.org/react-component/motion?type=dev
|
||||
[david-dev-image]: https://david-dm.org/react-component/motion/dev-status.svg?style=flat-square
|
||||
[download-image]: https://img.shields.io/npm/dm/@rc-component/motion.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/@rc-component/motion
|
||||
[bundlephobia-url]: https://bundlephobia.com/package/@rc-component/motion
|
||||
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@rc-component/motion
|
||||
[dumi-url]: https://github.com/umijs/dumi
|
||||
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
|
||||
|
||||
React lifecycle controlled motion library.
|
||||
|
||||
## Live Demo
|
||||
|
||||
https://react-component.github.io/motion/
|
||||
|
||||
## Install
|
||||
|
||||
[](https://npmjs.org/package/@rc-component/motion)
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import CSSMotion from '@rc-component/motion';
|
||||
|
||||
export default ({ visible }) => (
|
||||
<CSSMotion visible={visible} motionName="my-motion">
|
||||
{({ className, style }) => <div className={className} style={style} />}
|
||||
</CSSMotion>
|
||||
);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### CSSMotion
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| motionName | string | - | Config motion name, will dynamic update when status changed |
|
||||
| visible | boolean | true | Trigger motion events |
|
||||
| motionAppear | boolean | true | Use motion when appear |
|
||||
| motionEnter | boolean | true | Use motion when enter |
|
||||
| motionLeave | boolean | true | Use motion when leave |
|
||||
| motionLeaveImmediately | boolean | - | Will trigger leave even on mount |
|
||||
| motionDeadline | number | - | Trigger motion status change even when motion event not fire |
|
||||
| removeOnLeave | boolean | true | Remove element when motion leave end |
|
||||
| leavedClassName | string | - | Set leaved element className |
|
||||
| onAppearStart | (HTMLElement, Event) => CSSProperties \| void; | - | Trigger when appear start, return style will patch to element |
|
||||
| onEnterStart | (HTMLElement, Event) => CSSProperties \| void; | - | Trigger when enter start, return style will patch to element |
|
||||
| onLeaveStart | (HTMLElement, Event) => CSSProperties \| void; | - | Trigger when leave start, return style will patch to element |
|
||||
| onAppearActive | (HTMLElement, Event) => CSSProperties \| void; | - | Trigger when appear active, return style will patch to element |
|
||||
| onEnterActive | (HTMLElement, Event) => CSSProperties \| void; | - | Trigger when enter active, return style will patch to element |
|
||||
| onLeaveActive | (HTMLElement, Event) => CSSProperties \| void; | - | Trigger when leave active, return style will patch to element |
|
||||
| onAppearEnd | (HTMLElement, Event) => boolean \| void; | - | Trigger when appear end, will not finish when return false |
|
||||
| onEnterEnd | (HTMLElement, Event) => boolean \| void; | - | Trigger when enter end, will not finish when return false |
|
||||
| onLeaveEnd | (HTMLElement, Event) => boolean \| void; | - | Trigger when leave end, will not finish when return false |
|
||||
|
||||
### CSSMotionList
|
||||
|
||||
extends all the props from [CSSMotion](#CSSMotion)
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| --------- | ----------------------------- | ------- | ----------------- |
|
||||
| keys | React.Key[] | - | Motion list keys |
|
||||
| component | string \| React.ComponentType | div | wrapper component |
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@rc-component/motion is released under the MIT license.
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import * as React from 'react';
|
||||
import type { MotionEndEventHandler, MotionEventHandler, MotionPrepareEventHandler, MotionStatus } from './interface';
|
||||
export interface CSSMotionRef {
|
||||
nativeElement: HTMLElement;
|
||||
inMotion: () => boolean;
|
||||
enableMotion: () => boolean;
|
||||
}
|
||||
export type CSSMotionConfig = boolean | {
|
||||
transitionSupport?: boolean;
|
||||
};
|
||||
export type MotionName = string | {
|
||||
appear?: string;
|
||||
enter?: string;
|
||||
leave?: string;
|
||||
appearActive?: string;
|
||||
enterActive?: string;
|
||||
leaveActive?: string;
|
||||
};
|
||||
export interface CSSMotionProps {
|
||||
motionName?: MotionName;
|
||||
visible?: boolean;
|
||||
motionAppear?: boolean;
|
||||
motionEnter?: boolean;
|
||||
motionLeave?: boolean;
|
||||
motionLeaveImmediately?: boolean;
|
||||
motionDeadline?: number;
|
||||
/**
|
||||
* Create element in view even the element is invisible.
|
||||
* Will patch `display: none` style on it.
|
||||
*/
|
||||
forceRender?: boolean;
|
||||
/**
|
||||
* Remove element when motion end. This will not work when `forceRender` is set.
|
||||
*/
|
||||
removeOnLeave?: boolean;
|
||||
leavedClassName?: string;
|
||||
/** @private Used by CSSMotionList. Do not use in your production. */
|
||||
eventProps?: object;
|
||||
/** Prepare phase is used for measure element info. It will always trigger even motion is off */
|
||||
onAppearPrepare?: MotionPrepareEventHandler;
|
||||
/** Prepare phase is used for measure element info. It will always trigger even motion is off */
|
||||
onEnterPrepare?: MotionPrepareEventHandler;
|
||||
/** Prepare phase is used for measure element info. It will always trigger even motion is off */
|
||||
onLeavePrepare?: MotionPrepareEventHandler;
|
||||
onAppearStart?: MotionEventHandler;
|
||||
onEnterStart?: MotionEventHandler;
|
||||
onLeaveStart?: MotionEventHandler;
|
||||
onAppearActive?: MotionEventHandler;
|
||||
onEnterActive?: MotionEventHandler;
|
||||
onLeaveActive?: MotionEventHandler;
|
||||
onAppearEnd?: MotionEndEventHandler;
|
||||
onEnterEnd?: MotionEndEventHandler;
|
||||
onLeaveEnd?: MotionEndEventHandler;
|
||||
/** This will always trigger after final visible changed. Even if no motion configured. */
|
||||
onVisibleChanged?: (visible: boolean) => void;
|
||||
internalRef?: React.Ref<any>;
|
||||
children?: (props: {
|
||||
visible?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
[key: string]: any;
|
||||
}, ref: React.Ref<any>) => React.ReactElement;
|
||||
}
|
||||
export interface CSSMotionState {
|
||||
status?: MotionStatus;
|
||||
statusActive?: boolean;
|
||||
newStatus?: boolean;
|
||||
statusStyle?: React.CSSProperties;
|
||||
prevProps?: CSSMotionProps;
|
||||
}
|
||||
export declare function isRefNotConsumed(children?: CSSMotionProps['children']): boolean;
|
||||
/**
|
||||
* `transitionSupport` is used for none transition test case.
|
||||
* Default we use browser transition event support check.
|
||||
*/
|
||||
export declare function genCSSMotion(config: CSSMotionConfig): React.ForwardRefExoticComponent<CSSMotionProps & React.RefAttributes<CSSMotionRef>>;
|
||||
declare const _default: React.ForwardRefExoticComponent<CSSMotionProps & React.RefAttributes<CSSMotionRef>>;
|
||||
export default _default;
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/* eslint-disable react/default-props-match-prop-types, react/no-multi-comp, react/prop-types */
|
||||
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
|
||||
import { composeRef, getNodeRef, supportNodeRef } from "@rc-component/util/es/ref";
|
||||
import { clsx } from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { useRef } from 'react';
|
||||
import { Context } from "./context";
|
||||
import useStatus from "./hooks/useStatus";
|
||||
import { isActive } from "./hooks/useStepQueue";
|
||||
import { STATUS_NONE, STEP_PREPARE, STEP_START } from "./interface";
|
||||
import { getTransitionName, supportTransition } from "./util/motion";
|
||||
export function isRefNotConsumed(children) {
|
||||
return children?.length < 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* `transitionSupport` is used for none transition test case.
|
||||
* Default we use browser transition event support check.
|
||||
*/
|
||||
export function genCSSMotion(config) {
|
||||
let transitionSupport = config;
|
||||
if (typeof config === 'object') {
|
||||
({
|
||||
transitionSupport
|
||||
} = config);
|
||||
}
|
||||
function isSupportTransition(props, contextMotion) {
|
||||
return !!(props.motionName && transitionSupport && contextMotion !== false);
|
||||
}
|
||||
const CSSMotion = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
const {
|
||||
// Default config
|
||||
visible = true,
|
||||
removeOnLeave = true,
|
||||
forceRender,
|
||||
children,
|
||||
motionName,
|
||||
leavedClassName,
|
||||
eventProps
|
||||
} = props;
|
||||
const {
|
||||
motion: contextMotion
|
||||
} = React.useContext(Context);
|
||||
const supportMotion = isSupportTransition(props, contextMotion);
|
||||
|
||||
// Ref to the react node, it may be a HTMLElement
|
||||
const nodeRef = useRef();
|
||||
function getDomElement() {
|
||||
return getDOM(nodeRef.current);
|
||||
}
|
||||
const [getStatus, statusStep, statusStyle, mergedVisible, styleReady] = useStatus(supportMotion, visible, getDomElement, props);
|
||||
const status = getStatus();
|
||||
|
||||
// Record whether content has rendered
|
||||
// Will return null for un-rendered even when `removeOnLeave={false}`
|
||||
const renderedRef = React.useRef(mergedVisible);
|
||||
if (mergedVisible) {
|
||||
renderedRef.current = true;
|
||||
}
|
||||
|
||||
// ====================== Refs ======================
|
||||
const refObj = React.useMemo(() => {
|
||||
const obj = {};
|
||||
Object.defineProperties(obj, {
|
||||
nativeElement: {
|
||||
enumerable: true,
|
||||
get: getDomElement
|
||||
},
|
||||
inMotion: {
|
||||
enumerable: true,
|
||||
get: () => () => getStatus() !== STATUS_NONE
|
||||
},
|
||||
enableMotion: {
|
||||
enumerable: true,
|
||||
get: () => () => supportMotion
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}, []);
|
||||
|
||||
// We lock `deps` here since function return object
|
||||
// will repeat trigger ref from `refConfig` -> `null` -> `refConfig`
|
||||
React.useImperativeHandle(ref, () => refObj, []);
|
||||
|
||||
// ===================== Render =====================
|
||||
// return motionChildren as React.ReactElement;
|
||||
const idRef = React.useRef(0);
|
||||
if (styleReady) {
|
||||
idRef.current += 1;
|
||||
}
|
||||
|
||||
// We should render children when motionStyle is sync with stepStatus
|
||||
const returnNode = React.useMemo(() => {
|
||||
if (styleReady === 'NONE') {
|
||||
return null;
|
||||
}
|
||||
let motionChildren;
|
||||
const mergedProps = {
|
||||
...eventProps,
|
||||
visible
|
||||
};
|
||||
if (!children) {
|
||||
// No children
|
||||
motionChildren = null;
|
||||
} else if (status === STATUS_NONE) {
|
||||
// Stable children
|
||||
if (mergedVisible) {
|
||||
motionChildren = children({
|
||||
...mergedProps
|
||||
}, nodeRef);
|
||||
} else if (!removeOnLeave && renderedRef.current && leavedClassName) {
|
||||
motionChildren = children({
|
||||
...mergedProps,
|
||||
className: leavedClassName
|
||||
}, nodeRef);
|
||||
} else if (forceRender || !removeOnLeave && !leavedClassName) {
|
||||
motionChildren = children({
|
||||
...mergedProps,
|
||||
style: {
|
||||
display: 'none'
|
||||
}
|
||||
}, nodeRef);
|
||||
} else {
|
||||
motionChildren = null;
|
||||
}
|
||||
} else {
|
||||
// In motion
|
||||
let statusSuffix;
|
||||
if (statusStep === STEP_PREPARE) {
|
||||
statusSuffix = 'prepare';
|
||||
} else if (isActive(statusStep)) {
|
||||
statusSuffix = 'active';
|
||||
} else if (statusStep === STEP_START) {
|
||||
statusSuffix = 'start';
|
||||
}
|
||||
const motionCls = getTransitionName(motionName, `${status}-${statusSuffix}`);
|
||||
motionChildren = children({
|
||||
...mergedProps,
|
||||
className: clsx(getTransitionName(motionName, status), {
|
||||
[motionCls]: motionCls && statusSuffix,
|
||||
[motionName]: typeof motionName === 'string'
|
||||
}),
|
||||
style: statusStyle
|
||||
}, nodeRef);
|
||||
}
|
||||
return motionChildren;
|
||||
}, [idRef.current]);
|
||||
if (isRefNotConsumed(children) && supportNodeRef(returnNode)) {
|
||||
const originNodeRef = getNodeRef(returnNode);
|
||||
if (originNodeRef !== nodeRef) {
|
||||
return /*#__PURE__*/React.cloneElement(returnNode, {
|
||||
ref: composeRef(originNodeRef, nodeRef)
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnNode;
|
||||
});
|
||||
CSSMotion.displayName = 'CSSMotion';
|
||||
return CSSMotion;
|
||||
}
|
||||
export default genCSSMotion(supportTransition);
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import type { CSSMotionProps } from './CSSMotion';
|
||||
import type { KeyObject } from './util/diff';
|
||||
export interface CSSMotionListProps extends Omit<CSSMotionProps, 'onVisibleChanged' | 'children'>, Omit<React.HTMLAttributes<any>, 'children'> {
|
||||
keys: (React.Key | {
|
||||
key: React.Key;
|
||||
[name: string]: any;
|
||||
})[];
|
||||
component?: string | React.ComponentType | false;
|
||||
/** This will always trigger after final visible changed. Even if no motion configured. */
|
||||
onVisibleChanged?: (visible: boolean, info: {
|
||||
key: React.Key;
|
||||
}) => void;
|
||||
/** All motion leaves in the screen */
|
||||
onAllRemoved?: () => void;
|
||||
children?: (props: {
|
||||
visible?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
index?: number;
|
||||
[key: string]: any;
|
||||
}, ref: React.Ref<any>) => React.ReactElement;
|
||||
}
|
||||
export interface CSSMotionListState {
|
||||
keyEntities: KeyObject[];
|
||||
}
|
||||
/**
|
||||
* Generate a CSSMotionList component with config
|
||||
* @param transitionSupport No need since CSSMotionList no longer depends on transition support
|
||||
* @param CSSMotion CSSMotion component
|
||||
*/
|
||||
export declare function genCSSMotionList(transitionSupport: boolean, CSSMotion?: React.ForwardRefExoticComponent<CSSMotionProps & React.RefAttributes<import("./CSSMotion").CSSMotionRef>>): React.ComponentClass<CSSMotionListProps>;
|
||||
declare const _default: React.ComponentClass<CSSMotionListProps, any>;
|
||||
export default _default;
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
/* eslint react/prop-types: 0 */
|
||||
import * as React from 'react';
|
||||
import OriginCSSMotion, { isRefNotConsumed } from "./CSSMotion";
|
||||
import { diffKeys, parseKeys, STATUS_ADD, STATUS_KEEP, STATUS_REMOVE, STATUS_REMOVED } from "./util/diff";
|
||||
import { supportTransition } from "./util/motion";
|
||||
const MOTION_PROP_NAMES = ['eventProps', 'visible', 'children', 'motionName', 'motionAppear', 'motionEnter', 'motionLeave', 'motionLeaveImmediately', 'motionDeadline', 'removeOnLeave', 'leavedClassName', 'onAppearPrepare', 'onAppearStart', 'onAppearActive', 'onAppearEnd', 'onEnterStart', 'onEnterActive', 'onEnterEnd', 'onLeaveStart', 'onLeaveActive', 'onLeaveEnd'];
|
||||
/**
|
||||
* Generate a CSSMotionList component with config
|
||||
* @param transitionSupport No need since CSSMotionList no longer depends on transition support
|
||||
* @param CSSMotion CSSMotion component
|
||||
*/
|
||||
export function genCSSMotionList(transitionSupport, CSSMotion = OriginCSSMotion) {
|
||||
class CSSMotionList extends React.Component {
|
||||
static defaultProps = {
|
||||
component: 'div'
|
||||
};
|
||||
state = {
|
||||
keyEntities: []
|
||||
};
|
||||
static getDerivedStateFromProps({
|
||||
keys
|
||||
}, {
|
||||
keyEntities
|
||||
}) {
|
||||
const parsedKeyObjects = parseKeys(keys);
|
||||
const mixedKeyEntities = diffKeys(keyEntities, parsedKeyObjects);
|
||||
return {
|
||||
keyEntities: mixedKeyEntities.filter(entity => {
|
||||
const prevEntity = keyEntities.find(({
|
||||
key
|
||||
}) => entity.key === key);
|
||||
|
||||
// Remove if already mark as removed
|
||||
if (prevEntity && prevEntity.status === STATUS_REMOVED && entity.status === STATUS_REMOVE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// ZombieJ: Return the count of rest keys. It's safe to refactor if need more info.
|
||||
removeKey = removeKey => {
|
||||
this.setState(prevState => {
|
||||
const nextKeyEntities = prevState.keyEntities.map(entity => {
|
||||
if (entity.key !== removeKey) return entity;
|
||||
return {
|
||||
...entity,
|
||||
status: STATUS_REMOVED
|
||||
};
|
||||
});
|
||||
return {
|
||||
keyEntities: nextKeyEntities
|
||||
};
|
||||
}, () => {
|
||||
const {
|
||||
keyEntities
|
||||
} = this.state;
|
||||
const restKeysCount = keyEntities.filter(({
|
||||
status
|
||||
}) => status !== STATUS_REMOVED).length;
|
||||
if (restKeysCount === 0 && this.props.onAllRemoved) {
|
||||
this.props.onAllRemoved();
|
||||
}
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const {
|
||||
keyEntities
|
||||
} = this.state;
|
||||
const {
|
||||
component,
|
||||
children,
|
||||
onVisibleChanged,
|
||||
onAllRemoved,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const Component = component || React.Fragment;
|
||||
const motionProps = {};
|
||||
MOTION_PROP_NAMES.forEach(prop => {
|
||||
motionProps[prop] = restProps[prop];
|
||||
delete restProps[prop];
|
||||
});
|
||||
delete restProps.keys;
|
||||
return /*#__PURE__*/React.createElement(Component, restProps, keyEntities.map(({
|
||||
status,
|
||||
...eventProps
|
||||
}, index) => {
|
||||
const visible = status === STATUS_ADD || status === STATUS_KEEP;
|
||||
return /*#__PURE__*/React.createElement(CSSMotion, _extends({}, motionProps, {
|
||||
key: eventProps.key,
|
||||
visible: visible,
|
||||
eventProps: eventProps,
|
||||
onVisibleChanged: changedVisible => {
|
||||
onVisibleChanged?.(changedVisible, {
|
||||
key: eventProps.key
|
||||
});
|
||||
if (!changedVisible) {
|
||||
this.removeKey(eventProps.key);
|
||||
}
|
||||
}
|
||||
}), isRefNotConsumed(children) ? props => children({
|
||||
...props,
|
||||
index
|
||||
}) : (props, ref) => children({
|
||||
...props,
|
||||
index
|
||||
}, ref));
|
||||
}));
|
||||
}
|
||||
}
|
||||
return CSSMotionList;
|
||||
}
|
||||
export default genCSSMotionList(supportTransition);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
interface MotionContextProps {
|
||||
motion?: boolean;
|
||||
}
|
||||
export declare const Context: React.Context<MotionContextProps>;
|
||||
declare const MotionProvider: React.FC<React.PropsWithChildren<MotionContextProps>>;
|
||||
export default MotionProvider;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import * as React from 'react';
|
||||
export const Context = /*#__PURE__*/React.createContext({});
|
||||
const MotionProvider = props => {
|
||||
const {
|
||||
children,
|
||||
...rest
|
||||
} = props;
|
||||
const memoizedValue = React.useMemo(() => {
|
||||
return {
|
||||
motion: rest.motion
|
||||
};
|
||||
}, [rest.motion]);
|
||||
return /*#__PURE__*/React.createElement(Context.Provider, {
|
||||
value: memoizedValue
|
||||
}, children);
|
||||
};
|
||||
export default MotionProvider;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { MotionEvent } from '../interface';
|
||||
declare const _default: (onInternalMotionEnd: (event: MotionEvent) => void) => [(element: HTMLElement) => void, (element: HTMLElement) => void];
|
||||
export default _default;
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import { useRef } from 'react';
|
||||
import { animationEndName, transitionEndName } from "../util/motion";
|
||||
export default (onInternalMotionEnd => {
|
||||
const cacheElementRef = useRef();
|
||||
|
||||
// Remove events
|
||||
function removeMotionEvents(element) {
|
||||
if (element) {
|
||||
element.removeEventListener(transitionEndName, onInternalMotionEnd);
|
||||
element.removeEventListener(animationEndName, onInternalMotionEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Patch events
|
||||
function patchMotionEvents(element) {
|
||||
if (cacheElementRef.current && cacheElementRef.current !== element) {
|
||||
removeMotionEvents(cacheElementRef.current);
|
||||
}
|
||||
if (element && element !== cacheElementRef.current) {
|
||||
element.addEventListener(transitionEndName, onInternalMotionEnd);
|
||||
element.addEventListener(animationEndName, onInternalMotionEnd);
|
||||
|
||||
// Save as cache in case dom removed trigger by `motionDeadline`
|
||||
cacheElementRef.current = element;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up when removed
|
||||
React.useEffect(() => () => {
|
||||
removeMotionEvents(cacheElementRef.current);
|
||||
cacheElementRef.current = null;
|
||||
}, []);
|
||||
return [patchMotionEvents, removeMotionEvents];
|
||||
});
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import { useEffect } from 'react';
|
||||
declare const useIsomorphicLayoutEffect: typeof useEffect;
|
||||
export default useIsomorphicLayoutEffect;
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import canUseDom from "@rc-component/util/es/Dom/canUseDom";
|
||||
import { useEffect, useLayoutEffect } from 'react';
|
||||
|
||||
// It's safe to use `useLayoutEffect` but the warning is annoying
|
||||
const useIsomorphicLayoutEffect = canUseDom() ? useLayoutEffect : useEffect;
|
||||
export default useIsomorphicLayoutEffect;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
declare const _default: () => [(callback: (info: {
|
||||
isCanceled: () => boolean;
|
||||
}) => void) => void, () => void];
|
||||
export default _default;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import raf from "@rc-component/util/es/raf";
|
||||
import * as React from 'react';
|
||||
export default (() => {
|
||||
const nextFrameRef = React.useRef(null);
|
||||
function cancelNextFrame() {
|
||||
raf.cancel(nextFrameRef.current);
|
||||
}
|
||||
function nextFrame(callback, delay = 2) {
|
||||
cancelNextFrame();
|
||||
const nextFrameId = raf(() => {
|
||||
if (delay <= 1) {
|
||||
callback({
|
||||
isCanceled: () => nextFrameId !== nextFrameRef.current
|
||||
});
|
||||
} else {
|
||||
nextFrame(callback, delay - 1);
|
||||
}
|
||||
});
|
||||
nextFrameRef.current = nextFrameId;
|
||||
}
|
||||
React.useEffect(() => () => {
|
||||
cancelNextFrame();
|
||||
}, []);
|
||||
return [nextFrame, cancelNextFrame];
|
||||
});
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import type { CSSMotionProps } from '../CSSMotion';
|
||||
import type { MotionStatus, StepStatus } from '../interface';
|
||||
export default function useStatus(supportMotion: boolean, visible: boolean, getElement: () => HTMLElement, { motionEnter, motionAppear, motionLeave, motionDeadline, motionLeaveImmediately, onAppearPrepare, onEnterPrepare, onLeavePrepare, onAppearStart, onEnterStart, onLeaveStart, onAppearActive, onEnterActive, onLeaveActive, onAppearEnd, onEnterEnd, onLeaveEnd, onVisibleChanged, }: CSSMotionProps): [
|
||||
status: () => MotionStatus,
|
||||
stepStatus: StepStatus,
|
||||
style: React.CSSProperties,
|
||||
visible: boolean,
|
||||
styleReady: 'NONE' | boolean
|
||||
];
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
import { useEvent } from '@rc-component/util';
|
||||
import useSyncState from "@rc-component/util/es/hooks/useSyncState";
|
||||
import * as React from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { STATUS_APPEAR, STATUS_ENTER, STATUS_LEAVE, STATUS_NONE, STEP_ACTIVE, STEP_PREPARE, STEP_PREPARED, STEP_START } from "../interface";
|
||||
import useDomMotionEvents from "./useDomMotionEvents";
|
||||
import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect";
|
||||
import useStepQueue, { DoStep, isActive, SkipStep } from "./useStepQueue";
|
||||
export default function useStatus(supportMotion, visible, getElement, {
|
||||
motionEnter = true,
|
||||
motionAppear = true,
|
||||
motionLeave = true,
|
||||
motionDeadline,
|
||||
motionLeaveImmediately,
|
||||
onAppearPrepare,
|
||||
onEnterPrepare,
|
||||
onLeavePrepare,
|
||||
onAppearStart,
|
||||
onEnterStart,
|
||||
onLeaveStart,
|
||||
onAppearActive,
|
||||
onEnterActive,
|
||||
onLeaveActive,
|
||||
onAppearEnd,
|
||||
onEnterEnd,
|
||||
onLeaveEnd,
|
||||
onVisibleChanged
|
||||
}) {
|
||||
// Used for outer render usage to avoid `visible: false & status: none` to render nothing
|
||||
const [asyncVisible, setAsyncVisible] = React.useState();
|
||||
const [getStatus, setStatus] = useSyncState(STATUS_NONE);
|
||||
const [style, setStyle] = React.useState([null, null]);
|
||||
const currentStatus = getStatus();
|
||||
const mountedRef = useRef(false);
|
||||
const deadlineRef = useRef(null);
|
||||
|
||||
// =========================== Dom Node ===========================
|
||||
function getDomElement() {
|
||||
return getElement();
|
||||
}
|
||||
|
||||
// ========================== Motion End ==========================
|
||||
const activeRef = useRef(false);
|
||||
|
||||
/**
|
||||
* Clean up status & style
|
||||
*/
|
||||
function updateMotionEndStatus() {
|
||||
setStatus(STATUS_NONE);
|
||||
setStyle([null, null]);
|
||||
}
|
||||
const onInternalMotionEnd = useEvent(event => {
|
||||
const status = getStatus();
|
||||
// Do nothing since not in any transition status.
|
||||
// This may happen when `motionDeadline` trigger.
|
||||
if (status === STATUS_NONE) {
|
||||
return;
|
||||
}
|
||||
const element = getDomElement();
|
||||
if (event && !event.deadline && event.target !== element) {
|
||||
// event exists
|
||||
// not initiated by deadline
|
||||
// transitionEnd not fired by inner elements
|
||||
return;
|
||||
}
|
||||
const currentActive = activeRef.current;
|
||||
let canEnd;
|
||||
if (status === STATUS_APPEAR && currentActive) {
|
||||
canEnd = onAppearEnd?.(element, event);
|
||||
} else if (status === STATUS_ENTER && currentActive) {
|
||||
canEnd = onEnterEnd?.(element, event);
|
||||
} else if (status === STATUS_LEAVE && currentActive) {
|
||||
canEnd = onLeaveEnd?.(element, event);
|
||||
}
|
||||
|
||||
// Only update status when `canEnd` and not destroyed
|
||||
if (currentActive && canEnd !== false) {
|
||||
updateMotionEndStatus();
|
||||
}
|
||||
});
|
||||
const [patchMotionEvents] = useDomMotionEvents(onInternalMotionEnd);
|
||||
|
||||
// ============================= Step =============================
|
||||
const getEventHandlers = targetStatus => {
|
||||
switch (targetStatus) {
|
||||
case STATUS_APPEAR:
|
||||
return {
|
||||
[STEP_PREPARE]: onAppearPrepare,
|
||||
[STEP_START]: onAppearStart,
|
||||
[STEP_ACTIVE]: onAppearActive
|
||||
};
|
||||
case STATUS_ENTER:
|
||||
return {
|
||||
[STEP_PREPARE]: onEnterPrepare,
|
||||
[STEP_START]: onEnterStart,
|
||||
[STEP_ACTIVE]: onEnterActive
|
||||
};
|
||||
case STATUS_LEAVE:
|
||||
return {
|
||||
[STEP_PREPARE]: onLeavePrepare,
|
||||
[STEP_START]: onLeaveStart,
|
||||
[STEP_ACTIVE]: onLeaveActive
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
const eventHandlers = React.useMemo(() => getEventHandlers(currentStatus), [currentStatus]);
|
||||
const [startStep, step] = useStepQueue(currentStatus, !supportMotion, newStep => {
|
||||
// Only prepare step can be skip
|
||||
if (newStep === STEP_PREPARE) {
|
||||
const onPrepare = eventHandlers[STEP_PREPARE];
|
||||
if (!onPrepare) {
|
||||
return SkipStep;
|
||||
}
|
||||
return onPrepare(getDomElement());
|
||||
}
|
||||
|
||||
// Rest step is sync update
|
||||
if (newStep in eventHandlers) {
|
||||
setStyle([eventHandlers[newStep]?.(getDomElement(), null) || null, newStep]);
|
||||
}
|
||||
if (newStep === STEP_ACTIVE && currentStatus !== STATUS_NONE) {
|
||||
// Patch events when motion needed
|
||||
patchMotionEvents(getDomElement());
|
||||
if (motionDeadline > 0) {
|
||||
clearTimeout(deadlineRef.current);
|
||||
deadlineRef.current = setTimeout(() => {
|
||||
onInternalMotionEnd({
|
||||
deadline: true
|
||||
});
|
||||
}, motionDeadline);
|
||||
}
|
||||
}
|
||||
if (newStep === STEP_PREPARED) {
|
||||
updateMotionEndStatus();
|
||||
}
|
||||
return DoStep;
|
||||
});
|
||||
const active = isActive(step);
|
||||
activeRef.current = active;
|
||||
|
||||
// ============================ Status ============================
|
||||
const visibleRef = useRef(null);
|
||||
|
||||
// Update with new status
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
// When use Suspense, the `visible` will repeat trigger,
|
||||
// But not real change of the `visible`, we need to skip it.
|
||||
// https://github.com/ant-design/ant-design/issues/44379
|
||||
if (mountedRef.current && visibleRef.current === visible) {
|
||||
return;
|
||||
}
|
||||
setAsyncVisible(visible);
|
||||
const isMounted = mountedRef.current;
|
||||
mountedRef.current = true;
|
||||
|
||||
// if (!supportMotion) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
let nextStatus;
|
||||
|
||||
// Appear
|
||||
if (!isMounted && visible && motionAppear) {
|
||||
nextStatus = STATUS_APPEAR;
|
||||
}
|
||||
|
||||
// Enter
|
||||
if (isMounted && visible && motionEnter) {
|
||||
nextStatus = STATUS_ENTER;
|
||||
}
|
||||
|
||||
// Leave
|
||||
if (isMounted && !visible && motionLeave || !isMounted && motionLeaveImmediately && !visible && motionLeave) {
|
||||
nextStatus = STATUS_LEAVE;
|
||||
}
|
||||
const nextEventHandlers = getEventHandlers(nextStatus);
|
||||
|
||||
// Update to next status
|
||||
if (nextStatus && (supportMotion || nextEventHandlers[STEP_PREPARE])) {
|
||||
setStatus(nextStatus);
|
||||
startStep();
|
||||
} else {
|
||||
// Set back in case no motion but prev status has prepare step
|
||||
setStatus(STATUS_NONE);
|
||||
}
|
||||
visibleRef.current = visible;
|
||||
}, [visible]);
|
||||
|
||||
// ============================ Effect ============================
|
||||
// Reset when motion changed
|
||||
useEffect(() => {
|
||||
if (
|
||||
// Cancel appear
|
||||
currentStatus === STATUS_APPEAR && !motionAppear ||
|
||||
// Cancel enter
|
||||
currentStatus === STATUS_ENTER && !motionEnter ||
|
||||
// Cancel leave
|
||||
currentStatus === STATUS_LEAVE && !motionLeave) {
|
||||
setStatus(STATUS_NONE);
|
||||
}
|
||||
}, [motionAppear, motionEnter, motionLeave]);
|
||||
useEffect(() => () => {
|
||||
mountedRef.current = false;
|
||||
clearTimeout(deadlineRef.current);
|
||||
}, []);
|
||||
|
||||
// Trigger `onVisibleChanged`
|
||||
const firstMountChangeRef = React.useRef(false);
|
||||
useEffect(() => {
|
||||
// [visible & motion not end] => [!visible & motion end] still need trigger onVisibleChanged
|
||||
if (asyncVisible) {
|
||||
firstMountChangeRef.current = true;
|
||||
}
|
||||
if (asyncVisible !== undefined && currentStatus === STATUS_NONE) {
|
||||
// Skip first render is invisible since it's nothing changed
|
||||
if (firstMountChangeRef.current || asyncVisible) {
|
||||
onVisibleChanged?.(asyncVisible);
|
||||
}
|
||||
firstMountChangeRef.current = true;
|
||||
}
|
||||
}, [asyncVisible, currentStatus]);
|
||||
|
||||
// ============================ Styles ============================
|
||||
let mergedStyle = style[0];
|
||||
if (eventHandlers[STEP_PREPARE] && step === STEP_START) {
|
||||
mergedStyle = {
|
||||
transition: 'none',
|
||||
...mergedStyle
|
||||
};
|
||||
}
|
||||
const styleStep = style[1];
|
||||
return [getStatus, step, mergedStyle, asyncVisible ?? visible,
|
||||
// Appear Check
|
||||
!mountedRef.current && currentStatus === STATUS_NONE && supportMotion && motionAppear ? 'NONE' :
|
||||
// Enter or Leave check
|
||||
step === STEP_START || step === STEP_ACTIVE ? styleStep === step : true];
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { MotionStatus, StepStatus } from '../interface';
|
||||
/** Skip current step */
|
||||
export declare const SkipStep: false;
|
||||
/** Current step should be update in */
|
||||
export declare const DoStep: true;
|
||||
export declare function isActive(step: StepStatus): boolean;
|
||||
declare const _default: (status: MotionStatus, prepareOnly: boolean, callback: (step: StepStatus) => Promise<void> | void | typeof SkipStep | typeof DoStep) => [() => void, StepStatus];
|
||||
export default _default;
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import useState from "@rc-component/util/es/hooks/useState";
|
||||
import * as React from 'react';
|
||||
import { STEP_ACTIVATED, STEP_ACTIVE, STEP_NONE, STEP_PREPARE, STEP_PREPARED, STEP_START } from "../interface";
|
||||
import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect";
|
||||
import useNextFrame from "./useNextFrame";
|
||||
const FULL_STEP_QUEUE = [STEP_PREPARE, STEP_START, STEP_ACTIVE, STEP_ACTIVATED];
|
||||
const SIMPLE_STEP_QUEUE = [STEP_PREPARE, STEP_PREPARED];
|
||||
|
||||
/** Skip current step */
|
||||
export const SkipStep = false;
|
||||
/** Current step should be update in */
|
||||
export const DoStep = true;
|
||||
export function isActive(step) {
|
||||
return step === STEP_ACTIVE || step === STEP_ACTIVATED;
|
||||
}
|
||||
export default ((status, prepareOnly, callback) => {
|
||||
const [step, setStep] = useState(STEP_NONE);
|
||||
const [nextFrame, cancelNextFrame] = useNextFrame();
|
||||
function startQueue() {
|
||||
setStep(STEP_PREPARE, true);
|
||||
}
|
||||
const STEP_QUEUE = prepareOnly ? SIMPLE_STEP_QUEUE : FULL_STEP_QUEUE;
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
if (step !== STEP_NONE && step !== STEP_ACTIVATED) {
|
||||
const index = STEP_QUEUE.indexOf(step);
|
||||
const nextStep = STEP_QUEUE[index + 1];
|
||||
const result = callback(step);
|
||||
if (result === SkipStep) {
|
||||
// Skip when no needed
|
||||
setStep(nextStep, true);
|
||||
} else if (nextStep) {
|
||||
// Do as frame for step update
|
||||
nextFrame(info => {
|
||||
function doNext() {
|
||||
// Skip since current queue is ood
|
||||
if (info.isCanceled()) return;
|
||||
setStep(nextStep, true);
|
||||
}
|
||||
if (result === true) {
|
||||
doNext();
|
||||
} else {
|
||||
// Only promise should be async
|
||||
Promise.resolve(result).then(doNext);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [status, step]);
|
||||
React.useEffect(() => () => {
|
||||
cancelNextFrame();
|
||||
}, []);
|
||||
return [startQueue, step];
|
||||
});
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import type { CSSMotionProps } from './CSSMotion';
|
||||
import CSSMotion from './CSSMotion';
|
||||
import type { CSSMotionListProps } from './CSSMotionList';
|
||||
import CSSMotionList from './CSSMotionList';
|
||||
import type { MotionEndEventHandler, MotionEventHandler } from './interface';
|
||||
export { default as Provider } from './context';
|
||||
export { CSSMotionList };
|
||||
export type { CSSMotionProps, CSSMotionListProps, MotionEventHandler, MotionEndEventHandler, };
|
||||
export default CSSMotion;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import CSSMotion from "./CSSMotion";
|
||||
import CSSMotionList from "./CSSMotionList";
|
||||
export { default as Provider } from "./context";
|
||||
export { CSSMotionList };
|
||||
export default CSSMotion;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/// <reference types="react" />
|
||||
export declare const STATUS_NONE: "none";
|
||||
export declare const STATUS_APPEAR: "appear";
|
||||
export declare const STATUS_ENTER: "enter";
|
||||
export declare const STATUS_LEAVE: "leave";
|
||||
export type MotionStatus = typeof STATUS_NONE | typeof STATUS_APPEAR | typeof STATUS_ENTER | typeof STATUS_LEAVE;
|
||||
export declare const STEP_NONE: "none";
|
||||
export declare const STEP_PREPARE: "prepare";
|
||||
export declare const STEP_START: "start";
|
||||
export declare const STEP_ACTIVE: "active";
|
||||
export declare const STEP_ACTIVATED: "end";
|
||||
/**
|
||||
* Used for disabled motion case.
|
||||
* Prepare stage will still work but start & active will be skipped.
|
||||
*/
|
||||
export declare const STEP_PREPARED: "prepared";
|
||||
export type StepStatus = typeof STEP_NONE | typeof STEP_PREPARE | typeof STEP_START | typeof STEP_ACTIVE | typeof STEP_ACTIVATED | typeof STEP_PREPARED;
|
||||
export type MotionEvent = (TransitionEvent | AnimationEvent) & {
|
||||
deadline?: boolean;
|
||||
};
|
||||
export type MotionPrepareEventHandler = (element: HTMLElement) => Promise<any> | void;
|
||||
export type MotionEventHandler = (element: HTMLElement, event: MotionEvent) => React.CSSProperties | void;
|
||||
export type MotionEndEventHandler = (element: HTMLElement, event: MotionEvent) => boolean | void;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
export const STATUS_NONE = 'none';
|
||||
export const STATUS_APPEAR = 'appear';
|
||||
export const STATUS_ENTER = 'enter';
|
||||
export const STATUS_LEAVE = 'leave';
|
||||
export const STEP_NONE = 'none';
|
||||
export const STEP_PREPARE = 'prepare';
|
||||
export const STEP_START = 'start';
|
||||
export const STEP_ACTIVE = 'active';
|
||||
export const STEP_ACTIVATED = 'end';
|
||||
/**
|
||||
* Used for disabled motion case.
|
||||
* Prepare stage will still work but start & active will be skipped.
|
||||
*/
|
||||
export const STEP_PREPARED = 'prepared';
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/// <reference types="react" />
|
||||
export declare const STATUS_ADD: "add";
|
||||
export declare const STATUS_KEEP: "keep";
|
||||
export declare const STATUS_REMOVE: "remove";
|
||||
export declare const STATUS_REMOVED: "removed";
|
||||
export type DiffStatus = typeof STATUS_ADD | typeof STATUS_KEEP | typeof STATUS_REMOVE | typeof STATUS_REMOVED;
|
||||
type RawKeyType = string | number;
|
||||
export interface KeyObject {
|
||||
key: RawKeyType;
|
||||
status?: DiffStatus;
|
||||
}
|
||||
export declare function wrapKeyToObject(key: React.Key | KeyObject): {
|
||||
key: string;
|
||||
status?: DiffStatus;
|
||||
};
|
||||
export declare function parseKeys(keys?: any[]): {
|
||||
key: string;
|
||||
status?: DiffStatus;
|
||||
}[];
|
||||
export declare function diffKeys(prevKeys?: KeyObject[], currentKeys?: KeyObject[]): KeyObject[];
|
||||
export {};
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
export const STATUS_ADD = 'add';
|
||||
export const STATUS_KEEP = 'keep';
|
||||
export const STATUS_REMOVE = 'remove';
|
||||
export const STATUS_REMOVED = 'removed';
|
||||
export function wrapKeyToObject(key) {
|
||||
let keyObj;
|
||||
if (key && typeof key === 'object' && 'key' in key) {
|
||||
keyObj = key;
|
||||
} else {
|
||||
keyObj = {
|
||||
key: key
|
||||
};
|
||||
}
|
||||
return {
|
||||
...keyObj,
|
||||
key: String(keyObj.key)
|
||||
};
|
||||
}
|
||||
export function parseKeys(keys = []) {
|
||||
return keys.map(wrapKeyToObject);
|
||||
}
|
||||
export function diffKeys(prevKeys = [], currentKeys = []) {
|
||||
let list = [];
|
||||
let currentIndex = 0;
|
||||
const currentLen = currentKeys.length;
|
||||
const prevKeyObjects = parseKeys(prevKeys);
|
||||
const currentKeyObjects = parseKeys(currentKeys);
|
||||
|
||||
// Check prev keys to insert or keep
|
||||
prevKeyObjects.forEach(keyObj => {
|
||||
let hit = false;
|
||||
for (let i = currentIndex; i < currentLen; i += 1) {
|
||||
const currentKeyObj = currentKeyObjects[i];
|
||||
if (currentKeyObj.key === keyObj.key) {
|
||||
// New added keys should add before current key
|
||||
if (currentIndex < i) {
|
||||
list = list.concat(currentKeyObjects.slice(currentIndex, i).map(obj => ({
|
||||
...obj,
|
||||
status: STATUS_ADD
|
||||
})));
|
||||
currentIndex = i;
|
||||
}
|
||||
list.push({
|
||||
...currentKeyObj,
|
||||
status: STATUS_KEEP
|
||||
});
|
||||
currentIndex += 1;
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If not hit, it means key is removed
|
||||
if (!hit) {
|
||||
list.push({
|
||||
...keyObj,
|
||||
status: STATUS_REMOVE
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add rest to the list
|
||||
if (currentIndex < currentLen) {
|
||||
list = list.concat(currentKeyObjects.slice(currentIndex).map(obj => ({
|
||||
...obj,
|
||||
status: STATUS_ADD
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge same key when it remove and add again:
|
||||
* [1 - add, 2 - keep, 1 - remove] -> [1 - keep, 2 - keep]
|
||||
*/
|
||||
const keys = {};
|
||||
list.forEach(({
|
||||
key
|
||||
}) => {
|
||||
keys[key] = (keys[key] || 0) + 1;
|
||||
});
|
||||
const duplicatedKeys = Object.keys(keys).filter(key => keys[key] > 1);
|
||||
duplicatedKeys.forEach(matchKey => {
|
||||
// Remove `STATUS_REMOVE` node.
|
||||
list = list.filter(({
|
||||
key,
|
||||
status
|
||||
}) => key !== matchKey || status !== STATUS_REMOVE);
|
||||
|
||||
// Update `STATUS_ADD` to `STATUS_KEEP`
|
||||
list.forEach(node => {
|
||||
if (node.key === matchKey) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
node.status = STATUS_KEEP;
|
||||
}
|
||||
});
|
||||
});
|
||||
return list;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { MotionName } from '../CSSMotion';
|
||||
export declare function getVendorPrefixes(domSupport: boolean, win: object): {
|
||||
animationend: Record<string, string>;
|
||||
transitionend: Record<string, string>;
|
||||
};
|
||||
export declare function getVendorPrefixedEventName(eventName: string): any;
|
||||
export declare const supportTransition: boolean;
|
||||
export declare const animationEndName: any;
|
||||
export declare const transitionEndName: any;
|
||||
export declare function getTransitionName(transitionName: MotionName, transitionType: string): any;
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import canUseDOM from "@rc-component/util/es/Dom/canUseDom";
|
||||
// ================= Transition =================
|
||||
// Event wrapper. Copy from react source code
|
||||
function makePrefixMap(styleProp, eventName) {
|
||||
const prefixes = {};
|
||||
prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
|
||||
prefixes[`Webkit${styleProp}`] = `webkit${eventName}`;
|
||||
prefixes[`Moz${styleProp}`] = `moz${eventName}`;
|
||||
prefixes[`ms${styleProp}`] = `MS${eventName}`;
|
||||
prefixes[`O${styleProp}`] = `o${eventName.toLowerCase()}`;
|
||||
return prefixes;
|
||||
}
|
||||
export function getVendorPrefixes(domSupport, win) {
|
||||
const prefixes = {
|
||||
animationend: makePrefixMap('Animation', 'AnimationEnd'),
|
||||
transitionend: makePrefixMap('Transition', 'TransitionEnd')
|
||||
};
|
||||
if (domSupport) {
|
||||
if (!('AnimationEvent' in win)) {
|
||||
delete prefixes.animationend.animation;
|
||||
}
|
||||
if (!('TransitionEvent' in win)) {
|
||||
delete prefixes.transitionend.transition;
|
||||
}
|
||||
}
|
||||
return prefixes;
|
||||
}
|
||||
const vendorPrefixes = getVendorPrefixes(canUseDOM(), typeof window !== 'undefined' ? window : {});
|
||||
let style = {};
|
||||
if (canUseDOM()) {
|
||||
({
|
||||
style
|
||||
} = document.createElement('div'));
|
||||
}
|
||||
const prefixedEventNames = {};
|
||||
export function getVendorPrefixedEventName(eventName) {
|
||||
if (prefixedEventNames[eventName]) {
|
||||
return prefixedEventNames[eventName];
|
||||
}
|
||||
const prefixMap = vendorPrefixes[eventName];
|
||||
if (prefixMap) {
|
||||
const stylePropList = Object.keys(prefixMap);
|
||||
const len = stylePropList.length;
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
const styleProp = stylePropList[i];
|
||||
if (Object.prototype.hasOwnProperty.call(prefixMap, styleProp) && styleProp in style) {
|
||||
prefixedEventNames[eventName] = prefixMap[styleProp];
|
||||
return prefixedEventNames[eventName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
const internalAnimationEndName = getVendorPrefixedEventName('animationend');
|
||||
const internalTransitionEndName = getVendorPrefixedEventName('transitionend');
|
||||
export const supportTransition = !!(internalAnimationEndName && internalTransitionEndName);
|
||||
export const animationEndName = internalAnimationEndName || 'animationend';
|
||||
export const transitionEndName = internalTransitionEndName || 'transitionend';
|
||||
export function getTransitionName(transitionName, transitionType) {
|
||||
if (!transitionName) return null;
|
||||
if (typeof transitionName === 'object') {
|
||||
const type = transitionType.replace(/-\w/g, match => match[1].toUpperCase());
|
||||
return transitionName[type];
|
||||
}
|
||||
return `${transitionName}-${transitionType}`;
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import * as React from 'react';
|
||||
import type { MotionEndEventHandler, MotionEventHandler, MotionPrepareEventHandler, MotionStatus } from './interface';
|
||||
export interface CSSMotionRef {
|
||||
nativeElement: HTMLElement;
|
||||
inMotion: () => boolean;
|
||||
enableMotion: () => boolean;
|
||||
}
|
||||
export type CSSMotionConfig = boolean | {
|
||||
transitionSupport?: boolean;
|
||||
};
|
||||
export type MotionName = string | {
|
||||
appear?: string;
|
||||
enter?: string;
|
||||
leave?: string;
|
||||
appearActive?: string;
|
||||
enterActive?: string;
|
||||
leaveActive?: string;
|
||||
};
|
||||
export interface CSSMotionProps {
|
||||
motionName?: MotionName;
|
||||
visible?: boolean;
|
||||
motionAppear?: boolean;
|
||||
motionEnter?: boolean;
|
||||
motionLeave?: boolean;
|
||||
motionLeaveImmediately?: boolean;
|
||||
motionDeadline?: number;
|
||||
/**
|
||||
* Create element in view even the element is invisible.
|
||||
* Will patch `display: none` style on it.
|
||||
*/
|
||||
forceRender?: boolean;
|
||||
/**
|
||||
* Remove element when motion end. This will not work when `forceRender` is set.
|
||||
*/
|
||||
removeOnLeave?: boolean;
|
||||
leavedClassName?: string;
|
||||
/** @private Used by CSSMotionList. Do not use in your production. */
|
||||
eventProps?: object;
|
||||
/** Prepare phase is used for measure element info. It will always trigger even motion is off */
|
||||
onAppearPrepare?: MotionPrepareEventHandler;
|
||||
/** Prepare phase is used for measure element info. It will always trigger even motion is off */
|
||||
onEnterPrepare?: MotionPrepareEventHandler;
|
||||
/** Prepare phase is used for measure element info. It will always trigger even motion is off */
|
||||
onLeavePrepare?: MotionPrepareEventHandler;
|
||||
onAppearStart?: MotionEventHandler;
|
||||
onEnterStart?: MotionEventHandler;
|
||||
onLeaveStart?: MotionEventHandler;
|
||||
onAppearActive?: MotionEventHandler;
|
||||
onEnterActive?: MotionEventHandler;
|
||||
onLeaveActive?: MotionEventHandler;
|
||||
onAppearEnd?: MotionEndEventHandler;
|
||||
onEnterEnd?: MotionEndEventHandler;
|
||||
onLeaveEnd?: MotionEndEventHandler;
|
||||
/** This will always trigger after final visible changed. Even if no motion configured. */
|
||||
onVisibleChanged?: (visible: boolean) => void;
|
||||
internalRef?: React.Ref<any>;
|
||||
children?: (props: {
|
||||
visible?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
[key: string]: any;
|
||||
}, ref: React.Ref<any>) => React.ReactElement;
|
||||
}
|
||||
export interface CSSMotionState {
|
||||
status?: MotionStatus;
|
||||
statusActive?: boolean;
|
||||
newStatus?: boolean;
|
||||
statusStyle?: React.CSSProperties;
|
||||
prevProps?: CSSMotionProps;
|
||||
}
|
||||
export declare function isRefNotConsumed(children?: CSSMotionProps['children']): boolean;
|
||||
/**
|
||||
* `transitionSupport` is used for none transition test case.
|
||||
* Default we use browser transition event support check.
|
||||
*/
|
||||
export declare function genCSSMotion(config: CSSMotionConfig): React.ForwardRefExoticComponent<CSSMotionProps & React.RefAttributes<CSSMotionRef>>;
|
||||
declare const _default: React.ForwardRefExoticComponent<CSSMotionProps & React.RefAttributes<CSSMotionRef>>;
|
||||
export default _default;
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
exports.genCSSMotion = genCSSMotion;
|
||||
exports.isRefNotConsumed = isRefNotConsumed;
|
||||
var _findDOMNode = require("@rc-component/util/lib/Dom/findDOMNode");
|
||||
var _ref = require("@rc-component/util/lib/ref");
|
||||
var _clsx = require("clsx");
|
||||
var _react = _interopRequireWildcard(require("react"));
|
||||
var React = _react;
|
||||
var _context = require("./context");
|
||||
var _useStatus = _interopRequireDefault(require("./hooks/useStatus"));
|
||||
var _useStepQueue = require("./hooks/useStepQueue");
|
||||
var _interface = require("./interface");
|
||||
var _motion = require("./util/motion");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/* eslint-disable react/default-props-match-prop-types, react/no-multi-comp, react/prop-types */
|
||||
|
||||
function isRefNotConsumed(children) {
|
||||
return children?.length < 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* `transitionSupport` is used for none transition test case.
|
||||
* Default we use browser transition event support check.
|
||||
*/
|
||||
function genCSSMotion(config) {
|
||||
let transitionSupport = config;
|
||||
if (typeof config === 'object') {
|
||||
({
|
||||
transitionSupport
|
||||
} = config);
|
||||
}
|
||||
function isSupportTransition(props, contextMotion) {
|
||||
return !!(props.motionName && transitionSupport && contextMotion !== false);
|
||||
}
|
||||
const CSSMotion = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||||
const {
|
||||
// Default config
|
||||
visible = true,
|
||||
removeOnLeave = true,
|
||||
forceRender,
|
||||
children,
|
||||
motionName,
|
||||
leavedClassName,
|
||||
eventProps
|
||||
} = props;
|
||||
const {
|
||||
motion: contextMotion
|
||||
} = React.useContext(_context.Context);
|
||||
const supportMotion = isSupportTransition(props, contextMotion);
|
||||
|
||||
// Ref to the react node, it may be a HTMLElement
|
||||
const nodeRef = (0, _react.useRef)();
|
||||
function getDomElement() {
|
||||
return (0, _findDOMNode.getDOM)(nodeRef.current);
|
||||
}
|
||||
const [getStatus, statusStep, statusStyle, mergedVisible, styleReady] = (0, _useStatus.default)(supportMotion, visible, getDomElement, props);
|
||||
const status = getStatus();
|
||||
|
||||
// Record whether content has rendered
|
||||
// Will return null for un-rendered even when `removeOnLeave={false}`
|
||||
const renderedRef = React.useRef(mergedVisible);
|
||||
if (mergedVisible) {
|
||||
renderedRef.current = true;
|
||||
}
|
||||
|
||||
// ====================== Refs ======================
|
||||
const refObj = React.useMemo(() => {
|
||||
const obj = {};
|
||||
Object.defineProperties(obj, {
|
||||
nativeElement: {
|
||||
enumerable: true,
|
||||
get: getDomElement
|
||||
},
|
||||
inMotion: {
|
||||
enumerable: true,
|
||||
get: () => () => getStatus() !== _interface.STATUS_NONE
|
||||
},
|
||||
enableMotion: {
|
||||
enumerable: true,
|
||||
get: () => () => supportMotion
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}, []);
|
||||
|
||||
// We lock `deps` here since function return object
|
||||
// will repeat trigger ref from `refConfig` -> `null` -> `refConfig`
|
||||
React.useImperativeHandle(ref, () => refObj, []);
|
||||
|
||||
// ===================== Render =====================
|
||||
// return motionChildren as React.ReactElement;
|
||||
const idRef = React.useRef(0);
|
||||
if (styleReady) {
|
||||
idRef.current += 1;
|
||||
}
|
||||
|
||||
// We should render children when motionStyle is sync with stepStatus
|
||||
const returnNode = React.useMemo(() => {
|
||||
if (styleReady === 'NONE') {
|
||||
return null;
|
||||
}
|
||||
let motionChildren;
|
||||
const mergedProps = {
|
||||
...eventProps,
|
||||
visible
|
||||
};
|
||||
if (!children) {
|
||||
// No children
|
||||
motionChildren = null;
|
||||
} else if (status === _interface.STATUS_NONE) {
|
||||
// Stable children
|
||||
if (mergedVisible) {
|
||||
motionChildren = children({
|
||||
...mergedProps
|
||||
}, nodeRef);
|
||||
} else if (!removeOnLeave && renderedRef.current && leavedClassName) {
|
||||
motionChildren = children({
|
||||
...mergedProps,
|
||||
className: leavedClassName
|
||||
}, nodeRef);
|
||||
} else if (forceRender || !removeOnLeave && !leavedClassName) {
|
||||
motionChildren = children({
|
||||
...mergedProps,
|
||||
style: {
|
||||
display: 'none'
|
||||
}
|
||||
}, nodeRef);
|
||||
} else {
|
||||
motionChildren = null;
|
||||
}
|
||||
} else {
|
||||
// In motion
|
||||
let statusSuffix;
|
||||
if (statusStep === _interface.STEP_PREPARE) {
|
||||
statusSuffix = 'prepare';
|
||||
} else if ((0, _useStepQueue.isActive)(statusStep)) {
|
||||
statusSuffix = 'active';
|
||||
} else if (statusStep === _interface.STEP_START) {
|
||||
statusSuffix = 'start';
|
||||
}
|
||||
const motionCls = (0, _motion.getTransitionName)(motionName, `${status}-${statusSuffix}`);
|
||||
motionChildren = children({
|
||||
...mergedProps,
|
||||
className: (0, _clsx.clsx)((0, _motion.getTransitionName)(motionName, status), {
|
||||
[motionCls]: motionCls && statusSuffix,
|
||||
[motionName]: typeof motionName === 'string'
|
||||
}),
|
||||
style: statusStyle
|
||||
}, nodeRef);
|
||||
}
|
||||
return motionChildren;
|
||||
}, [idRef.current]);
|
||||
if (isRefNotConsumed(children) && (0, _ref.supportNodeRef)(returnNode)) {
|
||||
const originNodeRef = (0, _ref.getNodeRef)(returnNode);
|
||||
if (originNodeRef !== nodeRef) {
|
||||
return /*#__PURE__*/React.cloneElement(returnNode, {
|
||||
ref: (0, _ref.composeRef)(originNodeRef, nodeRef)
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnNode;
|
||||
});
|
||||
CSSMotion.displayName = 'CSSMotion';
|
||||
return CSSMotion;
|
||||
}
|
||||
var _default = exports.default = genCSSMotion(_motion.supportTransition);
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import type { CSSMotionProps } from './CSSMotion';
|
||||
import type { KeyObject } from './util/diff';
|
||||
export interface CSSMotionListProps extends Omit<CSSMotionProps, 'onVisibleChanged' | 'children'>, Omit<React.HTMLAttributes<any>, 'children'> {
|
||||
keys: (React.Key | {
|
||||
key: React.Key;
|
||||
[name: string]: any;
|
||||
})[];
|
||||
component?: string | React.ComponentType | false;
|
||||
/** This will always trigger after final visible changed. Even if no motion configured. */
|
||||
onVisibleChanged?: (visible: boolean, info: {
|
||||
key: React.Key;
|
||||
}) => void;
|
||||
/** All motion leaves in the screen */
|
||||
onAllRemoved?: () => void;
|
||||
children?: (props: {
|
||||
visible?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
index?: number;
|
||||
[key: string]: any;
|
||||
}, ref: React.Ref<any>) => React.ReactElement;
|
||||
}
|
||||
export interface CSSMotionListState {
|
||||
keyEntities: KeyObject[];
|
||||
}
|
||||
/**
|
||||
* Generate a CSSMotionList component with config
|
||||
* @param transitionSupport No need since CSSMotionList no longer depends on transition support
|
||||
* @param CSSMotion CSSMotion component
|
||||
*/
|
||||
export declare function genCSSMotionList(transitionSupport: boolean, CSSMotion?: React.ForwardRefExoticComponent<CSSMotionProps & React.RefAttributes<import("./CSSMotion").CSSMotionRef>>): React.ComponentClass<CSSMotionListProps>;
|
||||
declare const _default: React.ComponentClass<CSSMotionListProps, any>;
|
||||
export default _default;
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
exports.genCSSMotionList = genCSSMotionList;
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _CSSMotion = _interopRequireWildcard(require("./CSSMotion"));
|
||||
var _diff = require("./util/diff");
|
||||
var _motion = require("./util/motion");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /* eslint react/prop-types: 0 */
|
||||
const MOTION_PROP_NAMES = ['eventProps', 'visible', 'children', 'motionName', 'motionAppear', 'motionEnter', 'motionLeave', 'motionLeaveImmediately', 'motionDeadline', 'removeOnLeave', 'leavedClassName', 'onAppearPrepare', 'onAppearStart', 'onAppearActive', 'onAppearEnd', 'onEnterStart', 'onEnterActive', 'onEnterEnd', 'onLeaveStart', 'onLeaveActive', 'onLeaveEnd'];
|
||||
/**
|
||||
* Generate a CSSMotionList component with config
|
||||
* @param transitionSupport No need since CSSMotionList no longer depends on transition support
|
||||
* @param CSSMotion CSSMotion component
|
||||
*/
|
||||
function genCSSMotionList(transitionSupport, CSSMotion = _CSSMotion.default) {
|
||||
class CSSMotionList extends React.Component {
|
||||
static defaultProps = {
|
||||
component: 'div'
|
||||
};
|
||||
state = {
|
||||
keyEntities: []
|
||||
};
|
||||
static getDerivedStateFromProps({
|
||||
keys
|
||||
}, {
|
||||
keyEntities
|
||||
}) {
|
||||
const parsedKeyObjects = (0, _diff.parseKeys)(keys);
|
||||
const mixedKeyEntities = (0, _diff.diffKeys)(keyEntities, parsedKeyObjects);
|
||||
return {
|
||||
keyEntities: mixedKeyEntities.filter(entity => {
|
||||
const prevEntity = keyEntities.find(({
|
||||
key
|
||||
}) => entity.key === key);
|
||||
|
||||
// Remove if already mark as removed
|
||||
if (prevEntity && prevEntity.status === _diff.STATUS_REMOVED && entity.status === _diff.STATUS_REMOVE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// ZombieJ: Return the count of rest keys. It's safe to refactor if need more info.
|
||||
removeKey = removeKey => {
|
||||
this.setState(prevState => {
|
||||
const nextKeyEntities = prevState.keyEntities.map(entity => {
|
||||
if (entity.key !== removeKey) return entity;
|
||||
return {
|
||||
...entity,
|
||||
status: _diff.STATUS_REMOVED
|
||||
};
|
||||
});
|
||||
return {
|
||||
keyEntities: nextKeyEntities
|
||||
};
|
||||
}, () => {
|
||||
const {
|
||||
keyEntities
|
||||
} = this.state;
|
||||
const restKeysCount = keyEntities.filter(({
|
||||
status
|
||||
}) => status !== _diff.STATUS_REMOVED).length;
|
||||
if (restKeysCount === 0 && this.props.onAllRemoved) {
|
||||
this.props.onAllRemoved();
|
||||
}
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const {
|
||||
keyEntities
|
||||
} = this.state;
|
||||
const {
|
||||
component,
|
||||
children,
|
||||
onVisibleChanged,
|
||||
onAllRemoved,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const Component = component || React.Fragment;
|
||||
const motionProps = {};
|
||||
MOTION_PROP_NAMES.forEach(prop => {
|
||||
motionProps[prop] = restProps[prop];
|
||||
delete restProps[prop];
|
||||
});
|
||||
delete restProps.keys;
|
||||
return /*#__PURE__*/React.createElement(Component, restProps, keyEntities.map(({
|
||||
status,
|
||||
...eventProps
|
||||
}, index) => {
|
||||
const visible = status === _diff.STATUS_ADD || status === _diff.STATUS_KEEP;
|
||||
return /*#__PURE__*/React.createElement(CSSMotion, _extends({}, motionProps, {
|
||||
key: eventProps.key,
|
||||
visible: visible,
|
||||
eventProps: eventProps,
|
||||
onVisibleChanged: changedVisible => {
|
||||
onVisibleChanged?.(changedVisible, {
|
||||
key: eventProps.key
|
||||
});
|
||||
if (!changedVisible) {
|
||||
this.removeKey(eventProps.key);
|
||||
}
|
||||
}
|
||||
}), (0, _CSSMotion.isRefNotConsumed)(children) ? props => children({
|
||||
...props,
|
||||
index
|
||||
}) : (props, ref) => children({
|
||||
...props,
|
||||
index
|
||||
}, ref));
|
||||
}));
|
||||
}
|
||||
}
|
||||
return CSSMotionList;
|
||||
}
|
||||
var _default = exports.default = genCSSMotionList(_motion.supportTransition);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
interface MotionContextProps {
|
||||
motion?: boolean;
|
||||
}
|
||||
export declare const Context: React.Context<MotionContextProps>;
|
||||
declare const MotionProvider: React.FC<React.PropsWithChildren<MotionContextProps>>;
|
||||
export default MotionProvider;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = exports.Context = void 0;
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
const Context = exports.Context = /*#__PURE__*/React.createContext({});
|
||||
const MotionProvider = props => {
|
||||
const {
|
||||
children,
|
||||
...rest
|
||||
} = props;
|
||||
const memoizedValue = React.useMemo(() => {
|
||||
return {
|
||||
motion: rest.motion
|
||||
};
|
||||
}, [rest.motion]);
|
||||
return /*#__PURE__*/React.createElement(Context.Provider, {
|
||||
value: memoizedValue
|
||||
}, children);
|
||||
};
|
||||
var _default = exports.default = MotionProvider;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { MotionEvent } from '../interface';
|
||||
declare const _default: (onInternalMotionEnd: (event: MotionEvent) => void) => [(element: HTMLElement) => void, (element: HTMLElement) => void];
|
||||
export default _default;
|
||||
Generated
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _react = _interopRequireWildcard(require("react"));
|
||||
var React = _react;
|
||||
var _motion = require("../util/motion");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
var _default = onInternalMotionEnd => {
|
||||
const cacheElementRef = (0, _react.useRef)();
|
||||
|
||||
// Remove events
|
||||
function removeMotionEvents(element) {
|
||||
if (element) {
|
||||
element.removeEventListener(_motion.transitionEndName, onInternalMotionEnd);
|
||||
element.removeEventListener(_motion.animationEndName, onInternalMotionEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Patch events
|
||||
function patchMotionEvents(element) {
|
||||
if (cacheElementRef.current && cacheElementRef.current !== element) {
|
||||
removeMotionEvents(cacheElementRef.current);
|
||||
}
|
||||
if (element && element !== cacheElementRef.current) {
|
||||
element.addEventListener(_motion.transitionEndName, onInternalMotionEnd);
|
||||
element.addEventListener(_motion.animationEndName, onInternalMotionEnd);
|
||||
|
||||
// Save as cache in case dom removed trigger by `motionDeadline`
|
||||
cacheElementRef.current = element;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up when removed
|
||||
React.useEffect(() => () => {
|
||||
removeMotionEvents(cacheElementRef.current);
|
||||
cacheElementRef.current = null;
|
||||
}, []);
|
||||
return [patchMotionEvents, removeMotionEvents];
|
||||
};
|
||||
exports.default = _default;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import { useEffect } from 'react';
|
||||
declare const useIsomorphicLayoutEffect: typeof useEffect;
|
||||
export default useIsomorphicLayoutEffect;
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _canUseDom = _interopRequireDefault(require("@rc-component/util/lib/Dom/canUseDom"));
|
||||
var _react = require("react");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
// It's safe to use `useLayoutEffect` but the warning is annoying
|
||||
const useIsomorphicLayoutEffect = (0, _canUseDom.default)() ? _react.useLayoutEffect : _react.useEffect;
|
||||
var _default = exports.default = useIsomorphicLayoutEffect;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
declare const _default: () => [(callback: (info: {
|
||||
isCanceled: () => boolean;
|
||||
}) => void) => void, () => void];
|
||||
export default _default;
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _raf = _interopRequireDefault(require("@rc-component/util/lib/raf"));
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
var _default = () => {
|
||||
const nextFrameRef = React.useRef(null);
|
||||
function cancelNextFrame() {
|
||||
_raf.default.cancel(nextFrameRef.current);
|
||||
}
|
||||
function nextFrame(callback, delay = 2) {
|
||||
cancelNextFrame();
|
||||
const nextFrameId = (0, _raf.default)(() => {
|
||||
if (delay <= 1) {
|
||||
callback({
|
||||
isCanceled: () => nextFrameId !== nextFrameRef.current
|
||||
});
|
||||
} else {
|
||||
nextFrame(callback, delay - 1);
|
||||
}
|
||||
});
|
||||
nextFrameRef.current = nextFrameId;
|
||||
}
|
||||
React.useEffect(() => () => {
|
||||
cancelNextFrame();
|
||||
}, []);
|
||||
return [nextFrame, cancelNextFrame];
|
||||
};
|
||||
exports.default = _default;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import type { CSSMotionProps } from '../CSSMotion';
|
||||
import type { MotionStatus, StepStatus } from '../interface';
|
||||
export default function useStatus(supportMotion: boolean, visible: boolean, getElement: () => HTMLElement, { motionEnter, motionAppear, motionLeave, motionDeadline, motionLeaveImmediately, onAppearPrepare, onEnterPrepare, onLeavePrepare, onAppearStart, onEnterStart, onLeaveStart, onAppearActive, onEnterActive, onLeaveActive, onAppearEnd, onEnterEnd, onLeaveEnd, onVisibleChanged, }: CSSMotionProps): [
|
||||
status: () => MotionStatus,
|
||||
stepStatus: StepStatus,
|
||||
style: React.CSSProperties,
|
||||
visible: boolean,
|
||||
styleReady: 'NONE' | boolean
|
||||
];
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = useStatus;
|
||||
var _util = require("@rc-component/util");
|
||||
var _useSyncState = _interopRequireDefault(require("@rc-component/util/lib/hooks/useSyncState"));
|
||||
var _react = _interopRequireWildcard(require("react"));
|
||||
var React = _react;
|
||||
var _interface = require("../interface");
|
||||
var _useDomMotionEvents = _interopRequireDefault(require("./useDomMotionEvents"));
|
||||
var _useIsomorphicLayoutEffect = _interopRequireDefault(require("./useIsomorphicLayoutEffect"));
|
||||
var _useStepQueue = _interopRequireWildcard(require("./useStepQueue"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function useStatus(supportMotion, visible, getElement, {
|
||||
motionEnter = true,
|
||||
motionAppear = true,
|
||||
motionLeave = true,
|
||||
motionDeadline,
|
||||
motionLeaveImmediately,
|
||||
onAppearPrepare,
|
||||
onEnterPrepare,
|
||||
onLeavePrepare,
|
||||
onAppearStart,
|
||||
onEnterStart,
|
||||
onLeaveStart,
|
||||
onAppearActive,
|
||||
onEnterActive,
|
||||
onLeaveActive,
|
||||
onAppearEnd,
|
||||
onEnterEnd,
|
||||
onLeaveEnd,
|
||||
onVisibleChanged
|
||||
}) {
|
||||
// Used for outer render usage to avoid `visible: false & status: none` to render nothing
|
||||
const [asyncVisible, setAsyncVisible] = React.useState();
|
||||
const [getStatus, setStatus] = (0, _useSyncState.default)(_interface.STATUS_NONE);
|
||||
const [style, setStyle] = React.useState([null, null]);
|
||||
const currentStatus = getStatus();
|
||||
const mountedRef = (0, _react.useRef)(false);
|
||||
const deadlineRef = (0, _react.useRef)(null);
|
||||
|
||||
// =========================== Dom Node ===========================
|
||||
function getDomElement() {
|
||||
return getElement();
|
||||
}
|
||||
|
||||
// ========================== Motion End ==========================
|
||||
const activeRef = (0, _react.useRef)(false);
|
||||
|
||||
/**
|
||||
* Clean up status & style
|
||||
*/
|
||||
function updateMotionEndStatus() {
|
||||
setStatus(_interface.STATUS_NONE);
|
||||
setStyle([null, null]);
|
||||
}
|
||||
const onInternalMotionEnd = (0, _util.useEvent)(event => {
|
||||
const status = getStatus();
|
||||
// Do nothing since not in any transition status.
|
||||
// This may happen when `motionDeadline` trigger.
|
||||
if (status === _interface.STATUS_NONE) {
|
||||
return;
|
||||
}
|
||||
const element = getDomElement();
|
||||
if (event && !event.deadline && event.target !== element) {
|
||||
// event exists
|
||||
// not initiated by deadline
|
||||
// transitionEnd not fired by inner elements
|
||||
return;
|
||||
}
|
||||
const currentActive = activeRef.current;
|
||||
let canEnd;
|
||||
if (status === _interface.STATUS_APPEAR && currentActive) {
|
||||
canEnd = onAppearEnd?.(element, event);
|
||||
} else if (status === _interface.STATUS_ENTER && currentActive) {
|
||||
canEnd = onEnterEnd?.(element, event);
|
||||
} else if (status === _interface.STATUS_LEAVE && currentActive) {
|
||||
canEnd = onLeaveEnd?.(element, event);
|
||||
}
|
||||
|
||||
// Only update status when `canEnd` and not destroyed
|
||||
if (currentActive && canEnd !== false) {
|
||||
updateMotionEndStatus();
|
||||
}
|
||||
});
|
||||
const [patchMotionEvents] = (0, _useDomMotionEvents.default)(onInternalMotionEnd);
|
||||
|
||||
// ============================= Step =============================
|
||||
const getEventHandlers = targetStatus => {
|
||||
switch (targetStatus) {
|
||||
case _interface.STATUS_APPEAR:
|
||||
return {
|
||||
[_interface.STEP_PREPARE]: onAppearPrepare,
|
||||
[_interface.STEP_START]: onAppearStart,
|
||||
[_interface.STEP_ACTIVE]: onAppearActive
|
||||
};
|
||||
case _interface.STATUS_ENTER:
|
||||
return {
|
||||
[_interface.STEP_PREPARE]: onEnterPrepare,
|
||||
[_interface.STEP_START]: onEnterStart,
|
||||
[_interface.STEP_ACTIVE]: onEnterActive
|
||||
};
|
||||
case _interface.STATUS_LEAVE:
|
||||
return {
|
||||
[_interface.STEP_PREPARE]: onLeavePrepare,
|
||||
[_interface.STEP_START]: onLeaveStart,
|
||||
[_interface.STEP_ACTIVE]: onLeaveActive
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
const eventHandlers = React.useMemo(() => getEventHandlers(currentStatus), [currentStatus]);
|
||||
const [startStep, step] = (0, _useStepQueue.default)(currentStatus, !supportMotion, newStep => {
|
||||
// Only prepare step can be skip
|
||||
if (newStep === _interface.STEP_PREPARE) {
|
||||
const onPrepare = eventHandlers[_interface.STEP_PREPARE];
|
||||
if (!onPrepare) {
|
||||
return _useStepQueue.SkipStep;
|
||||
}
|
||||
return onPrepare(getDomElement());
|
||||
}
|
||||
|
||||
// Rest step is sync update
|
||||
if (newStep in eventHandlers) {
|
||||
setStyle([eventHandlers[newStep]?.(getDomElement(), null) || null, newStep]);
|
||||
}
|
||||
if (newStep === _interface.STEP_ACTIVE && currentStatus !== _interface.STATUS_NONE) {
|
||||
// Patch events when motion needed
|
||||
patchMotionEvents(getDomElement());
|
||||
if (motionDeadline > 0) {
|
||||
clearTimeout(deadlineRef.current);
|
||||
deadlineRef.current = setTimeout(() => {
|
||||
onInternalMotionEnd({
|
||||
deadline: true
|
||||
});
|
||||
}, motionDeadline);
|
||||
}
|
||||
}
|
||||
if (newStep === _interface.STEP_PREPARED) {
|
||||
updateMotionEndStatus();
|
||||
}
|
||||
return _useStepQueue.DoStep;
|
||||
});
|
||||
const active = (0, _useStepQueue.isActive)(step);
|
||||
activeRef.current = active;
|
||||
|
||||
// ============================ Status ============================
|
||||
const visibleRef = (0, _react.useRef)(null);
|
||||
|
||||
// Update with new status
|
||||
(0, _useIsomorphicLayoutEffect.default)(() => {
|
||||
// When use Suspense, the `visible` will repeat trigger,
|
||||
// But not real change of the `visible`, we need to skip it.
|
||||
// https://github.com/ant-design/ant-design/issues/44379
|
||||
if (mountedRef.current && visibleRef.current === visible) {
|
||||
return;
|
||||
}
|
||||
setAsyncVisible(visible);
|
||||
const isMounted = mountedRef.current;
|
||||
mountedRef.current = true;
|
||||
|
||||
// if (!supportMotion) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
let nextStatus;
|
||||
|
||||
// Appear
|
||||
if (!isMounted && visible && motionAppear) {
|
||||
nextStatus = _interface.STATUS_APPEAR;
|
||||
}
|
||||
|
||||
// Enter
|
||||
if (isMounted && visible && motionEnter) {
|
||||
nextStatus = _interface.STATUS_ENTER;
|
||||
}
|
||||
|
||||
// Leave
|
||||
if (isMounted && !visible && motionLeave || !isMounted && motionLeaveImmediately && !visible && motionLeave) {
|
||||
nextStatus = _interface.STATUS_LEAVE;
|
||||
}
|
||||
const nextEventHandlers = getEventHandlers(nextStatus);
|
||||
|
||||
// Update to next status
|
||||
if (nextStatus && (supportMotion || nextEventHandlers[_interface.STEP_PREPARE])) {
|
||||
setStatus(nextStatus);
|
||||
startStep();
|
||||
} else {
|
||||
// Set back in case no motion but prev status has prepare step
|
||||
setStatus(_interface.STATUS_NONE);
|
||||
}
|
||||
visibleRef.current = visible;
|
||||
}, [visible]);
|
||||
|
||||
// ============================ Effect ============================
|
||||
// Reset when motion changed
|
||||
(0, _react.useEffect)(() => {
|
||||
if (
|
||||
// Cancel appear
|
||||
currentStatus === _interface.STATUS_APPEAR && !motionAppear ||
|
||||
// Cancel enter
|
||||
currentStatus === _interface.STATUS_ENTER && !motionEnter ||
|
||||
// Cancel leave
|
||||
currentStatus === _interface.STATUS_LEAVE && !motionLeave) {
|
||||
setStatus(_interface.STATUS_NONE);
|
||||
}
|
||||
}, [motionAppear, motionEnter, motionLeave]);
|
||||
(0, _react.useEffect)(() => () => {
|
||||
mountedRef.current = false;
|
||||
clearTimeout(deadlineRef.current);
|
||||
}, []);
|
||||
|
||||
// Trigger `onVisibleChanged`
|
||||
const firstMountChangeRef = React.useRef(false);
|
||||
(0, _react.useEffect)(() => {
|
||||
// [visible & motion not end] => [!visible & motion end] still need trigger onVisibleChanged
|
||||
if (asyncVisible) {
|
||||
firstMountChangeRef.current = true;
|
||||
}
|
||||
if (asyncVisible !== undefined && currentStatus === _interface.STATUS_NONE) {
|
||||
// Skip first render is invisible since it's nothing changed
|
||||
if (firstMountChangeRef.current || asyncVisible) {
|
||||
onVisibleChanged?.(asyncVisible);
|
||||
}
|
||||
firstMountChangeRef.current = true;
|
||||
}
|
||||
}, [asyncVisible, currentStatus]);
|
||||
|
||||
// ============================ Styles ============================
|
||||
let mergedStyle = style[0];
|
||||
if (eventHandlers[_interface.STEP_PREPARE] && step === _interface.STEP_START) {
|
||||
mergedStyle = {
|
||||
transition: 'none',
|
||||
...mergedStyle
|
||||
};
|
||||
}
|
||||
const styleStep = style[1];
|
||||
return [getStatus, step, mergedStyle, asyncVisible ?? visible,
|
||||
// Appear Check
|
||||
!mountedRef.current && currentStatus === _interface.STATUS_NONE && supportMotion && motionAppear ? 'NONE' :
|
||||
// Enter or Leave check
|
||||
step === _interface.STEP_START || step === _interface.STEP_ACTIVE ? styleStep === step : true];
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { MotionStatus, StepStatus } from '../interface';
|
||||
/** Skip current step */
|
||||
export declare const SkipStep: false;
|
||||
/** Current step should be update in */
|
||||
export declare const DoStep: true;
|
||||
export declare function isActive(step: StepStatus): boolean;
|
||||
declare const _default: (status: MotionStatus, prepareOnly: boolean, callback: (step: StepStatus) => Promise<void> | void | typeof SkipStep | typeof DoStep) => [() => void, StepStatus];
|
||||
export default _default;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = exports.SkipStep = exports.DoStep = void 0;
|
||||
exports.isActive = isActive;
|
||||
var _useState = _interopRequireDefault(require("@rc-component/util/lib/hooks/useState"));
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _interface = require("../interface");
|
||||
var _useIsomorphicLayoutEffect = _interopRequireDefault(require("./useIsomorphicLayoutEffect"));
|
||||
var _useNextFrame = _interopRequireDefault(require("./useNextFrame"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
const FULL_STEP_QUEUE = [_interface.STEP_PREPARE, _interface.STEP_START, _interface.STEP_ACTIVE, _interface.STEP_ACTIVATED];
|
||||
const SIMPLE_STEP_QUEUE = [_interface.STEP_PREPARE, _interface.STEP_PREPARED];
|
||||
|
||||
/** Skip current step */
|
||||
const SkipStep = exports.SkipStep = false;
|
||||
/** Current step should be update in */
|
||||
const DoStep = exports.DoStep = true;
|
||||
function isActive(step) {
|
||||
return step === _interface.STEP_ACTIVE || step === _interface.STEP_ACTIVATED;
|
||||
}
|
||||
var _default = (status, prepareOnly, callback) => {
|
||||
const [step, setStep] = (0, _useState.default)(_interface.STEP_NONE);
|
||||
const [nextFrame, cancelNextFrame] = (0, _useNextFrame.default)();
|
||||
function startQueue() {
|
||||
setStep(_interface.STEP_PREPARE, true);
|
||||
}
|
||||
const STEP_QUEUE = prepareOnly ? SIMPLE_STEP_QUEUE : FULL_STEP_QUEUE;
|
||||
(0, _useIsomorphicLayoutEffect.default)(() => {
|
||||
if (step !== _interface.STEP_NONE && step !== _interface.STEP_ACTIVATED) {
|
||||
const index = STEP_QUEUE.indexOf(step);
|
||||
const nextStep = STEP_QUEUE[index + 1];
|
||||
const result = callback(step);
|
||||
if (result === SkipStep) {
|
||||
// Skip when no needed
|
||||
setStep(nextStep, true);
|
||||
} else if (nextStep) {
|
||||
// Do as frame for step update
|
||||
nextFrame(info => {
|
||||
function doNext() {
|
||||
// Skip since current queue is ood
|
||||
if (info.isCanceled()) return;
|
||||
setStep(nextStep, true);
|
||||
}
|
||||
if (result === true) {
|
||||
doNext();
|
||||
} else {
|
||||
// Only promise should be async
|
||||
Promise.resolve(result).then(doNext);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [status, step]);
|
||||
React.useEffect(() => () => {
|
||||
cancelNextFrame();
|
||||
}, []);
|
||||
return [startQueue, step];
|
||||
};
|
||||
exports.default = _default;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import type { CSSMotionProps } from './CSSMotion';
|
||||
import CSSMotion from './CSSMotion';
|
||||
import type { CSSMotionListProps } from './CSSMotionList';
|
||||
import CSSMotionList from './CSSMotionList';
|
||||
import type { MotionEndEventHandler, MotionEventHandler } from './interface';
|
||||
export { default as Provider } from './context';
|
||||
export { CSSMotionList };
|
||||
export type { CSSMotionProps, CSSMotionListProps, MotionEventHandler, MotionEndEventHandler, };
|
||||
export default CSSMotion;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "CSSMotionList", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _CSSMotionList.default;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "Provider", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _context.default;
|
||||
}
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _CSSMotion = _interopRequireDefault(require("./CSSMotion"));
|
||||
var _CSSMotionList = _interopRequireDefault(require("./CSSMotionList"));
|
||||
var _context = _interopRequireDefault(require("./context"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
var _default = exports.default = _CSSMotion.default;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/// <reference types="react" />
|
||||
export declare const STATUS_NONE: "none";
|
||||
export declare const STATUS_APPEAR: "appear";
|
||||
export declare const STATUS_ENTER: "enter";
|
||||
export declare const STATUS_LEAVE: "leave";
|
||||
export type MotionStatus = typeof STATUS_NONE | typeof STATUS_APPEAR | typeof STATUS_ENTER | typeof STATUS_LEAVE;
|
||||
export declare const STEP_NONE: "none";
|
||||
export declare const STEP_PREPARE: "prepare";
|
||||
export declare const STEP_START: "start";
|
||||
export declare const STEP_ACTIVE: "active";
|
||||
export declare const STEP_ACTIVATED: "end";
|
||||
/**
|
||||
* Used for disabled motion case.
|
||||
* Prepare stage will still work but start & active will be skipped.
|
||||
*/
|
||||
export declare const STEP_PREPARED: "prepared";
|
||||
export type StepStatus = typeof STEP_NONE | typeof STEP_PREPARE | typeof STEP_START | typeof STEP_ACTIVE | typeof STEP_ACTIVATED | typeof STEP_PREPARED;
|
||||
export type MotionEvent = (TransitionEvent | AnimationEvent) & {
|
||||
deadline?: boolean;
|
||||
};
|
||||
export type MotionPrepareEventHandler = (element: HTMLElement) => Promise<any> | void;
|
||||
export type MotionEventHandler = (element: HTMLElement, event: MotionEvent) => React.CSSProperties | void;
|
||||
export type MotionEndEventHandler = (element: HTMLElement, event: MotionEvent) => boolean | void;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.STEP_START = exports.STEP_PREPARED = exports.STEP_PREPARE = exports.STEP_NONE = exports.STEP_ACTIVE = exports.STEP_ACTIVATED = exports.STATUS_NONE = exports.STATUS_LEAVE = exports.STATUS_ENTER = exports.STATUS_APPEAR = void 0;
|
||||
const STATUS_NONE = exports.STATUS_NONE = 'none';
|
||||
const STATUS_APPEAR = exports.STATUS_APPEAR = 'appear';
|
||||
const STATUS_ENTER = exports.STATUS_ENTER = 'enter';
|
||||
const STATUS_LEAVE = exports.STATUS_LEAVE = 'leave';
|
||||
const STEP_NONE = exports.STEP_NONE = 'none';
|
||||
const STEP_PREPARE = exports.STEP_PREPARE = 'prepare';
|
||||
const STEP_START = exports.STEP_START = 'start';
|
||||
const STEP_ACTIVE = exports.STEP_ACTIVE = 'active';
|
||||
const STEP_ACTIVATED = exports.STEP_ACTIVATED = 'end';
|
||||
/**
|
||||
* Used for disabled motion case.
|
||||
* Prepare stage will still work but start & active will be skipped.
|
||||
*/
|
||||
const STEP_PREPARED = exports.STEP_PREPARED = 'prepared';
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/// <reference types="react" />
|
||||
export declare const STATUS_ADD: "add";
|
||||
export declare const STATUS_KEEP: "keep";
|
||||
export declare const STATUS_REMOVE: "remove";
|
||||
export declare const STATUS_REMOVED: "removed";
|
||||
export type DiffStatus = typeof STATUS_ADD | typeof STATUS_KEEP | typeof STATUS_REMOVE | typeof STATUS_REMOVED;
|
||||
type RawKeyType = string | number;
|
||||
export interface KeyObject {
|
||||
key: RawKeyType;
|
||||
status?: DiffStatus;
|
||||
}
|
||||
export declare function wrapKeyToObject(key: React.Key | KeyObject): {
|
||||
key: string;
|
||||
status?: DiffStatus;
|
||||
};
|
||||
export declare function parseKeys(keys?: any[]): {
|
||||
key: string;
|
||||
status?: DiffStatus;
|
||||
}[];
|
||||
export declare function diffKeys(prevKeys?: KeyObject[], currentKeys?: KeyObject[]): KeyObject[];
|
||||
export {};
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.STATUS_REMOVED = exports.STATUS_REMOVE = exports.STATUS_KEEP = exports.STATUS_ADD = void 0;
|
||||
exports.diffKeys = diffKeys;
|
||||
exports.parseKeys = parseKeys;
|
||||
exports.wrapKeyToObject = wrapKeyToObject;
|
||||
const STATUS_ADD = exports.STATUS_ADD = 'add';
|
||||
const STATUS_KEEP = exports.STATUS_KEEP = 'keep';
|
||||
const STATUS_REMOVE = exports.STATUS_REMOVE = 'remove';
|
||||
const STATUS_REMOVED = exports.STATUS_REMOVED = 'removed';
|
||||
function wrapKeyToObject(key) {
|
||||
let keyObj;
|
||||
if (key && typeof key === 'object' && 'key' in key) {
|
||||
keyObj = key;
|
||||
} else {
|
||||
keyObj = {
|
||||
key: key
|
||||
};
|
||||
}
|
||||
return {
|
||||
...keyObj,
|
||||
key: String(keyObj.key)
|
||||
};
|
||||
}
|
||||
function parseKeys(keys = []) {
|
||||
return keys.map(wrapKeyToObject);
|
||||
}
|
||||
function diffKeys(prevKeys = [], currentKeys = []) {
|
||||
let list = [];
|
||||
let currentIndex = 0;
|
||||
const currentLen = currentKeys.length;
|
||||
const prevKeyObjects = parseKeys(prevKeys);
|
||||
const currentKeyObjects = parseKeys(currentKeys);
|
||||
|
||||
// Check prev keys to insert or keep
|
||||
prevKeyObjects.forEach(keyObj => {
|
||||
let hit = false;
|
||||
for (let i = currentIndex; i < currentLen; i += 1) {
|
||||
const currentKeyObj = currentKeyObjects[i];
|
||||
if (currentKeyObj.key === keyObj.key) {
|
||||
// New added keys should add before current key
|
||||
if (currentIndex < i) {
|
||||
list = list.concat(currentKeyObjects.slice(currentIndex, i).map(obj => ({
|
||||
...obj,
|
||||
status: STATUS_ADD
|
||||
})));
|
||||
currentIndex = i;
|
||||
}
|
||||
list.push({
|
||||
...currentKeyObj,
|
||||
status: STATUS_KEEP
|
||||
});
|
||||
currentIndex += 1;
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If not hit, it means key is removed
|
||||
if (!hit) {
|
||||
list.push({
|
||||
...keyObj,
|
||||
status: STATUS_REMOVE
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add rest to the list
|
||||
if (currentIndex < currentLen) {
|
||||
list = list.concat(currentKeyObjects.slice(currentIndex).map(obj => ({
|
||||
...obj,
|
||||
status: STATUS_ADD
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge same key when it remove and add again:
|
||||
* [1 - add, 2 - keep, 1 - remove] -> [1 - keep, 2 - keep]
|
||||
*/
|
||||
const keys = {};
|
||||
list.forEach(({
|
||||
key
|
||||
}) => {
|
||||
keys[key] = (keys[key] || 0) + 1;
|
||||
});
|
||||
const duplicatedKeys = Object.keys(keys).filter(key => keys[key] > 1);
|
||||
duplicatedKeys.forEach(matchKey => {
|
||||
// Remove `STATUS_REMOVE` node.
|
||||
list = list.filter(({
|
||||
key,
|
||||
status
|
||||
}) => key !== matchKey || status !== STATUS_REMOVE);
|
||||
|
||||
// Update `STATUS_ADD` to `STATUS_KEEP`
|
||||
list.forEach(node => {
|
||||
if (node.key === matchKey) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
node.status = STATUS_KEEP;
|
||||
}
|
||||
});
|
||||
});
|
||||
return list;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { MotionName } from '../CSSMotion';
|
||||
export declare function getVendorPrefixes(domSupport: boolean, win: object): {
|
||||
animationend: Record<string, string>;
|
||||
transitionend: Record<string, string>;
|
||||
};
|
||||
export declare function getVendorPrefixedEventName(eventName: string): any;
|
||||
export declare const supportTransition: boolean;
|
||||
export declare const animationEndName: any;
|
||||
export declare const transitionEndName: any;
|
||||
export declare function getTransitionName(transitionName: MotionName, transitionType: string): any;
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.animationEndName = void 0;
|
||||
exports.getTransitionName = getTransitionName;
|
||||
exports.getVendorPrefixedEventName = getVendorPrefixedEventName;
|
||||
exports.getVendorPrefixes = getVendorPrefixes;
|
||||
exports.transitionEndName = exports.supportTransition = void 0;
|
||||
var _canUseDom = _interopRequireDefault(require("@rc-component/util/lib/Dom/canUseDom"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
// ================= Transition =================
|
||||
// Event wrapper. Copy from react source code
|
||||
function makePrefixMap(styleProp, eventName) {
|
||||
const prefixes = {};
|
||||
prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
|
||||
prefixes[`Webkit${styleProp}`] = `webkit${eventName}`;
|
||||
prefixes[`Moz${styleProp}`] = `moz${eventName}`;
|
||||
prefixes[`ms${styleProp}`] = `MS${eventName}`;
|
||||
prefixes[`O${styleProp}`] = `o${eventName.toLowerCase()}`;
|
||||
return prefixes;
|
||||
}
|
||||
function getVendorPrefixes(domSupport, win) {
|
||||
const prefixes = {
|
||||
animationend: makePrefixMap('Animation', 'AnimationEnd'),
|
||||
transitionend: makePrefixMap('Transition', 'TransitionEnd')
|
||||
};
|
||||
if (domSupport) {
|
||||
if (!('AnimationEvent' in win)) {
|
||||
delete prefixes.animationend.animation;
|
||||
}
|
||||
if (!('TransitionEvent' in win)) {
|
||||
delete prefixes.transitionend.transition;
|
||||
}
|
||||
}
|
||||
return prefixes;
|
||||
}
|
||||
const vendorPrefixes = getVendorPrefixes((0, _canUseDom.default)(), typeof window !== 'undefined' ? window : {});
|
||||
let style = {};
|
||||
if ((0, _canUseDom.default)()) {
|
||||
({
|
||||
style
|
||||
} = document.createElement('div'));
|
||||
}
|
||||
const prefixedEventNames = {};
|
||||
function getVendorPrefixedEventName(eventName) {
|
||||
if (prefixedEventNames[eventName]) {
|
||||
return prefixedEventNames[eventName];
|
||||
}
|
||||
const prefixMap = vendorPrefixes[eventName];
|
||||
if (prefixMap) {
|
||||
const stylePropList = Object.keys(prefixMap);
|
||||
const len = stylePropList.length;
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
const styleProp = stylePropList[i];
|
||||
if (Object.prototype.hasOwnProperty.call(prefixMap, styleProp) && styleProp in style) {
|
||||
prefixedEventNames[eventName] = prefixMap[styleProp];
|
||||
return prefixedEventNames[eventName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
const internalAnimationEndName = getVendorPrefixedEventName('animationend');
|
||||
const internalTransitionEndName = getVendorPrefixedEventName('transitionend');
|
||||
const supportTransition = exports.supportTransition = !!(internalAnimationEndName && internalTransitionEndName);
|
||||
const animationEndName = exports.animationEndName = internalAnimationEndName || 'animationend';
|
||||
const transitionEndName = exports.transitionEndName = internalTransitionEndName || 'transitionend';
|
||||
function getTransitionName(transitionName, transitionType) {
|
||||
if (!transitionName) return null;
|
||||
if (typeof transitionName === 'object') {
|
||||
const type = transitionType.replace(/-\w/g, match => match[1].toUpperCase());
|
||||
return transitionName[type];
|
||||
}
|
||||
return `${transitionName}-${transitionType}`;
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"name": "@rc-component/motion",
|
||||
"version": "1.3.2",
|
||||
"description": "React lifecycle controlled motion library",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-component",
|
||||
"react-motion",
|
||||
"motion",
|
||||
"antd",
|
||||
"ant-design"
|
||||
],
|
||||
"homepage": "https://react-component.github.io/motion",
|
||||
"bugs": {
|
||||
"url": "http://github.com/react-component/motion/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:react-component/motion.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "./lib/index",
|
||||
"module": "./es/index",
|
||||
"typings": "es/index.d.ts",
|
||||
"files": [
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"compile": "father build",
|
||||
"docs:build": "dumi build",
|
||||
"docs:deploy": "gh-pages -d .doc",
|
||||
"lint": "eslint src/ --ext .tsx,.ts",
|
||||
"lint:tsc": "tsc --noEmit",
|
||||
"now-build": "npm run docs:build",
|
||||
"prepare": "husky install",
|
||||
"prepublishOnly": "npm run compile && rc-np",
|
||||
"prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"",
|
||||
"postpublish": "npm run docs:build && npm run docs:deploy",
|
||||
"start": "dumi dev",
|
||||
"test": "rc-test"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,jsx,tsx,ts,md,json}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/minimatch": "5.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rc-component/util": "^1.2.0",
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rc-component/father-plugin": "^2.0.1",
|
||||
"@rc-component/np": "^1.0.3",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@types/jest": "^26.0.8",
|
||||
"@types/node": "^24.5.2",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@umijs/fabric": "^4.0.0",
|
||||
"dumi": "^2.0.18",
|
||||
"eslint": "^8.0.0",
|
||||
"father": "^4.1.2",
|
||||
"gh-pages": "^6.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^14.0.1",
|
||||
"prettier": "^2.1.1",
|
||||
"rc-test": "^7.0.14",
|
||||
"react": "^18.3.0",
|
||||
"react-dom": "^18.3.0",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0",
|
||||
"react-dom": ">=16.9.0"
|
||||
},
|
||||
"cnpm": {
|
||||
"mode": "npm"
|
||||
},
|
||||
"tnpm": {
|
||||
"mode": "npm"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user