Skip to content

feat: add useDocumentElement option #54

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion .storybook/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import MobileBreakpoint from '../components/MobileBreakpoint'
import withSizes from '../../src/withSizes'
import SizesProvider from '../../src/SizesProvider'

const mapSizesToProps = sizes => ({
const mapSizesToProps = (sizes) => ({
backgroundColor: sizes.width > 800 ? 'green' : 'blue',
isMobile: withSizes.isMobile(sizes),
isTablet: withSizes.isTablet(sizes),
Expand All @@ -25,6 +25,73 @@ const ExampleSizedComponent = withSizes(mapSizesToProps)(
)
)

class DocumentElementExample extends React.Component {
state = {
windowWidth: window.innerWidth,
documentWidth: document.documentElement.clientWidth,
}

constructor(...args) {
super(...args)
this.resetState = this.resetState.bind(this)
}

resetState() {
this.setState({
windowWidth: window.innerWidth,
documentWidth: document.documentElement.clientWidth,
})
}

componentDidMount() {
window.addEventListener('resize', this.resetState)
this.resetState()
}

componentWillUnmount() {
window.removeEventListener('resize', this.resetState)
}

render() {
return (
<>
<style>
{`html {
overflow-y: scroll;
}`}
</style>
<SizesProvider config={{ useDocumentElement: true }}>
<Result>
<ExampleSizedComponent />
<table>
<thead>
<tr>
<th>
<pre>window.innerWidth</pre>
</th>
<th>
<pre>document.documentElement.clientWidth</pre>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<pre>{this.state.windowWidth}</pre>
</td>
<td>
<pre>{this.state.documentWidth}</pre>
</td>
</tr>
</tbody>
</table>
</Result>
</SizesProvider>
</>
)
}
}

class ForceFallbackExample extends React.Component {
state = {
forceFallback: true,
Expand Down Expand Up @@ -82,6 +149,7 @@ const ExampleSizedComponent = withSizes(mapSizesToProps)(
</Code>
</div>
))
.add('useDocumentElement', () => <DocumentElementExample />)
.add('mobileBreakpoint', () => (
<div>
<MobileBreakpoint breakpoint={300} />
Expand Down
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const MyComponent = enhancer(({ isMobile, counter, setCounter }) => (
<div>
<div>
Count: {counter}{' '}
<button onClick={() => setCounter(n => n + 1)}>Increment</button>
<button onClick={() => setCounter((n) => n + 1)}>Increment</button>
</div>
<div>{isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
</div>
Expand Down Expand Up @@ -154,14 +154,14 @@ withSizes.isMobile = ({ width }) => width < 480
withSizes.isTablet = ({ width }) => width >= 480 && width < 1024
withSizes.isDesktop = ({ width }) => width >= 1024

withSizes.isGtMobile = sizes => !withSizes.isMobile(sizes)
withSizes.isGtTablet = sizes => withSizes.isDesktop(sizes)
withSizes.isGtMobile = (sizes) => !withSizes.isMobile(sizes)
withSizes.isGtTablet = (sizes) => withSizes.isDesktop(sizes)

withSizes.isStTablet = sizes => withSizes.isMobile(sizes)
withSizes.isStDesktop = sizes => !withSizes.isStDesktop(sizes)
withSizes.isStTablet = (sizes) => withSizes.isMobile(sizes)
withSizes.isStDesktop = (sizes) => !withSizes.isStDesktop(sizes)

withSizes.isTabletAndGreater = sizes => !withSizes.isMobile(sizes)
withSizes.isTabletAndSmaller = sizes => !withSizes.isStDesktop(sizes)
withSizes.isTabletAndGreater = (sizes) => !withSizes.isMobile(sizes)
withSizes.isTabletAndSmaller = (sizes) => !withSizes.isStDesktop(sizes)
```

If it don't fit to your needs, you can create your own selectors.
Expand All @@ -174,30 +174,40 @@ export const backgroundColor = ({ width }) => (width < 480 ? 'red' : 'green')
// your component
import { isntDesktop, backgroundColor } from 'utils/sizes/selectors'

const mapSizesToProps = sizes => ({
const mapSizesToProps = (sizes) => ({
canDisplayMobileFeature: isntDesktop(sizes),
backgroundColor: backgroundColor(sizes),
})
```

> `sizes` argument is an object with `width` and `height` properties and represents DOM window width and height.

## Window Width Resource

By default react-sizes will use `window.innerWidth` to determine the width of the current window. This will also take in to account a body scroll bar. If you'd rather ignore the body's scrollbar and base the size on the document's client width, pass the `useDocumentElement` option:

```jsx
<SizeProvider config={{ useDocumentElement: true }}>
<App />
</SizeProvider>
```

## Guide

#### mapSizesToProps(sizes)

`sizes` argument is an object with `width` and `height` of DOM window.

```js
const mapSizesToProps = sizes => {
const mapSizesToProps = (sizes) => {
console.log(sizes) // { width: 1200, height: 720 } (example)
}
```

In pratice, it is a callback that return props that will injected into your Component.

```js
const mapSizesToProps = function(sizes) {
const mapSizesToProps = function (sizes) {
const props = {
backgroundColor: sizes.width < 700 ? 'red' : 'green',
}
Expand Down Expand Up @@ -238,7 +248,7 @@ import Express from 'express'
import { SizesProvider } from 'react-sizes'
// All other imports

const getSizesFallback = userAgent => {
const getSizesFallback = (userAgent) => {
const md = new MobileDetect(userAgent)

if (!!md.mobile()) {
Expand Down
1 change: 1 addition & 0 deletions src/SizesContext.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'

const SizesContext = React.createContext({
useDocumentElement: false,
fallbackWidth: null,
fallbackHeight: null,
forceFallback: false,
Expand Down
21 changes: 18 additions & 3 deletions src/utils/getWindowSizes.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
const getWindowSizes = ({ fallbackWidth = null, fallbackHeight = null, forceFallback = false }) => {
const getWindowSizes = ({
useDocumentElement = false,
fallbackWidth = null,
fallbackHeight = null,
forceFallback = false,
}) => {
const canUseDOM = typeof window !== 'undefined'

return {
width: canUseDOM && !forceFallback ? window.innerWidth : fallbackWidth,
height: canUseDOM && !forceFallback ? window.innerHeight : fallbackHeight,
width:
canUseDOM && !forceFallback
? useDocumentElement
? document.documentElement.clientWidth
: window.innerWidth
: fallbackWidth,
height:
canUseDOM && !forceFallback
? useDocumentElement
? document.documentElement.clientHeight
: window.innerHeight
: fallbackHeight,
canUseDOM,
}
}
Expand Down
24 changes: 17 additions & 7 deletions src/withSizes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,25 @@ import getWindowSizes from './utils/getWindowSizes'
import SizesContext from './SizesContext'
import * as presets from './presets'

const getWindowSizesWithFallback = props => {
const { fallbackHeight, fallbackWidth, forceFallback } = props
return getWindowSizes({ fallbackHeight, fallbackWidth, forceFallback })
const getWindowSizesWithFallback = (props) => {
const {
useDocumentElement,
fallbackHeight,
fallbackWidth,
forceFallback,
} = props
return getWindowSizes({
useDocumentElement,
fallbackHeight,
fallbackWidth,
forceFallback,
})
}

const withSizes = (...mappedSizesToProps) => WrappedComponent => {
const withSizes = (...mappedSizesToProps) => (WrappedComponent) => {
const parseMappedSizesToProps = (dimensions, props) =>
mappedSizesToProps
.map(check => check(dimensions, props))
.map((check) => check(dimensions, props))
.reduce((acc, props) => ({ ...acc, ...props }), {})

class ComponentWithSizes extends PureComponent {
Expand Down Expand Up @@ -85,9 +95,9 @@ const withSizes = (...mappedSizesToProps) => WrappedComponent => {
}
}

const WithSizes = props => (
const WithSizes = (props) => (
<SizesContext.Consumer>
{config => <ComponentWithSizes {...config} {...props} />}
{(config) => <ComponentWithSizes {...config} {...props} />}
</SizesContext.Consumer>
)

Expand Down