-
Notifications
You must be signed in to change notification settings - Fork 257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tweaks to new Wrapperless mode #204
Changes from 3 commits
809f869
b59b152
339465d
6b89a9d
e19607f
1464b24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,4 @@ | |
{ | ||
"javascript.validate.enable": false, | ||
"eslint.enable": true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,21 +24,6 @@ Flip Move uses the [_FLIP technique_](https://aerotwist.com/blog/flip-your-anima | |
* <a href="http://joshwcomeau.github.io/react-flip-move/examples/#/scrabble" target="_blank">__Scrabble__</a> | ||
* <a href="http://joshwcomeau.github.io/react-flip-move/examples/#/laboratory" target="_blank">__Laboratory__</a> | ||
|
||
## Table of Contents | ||
|
||
* [Installation](#installation) | ||
* [Features](#features) | ||
* [Quickstart](#quickstart) | ||
* [Compatibility](#compatibility) | ||
* [Enter/Leave Animations](https://github.com/joshwcomeau/react-flip-move/blob/master/documentation/enter_leave_animations.md) | ||
* [API Reference](https://github.com/joshwcomeau/react-flip-move/blob/master/documentation/api_reference.md) | ||
* [Gotchas](#gotchas) | ||
* [Known Issues](#known-issues) | ||
* [Contributions](#contributions) | ||
* [Development](#development) | ||
* [Flow support](#flow-support) | ||
* [License](#license) | ||
|
||
|
||
|
||
## Installation | ||
|
@@ -114,45 +99,95 @@ Curious how this works, under the hood? [__Read the Medium post__](https://mediu | |
|
||
--- | ||
|
||
### HTML Attributes | ||
### Wrapping Element | ||
|
||
FlipMove creates its own DOM node to wrap the children it needs to animate. Sometimes, you'll want to be able to pass specific HTML attributes to this node. | ||
By default, FlipMove wraps the children you pass it in a `<div>`: | ||
|
||
All props other than the ones listed above will be delegated to this new node, so you can apply them directly to FlipMove. For example: | ||
```jsx | ||
// JSX | ||
<FlipMove> | ||
<div key="a">Hello</div> | ||
<div key="b">World</div> | ||
</FlipMove> | ||
|
||
```html | ||
// HTML | ||
<div> | ||
<FlipMove typeName="ul" className="row" style={{ backgroundColor: 'red' }}> | ||
<li className="col">Column 1</li> | ||
<li className="col">Column 2</li> | ||
</FlipMove> | ||
<div>Hello</div> | ||
<div>World</div> | ||
</div> | ||
``` | ||
|
||
FlipMove passes the `className` and `style` props along to the `ul` that needs to be created. Here's how it renders: | ||
Any unrecognized props to `<FlipMove>` will be delegated to this wrapper element: | ||
|
||
```html | ||
<div> | ||
<ul class="row" style="background-color: red"> | ||
<li class="col">Column 1</li> | ||
<li class="col">Column 2</li> | ||
</ul> | ||
```jsx | ||
// JSX | ||
<FlipMove className="flip-wrapper" style={{ color: 'red' }}> | ||
<div key="a">Hello</div> | ||
<div key="b">World</div> | ||
</FlipMove> | ||
|
||
// HTML | ||
<div class="flip-wrapper" style="color: red;"> | ||
<div key="a">Hello</div> | ||
<div key="b">World</div> | ||
</div> | ||
``` | ||
|
||
This works for all HTML props - there's no validation. | ||
You can supply a different element type with the `typeName` prop: | ||
|
||
You can pass `false` to `typeName` and a valid HTMLElement reference as `anchor` to render the children without a wrapping DOM node. Note that you will have to use react 16.0.0 or higher, and that all other props (such as `className` or `style`) will have no effect. | ||
```html | ||
<div ref={node => this.myCustomParent = node}> | ||
<FlipMove typeName={false} anchor={this.myCustomParent}> | ||
<li className="col">Column 1</li> | ||
<li className="col">Column 2</li> | ||
```jsx | ||
// JSX | ||
<FlipMove typeName="ul"> | ||
<li key="a">Hello</li> | ||
<li key="b">World</li> | ||
</FlipMove> | ||
|
||
// HTML | ||
<ul style="color: red;"> | ||
<li key="a">Hello</li> | ||
<li key="b">World</li> | ||
</ul> | ||
``` | ||
|
||
Finally, if you're using React 16 or higher, and Flip Move 2.10 or higher, you can use the new "wrapperless" mode. This takes advantage of a React Fiber feature, which allows us to omit this wrapping element: | ||
|
||
```jsx | ||
// JSX | ||
<div className="your-own-element"> | ||
<FlipMove typeName={null}> | ||
<div key="a">Hello</div> | ||
<div key="b">World</div> | ||
</FlipMove> | ||
</div> | ||
|
||
// HTML | ||
<div class="your-own-element"> | ||
<div key="a">Hello</div> | ||
<div key="b">World</div> | ||
</div> | ||
``` | ||
|
||
If no anchor is provided, react-flip-move will try to use the components parent node. This, however, is a costly operation that violates component separation principles and thus is [highly discouraged](https://reactjs.org/docs/react-dom.html#finddomnode). | ||
Wrapperless mode is nice, because it makes FlipMove more "invisible", and makes it easier to integrate with parent-child CSS properties like flexbox. However, there are some things to note: | ||
|
||
- This is a new feature in FlipMove, and isn't as battle-tested as the traditional method. Please test thoroughly before using in production, and report any bugs! | ||
- FlipMove does some positioning magic for enter/exit animations - specifically, it temporarily applies `position: absolute` to its children. For this to work correctly, you'll need to make sure that `<FlipMove>` is within a container that has a non-static position (eg. `position: relative`), and no padding: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is something I noticed in Storybook; It's kind of a bummer :( and makes me think that wrapperless mode will likely cause more trouble than it's worth... but I figure we'll see. If we get a ton of confusing issues, maybe we can add a check that console.warns when the parent DOM element isn't configured correctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we already have the dom node, those styles could be overwritten after findDOMContainer = () => {
// eslint-disable-next-line react/no-find-dom-node
const domNode = ReactDOM.findDOMNode(this);
const parentNode = domNode && domNode.parentNode;
// This ought to be impossible, but handling it for Flow's sake.
if (!parentNode || !(parentNode instanceof HTMLElement)) {
return;
}
// Make sure parentNode has position relative and no padding
parentNode.style.position = 'relative';
parentNode.style.padding = 0;
this.parentData.domNode = parentNode;
}; should be mentioned in the docs though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We can do that only if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, so I thought about applying a "default" relative position if it isn't set to something other than So either we apply position: relative, which fixes FlipMove but breaks their layout, or we do nothing, and it breaks enter/exit animations. The latter is clearly a FlipMove issue, and so they'll come here and we can advise them... but the former might appear to them as a CSS issue / they won't understand why things are broken, why a random Maybe we can apply relative positioning if it's static, and also That said, it's really rare that Gonna do it in a subsequent PR to make reviews easier, but I'll apply @Hypnosphi's static check, and issue a console.warn when the change is necessary. |
||
|
||
```jsx | ||
// BAD - this will cause children to jump to a new position before exiting: | ||
<div style={{ padding: 20 }}> | ||
<FlipMove typeName={null}> | ||
<div key="a">Hello world</div> | ||
</FlipMove> | ||
</div> | ||
|
||
// GOOD - a non-static position and a tight-fitting wrapper means children will | ||
// stay in place while exiting: | ||
<div style={{ position: 'relative' }}> | ||
<FlipMove typeName={null}> | ||
<div key="a">Hello world</div> | ||
</FlipMove> | ||
</div> | ||
``` | ||
|
||
--- | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -277,15 +277,17 @@ In general, it is advisable to ignore the `domNodes` argument and work with the | |
|
||
### `typeName` | ||
|
||
| **Accepted Types:** | **Default Value** | | ||
|---------------------|-------------------| | ||
| `String` | 'div' | | ||
| **Accepted Types:** | **Default Value** | | ||
|----------------------|-------------------| | ||
| `String` | `null` | 'div' | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mentioned this in #202, and didn't see any strong opinions; my preference is that we use If y'all think there's value in also supporting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer |
||
|
||
|
||
Flip Move wraps your children in a container element. By default, this element is a `div`, but you may wish to provide a custom HTML element (for example, if your children are list items, you may wish to set this to `ul`). | ||
|
||
Any valid HTML element type is accepted, but peculiar things may happen if you use an unconventional element. | ||
|
||
With React 16, Flip Move can opt not to use a container element: set `typeName` to `null` to use this new "wrapperless" behaviour. [Read more](https://github.com/joshwcomeau/react-flip-move/blob/master/README.md#wrapping-elements). | ||
|
||
--- | ||
|
||
### `disableAllAnimations` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,23 @@ | ||
declare module "react-flip-move" { | ||
declare module 'react-flip-move' { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Argh, Prettier updated all the formatting here, adding trailing commas and single-quotes. @Hypnosphi does this matter? Also, I renamed this file from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, pretty sure there should be. we don't want to allow null as a value on versions lower than 2.10.x you can use wildcards in the name to span multiple versions though, like we're currently doing with the patch version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If it doesn't for you, you shouldn't use prettier =) And I believe, trailing commas are one of the few things that are configurable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep, makes sense, thanks!
You misunderstand; I was asking specifically about But yeah, sounds like it's a non-issue, so I'll go ahead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well we kinda introduce new API, so it requires an updated libdef There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And I think we should disable eslint for flow-typed directory so that it’s easier to sync it with the version in flow-typed repo |
||
declare export type Styles = { | ||
[key: string]: string | ||
[key: string]: string, | ||
}; | ||
|
||
declare type ReactStyles = { | ||
[key: string]: string | number | ||
[key: string]: string | number, | ||
}; | ||
|
||
declare export type Animation = { | ||
from: Styles, | ||
to: Styles | ||
to: Styles, | ||
}; | ||
|
||
declare export type Presets = { | ||
elevator: Animation, | ||
fade: Animation, | ||
accordionVertical: Animation, | ||
accordionHorizontal: Animation, | ||
none: null | ||
none: null, | ||
}; | ||
|
||
declare export type AnimationProp = $Keys<Presets> | boolean | Animation; | ||
|
@@ -28,27 +28,27 @@ declare module "react-flip-move" { | |
bottom: number, | ||
left: number, | ||
height: number, | ||
width: number | ||
width: number, | ||
}; | ||
|
||
// can't use $Shape<React$Element<*>> here, because we use it in intersection | ||
declare export type ElementShape = { | ||
+type: $PropertyType<React$Element<*>, "type">, | ||
+props: $PropertyType<React$Element<*>, "props">, | ||
+key: $PropertyType<React$Element<*>, "key">, | ||
+ref: $PropertyType<React$Element<*>, "ref"> | ||
+type: $PropertyType<React$Element<*>, 'type'>, | ||
+props: $PropertyType<React$Element<*>, 'props'>, | ||
+key: $PropertyType<React$Element<*>, 'key'>, | ||
+ref: $PropertyType<React$Element<*>, 'ref'>, | ||
}; | ||
|
||
declare type ChildHook = (element: ElementShape, node: ?HTMLElement) => mixed; | ||
|
||
declare export type ChildrenHook = ( | ||
elements: Array<ElementShape>, | ||
nodes: Array<?HTMLElement> | ||
nodes: Array<?HTMLElement>, | ||
) => mixed; | ||
|
||
declare export type GetPosition = (node: HTMLElement) => ClientRect; | ||
|
||
declare export type VerticalAlignment = "top" | "bottom"; | ||
declare export type VerticalAlignment = 'top' | 'bottom'; | ||
|
||
declare export type Child = void | null | boolean | React$Element<*>; | ||
|
||
|
@@ -61,7 +61,7 @@ declare module "react-flip-move" { | |
disableAllAnimations: boolean, | ||
getPosition: GetPosition, | ||
maintainContainerHeight: boolean, | ||
verticalAlignment: VerticalAlignment | ||
verticalAlignment: VerticalAlignment, | ||
}; | ||
|
||
declare type PolymorphicProps = { | ||
|
@@ -70,37 +70,36 @@ declare module "react-flip-move" { | |
staggerDurationBy: string | number, | ||
staggerDelayBy: string | number, | ||
enterAnimation: AnimationProp, | ||
leaveAnimation: AnimationProp | ||
leaveAnimation: AnimationProp, | ||
}; | ||
|
||
declare type Hooks = { | ||
onStart?: ChildHook, | ||
onFinish?: ChildHook, | ||
onStartAll?: ChildrenHook, | ||
onFinishAll?: ChildrenHook | ||
onFinishAll?: ChildrenHook, | ||
}; | ||
|
||
declare export type DelegatedProps = { | ||
style?: ReactStyles, | ||
anchor?: HTMLElement | ||
}; | ||
|
||
declare export type FlipMoveDefaultProps = BaseProps & PolymorphicProps; | ||
|
||
declare export type CommonProps = BaseProps & | ||
Hooks & { | ||
children?: ChildrenArray<Child> | ||
children?: ChildrenArray<Child>, | ||
}; | ||
|
||
declare export type FlipMoveProps = FlipMoveDefaultProps & | ||
CommonProps & | ||
DelegatedProps & { | ||
appearAnimation?: AnimationProp, | ||
disableAnimations?: boolean // deprecated, use disableAllAnimations instead | ||
disableAnimations?: boolean, // deprecated, use disableAllAnimations instead | ||
}; | ||
|
||
declare class FlipMove extends React$Component<FlipMoveProps> { | ||
static defaultProps: FlipMoveDefaultProps | ||
static defaultProps: FlipMoveDefaultProps, | ||
} | ||
|
||
declare export default typeof FlipMove | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This TOC was quite outdated, and I realized it'll always be problematic. This doc isn't so long now that
API Reference
is its own thing, so I ditched the TOC.