This repository has been archived by the owner on Jul 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
653 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from 'react'; | ||
import { compose, withProps } from 'recompose'; | ||
import 'resize-observer-polyfill/dist/ResizeObserver.global'; | ||
|
||
import withResizeObserverProps from '../src/'; | ||
|
||
const Demo = ({ onRef, hasNarrowWidth, hasLongHeight }) => ( | ||
<textarea | ||
readOnly={true} | ||
ref={onRef} | ||
style={{ width: 400, height: 100 }} | ||
value={`resize me!\n${JSON.stringify({ hasNarrowWidth, hasLongHeight })}`} | ||
/> | ||
); | ||
|
||
export default compose( | ||
withProps({ | ||
myWidthBreakpoint: 500, | ||
myHeightBreakpoint: 300 | ||
}), | ||
withResizeObserverProps( | ||
({ myWidthBreakpoint, myHeightBreakpoint }) => ({ width, height }) => ({ | ||
hasNarrowWidth: width < myWidthBreakpoint, | ||
hasLongHeight: height >= myHeightBreakpoint | ||
}) | ||
) | ||
)(Demo); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{ | ||
"name": "@hocs/with-resize-observer-props", | ||
"library": "withResizeObserverProps", | ||
"version": "0.1.0", | ||
"description": "Resize Observer HOC for React", | ||
"keywords": [ | ||
"react", | ||
"hoc", | ||
"recompose", | ||
"resize", | ||
"resizeobserver" | ||
], | ||
"main": "lib/index.js", | ||
"module": "es/index.js", | ||
"files": [ | ||
"dist/", | ||
"es/", | ||
"lib/" | ||
], | ||
"repository": "deepsweet/hocs", | ||
"author": "Kir Belevich <[email protected]> (https://github.com/deepsweet)", | ||
"license": { | ||
"type": "MIT", | ||
"url": "https://github.com/deepsweet/hocs/blob/master/license.md" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"peerDependencies": { | ||
"react": "^15.6.1", | ||
"recompose": "^0.25.0" | ||
}, | ||
"devDependencies": { | ||
"resize-observer-polyfill": "^1.4.2" | ||
}, | ||
"dependencies": { | ||
"shallowequal": "^1.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# :left_right_arrow: with-resize-observer-props | ||
|
||
[![npm](https://img.shields.io/npm/v/@hocs/with-resize-observer-props.svg?style=flat-square)](https://www.npmjs.com/package/@hocs/with-resize-observer-props) [![ci](https://img.shields.io/travis/deepsweet/hocs/master.svg?style=flat-square)](https://travis-ci.org/deepsweet/hocs) [![coverage](https://img.shields.io/codecov/c/github/deepsweet/hocs/master.svg?style=flat-square)](https://codecov.io/github/deepsweet/hocs) [![deps](https://david-dm.org/deepsweet/hocs.svg?path=packages/with-resize-observer-props&style=flat-square)](https://david-dm.org/deepsweet/hocs?path=packages/with-resize-observer-props) | ||
|
||
Part of a [collection](https://github.com/deepsweet/hocs) of Higher-Order Components for React, especially useful with [Recompose](https://github.com/acdlite/recompose). | ||
|
||
Dynamically map *component* size and position changes to props using [Resize Observer API](https://github.com/WICG/ResizeObserver) ([Can I use?](https://caniuse.com/#feat=resizeobserver) :see_no_evil:). | ||
|
||
## Install | ||
|
||
``` | ||
yarn add recompose @hocs/with-resize-observer-props | ||
``` | ||
|
||
## Usage | ||
|
||
```js | ||
withResizeObserverProps( | ||
callback: (ownerProps: Object) => (observerState: Object) => Object | void, | ||
onRefHandlerName?: string | ||
): HigherOrderComponent | ||
``` | ||
|
||
Where: | ||
|
||
* `observerState` – [`contentRect` object](https://wicg.github.io/ResizeObserver/#dom-resizeobserverentry-contentrect) with `width`, `height`, `top` and `left` properties. | ||
* `onRefHandlerName` – in some cases you might want to change it. `'onRef'` by default. | ||
|
||
```js | ||
import React from 'react'; | ||
import { compose, withProps } from 'recompose'; | ||
import 'resize-observer-polyfill/dist/ResizeObserver.global'; | ||
import withResizeObserverProps from '@hocs/with-resize-observer-props'; | ||
|
||
const Demo = ({ onRef, hasNarrowWidth, hasLongHeight }) => ( | ||
<textarea | ||
readOnly={true} | ||
ref={onRef} | ||
style={{ width: 400, height: 100 }} | ||
value={`resize me!\n${JSON.stringify({ hasNarrowWidth, hasLongHeight })}`} | ||
/> | ||
); | ||
|
||
export default compose( | ||
withProps({ | ||
myWidthBreakpoint: 500, | ||
myHeightBreakpoint: 300 | ||
}), | ||
withResizeObserverProps( | ||
({ myWidthBreakpoint, myHeightBreakpoint }) => ({ width, height }) => ({ | ||
hasNarrowWidth: width < myWidthBreakpoint, | ||
hasLongHeight: height >= myHeightBreakpoint | ||
}) | ||
) | ||
)(Demo); | ||
``` | ||
|
||
It uses "shallow equal" under the hood to compare computed props so target component will be re-rendered only when it needs to. | ||
|
||
In addition, it's possible to not return computed props from a callback at all. It's very usefull if you need just to propagate size changes to "outside" of component without self re-renders, for example to animate width/height with [react-motion](https://github.com/chenglou/react-motion) or similar libs. | ||
|
||
```js | ||
withResizeObserverProps( | ||
({ onResize }) => onResize | ||
) | ||
``` | ||
|
||
:tv: [Check out live demo](https://www.webpackbin.com/bins/-KsUVUj_IHaULBEW0oKx). | ||
|
||
## Notes | ||
|
||
* You still might need a [polyfill](https://github.com/que-etc/resize-observer-polyfill) – contains many details on why this particular polyfill is just technically amazing. | ||
* "`ref` approach" is used instead of `findDOMNode(this)` because it's just [less evil](https://facebook.github.io/react/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components). Also it's more flexible so you can pass it to whatever children you want. | ||
* Target Component will be just passed through on unsupported platforms (i.e. `global.ResizeObserver` is not a function) like IE9, JSDOM (so Jest as well) or with Server-Side Rendering. This means that there will be no boolean props (i.e. `undefined`) which might be expected, but you can take care of it using Recompose [`defaultProps`](https://github.com/acdlite/recompose/blob/master/docs/API.md#defaultprops) HOC if it's really necessary. | ||
|
||
## Related | ||
|
||
* :left_right_arrow: [with-match-media-props](../with-match-media-props) | ||
* :eyes: [with-intersection-observer-props](../with-intersection-observer-props) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Component } from 'react'; | ||
import { createEagerFactory, setDisplayName, wrapDisplayName } from 'recompose'; | ||
import shallowEqual from 'shallowequal'; | ||
|
||
const isResizeObserverSupported = typeof global.ResizeObserver === 'function'; | ||
|
||
const withResizeObserverProps = (propsCallback, onRefHandlerName = 'onRef') => (Target) => { | ||
if (!isResizeObserverSupported) { | ||
return Target; | ||
} | ||
|
||
const factory = createEagerFactory(Target); | ||
|
||
class WithResizeObserverProps extends Component { | ||
constructor(props, context) { | ||
super(props, context); | ||
|
||
this.state = {}; | ||
this.onObserve = this.onObserve.bind(this); | ||
this.onRef = this.onRef.bind(this); | ||
this.observer = new global.ResizeObserver(this.onObserve); | ||
} | ||
|
||
componentDidMount() { | ||
this.observer.observe(this.domNode); | ||
} | ||
|
||
componentWillUnmount() { | ||
this.observer.disconnect(); | ||
} | ||
|
||
onRef(ref) { | ||
this.domNode = ref; | ||
|
||
if (typeof this.props[onRefHandlerName] === 'function') { | ||
this.props[onRefHandlerName](ref); | ||
} | ||
} | ||
|
||
onObserve(entries) { | ||
const stateCallback = propsCallback(this.props); | ||
const nextState = entries.reduce((result, entry) => ({ | ||
...result, | ||
...stateCallback(entry.contentRect) | ||
}), {}); | ||
|
||
if ( | ||
typeof nextState !== 'undefined' && | ||
shallowEqual(this.state, nextState) === false | ||
) { | ||
this.setState(nextState); | ||
} | ||
} | ||
|
||
render() { | ||
return factory({ | ||
...this.props, | ||
...this.state, | ||
[onRefHandlerName]: this.onRef | ||
}); | ||
} | ||
} | ||
|
||
if (process.env.NODE_ENV !== 'production') { | ||
return setDisplayName( | ||
wrapDisplayName(Target, 'withResizeObserverProps') | ||
)(WithResizeObserverProps); | ||
} | ||
|
||
return WithResizeObserverProps; | ||
}; | ||
|
||
export default withResizeObserverProps; |
Oops, something went wrong.