Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1bd3e3c
add optional columns prop
mmmurray Feb 4, 2019
d94e3d6
make ratios prop optional
mmmurray Feb 4, 2019
5c6c1bd
total -> totalRatio
mmmurray Feb 4, 2019
5c56481
use columns prop instead of ratios
mmmurray Feb 4, 2019
1cff593
add fixed column stories
mmmurray Feb 4, 2019
4156b5d
support fixed width columns
mmmurray Feb 4, 2019
66872ca
support wrapped column items
mmmurray Feb 4, 2019
b0aff9a
forward props
mmmurray Feb 4, 2019
05de57b
created cx helper
mmmurray Feb 4, 2019
627517a
allow additional class names in column
mmmurray Feb 4, 2019
b164cd1
sort
mmmurray Feb 4, 2019
70072c2
forward belt props
mmmurray Feb 4, 2019
3a5d251
props default
mmmurray Feb 4, 2019
b4fb1eb
allow additional belt class names
mmmurray Feb 4, 2019
d8c0693
use implicit return
mmmurray Feb 4, 2019
b7d6c39
create mq with single range
mmmurray Feb 4, 2019
f349c3f
create mq with multiple ranges
mmmurray Feb 4, 2019
c0dc8f7
create mq with zero minmum
mmmurray Feb 4, 2019
7705776
create mq with infinity maximum
mmmurray Feb 4, 2019
e1215a6
formatting
mmmurray Feb 4, 2019
dd58fbb
support columnGap and rowGap props with gap default
mmmurray Feb 4, 2019
74d9854
add story with row gap
mmmurray Feb 4, 2019
71e2adc
add use media query hook
mmmurray Feb 4, 2019
89383bf
update box to use custom hook
mmmurray Feb 4, 2019
09c2f51
created useCSS custom hook
mmmurray Feb 4, 2019
9e19900
update box to use custom hook
mmmurray Feb 4, 2019
b567041
remove unused
mmmurray Feb 4, 2019
d6595a6
use named exports
mmmurray Feb 4, 2019
b0ae2ce
update peer dependency
mmmurray Feb 4, 2019
f46b352
update react version
mmmurray Feb 4, 2019
711f63b
update author
mmmurray Feb 4, 2019
1af4134
update and pin dependencies
mmmurray Feb 4, 2019
cac6cd7
swap box colours
mmmurray Feb 4, 2019
062f5d6
update year in license
mmmurray Feb 4, 2019
078bf92
0.2.0
mmmurray Feb 4, 2019
a1eb43b
update README
mmmurray Feb 4, 2019
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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Mark Murray
Copyright (c) 2019 Mark Murray

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
49 changes: 31 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,35 @@ In order to generate accurate media queries, all horizontal spacing must be appl

The components rely on a CSS-in-JS library of your choosing. You must provide a function which generates a class name based on an object of styles.

