Skip to content

Commit 3eb5e34

Browse files
authored
Merge pull request #2 from mmmurray/grid
Partial grid support
2 parents 1cd4d3f + a1eb43b commit 3eb5e34

File tree

14 files changed

+2487
-1433
lines changed

14 files changed

+2487
-1433
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2018 Mark Murray
3+
Copyright (c) 2019 Mark Murray
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,35 @@ In order to generate accurate media queries, all horizontal spacing must be appl
2727

2828
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.
2929

30-
Example using emotion ([CodeSandbox link](https://codesandbox.io/s/rl0vjonq1n)):
30+
### Example using emotion
31+
32+
[![Edit 7ylpq13poq](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/7ylpq13poq)
3133

3234
```jsx
3335
import ReactDOM from 'react-dom'
34-
import React, { useContext } from 'react'
36+
import React from 'react'
3537
import { css } from 'emotion'
36-
import { Belt, Columns, CSSProvider, MQContext } from 'react-responsive-layout'
37-
38-
const createStyles = maxWidth => css`
38+
import {
39+
Belt,
40+
Columns,
41+
CSSProvider,
42+
useMediaQuery,
43+
} from 'react-responsive-layout'
44+
45+
const createStyles = mq => css`
3946
background-color: red;
4047
height: 100px;
4148
padding: 10px;
4249
43-
@media (max-width: ${maxWidth}px) {
50+
${mq} {
4451
background-color: lime;
4552
}
4653
`
4754

4855
const MyResponsiveComponent = () => {
49-
const { mq } = useContext(MQContext)
50-
const breakpoint = mq(200)
51-
const maxWidth = isFinite(breakpoint) ? breakpoint - 1 : 1000000
56+
const mq = useMediaQuery(200)
5257

53-
return <div className={createStyles(maxWidth)}>Hello</div>
58+
return <div className={createStyles(mq)}>Hello</div>
5459
}
5560

5661
const notches = [
@@ -62,7 +67,14 @@ const notches = [
6267
const App = () => (
6368
<CSSProvider value={{ css }}>
6469
<Belt notches={notches}>
65-
<Columns ratios={[1, 2, 1]} gap={10}>
70+
<Columns
71+
columns={[
72+
{ type: 'ratio', value: 1 },
73+
{ type: 'ratio', value: 2 },
74+
{ type: 'ratio', value: 1 },
75+
]}
76+
gap={10}
77+
>
6678
<MyResponsiveComponent />
6779
<MyResponsiveComponent />
6880
<MyResponsiveComponent />
@@ -71,8 +83,7 @@ const App = () => (
7183
</CSSProvider>
7284
)
7385

74-
const rootElement = document.getElementById('root')
75-
ReactDOM.render(<App />, rootElement)
86+
ReactDOM.render(<App />, document.getElementById('root'))
7687
```
7788

7889
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:
@@ -95,10 +106,12 @@ This will render 3 columns with the middle column being twice as wide as the oth
95106

96107
Props
97108

98-
| Name | Type | Default | Description |
99-
| -------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
100-
| `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). |
101-
| `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) |
109+
| Name | Type | Default | Description |
110+
| ----------- | ------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
111+
| `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). |
112+
| `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) |
113+
| `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) |
114+
| `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) |
102115

103116
### Belt
104117

@@ -108,4 +121,4 @@ Props
108121

109122
| Name | Type | Default | Description |
110123
| --------- | ----------------------------------------- | -------- | --------------------------------------------------------- |
111-
| `notches` | `Array<{ width: number, fluio: boolean}>` | Required | The widths at which the content should be constrained to. |
124+
| `notches` | `Array<{ width: number, fluid: boolean}>` | Required | The widths at which the content should be constrained to. |

package.json

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "react-responsive-layout",
3-
"version": "0.1.3",
4-
"author": "Mark Murray <mark@murray.xyz>",
3+
"version": "0.2.0",
4+
"author": "Mark Murray",
55
"license": "MIT",
66
"repository": "git@github.com:mmmurray/react-responsive-layout.git",
77
"main": "./lib/index.js",
@@ -15,27 +15,24 @@
1515
"test": "mmm coverage"
1616
},
1717
"peerDependencies": {
18-
"react": ">16"
18+
"react": ">=16.8.0"
1919
},
2020
"devDependencies": {
21-
"@babel/core": "^7.1.6",
22-
"@storybook/addon-knobs": "^4.0.7",
23-
"@storybook/addon-options": "^4.0.7",
24-
"@storybook/components": "^4.0.7",
25-
"@storybook/react": "^4.0.7",
26-
"@types/jest": "^23.3.9",
27-
"@types/node": "^10.12.9",
28-
"@types/react": "^16.7.6",
29-
"@types/storybook__addon-knobs": "^3.4.1",
30-
"@types/storybook__react": "^4.0.0",
31-
"babel-loader": "^8.0.4",
32-
"emotion": "^9.2.12",
33-
"mmm-scripts": "^0.0.8",
34-
"prettier": "^1.15.2",
35-
"react": "^16.7.0-alpha.2",
36-
"react-dom": "^16.7.0-alpha.2",
37-
"react-emotion": "^9.2.12",
38-
"ts-loader": "^5.3.0",
39-
"typescript": "^3.1.6"
21+
"@babel/core": "7.2.2",
22+
"@storybook/addon-knobs": "4.1.11",
23+
"@storybook/addon-options": "4.1.11",
24+
"@storybook/components": "4.1.11",
25+
"@storybook/react": "4.1.11",
26+
"@types/react": "16.8.1",
27+
"@types/storybook__addon-knobs": "4.0.0",
28+
"@types/storybook__react": "4.0.0",
29+
"babel-loader": "8.0.5",
30+
"emotion": "9.2.12",
31+
"mmm-scripts": "0.0.16",
32+
"react": "16.8.0-alpha.1",
33+
"react-dom": "16.8.0-alpha.1",
34+
"react-emotion": "9.2.12",
35+
"ts-loader": "5.3.3",
36+
"typescript": "3.3.1"
4037
}
4138
}

src/belt.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import { CSSConsumer } from './css-context'
33
import { MQProvider } from './mq-context'
44
import Notch from './types/notch'
55
import notchesMQ from './helpers/notches'
6+
import cx from './helpers/cx'
67

78
type BeltProps = {
89
notches: Notch[]
10+
props?: React.HTMLProps<HTMLDivElement>
911
}
1012

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

33-
const Belt: React.SFC<BeltProps> = ({ notches, children }) => {
35+
const Belt: React.SFC<BeltProps> = ({ notches, props = {}, children }) => {
3436
const mq = (width: number) => notchesMQ(notches, width)
3537

3638
return (
3739
<CSSConsumer>
38-
{({ css }) => {
39-
return (
40-
<MQProvider mq={mq}>
41-
<div className={css(createStyles(notches))}>{children}</div>
42-
</MQProvider>
43-
)
44-
}}
40+
{({ css }) => (
41+
<MQProvider mq={mq}>
42+
<div
43+
{...props}
44+
className={cx(css(createStyles(notches)), props.className)}
45+
>
46+
{children}
47+
</div>
48+
</MQProvider>
49+
)}
4550
</CSSConsumer>
4651
)
4752
}

src/columns.tsx

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,77 @@
11
import * as React from 'react'
22
import { CSSConsumer } from './css-context'
3+
import cx from './helpers/cx'
34
import { MQProvider } from './mq-context'
45

6+
type Column = {
7+
type: 'ratio' | 'fixed'
8+
value: number
9+
}
10+
511
type ColumnsProps = {
612
gap?: number
7-
ratios: number[]
13+
rowGap?: number
14+
columnGap?: number
15+
ratios?: number[]
16+
columns?: Column[]
17+
props?: React.HTMLProps<HTMLDivElement>
18+
}
19+
20+
const createSizeFromColumn = ({ type, value }: Column): string => {
21+
if (type === 'ratio') {
22+
return `${value}fr`
23+
}
24+
if (type === 'fixed') {
25+
return `${value}px`
26+
}
27+
return '0'
828
}
929

10-
const createStyles = (gap: number, ratios: number[]) => ({
30+
const createStyles = (props: {
31+
columns: Column[]
32+
rowGap: number
33+
columnGap: number
34+
}) => ({
1135
display: 'grid',
12-
gridColumnGap: `${gap}px`,
13-
gridTemplateColumns: ratios.map(ratio => `minmax(0, ${ratio}fr)`).join(' '),
36+
gridColumnGap: `${props.columnGap}px`,
37+
gridRowGap: `${props.rowGap}px`,
38+
gridTemplateColumns: props.columns
39+
.map(column => `minmax(0, ${createSizeFromColumn(column)})`)
40+
.join(' '),
1441
width: '100%',
1542
})
1643

17-
const Columns: React.SFC<ColumnsProps> = ({ gap = 0, ratios, children }) => {
18-
const total = ratios.reduce((acc, ratio) => acc + ratio)
19-
const totalGap = gap * (ratios.length - 1)
44+
const Columns: React.SFC<ColumnsProps> = ({
45+
gap = 0,
46+
rowGap = gap,
47+
columnGap = gap,
48+
ratios = [],
49+
columns = ratios.map<Column>(value => ({ type: 'ratio', value })),
50+
props = {},
51+
children,
52+
}) => {
53+
const totalRatio = columns.reduce(
54+
(acc, { type, value }) => (type === 'ratio' ? acc + value : acc),
55+
0,
56+
)
57+
const totalFixed = columns.reduce(
58+
(acc, { type, value }) => (type === 'fixed' ? acc + value : acc),
59+
0,
60+
)
61+
const totalGap = columnGap * (columns.length - 1)
2062

2163
return (
2264
<CSSConsumer>
2365
{({ css }) => {
2466
const wrappedChildren = React.Children.map(children, (child, index) => {
25-
const ratio = ratios[index]
26-
const mq = (width: number) => width * (total / ratio) + totalGap
67+
const { type, value } = columns[index % columns.length]
68+
const mq = (width: number) => {
69+
if (type === 'ratio') {
70+
return width * (totalRatio / value) + totalGap + totalFixed
71+
}
72+
73+
return value < width ? Infinity : 0
74+
}
2775

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

3583
return (
36-
<div className={css(createStyles(gap, ratios))}>
84+
<div
85+
{...props}
86+
className={cx(
87+
css(createStyles({ columns, columnGap, rowGap })),
88+
props.className,
89+
)}
90+
>
3791
{wrappedChildren}
3892
</div>
3993
)

src/css-context.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ const CSSContext = React.createContext<CSSContext>({ css: () => '' })
1010
const CSSConsumer = CSSContext.Consumer
1111
const CSSProvider = CSSContext.Provider
1212

13-
export default CSSContext
14-
export { CSSConsumer, CSSProvider }
13+
const useCSS = () => React.useContext(CSSContext).css
14+
15+
export { CSSContext, CSSConsumer, CSSProvider, useCSS }

src/helpers/cx.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const cx = (c1: string = '', c2: string = '') => `${c1} ${c2}`
2+
3+
export default cx

src/helpers/mq.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
type Range = { min: number; max: number }
2+
3+
const createRange = ({ min, max }: Range): string => {
4+
const minPart = min === 0 ? '' : ` and (min-width: ${min}px)`
5+
const maxPart = max === Infinity ? '' : ` and (max-width: ${max}px)`
6+
7+
return `screen${minPart}${maxPart}`
8+
}
9+
10+
const createMq = (ranges: Range[]): string => {
11+
return `@media ${ranges.map(createRange).join(', ')}`
12+
}
13+
14+
export default createMq

src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
export { default as Belt } from './belt'
22
export { default as Columns } from './columns'
33
export * from './css-context'
4-
export { default as CSSContext } from './css-context'
54
export * from './mq-context'
6-
export { default as MQContext } from './mq-context'

src/mq-context.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react'
2+
import createMq from './helpers/mq'
23

34
type MQFunction = (width: number) => number
45

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

27-
export default MQContext
28-
export { MQConsumer, MQProvider }
28+
const useMediaQuery = (width: number) => {
29+
const { mq } = React.useContext(MQContext)
30+
31+
return createMq([{ min: mq(width), max: Infinity }])
32+
}
33+
34+
export { MQContext, MQConsumer, MQProvider, useMediaQuery }

0 commit comments

Comments
 (0)