├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── full-width.md
├── sample-grid.jpg
├── why-1.jpg
├── why-2.jpg
├── why-3.jpg
├── why-4.jpg
└── why-cellblock.md
├── example
├── Layout.js
├── examples
│ ├── Chess.js
│ ├── Copyfit.js
│ ├── Shuffle.js
│ ├── Wave.js
│ └── index.js
├── index.js
├── modules
│ ├── Banner.js
│ ├── Blocker.js
│ ├── Nav.js
│ ├── Optimized.js
│ └── TypeSize.js
└── style
│ ├── reset.scss
│ └── style.scss
├── index.html
├── package-lock.json
├── package.json
├── src
├── Column.js
├── Grid.js
├── Row.js
├── index.js
├── observeGrid.js
└── util
│ ├── Style.js
│ ├── constants.js
│ ├── context.js
│ ├── getThreshold.js
│ ├── handleStaleContext.js
│ └── validators.js
├── test
├── Grid-test.js
├── _helpers
│ └── boot.js
└── validators-test.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react"
5 | ],
6 | "plugins": [
7 | "transform-decorators-legacy"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = false
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "mocha": true
7 | },
8 | "plugins": [
9 | "react"
10 | ],
11 | "rules": {
12 | "react/jsx-no-undef": 1,
13 | "jsx-quotes": [2, "prefer-double"],
14 | "react/sort-prop-types": 2,
15 | "react/jsx-uses-react": 1,
16 | "react/jsx-uses-vars": 1,
17 | "react/no-did-mount-set-state": 2,
18 | "react/no-did-update-set-state": 2,
19 | "react/no-unknown-property": 1,
20 | "react/prop-types": 1,
21 | "react/react-in-jsx-scope": 1,
22 | "react/self-closing-comp": 1,
23 | "react/sort-comp": 2,
24 | "react/jsx-wrap-multilines": 2,
25 | "strict": 0,
26 | "curly": 0,
27 | "quotes": [2, "single"],
28 | "indent": [2, 2, {"SwitchCase": 1}],
29 | "camelcase": 0,
30 | "no-underscore-dangle": 0,
31 | "no-mixed-requires": 0,
32 | "no-use-before-define": 0
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log*
2 | /node_modules
3 | /coverage
4 | lib/
5 | dist/
6 | cookies.txt
7 | .sass-cache/
8 | FIREFOX*
9 | dump.rdb
10 | newrelic.js
11 | .DS_STORE
12 | .nyc_output
13 | .idea/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 |
2 | language: node_js
3 |
4 | node_js:
5 | - 6.10.0
6 | env:
7 | - CXX=g++-4.8
8 | addons:
9 | apt:
10 | sources:
11 | - ubuntu-toolchain-r-test
12 | packages:
13 | - g++-4.8
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Dow Jones & Company
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-cellblock
2 |
3 | 
4 |
5 | 
6 | 
7 | 
8 |
9 | React Cellblock makes it easy to build components that respond not only to a grid’s break point, but also to the size of their containing column.
10 |
11 | [Check out the demo page.](http://dowjones.github.io/react-cellblock/)
12 |
13 | Cellblock shares some conventions with popular grids, such as Bootstrap and Foundation, but it combines the benefits of fractional grids with the benefits of grids that use fixed units.
14 |
15 | [Why is this helpful?](docs/why-cellblock.md)
16 |
17 | ## Features
18 |
19 | * Your breakpoints can be fixed or flexible.
20 | * You can interleave grid sections with non-grid sections.
21 | * You can specify any grid division you want (not always fractions of 12 for instance).
22 | * Your components can observe the grid’s break point as it changes.
23 | * Your components can observe the width of their containing column as it changes.
24 |
25 | ## Use
26 |
27 | ```
28 | $ npm install react-cellblock
29 | ```
30 |
31 | To run examples
32 | ```
33 | $ cd react-cellblock
34 | $ npm install
35 | $ npm start
36 | ```
37 |
38 | Open http://localhost:8080 and start playing around.
39 |
40 | ## What’s included
41 |
42 | * [__Grid__](#grid) provides a context and configuration for all other grid components.
43 | * [__Row__](#row) creates a container for a set of columns.
44 | * [__Column__](#column) divides rows into regions.
45 | * [__observeGrid__](#observegrid) takes a component and returns a new one that recieves grid properties (Higher order component).
46 |
47 | ## A Simple Example of a Layout
48 |
49 | To create a layout, you must have one `` component. Inside you can use `` and `` components. You may also use custom components (which could include furthur `` and `` components.
50 |
51 | Here is a layout divided into two equal columns:
52 | ```js
53 | import React from 'react';
54 | import {Grid, Row, Column} from 'react-cellblock';
55 |
56 | const Layout = () => (
57 |
58 |
59 |
60 | Left!
61 |
62 |
63 | Right!
64 |
65 |
66 |
67 | );
68 | ```
69 |
70 | ## A Simple Example of a Responsive Component
71 |
72 | Here is a custom component that displays a “small view” when it is in a small column and a “big view” when it is in a big column.
73 | ```js
74 | import React from 'react';
75 | import {observeGrid} from 'react-cellblock';
76 |
77 | class YourComponent extends React.Component {
78 | static propTypes = {
79 | colWidth: React.PropTypes.number // How wide is this component in “grid units”?
80 | };
81 |
82 | render() {
83 | return (this.props.colWidth > 4) ?
84 | (
Big View
) : (
Small View
);
85 | }
86 | });
87 |
88 | const YourNewResponsiveComponent = observeGrid(YourComponent);
89 |
90 | // then inside some other component or jsx
91 | // on the "12 unit breakpoint"
92 |
93 | // 3 units wide on 12 unit breakpoint
94 | // this will render “Small View”
95 |
96 | // 9 units wide on 12 unit breakpoint
97 | // this will render “Big View”
98 |
99 | ```
100 |
101 | If your component is simple, you can take advantage of react’s [Stateless functional components](https://facebook.github.io/react/blog/#stateless-functional-components) to make the example above more succinct:
102 |
103 | ```js
104 | const ResponsiveComponent = observeGrid(({colWidth}) => (
105 | (colWidth > 4) ? (
106 |
Big View
107 | ) : (
108 |
Small View
109 | );
110 | ));
111 | ```
112 |
113 | ## Grid
114 |
115 | The `` component provides a context and configuration for all other Cellblock components. _It has no visual styles associated with it_. This allows you to have full-width items inside the grid that are not constrained by the grid.
116 |
117 | ##### Configuring the Grid Component
118 |
119 | When you set up a Cellblock grid, you choose how wide you want your columns and gutters to be and you choose how many columns you want to fit in each breakpoint (you can have as many breakpoints as you need).
120 |
121 | For example, if you want to have two breakpoints that contain 4 and 8 columns respectively you would do this:
122 | ```js
123 |
124 | ```
125 | As a result you would have a “4 unit” breakpoint and an “8 unit” breakpoint. The 8 unit breakpoint would trigger as soon as the screen is wide enough to fit 8 columns.
126 |
127 | If you want the grid to be flexible at all breakpoints:
128 | ```js
129 |
130 | ```
131 | And if you want the only certain breakpoints to be flexible:
132 | ```js
133 |
134 | ```
135 |
136 | ##### Example Configuration
137 |
138 | Here is what the default configuration looks like:
139 | ```js
140 | {}} // fires every time breakpoint changes
146 | />
147 | ```
148 |
149 | ##### Grid Properties
150 |
151 | Property | Type | Default | Description
152 | :------- | :--- | :------ | :----------
153 | columnWidth | `Number` | `60` | The width (in pixels) of each column unit. On flexible breakpoints this will be the minimum width.
154 | gutterWidth | `Number` | `20` | The width (in pixels) of the gutter between Columns.
155 | breakpoints | `Array` | `[4,8,12,16]` | A sorted list of how many columns are in each breakpoint.
156 | initialBreakpoint | `Number` | `undefined` | The first breakpoint to render (useful for isomorphic rendering)
157 | flexible | `Array/Bool` | `[4]` | A list of which breakpoints are flexible. If `true`, all breakpoints will flex.
158 | onChange | `Func` | `noop` | Fires every time the breakpoint changes. Recieves the new breakpoint as its first argument.
159 | className | `String` | `undefined` | A custom class name.
160 |
161 | > _Note: You should never put a `` inside another ``._
162 |
163 | ## Row
164 |
165 | The `` component creates a container for a set of columns. Any time you want to nest columns, you must have a row around them.
166 |
167 | At the highest level, rows constrain the grid and give meaning to columns:
168 | ```js
169 |
170 | // The row establishes the outer grid boundry.
171 |
172 |
173 |
174 |
Full Width
// Without a row, elements stretch to fill the screen.
175 |
176 |
177 |
178 |
179 |
180 | ```
181 |
182 | So this is an example of what __NOT__ to do:
183 | ```js
184 |
185 | // This is BAD! there is no row around the columns.
186 |
187 |
188 | ```
189 |
190 | Inside columns, rows allow complex nesting:
191 | ```js
192 |
193 |
194 |
195 |
196 | // The Row allows you to nest columns.
197 |
198 |
199 |
200 |
201 |
202 |
203 | ```
204 |
205 | And an example of what __NOT__ to do:
206 | ```js
207 |
208 |
209 |
210 | // This is BAD! The column nesting won’t work without the row.
211 |
212 |
213 |
214 |
215 | ```
216 |
217 | The fact that rows function in both these ways can make your components more generic.
218 |
219 | Take the following example:
220 | ```js
221 | class YourComponent extends Component {
222 | render() {
223 | return (
224 |
225 |
226 |
227 |
228 | );
229 | }
230 | }
231 |
232 | // elsewhere in your code...
233 |
234 |
235 | // YourComponent works at the top level
236 |
237 |
238 | // YourComponent also works inside a column
239 |
240 |
241 |
242 |
243 |
244 |
245 | ```
246 |
247 | Notice how the use of row as the outer container for the grid and the mechanism for nesting columns allows your component to function in more places. This allows for more sharable fragments of layouts.
248 |
249 | ##### Row Properties
250 |
251 | Property | Type | Default | Description
252 | :------- | :--- | :------ | :----------
253 | className | `String` | `undefined` | A custom class name.
254 |
255 | > _Note: You should not put a `` directly inside another ``, there should always be a Column barrier._
256 |
257 | ## Column
258 |
259 | The `` component divides rows into regions. Even though you configure the grid in terms of units (an 8 unit view or 12 unit view) You specify the width of a columns as a fraction of its parent. This keeps nesting from becoming brittle. In this sense, the column nesting is similar to bootstrap and foundation.
260 |
261 | Here is an example of using fractions:
262 | ```js
263 |
264 |
265 | I am one sixth
266 | I am two sixths
267 | I am three sixths
268 |
269 |
270 | I am one half
271 | I am one quarter
272 | I am one quarter
273 |
274 |
275 | ```
276 | Notice that the fractions only need to make sense within each row. You can use quarters in one row and thirds in the next, whatever makes sense for that section of the layout.
277 |
278 | If you want to keep the units you are dealing with in mind, it can be helpful to express your fractions using those units as denominators. Here is an example of what that might look like:
279 | ```js
280 |
281 | I am three units
282 |
283 | I am five units
284 |
285 |
286 | I am three of the five units above
287 |
288 |
289 | I am two of the five units above
290 |
291 |
292 |
293 |
294 | ```
295 |
296 | ##### Column Properties
297 |
298 | Property | Type | Default | Description
299 | :------- | :--- | :------ | :----------
300 | width | `FractionString` | `undefined` | The width of the column expressed as a fraction string. For example: as `"1/3"` or `"3/7"`.
301 | offset | `FractionString` | `undefined` | The left offset of the Column.
302 | className | `String` | `undefined` | A custom class name.
303 |
304 | #### So why bother with grid units at all?
305 |
306 | Well, take a look at this example:
307 | ```js
308 |
309 |
310 | I am one half!
311 |
312 |
313 |
314 |
315 | I am one half!
316 |
317 |
318 | I am one half!
319 |
320 |
321 |
322 |
323 | ```
324 |
325 | In the example above, If all you know about is fractions, every column only knows that it is 1/2. But this means a very different size if you are a deeply nested column or if you are on a bigger or smaller breakpoint. So even though you have more flexibility, you have lost an absolute sense of scale. 1/2 of what? 1/2 of a small screen? 1/2 of a 1/2 column? 1/2 of a 1/3 column on a medium screen? It gets hard to think about.
326 |
327 | Grid units give you a convention to measure against. A convention that is __constant__ across all breakpoints and no matter how deeply you nest your columns. So while you may be thinking in fractions while you create your layout. Your components can still think in terms of grid units when they decide how to display.
328 |
329 | Your components can access their absoulte size in grid units by using `observeGrid()`...
330 |
331 | ## observeGrid
332 |
333 | `observeGrid(Component)` is a higher order component. It takes another component as an argument and returns a new one that is aware of grid related properties. This allows you to create simple state-free components that expect properties about their column width or breakpoint and let `observeGrid` handle the updates.
334 |
335 | Here is the information you get when you use `observeGrid`:
336 | ```js
337 | const YourComponent = observeGrid(class extends Component {
338 | static propTypes = {
339 | colWidth: PropTypes.number, // column width in “grid units”
340 | colMinPixelWidth: PropTypes.number, // minimum number of pixels available in this column
341 | colMaxPixelWidth: PropTypes.number, // maximum number of pixels available in this column
342 | breakpoint: PropTypes.number, // current grid breakpoint
343 | };
344 | // ...
345 | });
346 | ```
347 | `colWidth` is ideal for most responsive use cases, because it doesn’t matter whether or not the breakpoint is flexible. 3 grid units is always 3 grid units. However, for more difficult cases, you can use `colMinPixelWidth` and `colMaxPixelWidth` (they will be the same for non-flexible breakpoints). `breakpoint` is available, but not typically as useful.
348 |
349 | ##### A Note on responsive components
350 |
351 | The good part about `colWidth` is that you don't need to know how your column got its size (whether it is 1/2 or 1/2 of 1/3 for example), but you should not assume at the component level that your `colWidth` will be an integer. Always assume it can be a decimal as well.
352 |
353 | For example, this is what you do __NOT__ want to do in your render method:
354 | ```js
355 | render() {
356 | // This is BAD! '===' assumes colWidth is an integer
357 | return (colWidth === 4) ? '4 unit view' : 'default view';
358 | }
359 | ```
360 |
361 | Instead, consider this alternative:
362 | ```js
363 | render() {
364 | // This is GOOD! '<=' allows for decimal values for columns
365 | return (colWidth <= 4) ? '4 unit view' : 'default view';
366 | }
367 | ```
368 |
369 | This makes more sense, because no matter what size column your component is in it will render the correct view. (even if the column is 3.333 grid units wide)
370 |
371 | ## License
372 |
373 | [MIT](/LICENSE)
374 |
375 |
376 | Released 2015 by [Greg Skiano](https://github.com/skiano) @ [Dow Jones](https://github.com/dowjones)
377 |
--------------------------------------------------------------------------------
/docs/full-width.md:
--------------------------------------------------------------------------------
1 |
2 | ### An example of fullwidth items in a layout
3 |
4 | Sometimes you may want a field behind part of your grid that extends to the edges of the screen. Or maybe you want a full-width component that does not want anything to do with the grid between two grid sections.
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/sample-grid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dowjones/react-cellblock/6a94199f5ef4309137686fa1827beb00bce13459/docs/sample-grid.jpg
--------------------------------------------------------------------------------
/docs/why-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dowjones/react-cellblock/6a94199f5ef4309137686fa1827beb00bce13459/docs/why-1.jpg
--------------------------------------------------------------------------------
/docs/why-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dowjones/react-cellblock/6a94199f5ef4309137686fa1827beb00bce13459/docs/why-2.jpg
--------------------------------------------------------------------------------
/docs/why-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dowjones/react-cellblock/6a94199f5ef4309137686fa1827beb00bce13459/docs/why-3.jpg
--------------------------------------------------------------------------------
/docs/why-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dowjones/react-cellblock/6a94199f5ef4309137686fa1827beb00bce13459/docs/why-4.jpg
--------------------------------------------------------------------------------
/docs/why-cellblock.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | 
5 |
6 | 
7 |
8 | 
9 |
--------------------------------------------------------------------------------
/example/Layout.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import PropTypes from 'prop-types';
4 | import {Grid} from '../src';
5 | import examples from './examples';
6 | import Banner from './modules/Banner';
7 | import Nav from './modules/Nav';
8 |
9 | import Optimized from './modules/Optimized';
10 | import Blocker from './modules/Blocker';
11 |
12 | const DEFAULT = Object.keys(examples)[0];
13 |
14 | export default React.createClass({
15 | propTypes: {
16 | onChange: PropTypes.func
17 | },
18 |
19 | getInitialState() {
20 | const initialExample = window.location.hash.replace('#',''); // lame routing
21 | return {
22 | example: examples.hasOwnProperty(initialExample) ? initialExample : DEFAULT
23 | };
24 | },
25 |
26 | changeExample(newExample) {
27 | this.setState({
28 | example: newExample
29 | });
30 | window.location.hash = newExample;
31 | },
32 |
33 | render() {
34 | const Example = examples[this.state.example];
35 | return (
36 |
37 |
38 | Cellblock
39 |
40 |
48 | );
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/example/examples/Chess.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import {Column, Row, observeGrid} from '../../src';
4 |
5 | const Cell = observeGrid(function ({dark, colWidth}) {
6 | const style = {
7 | position: 'relative',
8 | height: 0,
9 | paddingBottom: '100%',
10 | background: dark ? '#113300' : '#ffaa00',
11 | marginBottom: 20
12 | };
13 | return (
14 |
15 | {colWidth >= 1 ? colWidth : ''}
16 |
17 | );
18 | });
19 |
20 | export default function Chess() {
21 | const Rows = [];
22 |
23 | for (let r = 0; r < 8; r += 1) {
24 | const Columns = [];
25 |
26 | for (let c = 0; c < 8; c += 1) {
27 | Columns.push(
28 |
29 |
30 |
31 | );
32 | }
33 |
34 | Rows.push({Columns});
35 | }
36 |
37 | return