Example using emotion ([CodeSandbox link](https://codesandbox.io/s/rl0vjonq1n)):
### Example using emotion

[![Edit 7ylpq13poq](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/7ylpq13poq)

```jsx
import ReactDOM from 'react-dom'
import React, { useContext } from 'react'
import React from 'react'
import { css } from 'emotion'
import { Belt, Columns, CSSProvider, MQContext } from 'react-responsive-layout'

const createStyles = maxWidth => css`
import {
Belt,
Columns,
CSSProvider,
useMediaQuery,
} from 'react-responsive-layout'

const createStyles = mq => css`
background-color: red;
height: 100px;
padding: 10px;

@media (max-width: ${maxWidth}px) {
${mq} {
background-color: lime;
}
`

const MyResponsiveComponent = () => {
const { mq } = useContext(MQContext)
const breakpoint = mq(200)
const maxWidth = isFinite(breakpoint) ? breakpoint - 1 : 1000000
const mq = useMediaQuery(200)

return <div className={createStyles(maxWidth)}>Hello</div>
return <div className={createStyles(mq)}>Hello</div>
}

const notches = [
Expand All @@ -62,7 +67,14 @@ const notches = [
const App = () => (
<CSSProvider value={{ css }}>
<Belt notches={notches}>
<Columns ratios={[1, 2, 1]} gap={10}>
<Columns
columns={[
{ type: 'ratio', value: 1 },
{ type: 'ratio', value: 2 },
{ type: 'ratio', value: 1 },
]}
gap={10}
>
<MyResponsiveComponent />
<MyResponsiveComponent />
<MyResponsiveComponent />
Expand All @@ -71,8 +83,7 @@ const App = () => (
</CSSProvider>
)

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
ReactDOM.render(<App />, document.getElementById('root'))
```

This will render 3 columns with the middle column being twice as wide as the other two. At different window widths, the following will be rendered:
Expand All @@ -95,10 +106,12 @@ This will render 3 columns with the middle column being twice as wide as the oth

Props

| Name | Type | Default | Description |
| -------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ratios` | `number[]` | Required | The proportions to render each column. Equates to [`grid-template-columns`](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns). |
| `gap` | `number` | `0` | The fixed spacing between each column (in pixels). Equates to [`grid-column-gap`](https://developer.mozilla.org/en-US/docs/Web/CSS/column-gap) |
| Name | Type | Default | Description |
| ----------- | ------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `columns` | `Array<{type: 'ratio' | 'fixed', value: number }` | Required | The proportions to render each column. Equates to [`grid-template-columns`](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns). |
| `gap` | `number` | `0` | The fixed spacing between each column and row (in pixels). Equates to [`grid-gap`](https://developer.mozilla.org/en-US/docs/Web/CSS/gap) |
| `columnGap` | `number` | `gap` | The fixed spacing between each column and row (in pixels). Equates to [`grid-column-gap`](https://developer.mozilla.org/en-US/docs/Web/CSS/column-gap) |
| `rowGap` | `number` | `gap` | The fixed spacing between each row and row (in pixels). Equates to [`grid-row-gap`](https://developer.mozilla.org/en-US/docs/Web/CSS/row-gap) |

### Belt

Expand All @@ -108,4 +121,4 @@ Props

| Name | Type | Default | Description |
| --------- | ----------------------------------------- | -------- | --------------------------------------------------------- |
| `notches` | `Array<{ width: number, fluio: boolean}>` | Required | The widths at which the content should be constrained to. |
| `notches` | `Array<{ width: number, fluid: boolean}>` | Required | The widths at which the content should be constrained to. |
41 changes: 19 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-responsive-layout",
"version": "0.1.3",
"author": "Mark Murray <mark@murray.xyz>",
"version": "0.2.0",
"author": "Mark Murray",
"license": "MIT",
"repository": "git@github.com:mmmurray/react-responsive-layout.git",
"main": "./lib/index.js",
Expand All @@ -15,27 +15,24 @@
"test": "mmm coverage"
},
"peerDependencies": {
"react": ">16"
"react": ">=16.8.0"
},
"devDependencies": {
"@babel/core": "^7.1.6",
"@storybook/addon-knobs": "^4.0.7",
"@storybook/addon-options": "^4.0.7",
"@storybook/components": "^4.0.7",
"@storybook/react": "^4.0.7",
"@types/jest": "^23.3.9",
"@types/node": "^10.12.9",
"@types/react": "^16.7.6",
"@types/storybook__addon-knobs": "^3.4.1",
"@types/storybook__react": "^4.0.0",
"babel-loader": "^8.0.4",
"emotion": "^9.2.12",
"mmm-scripts": "^0.0.8",
"prettier": "^1.15.2",
"react": "^16.7.0-alpha.2",
"react-dom": "^16.7.0-alpha.2",
"react-emotion": "^9.2.12",
"ts-loader": "^5.3.0",
"typescript": "^3.1.6"
"@babel/core": "7.2.2",
"@storybook/addon-knobs": "4.1.11",
"@storybook/addon-options": "4.1.11",
"@storybook/components": "4.1.11",
"@storybook/react": "4.1.11",
"@types/react": "16.8.1",
"@types/storybook__addon-knobs": "4.0.0",
"@types/storybook__react": "4.0.0",
"babel-loader": "8.0.5",
"emotion": "9.2.12",
"mmm-scripts": "0.0.16",
"react": "16.8.0-alpha.1",
"react-dom": "16.8.0-alpha.1",
"react-emotion": "9.2.12",
"ts-loader": "5.3.3",
"typescript": "3.3.1"
}
}
21 changes: 13 additions & 8 deletions src/belt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { CSSConsumer } from './css-context'
import { MQProvider } from './mq-context'
import Notch from './types/notch'
import notchesMQ from './helpers/notches'
import cx from './helpers/cx'

type BeltProps = {
notches: Notch[]
props?: React.HTMLProps<HTMLDivElement>
}

const createNotchStyles = (maxWidth: number = 0, fluid: boolean = false) => ({
Expand All @@ -30,18 +32,21 @@ const createStyles = (notches: Notch[]) => {
}, {})
}

const Belt: React.SFC<BeltProps> = ({ notches, children }) => {
const Belt: React.SFC<BeltProps> = ({ notches, props = {}, children }) => {
const mq = (width: number) => notchesMQ(notches, width)

return (
<CSSConsumer>
{({ css }) => {
return (
<MQProvider mq={mq}>
<div className={css(createStyles(notches))}>{children}</div>
</MQProvider>
)
}}
{({ css }) => (
<MQProvider mq={mq}>
<div
{...props}
className={cx(css(createStyles(notches)), props.className)}
>
{children}
</div>
</MQProvider>
)}
</CSSConsumer>
)
}
Expand Down
74 changes: 64 additions & 10 deletions src/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,77 @@
import * as React from 'react'
import { CSSConsumer } from './css-context'
import cx from './helpers/cx'
import { MQProvider } from './mq-context'

type Column = {
type: 'ratio' | 'fixed'
value: number
}

type ColumnsProps = {
gap?: number
ratios: number[]
rowGap?: number
columnGap?: number
ratios?: number[]
columns?: Column[]
props?: React.HTMLProps<HTMLDivElement>
}

const createSizeFromColumn = ({ type, value }: Column): string => {
if (type === 'ratio') {
return `${value}fr`
}
if (type === 'fixed') {
return `${value}px`
}
return '0'
}

const createStyles = (gap: number, ratios: number[]) => ({
const createStyles = (props: {
columns: Column[]
rowGap: number
columnGap: number
}) => ({
display: 'grid',
gridColumnGap: `${gap}px`,
gridTemplateColumns: ratios.map(ratio => `minmax(0, ${ratio}fr)`).join(' '),
gridColumnGap: `${props.columnGap}px`,
gridRowGap: `${props.rowGap}px`,
gridTemplateColumns: props.columns
.map(column => `minmax(0, ${createSizeFromColumn(column)})`)
.join(' '),
width: '100%',
})

const Columns: React.SFC<ColumnsProps> = ({ gap = 0, ratios, children }) => {
const total = ratios.reduce((acc, ratio) => acc + ratio)
const totalGap = gap * (ratios.length - 1)
const Columns: React.SFC<ColumnsProps> = ({
gap = 0,
rowGap = gap,
columnGap = gap,
ratios = [],
columns = ratios.map<Column>(value => ({ type: 'ratio', value })),
props = {},
children,
}) => {
const totalRatio = columns.reduce(
(acc, { type, value }) => (type === 'ratio' ? acc + value : acc),
0,
)
const totalFixed = columns.reduce(
(acc, { type, value }) => (type === 'fixed' ? acc + value : acc),
0,
)
const totalGap = columnGap * (columns.length - 1)

return (
<CSSConsumer>
{({ css }) => {
const wrappedChildren = React.Children.map(children, (child, index) => {
const ratio = ratios[index]
const mq = (width: number) => width * (total / ratio) + totalGap
const { type, value } = columns[index % columns.length]
const mq = (width: number) => {
if (type === 'ratio') {
return width * (totalRatio / value) + totalGap + totalFixed
}

return value < width ? Infinity : 0
}

return (
<MQProvider key={index} mq={mq}>
Expand All @@ -33,7 +81,13 @@ const Columns: React.SFC<ColumnsProps> = ({ gap = 0, ratios, children }) => {
})

return (
<div className={css(createStyles(gap, ratios))}>
<div
{...props}
className={cx(
css(createStyles({ columns, columnGap, rowGap })),
props.className,
)}
>
{wrappedChildren}
</div>
)
Expand Down
5 changes: 3 additions & 2 deletions src/css-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ const CSSContext = React.createContext<CSSContext>({ css: () => '' })
const CSSConsumer = CSSContext.Consumer
const CSSProvider = CSSContext.Provider

export default CSSContext
export { CSSConsumer, CSSProvider }
const useCSS = () => React.useContext(CSSContext).css

export { CSSContext, CSSConsumer, CSSProvider, useCSS }
3 changes: 3 additions & 0 deletions src/helpers/cx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const cx = (c1: string = '', c2: string = '') => `${c1} ${c2}`

export default cx
14 changes: 14 additions & 0 deletions src/helpers/mq.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
type Range = { min: number; max: number }

const createRange = ({ min, max }: Range): string => {
const minPart = min === 0 ? '' : ` and (min-width: ${min}px)`
const maxPart = max === Infinity ? '' : ` and (max-width: ${max}px)`

return `screen${minPart}${maxPart}`
}

const createMq = (ranges: Range[]): string => {
return `@media ${ranges.map(createRange).join(', ')}`
}

export default createMq
2 changes: 0 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export { default as Belt } from './belt'
export { default as Columns } from './columns'
export * from './css-context'
export { default as CSSContext } from './css-context'
export * from './mq-context'
export { default as MQContext } from './mq-context'
10 changes: 8 additions & 2 deletions src/mq-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react'
import createMq from './helpers/mq'

type MQFunction = (width: number) => number

Expand All @@ -24,5 +25,10 @@ const MQProvider: React.SFC<MQProviderProps> = ({ mq, children }) => (
</MQConsumer>
)

export default MQContext
export { MQConsumer, MQProvider }
const useMediaQuery = (width: number) => {
const { mq } = React.useContext(MQContext)

return createMq([{ min: mq(width), max: Infinity }])
}

export { MQContext, MQConsumer, MQProvider, useMediaQuery }
Loading