├── .babelrc
├── .editorconfig
├── .eslintrc
├── .flowconfig
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs
├── .gitignore
├── .nojekyll
├── css
│ ├── normalize.css
│ ├── rc-slider.css
│ ├── react-select.css
│ └── style.css
├── favicon.ico
├── images
│ ├── logo.png
│ ├── pattern1.png
│ ├── pattern2.png
│ ├── pattern3.png
│ └── photos
│ │ ├── photo01.jpg
│ │ ├── photo02.jpg
│ │ ├── photo03.jpg
│ │ ├── photo04.jpg
│ │ ├── photo05.jpg
│ │ ├── photo06.jpg
│ │ ├── photo07.jpg
│ │ ├── photo08.jpg
│ │ ├── photo09.jpg
│ │ ├── photo10.jpg
│ │ ├── photo11.jpg
│ │ ├── photo12.jpg
│ │ ├── photo13.jpg
│ │ ├── photo14.jpg
│ │ ├── photo15.jpg
│ │ ├── photo16.jpg
│ │ ├── photo17.jpg
│ │ ├── photo18.jpg
│ │ ├── photo19.jpg
│ │ ├── photo20.jpg
│ │ ├── photo21.jpg
│ │ └── photo22.jpg
├── index.html
├── js
│ ├── App.js
│ ├── Root.js
│ ├── components
│ │ ├── DemoControl.js
│ │ └── Header.js
│ ├── index.js
│ ├── pages
│ │ ├── ChangeSize.js
│ │ ├── Home.js
│ │ ├── HorizontalFlow.js
│ │ └── RealWorld.js
│ └── routes.js
├── webpack.config.js
└── webpack.config.production.js
├── jest
└── setup.js
├── package.json
├── src
├── animations
│ ├── easings.js
│ ├── request-animation-frame.js
│ └── transitions
│ │ ├── fade-down.js
│ │ ├── fade-up.js
│ │ ├── fade.js
│ │ ├── flip.js
│ │ ├── helix.js
│ │ ├── index.js
│ │ ├── scale-down.js
│ │ └── scale-up.js
├── components
│ ├── GridItem.js
│ ├── StackGrid.js
│ └── __tests__
│ │ ├── GridItem.spec.js
│ │ └── StackGrid.spec.js
├── declares
│ └── vendor.js.flow
├── index.js
├── types
│ └── index.js
└── utils
│ ├── __tests__
│ └── style-helper.spec.js
│ └── style-helper.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "flow",
4 | "es2015",
5 | "react",
6 | "stage-1"
7 | ],
8 | "plugins": [
9 | "react-hot-loader/babel"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = false
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "browser": true,
5 | "node": true,
6 | "jest/globals": true
7 | },
8 | "parser": "babel-eslint",
9 | "parserOptions": {
10 | "ecmaVersion": 2017,
11 | "sourceType": "module",
12 | "ecmaFeatures": {
13 | "jsx": true
14 | }
15 | },
16 | "plugins": [
17 | "jest",
18 | "flowtype"
19 | ],
20 | "extends": [
21 | "airbnb",
22 | "plugin:jest/recommended",
23 | "plugin:flowtype/recommended"
24 | ],
25 | "rules": {
26 | "no-prototype-builtins": "off",
27 | "comma-dangle": [
28 | "error",
29 | {
30 | "arrays": "always-multiline",
31 | "objects": "always-multiline",
32 | "imports": "always-multiline",
33 | "exports": "always-multiline",
34 | "functions": "ignore"
35 | }
36 | ],
37 | "function-paren-newline": "off",
38 | "object-curly-newline": "off",
39 | "react/no-find-dom-node": "off",
40 | "react/jsx-filename-extension": "off",
41 | "react/sort-comp": "off",
42 | "react/require-default-props": "off",
43 | "react/no-multi-comp": "off",
44 | "jsx-a11y/href-no-hash": "off",
45 | "jsx-a11y/label-has-for": "off",
46 | "jsx-a11y/img-has-alt": "off"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/node_modules/.*
3 |
4 | [include]
5 |
6 | [libs]
7 | ./src/declares
8 |
9 | [options]
10 | esproposal.class_static_fields=enable
11 | esproposal.export_star_as=enable
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /lib
2 | /tmp
3 |
4 | # Created by https://www.gitignore.io/api/osx,windows,node
5 |
6 | ### Node ###
7 | # Logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (http://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Typescript v1 declaration files
46 | typings/
47 |
48 | # Optional npm cache directory
49 | .npm
50 |
51 | # Optional eslint cache
52 | .eslintcache
53 |
54 | # Optional REPL history
55 | .node_repl_history
56 |
57 | # Output of 'npm pack'
58 | *.tgz
59 |
60 | # Yarn Integrity file
61 | .yarn-integrity
62 |
63 | # dotenv environment variables file
64 | .env
65 |
66 |
67 | ### OSX ###
68 | *.DS_Store
69 | .AppleDouble
70 | .LSOverride
71 |
72 | # Icon must end with two \r
73 | Icon
74 |
75 | # Thumbnails
76 | ._*
77 |
78 | # Files that might appear in the root of a volume
79 | .DocumentRevisions-V100
80 | .fseventsd
81 | .Spotlight-V100
82 | .TemporaryItems
83 | .Trashes
84 | .VolumeIcon.icns
85 | .com.apple.timemachine.donotpresent
86 |
87 | # Directories potentially created on remote AFP share
88 | .AppleDB
89 | .AppleDesktop
90 | Network Trash Folder
91 | Temporary Items
92 | .apdisk
93 |
94 | ### Windows ###
95 | # Windows thumbnail cache files
96 | Thumbs.db
97 | ehthumbs.db
98 | ehthumbs_vista.db
99 |
100 | # Folder config file
101 | Desktop.ini
102 |
103 | # Recycle Bin used on file shares
104 | $RECYCLE.BIN/
105 |
106 | # Windows Installer files
107 | *.cab
108 | *.msi
109 | *.msm
110 | *.msp
111 |
112 | # Windows shortcuts
113 | *.lnk
114 |
115 | # End of https://www.gitignore.io/api/osx,windows,node
116 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | branches:
2 | only:
3 | - master
4 | notifications:
5 | email: false
6 | language: node_js
7 | node_js:
8 | - "8"
9 | - "7"
10 | - "6"
11 | sudo: false
12 | cache:
13 | directories:
14 | - node_modules
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.7.1
2 |
3 | * Make transform coordinates round to the nearest integer to avoid blurriness. Thank you [@xaviergonz](https://github.com/xaviergonz)
4 |
5 |
6 | ## 0.7.0
7 |
8 | * Add `itemComponent` props. Thank you [@solomonhawk](https://github.com/solomonhawk) ! [#35](https://github.com/tsuyoshiwada/react-stack-grid/issues/35)
9 |
10 |
11 |
12 | ## 0.6.0
13 |
14 | * Add `rtl` props. Thank you [@azizghuloum](https://github.com/azizghuloum) !
15 |
16 |
17 |
18 | ## 0.5.0
19 |
20 | * Add `horizontal` props. Thank you [@ilyalesik](https://github.com/ilyalesik) !
21 |
22 |
23 |
24 | ## 0.4.0
25 |
26 | * Add `onLayout` props. Thanks for [@jarib](https://github.com/jarib) !
27 |
28 |
29 |
30 | ## 0.3.0
31 |
32 | * Add `gridRef` props. Thanks for [@mquandalle](https://github.com/mquandalle) and [@derwydd](https://github.com/derwydd) !
33 |
34 |
35 |
36 | ## 0.2.2
37 |
38 | * Fix calculations for percentage columns width [#16](https://github.com/tsuyoshiwada/react-stack-grid/pull/16)
39 | Thanks [@mquandalle](https://github.com/mquandalle)!
40 | * Migrate from react-addons-transition-group to react-transition-group. [#17](https://github.com/tsuyoshiwada/react-stack-grid/issues/17)
41 | * Remove setState warning that occurs after unmounting.
42 |
43 |
44 |
45 | ## 0.2.1
46 |
47 | * Support for React v15.5.x ~ (add `prop-types` package) [#2](https://github.com/tsuyoshiwada/react-stack-grid/issues/12)
48 | * Fix lint and typechecking (flowtype)
49 | * Update devDependencies
50 |
51 |
52 |
53 | ## 0.2.0
54 |
55 | * Add `enableSSR` props. Thanks for [@egorAva](https://github.com/egorAva)! [#7](https://github.com/tsuyoshiwada/react-stack-grid/pull/7)
56 |
57 |
58 |
59 | ## 0.1.1
60 |
61 | * Fix transition animation when sort order changed
62 | * Update demo page (Added shuffle button)
63 |
64 |
65 |
66 | ## 0.1.0
67 |
68 | ### New feature
69 |
70 | * Support column percentage width
71 |
72 | ### Bugfix
73 |
74 | * Change waiting for size calculation of react-sizeme before rendering
75 | * Fix warning when deleted before appear
76 | * Remove propTypes warning when there is no children
77 |
78 |
79 |
80 | ## 0.0.2
81 |
82 | * Fix files field in package.json
83 |
84 |
85 |
86 | ## 0.0.1
87 |
88 | * First release.
89 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 tsuyoshiwada
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-stack-grid
2 |
3 | [](https://travis-ci.org/tsuyoshiwada/react-stack-grid)
4 | [](http://badge.fury.io/js/react-stack-grid)
5 |
6 |
7 | Pinterest like layout components for React.js.
8 |
9 |
10 |
11 |
12 | ## Table of Contents
13 |
14 | * [Live Demo](#live-demo)
15 | * [Install](#install)
16 | * [Quick Example](#quick-example)
17 | * [Props](#props)
18 | * [Instance API](#instance-api)
19 | * [updateLayout(): void](#updatelayout-void)
20 | * [Animations](#animations)
21 | * [Tips](#tips)
22 | * [Performance when using images](#performance-when-using-images)
23 | * [When animation is unnecessary](#when-animation-is-unnecessary)
24 | * [How to manually update layout ?](#how-to-manually-update-layout-)
25 | * [Responsive layout](#responsive-layout)
26 | * [Thanks](#thanks)
27 | * [License](#license)
28 | * [ChangeLog](#changelog)
29 | * [Author](#author)
30 | * [Development](#development)
31 | * [Contribution](#contribution)
32 |
33 |
34 |
35 |
36 | ## Live Demo
37 |
38 | 
39 |
40 | [https://tsuyoshiwada.github.io/react-stack-grid/](https://tsuyoshiwada.github.io/react-stack-grid/)
41 |
42 |
43 |
44 |
45 | ## Install
46 |
47 | You can install the [react-stack-grid](https://www.npmjs.com/package/react-stack-grid) from [npm](https://www.npmjs.com/).
48 |
49 | ```bash
50 | $ npm install react-stack-grid
51 | ```
52 |
53 |
54 |
55 |
56 | ## Quick Example
57 |
58 | Following code is simplest usage.
59 |
60 | ```javascript
61 | import React, { Component } from "react";
62 | import StackGrid from "react-stack-grid";
63 |
64 | class MyComponent extends Component {
65 | render() {
66 | return (
67 |
70 | Item 1
71 | Item 2
72 | Item 3
73 |
74 | );
75 | }
76 | }
77 | ```
78 |
79 | width of parent is managed by [react-sizeme](https://github.com/ctrlplusb/react-sizeme).
80 |
81 |
82 |
83 |
84 | ## Props
85 |
86 | You can set the following properties.
87 |
88 | | Property | Type | Default | Description |
89 | |:----------------------|:------------------------------------------------------------|:---------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------|
90 | | `className` | `PropTypes.string` | `undefined` | Specify `className` of component. |
91 | | `style` | `PropTypes.object` | `{}` | Original style of component. Following styles are ignored. (`position`, `height`, `transition`) |
92 | | `gridRef` | `PropTypes.func` | `null` | Reference the instance of StackGrid. Unlike ordinary `ref`, it accepts only functions. |
93 | | `component` | `PropTypes.string` | `"div"` | See [ReactTransitionGroup](https://facebook.github.io/react/docs/animation.html#rendering-a-different-component) |
94 | | `itemComponent` | `PropTypes.string` | `"span"` | Specify the component of the grid item. |
95 | | `columnWidth` | `PropTypes.oneOfType([PropTypes.number, PropTypes.string])` | `150` | Specify column width as an number(`px`), or percentage string. (Example `"33.33%"`) |
96 | | `gutterWidth` | `PropTypes.number` | `5` | Specify gutter width as an number. |
97 | | `gutterHeight` | `PropTypes.number` | `5` | Specify gutter height as an number. |
98 | | `duration` | `PropTypes.number` | `480` | Specify duration of animation in ms. |
99 | | `easing` | `PropTypes.string` | `easings.quartOut` | Specify a css valid transition-timing-function string. It can be easily specification by using `easings`. |
100 | | `appearDelay` | `PropTypes.number` | `30` | Specify delay of the initial animation in ms. |
101 | | `appear` | `PropTypes.func` | `fadeUp.appear` | See Animations section. |
102 | | `appeared` | `PropTypes.func` | `fadeUp.appear` | ... |
103 | | `enter` | `PropTypes.func` | `fadeUp.appear` | ... |
104 | | `entered` | `PropTypes.func` | `fadeUp.appear` | ... |
105 | | `leaved` | `PropTypes.func` | `fadeUp.appear` | ... |
106 | | `units` | `PropTypes.func` | `{ length: "px", angle: "deg" }` | ... |
107 | | `monitorImagesLoaded` | `PropTypes.bool` | `false` | If set to `true`, images reading is monitored. use [imagesloaded](https://github.com/desandro/imagesloaded). |
108 | | `vendorPrefix` | `PropTypes.bool` | `false` | If set to `true`, add a vendor prefix to styles add dynamically. |
109 | | `userAgent` | `PropTypes.string` | `undefined` | Specify userAgent for determinig the vendor prefix. See [inline-style-prefixer](https://github.com/rofrischmann/inline-style-prefixer). |
110 | | `enableSSR` | `PropTypes.bool` | `false` | Render component on the server side. [More info](https://github.com/ctrlplusb/react-sizeme#server-side-rendering). |
111 | | `onLayout` | `PropTypes.func` | `null` | It is called at the timing when the layout is confirmed, or at the updated timing. (Called only by client.) |
112 | | `horizontal` | `PropTypes.bool` | `false` | The transposed (horizontal) order of drawing elements. Retains the original order of the items. |
113 | | `rtl` | `PropTypes.bool` | `false` | When true, items are placed right-to-left instead of the default left-to-right. Useful for RTL languages such as Arabic and Hebrew. |
114 |
115 |
116 | ## Instance API
117 |
118 | ### updateLayout(): void
119 |
120 | Update the current layout.
121 |
122 |
123 |
124 |
125 | ## Animations
126 |
127 | The following function must return styles related to animation.
128 | See [ReactTransitionGroup](https://facebook.github.io/react/docs/animation.html#rendering-a-different-component) for details.
129 |
130 | * `appear`
131 | * `appeared`
132 | * `enter`
133 | * `entered`
134 | * `leaved`
135 |
136 | You can use extended syntax for transform's style. For example properties like `translateX` and` scale`.
137 | See [easy-css-transform-builder](https://github.com/tsuyoshiwada/easy-css-transform-builder).
138 |
139 | Each function is given the following arguments.
140 |
141 | * `rect: { top: number; left: number; width: number; height: number; }`
142 | * `containerSize: { width: number; height: number; }`
143 | * `index: number`
144 |
145 | It is easiest to use them because you have several presets.
146 |
147 | * `fade`
148 | * `fadeDown`
149 | * `fadeUp`
150 | * `scaleDown`
151 | * `scaleUp`
152 | * `flip`
153 | * `helix`
154 |
155 | It's an actual use example.
156 |
157 | ```javascript
158 | import StackGrid, { transitions } from "react-stack-grid";
159 |
160 | const { scaleDown } = transitions;
161 |
162 | class MyComponent extends Component {
163 | render() {
164 | return (
165 |
173 | ...
174 |
175 | );
176 | }
177 | }
178 | ```
179 |
180 | Please try actual demonstration in [live demo](https://tsuyoshiwada.github.io/react-stack-grid/).
181 |
182 |
183 |
184 |
185 | ## Tips
186 |
187 |
188 | ### Performance when using images
189 |
190 | When `true` is specified for `monitorImagesLoaded`, reloading occurs when the image loading is completed.
191 | If you know the size in advance, specify `monitorImagesLoaded` as `false`.
192 |
193 |
194 | ### When animation is unnecessary
195 |
196 | By default animation is enabled.
197 | If it's not necessary, specify `0` for `duration` property.
198 |
199 | ```javascript
200 |
204 | ...
205 |
206 | ```
207 |
208 |
209 | ### How to manually update layout ?
210 |
211 | If the size of an item is changed by an action such as a click event, there are cases where you want to update the layout manually.
212 | You can manually update the layout by referring to the StackGrid instance with `gridRef` and executing the`updateLayout()` method.
213 |
214 | ```javascript
215 | class MyComponent extends React.Component {
216 |
217 | // When the size of the item is changed...
218 | something = () => {
219 | this.grid.updateLayout();
220 | };
221 |
222 | render() {
223 | return (
224 | this.grid = grid}
226 | >
227 | {/* items ... */}
228 |
229 | );
230 | }
231 | }
232 | ```
233 |
234 |
235 | ### Responsive layout
236 |
237 | You can get width using [react-sizeme](https://github.com/ctrlplusb/react-sizeme) and change columnWidth according to width.
238 | This is a solution, but we can respond in other ways!
239 |
240 | ```javascript
241 | import React, { Component } from 'react';
242 | import sizeMe from 'react-sizeme';
243 | import StackGrid from 'react-stack-grid';
244 |
245 | class YourComponent extends Component {
246 | render() {
247 | const {
248 | size: {
249 | width
250 | }
251 | } = this.props;
252 |
253 | return (
254 |
258 | // Grid items...
259 |
260 | );
261 | }
262 | }
263 |
264 | export default sizeMe()(YourComponent);
265 | ```
266 |
267 |
268 |
269 |
270 |
271 | ## Thanks
272 |
273 | * Layout inspired by [Pinterest](https://pinterest.com/).
274 | * API inspired by [dantrain/react-stonecutter](https://github.com/dantrain/react-stonecutter).
275 |
276 |
277 |
278 |
279 | ## License
280 |
281 | Released under the [MIT Licence](https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/master/LICENSE)
282 |
283 |
284 |
285 |
286 | ## ChangeLog
287 |
288 | See [CHANGELOG.md](./CHANGELOG.md)
289 |
290 |
291 |
292 |
293 | ## Author
294 |
295 | [tsuyoshiwada](https://github.com/tsuyoshiwada)
296 |
297 |
298 |
299 |
300 | ## Development
301 |
302 | Initialization of the project.
303 |
304 | ```bash
305 | $ cd /your/project/dir
306 | $ git clone https://github.com/tsuyoshiwada/react-stack-grid.git
307 | ```
308 |
309 | Install some dependencies.
310 |
311 | ```bash
312 | $ npm install
313 | ```
314 |
315 | Start the development and can you see demo page (access to the `http://localhost:3000/`).
316 |
317 | ```bash
318 | $ npm start
319 | ```
320 |
321 | Run lint and testing.
322 |
323 | ```bash
324 | $ npm test
325 | ```
326 |
327 | Generates build file.
328 |
329 | ```bash
330 | $ npm run build
331 | ```
332 |
333 |
334 |
335 | ## Contribution
336 |
337 | Thank you for your interest in react-stack-grid.js.
338 | Bugs, feature requests and comments are more than welcome in the [issues](https://github.com/tsuyoshiwada/react-stack-grid/issues).
339 |
340 | **Before you open a PR:**
341 |
342 | Be careful to follow the code style of the project. Run `npm test` after your changes and ensure you do not introduce any new errors or warnings.
343 | All new features and changes need documentation.
344 |
345 | Thanks!
346 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /**
4 | * 1. Change the default font family in all browsers (opinionated).
5 | * 2. Correct the line height in all browsers.
6 | * 3. Prevent adjustments of font size after orientation changes in
7 | * IE on Windows Phone and in iOS.
8 | */
9 |
10 | /* Document
11 | ========================================================================== */
12 |
13 | html {
14 | font-family: sans-serif; /* 1 */
15 | line-height: 1.15; /* 2 */
16 | -ms-text-size-adjust: 100%; /* 3 */
17 | -webkit-text-size-adjust: 100%; /* 3 */
18 | }
19 |
20 | /* Sections
21 | ========================================================================== */
22 |
23 | /**
24 | * Remove the margin in all browsers (opinionated).
25 | */
26 |
27 | body {
28 | margin: 0;
29 | }
30 |
31 | /**
32 | * Add the correct display in IE 9-.
33 | */
34 |
35 | article,
36 | aside,
37 | footer,
38 | header,
39 | nav,
40 | section {
41 | display: block;
42 | }
43 |
44 | /**
45 | * Correct the font size and margin on `h1` elements within `section` and
46 | * `article` contexts in Chrome, Firefox, and Safari.
47 | */
48 |
49 | h1 {
50 | font-size: 2em;
51 | margin: 0.67em 0;
52 | }
53 |
54 | /* Grouping content
55 | ========================================================================== */
56 |
57 | /**
58 | * Add the correct display in IE 9-.
59 | * 1. Add the correct display in IE.
60 | */
61 |
62 | figcaption,
63 | figure,
64 | main { /* 1 */
65 | display: block;
66 | }
67 |
68 | /**
69 | * Add the correct margin in IE 8.
70 | */
71 |
72 | figure {
73 | margin: 1em 40px;
74 | }
75 |
76 | /**
77 | * 1. Add the correct box sizing in Firefox.
78 | * 2. Show the overflow in Edge and IE.
79 | */
80 |
81 | hr {
82 | box-sizing: content-box; /* 1 */
83 | height: 0; /* 1 */
84 | overflow: visible; /* 2 */
85 | }
86 |
87 | /**
88 | * 1. Correct the inheritance and scaling of font size in all browsers.
89 | * 2. Correct the odd `em` font sizing in all browsers.
90 | */
91 |
92 | pre {
93 | font-family: monospace, monospace; /* 1 */
94 | font-size: 1em; /* 2 */
95 | }
96 |
97 | /* Text-level semantics
98 | ========================================================================== */
99 |
100 | /**
101 | * 1. Remove the gray background on active links in IE 10.
102 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
103 | */
104 |
105 | a {
106 | background-color: transparent; /* 1 */
107 | -webkit-text-decoration-skip: objects; /* 2 */
108 | }
109 |
110 | /**
111 | * Remove the outline on focused links when they are also active or hovered
112 | * in all browsers (opinionated).
113 | */
114 |
115 | a:active,
116 | a:hover {
117 | outline-width: 0;
118 | }
119 |
120 | /**
121 | * 1. Remove the bottom border in Firefox 39-.
122 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
123 | */
124 |
125 | abbr[title] {
126 | border-bottom: none; /* 1 */
127 | text-decoration: underline; /* 2 */
128 | text-decoration: underline dotted; /* 2 */
129 | }
130 |
131 | /**
132 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
133 | */
134 |
135 | b,
136 | strong {
137 | font-weight: inherit;
138 | }
139 |
140 | /**
141 | * Add the correct font weight in Chrome, Edge, and Safari.
142 | */
143 |
144 | b,
145 | strong {
146 | font-weight: bolder;
147 | }
148 |
149 | /**
150 | * 1. Correct the inheritance and scaling of font size in all browsers.
151 | * 2. Correct the odd `em` font sizing in all browsers.
152 | */
153 |
154 | code,
155 | kbd,
156 | samp {
157 | font-family: monospace, monospace; /* 1 */
158 | font-size: 1em; /* 2 */
159 | }
160 |
161 | /**
162 | * Add the correct font style in Android 4.3-.
163 | */
164 |
165 | dfn {
166 | font-style: italic;
167 | }
168 |
169 | /**
170 | * Add the correct background and color in IE 9-.
171 | */
172 |
173 | mark {
174 | background-color: #ff0;
175 | color: #000;
176 | }
177 |
178 | /**
179 | * Add the correct font size in all browsers.
180 | */
181 |
182 | small {
183 | font-size: 80%;
184 | }
185 |
186 | /**
187 | * Prevent `sub` and `sup` elements from affecting the line height in
188 | * all browsers.
189 | */
190 |
191 | sub,
192 | sup {
193 | font-size: 75%;
194 | line-height: 0;
195 | position: relative;
196 | vertical-align: baseline;
197 | }
198 |
199 | sub {
200 | bottom: -0.25em;
201 | }
202 |
203 | sup {
204 | top: -0.5em;
205 | }
206 |
207 | /* Embedded content
208 | ========================================================================== */
209 |
210 | /**
211 | * Add the correct display in IE 9-.
212 | */
213 |
214 | audio,
215 | video {
216 | display: inline-block;
217 | }
218 |
219 | /**
220 | * Add the correct display in iOS 4-7.
221 | */
222 |
223 | audio:not([controls]) {
224 | display: none;
225 | height: 0;
226 | }
227 |
228 | /**
229 | * Remove the border on images inside links in IE 10-.
230 | */
231 |
232 | img {
233 | border-style: none;
234 | }
235 |
236 | /**
237 | * Hide the overflow in IE.
238 | */
239 |
240 | svg:not(:root) {
241 | overflow: hidden;
242 | }
243 |
244 | /* Forms
245 | ========================================================================== */
246 |
247 | /**
248 | * 1. Change the font styles in all browsers (opinionated).
249 | * 2. Remove the margin in Firefox and Safari.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | font-family: sans-serif; /* 1 */
258 | font-size: 100%; /* 1 */
259 | line-height: 1.15; /* 1 */
260 | margin: 0; /* 2 */
261 | }
262 |
263 | /**
264 | * Show the overflow in IE.
265 | * 1. Show the overflow in Edge.
266 | */
267 |
268 | button,
269 | input { /* 1 */
270 | overflow: visible;
271 | }
272 |
273 | /**
274 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
275 | * 1. Remove the inheritance of text transform in Firefox.
276 | */
277 |
278 | button,
279 | select { /* 1 */
280 | text-transform: none;
281 | }
282 |
283 | /**
284 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
285 | * controls in Android 4.
286 | * 2. Correct the inability to style clickable types in iOS and Safari.
287 | */
288 |
289 | button,
290 | html [type="button"], /* 1 */
291 | [type="reset"],
292 | [type="submit"] {
293 | -webkit-appearance: button; /* 2 */
294 | }
295 |
296 | /**
297 | * Remove the inner border and padding in Firefox.
298 | */
299 |
300 | button::-moz-focus-inner,
301 | [type="button"]::-moz-focus-inner,
302 | [type="reset"]::-moz-focus-inner,
303 | [type="submit"]::-moz-focus-inner {
304 | border-style: none;
305 | padding: 0;
306 | }
307 |
308 | /**
309 | * Restore the focus styles unset by the previous rule.
310 | */
311 |
312 | button:-moz-focusring,
313 | [type="button"]:-moz-focusring,
314 | [type="reset"]:-moz-focusring,
315 | [type="submit"]:-moz-focusring {
316 | outline: 1px dotted ButtonText;
317 | }
318 |
319 | /**
320 | * Change the border, margin, and padding in all browsers (opinionated).
321 | */
322 |
323 | fieldset {
324 | border: 1px solid #c0c0c0;
325 | margin: 0 2px;
326 | padding: 0.35em 0.625em 0.75em;
327 | }
328 |
329 | /**
330 | * 1. Correct the text wrapping in Edge and IE.
331 | * 2. Correct the color inheritance from `fieldset` elements in IE.
332 | * 3. Remove the padding so developers are not caught out when they zero out
333 | * `fieldset` elements in all browsers.
334 | */
335 |
336 | legend {
337 | box-sizing: border-box; /* 1 */
338 | color: inherit; /* 2 */
339 | display: table; /* 1 */
340 | max-width: 100%; /* 1 */
341 | padding: 0; /* 3 */
342 | white-space: normal; /* 1 */
343 | }
344 |
345 | /**
346 | * 1. Add the correct display in IE 9-.
347 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
348 | */
349 |
350 | progress {
351 | display: inline-block; /* 1 */
352 | vertical-align: baseline; /* 2 */
353 | }
354 |
355 | /**
356 | * Remove the default vertical scrollbar in IE.
357 | */
358 |
359 | textarea {
360 | overflow: auto;
361 | }
362 |
363 | /**
364 | * 1. Add the correct box sizing in IE 10-.
365 | * 2. Remove the padding in IE 10-.
366 | */
367 |
368 | [type="checkbox"],
369 | [type="radio"] {
370 | box-sizing: border-box; /* 1 */
371 | padding: 0; /* 2 */
372 | }
373 |
374 | /**
375 | * Correct the cursor style of increment and decrement buttons in Chrome.
376 | */
377 |
378 | [type="number"]::-webkit-inner-spin-button,
379 | [type="number"]::-webkit-outer-spin-button {
380 | height: auto;
381 | }
382 |
383 | /**
384 | * 1. Correct the odd appearance in Chrome and Safari.
385 | * 2. Correct the outline style in Safari.
386 | */
387 |
388 | [type="search"] {
389 | -webkit-appearance: textfield; /* 1 */
390 | outline-offset: -2px; /* 2 */
391 | }
392 |
393 | /**
394 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
395 | */
396 |
397 | [type="search"]::-webkit-search-cancel-button,
398 | [type="search"]::-webkit-search-decoration {
399 | -webkit-appearance: none;
400 | }
401 |
402 | /**
403 | * 1. Correct the inability to style clickable types in iOS and Safari.
404 | * 2. Change font properties to `inherit` in Safari.
405 | */
406 |
407 | ::-webkit-file-upload-button {
408 | -webkit-appearance: button; /* 1 */
409 | font: inherit; /* 2 */
410 | }
411 |
412 | /* Interactive
413 | ========================================================================== */
414 |
415 | /*
416 | * Add the correct display in IE 9-.
417 | * 1. Add the correct display in Edge, IE, and Firefox.
418 | */
419 |
420 | details, /* 1 */
421 | menu {
422 | display: block;
423 | }
424 |
425 | /*
426 | * Add the correct display in all browsers.
427 | */
428 |
429 | summary {
430 | display: list-item;
431 | }
432 |
433 | /* Scripting
434 | ========================================================================== */
435 |
436 | /**
437 | * Add the correct display in IE 9-.
438 | */
439 |
440 | canvas {
441 | display: inline-block;
442 | }
443 |
444 | /**
445 | * Add the correct display in IE.
446 | */
447 |
448 | template {
449 | display: none;
450 | }
451 |
452 | /* Hidden
453 | ========================================================================== */
454 |
455 | /**
456 | * Add the correct display in IE 10-.
457 | */
458 |
459 | [hidden] {
460 | display: none;
461 | }
462 |
--------------------------------------------------------------------------------
/docs/css/rc-slider.css:
--------------------------------------------------------------------------------
1 | .rc-slider {
2 | position: relative;
3 | height: 14px;
4 | padding: 5px 0;
5 | width: 100%;
6 | border-radius: 6px;
7 | box-sizing: border-box;
8 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
9 | }
10 | .rc-slider * {
11 | box-sizing: border-box;
12 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
13 | }
14 | .rc-slider-rail {
15 | position: absolute;
16 | width: 100%;
17 | background-color: #e9e9e9;
18 | height: 4px;
19 | }
20 | .rc-slider-track {
21 | position: absolute;
22 | left: 0;
23 | height: 4px;
24 | border-radius: 6px;
25 | background-color: #abe2fb;
26 | }
27 | .rc-slider-handle {
28 | position: absolute;
29 | margin-left: -7px;
30 | margin-top: -5px;
31 | width: 14px;
32 | height: 14px;
33 | cursor: pointer;
34 | border-radius: 50%;
35 | border: solid 2px #96dbfa;
36 | background-color: #fff;
37 | }
38 | .rc-slider-handle:hover {
39 | border-color: #57c5f7;
40 | }
41 | .rc-slider-handle-active:active {
42 | border-color: #57c5f7;
43 | box-shadow: 0 0 5px #57c5f7;
44 | }
45 | .rc-slider-mark {
46 | position: absolute;
47 | top: 18px;
48 | left: 0;
49 | width: 100%;
50 | font-size: 12px;
51 | }
52 | .rc-slider-mark-text {
53 | position: absolute;
54 | display: inline-block;
55 | vertical-align: middle;
56 | text-align: center;
57 | cursor: pointer;
58 | color: #999;
59 | }
60 | .rc-slider-mark-text-active {
61 | color: #666;
62 | }
63 | .rc-slider-step {
64 | position: absolute;
65 | width: 100%;
66 | height: 4px;
67 | background: transparent;
68 | }
69 | .rc-slider-dot {
70 | position: absolute;
71 | bottom: -2px;
72 | margin-left: -4px;
73 | width: 8px;
74 | height: 8px;
75 | border: 2px solid #e9e9e9;
76 | background-color: #fff;
77 | cursor: pointer;
78 | border-radius: 50%;
79 | vertical-align: middle;
80 | }
81 | .rc-slider-dot:first-child {
82 | margin-left: -4px;
83 | }
84 | .rc-slider-dot:last-child {
85 | margin-left: -4px;
86 | }
87 | .rc-slider-dot-active {
88 | border-color: #96dbfa;
89 | }
90 | .rc-slider-disabled {
91 | background-color: #e9e9e9;
92 | }
93 | .rc-slider-disabled .rc-slider-track {
94 | background-color: #ccc;
95 | }
96 | .rc-slider-disabled .rc-slider-handle,
97 | .rc-slider-disabled .rc-slider-dot {
98 | border-color: #ccc;
99 | background-color: #fff;
100 | cursor: not-allowed;
101 | }
102 | .rc-slider-disabled .rc-slider-mark-text,
103 | .rc-slider-disabled .rc-slider-dot {
104 | cursor: not-allowed !important;
105 | }
106 | .rc-slider-vertical {
107 | width: 14px;
108 | height: 100%;
109 | padding: 0 5px;
110 | }
111 | .rc-slider-vertical .rc-slider-rail {
112 | height: 100%;
113 | width: 4px;
114 | }
115 | .rc-slider-vertical .rc-slider-track {
116 | left: 5px;
117 | bottom: 0;
118 | width: 4px;
119 | }
120 | .rc-slider-vertical .rc-slider-handle {
121 | margin-left: -5px;
122 | margin-bottom: -7px;
123 | }
124 | .rc-slider-vertical .rc-slider-mark {
125 | top: 0;
126 | left: 18px;
127 | height: 100%;
128 | }
129 | .rc-slider-vertical .rc-slider-step {
130 | height: 100%;
131 | width: 4px;
132 | }
133 | .rc-slider-vertical .rc-slider-dot {
134 | left: 2px;
135 | margin-bottom: -4px;
136 | }
137 | .rc-slider-vertical .rc-slider-dot:first-child {
138 | margin-bottom: -4px;
139 | }
140 | .rc-slider-vertical .rc-slider-dot:last-child {
141 | margin-bottom: -4px;
142 | }
143 | .rc-slider-tooltip-zoom-down-enter,
144 | .rc-slider-tooltip-zoom-down-appear {
145 | -webkit-animation-duration: .3s;
146 | animation-duration: .3s;
147 | -webkit-animation-fill-mode: both;
148 | animation-fill-mode: both;
149 | display: block !important;
150 | -webkit-animation-play-state: paused;
151 | animation-play-state: paused;
152 | }
153 | .rc-slider-tooltip-zoom-down-leave {
154 | -webkit-animation-duration: .3s;
155 | animation-duration: .3s;
156 | -webkit-animation-fill-mode: both;
157 | animation-fill-mode: both;
158 | display: block !important;
159 | -webkit-animation-play-state: paused;
160 | animation-play-state: paused;
161 | }
162 | .rc-slider-tooltip-zoom-down-enter.rc-slider-tooltip-zoom-down-enter-active,
163 | .rc-slider-tooltip-zoom-down-appear.rc-slider-tooltip-zoom-down-appear-active {
164 | -webkit-animation-name: rcSliderTooltipZoomDownIn;
165 | animation-name: rcSliderTooltipZoomDownIn;
166 | -webkit-animation-play-state: running;
167 | animation-play-state: running;
168 | }
169 | .rc-slider-tooltip-zoom-down-leave.rc-slider-tooltip-zoom-down-leave-active {
170 | -webkit-animation-name: rcSliderTooltipZoomDownOut;
171 | animation-name: rcSliderTooltipZoomDownOut;
172 | -webkit-animation-play-state: running;
173 | animation-play-state: running;
174 | }
175 | .rc-slider-tooltip-zoom-down-enter,
176 | .rc-slider-tooltip-zoom-down-appear {
177 | -webkit-transform: scale(0, 0);
178 | transform: scale(0, 0);
179 | -webkit-animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
180 | animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
181 | }
182 | .rc-slider-tooltip-zoom-down-leave {
183 | -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
184 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
185 | }
186 | @-webkit-keyframes rcSliderTooltipZoomDownIn {
187 | 0% {
188 | opacity: 0;
189 | -webkit-transform-origin: 50% 100%;
190 | transform-origin: 50% 100%;
191 | -webkit-transform: scale(0, 0);
192 | transform: scale(0, 0);
193 | }
194 | 100% {
195 | -webkit-transform-origin: 50% 100%;
196 | transform-origin: 50% 100%;
197 | -webkit-transform: scale(1, 1);
198 | transform: scale(1, 1);
199 | }
200 | }
201 | @keyframes rcSliderTooltipZoomDownIn {
202 | 0% {
203 | opacity: 0;
204 | -webkit-transform-origin: 50% 100%;
205 | transform-origin: 50% 100%;
206 | -webkit-transform: scale(0, 0);
207 | transform: scale(0, 0);
208 | }
209 | 100% {
210 | -webkit-transform-origin: 50% 100%;
211 | transform-origin: 50% 100%;
212 | -webkit-transform: scale(1, 1);
213 | transform: scale(1, 1);
214 | }
215 | }
216 | @-webkit-keyframes rcSliderTooltipZoomDownOut {
217 | 0% {
218 | -webkit-transform-origin: 50% 100%;
219 | transform-origin: 50% 100%;
220 | -webkit-transform: scale(1, 1);
221 | transform: scale(1, 1);
222 | }
223 | 100% {
224 | opacity: 0;
225 | -webkit-transform-origin: 50% 100%;
226 | transform-origin: 50% 100%;
227 | -webkit-transform: scale(0, 0);
228 | transform: scale(0, 0);
229 | }
230 | }
231 | @keyframes rcSliderTooltipZoomDownOut {
232 | 0% {
233 | -webkit-transform-origin: 50% 100%;
234 | transform-origin: 50% 100%;
235 | -webkit-transform: scale(1, 1);
236 | transform: scale(1, 1);
237 | }
238 | 100% {
239 | opacity: 0;
240 | -webkit-transform-origin: 50% 100%;
241 | transform-origin: 50% 100%;
242 | -webkit-transform: scale(0, 0);
243 | transform: scale(0, 0);
244 | }
245 | }
246 | .rc-slider-tooltip {
247 | position: absolute;
248 | left: -9999px;
249 | top: -9999px;
250 | visibility: visible;
251 | box-sizing: border-box;
252 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
253 | }
254 | .rc-slider-tooltip * {
255 | box-sizing: border-box;
256 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
257 | }
258 | .rc-slider-tooltip-hidden {
259 | display: none;
260 | }
261 | .rc-slider-tooltip-placement-top {
262 | padding: 4px 0 8px 0;
263 | }
264 | .rc-slider-tooltip-inner {
265 | padding: 6px 2px;
266 | min-width: 24px;
267 | height: 24px;
268 | font-size: 12px;
269 | line-height: 1;
270 | color: #fff;
271 | text-align: center;
272 | text-decoration: none;
273 | background-color: #6c6c6c;
274 | border-radius: 6px;
275 | box-shadow: 0 0 4px #d9d9d9;
276 | }
277 | .rc-slider-tooltip-arrow {
278 | position: absolute;
279 | width: 0;
280 | height: 0;
281 | border-color: transparent;
282 | border-style: solid;
283 | }
284 | .rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow {
285 | bottom: 4px;
286 | left: 50%;
287 | margin-left: -4px;
288 | border-width: 4px 4px 0;
289 | border-top-color: #6c6c6c;
290 | }
291 |
--------------------------------------------------------------------------------
/docs/css/react-select.css:
--------------------------------------------------------------------------------
1 | /**
2 | * React Select
3 | * ============
4 | * Created by Jed Watson and Joss Mackison for KeystoneJS, http://www.keystonejs.com/
5 | * https://twitter.com/jedwatson https://twitter.com/jossmackison https://twitter.com/keystonejs
6 | * MIT License: https://github.com/JedWatson/react-select
7 | */
8 | .Select {
9 | position: relative;
10 | }
11 | .Select,
12 | .Select div,
13 | .Select input,
14 | .Select span {
15 | -webkit-box-sizing: border-box;
16 | -moz-box-sizing: border-box;
17 | box-sizing: border-box;
18 | }
19 | .Select.is-disabled > .Select-control {
20 | background-color: #f9f9f9;
21 | }
22 | .Select.is-disabled > .Select-control:hover {
23 | box-shadow: none;
24 | }
25 | .Select.is-disabled .Select-arrow-zone {
26 | cursor: default;
27 | pointer-events: none;
28 | opacity: 0.35;
29 | }
30 | .Select-control {
31 | background-color: #fff;
32 | border-color: #d9d9d9 #ccc #b3b3b3;
33 | border-radius: 4px;
34 | border: 1px solid #ccc;
35 | color: #333;
36 | cursor: default;
37 | display: table;
38 | border-spacing: 0;
39 | border-collapse: separate;
40 | height: 36px;
41 | outline: none;
42 | overflow: hidden;
43 | position: relative;
44 | width: 100%;
45 | }
46 | .Select-control:hover {
47 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
48 | }
49 | .Select-control .Select-input:focus {
50 | outline: none;
51 | }
52 | .is-searchable.is-open > .Select-control {
53 | cursor: text;
54 | }
55 | .is-open > .Select-control {
56 | border-bottom-right-radius: 0;
57 | border-bottom-left-radius: 0;
58 | background: #fff;
59 | border-color: #b3b3b3 #ccc #d9d9d9;
60 | }
61 | .is-open > .Select-control > .Select-arrow {
62 | border-color: transparent transparent #999;
63 | border-width: 0 5px 5px;
64 | }
65 | .is-searchable.is-focused:not(.is-open) > .Select-control {
66 | cursor: text;
67 | }
68 | .is-focused:not(.is-open) > .Select-control {
69 | border-color: #007eff;
70 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 3px rgba(0, 126, 255, 0.1);
71 | }
72 | .Select-placeholder,
73 | .Select--single > .Select-control .Select-value {
74 | bottom: 0;
75 | color: #aaa;
76 | left: 0;
77 | line-height: 34px;
78 | padding-left: 10px;
79 | padding-right: 10px;
80 | position: absolute;
81 | right: 0;
82 | top: 0;
83 | max-width: 100%;
84 | overflow: hidden;
85 | text-overflow: ellipsis;
86 | white-space: nowrap;
87 | }
88 | .has-value.Select--single > .Select-control .Select-value .Select-value-label,
89 | .has-value.is-pseudo-focused.Select--single > .Select-control .Select-value .Select-value-label {
90 | color: #333;
91 | }
92 | .has-value.Select--single > .Select-control .Select-value a.Select-value-label,
93 | .has-value.is-pseudo-focused.Select--single > .Select-control .Select-value a.Select-value-label {
94 | cursor: pointer;
95 | text-decoration: none;
96 | }
97 | .has-value.Select--single > .Select-control .Select-value a.Select-value-label:hover,
98 | .has-value.is-pseudo-focused.Select--single > .Select-control .Select-value a.Select-value-label:hover,
99 | .has-value.Select--single > .Select-control .Select-value a.Select-value-label:focus,
100 | .has-value.is-pseudo-focused.Select--single > .Select-control .Select-value a.Select-value-label:focus {
101 | color: #007eff;
102 | outline: none;
103 | text-decoration: underline;
104 | }
105 | .Select-input {
106 | height: 34px;
107 | padding-left: 10px;
108 | padding-right: 10px;
109 | vertical-align: middle;
110 | }
111 | .Select-input > input {
112 | width: 100%;
113 | background: none transparent;
114 | border: 0 none;
115 | box-shadow: none;
116 | cursor: default;
117 | display: inline-block;
118 | font-family: inherit;
119 | font-size: inherit;
120 | margin: 0;
121 | outline: none;
122 | line-height: 14px;
123 | /* For IE 8 compatibility */
124 | padding: 8px 0 12px;
125 | /* For IE 8 compatibility */
126 | -webkit-appearance: none;
127 | }
128 | .is-focused .Select-input > input {
129 | cursor: text;
130 | }
131 | .has-value.is-pseudo-focused .Select-input {
132 | opacity: 0;
133 | }
134 | .Select-control:not(.is-searchable) > .Select-input {
135 | outline: none;
136 | }
137 | .Select-loading-zone {
138 | cursor: pointer;
139 | display: table-cell;
140 | position: relative;
141 | text-align: center;
142 | vertical-align: middle;
143 | width: 16px;
144 | }
145 | .Select-loading {
146 | -webkit-animation: Select-animation-spin 400ms infinite linear;
147 | -o-animation: Select-animation-spin 400ms infinite linear;
148 | animation: Select-animation-spin 400ms infinite linear;
149 | width: 16px;
150 | height: 16px;
151 | box-sizing: border-box;
152 | border-radius: 50%;
153 | border: 2px solid #ccc;
154 | border-right-color: #333;
155 | display: inline-block;
156 | position: relative;
157 | vertical-align: middle;
158 | }
159 | .Select-clear-zone {
160 | -webkit-animation: Select-animation-fadeIn 200ms;
161 | -o-animation: Select-animation-fadeIn 200ms;
162 | animation: Select-animation-fadeIn 200ms;
163 | color: #999;
164 | cursor: pointer;
165 | display: table-cell;
166 | position: relative;
167 | text-align: center;
168 | vertical-align: middle;
169 | width: 17px;
170 | }
171 | .Select-clear-zone:hover {
172 | color: #D0021B;
173 | }
174 | .Select-clear {
175 | display: inline-block;
176 | font-size: 18px;
177 | line-height: 1;
178 | }
179 | .Select--multi .Select-clear-zone {
180 | width: 17px;
181 | }
182 | .Select-arrow-zone {
183 | cursor: pointer;
184 | display: table-cell;
185 | position: relative;
186 | text-align: center;
187 | vertical-align: middle;
188 | width: 25px;
189 | padding-right: 5px;
190 | }
191 | .Select-arrow {
192 | border-color: #999 transparent transparent;
193 | border-style: solid;
194 | border-width: 5px 5px 2.5px;
195 | display: inline-block;
196 | height: 0;
197 | width: 0;
198 | }
199 | .is-open .Select-arrow,
200 | .Select-arrow-zone:hover > .Select-arrow {
201 | border-top-color: #666;
202 | }
203 | .Select--multi .Select-multi-value-wrapper {
204 | display: inline-block;
205 | }
206 | .Select .Select-aria-only {
207 | display: inline-block;
208 | height: 1px;
209 | width: 1px;
210 | margin: -1px;
211 | clip: rect(0, 0, 0, 0);
212 | overflow: hidden;
213 | }
214 | @-webkit-keyframes Select-animation-fadeIn {
215 | from {
216 | opacity: 0;
217 | }
218 | to {
219 | opacity: 1;
220 | }
221 | }
222 | @keyframes Select-animation-fadeIn {
223 | from {
224 | opacity: 0;
225 | }
226 | to {
227 | opacity: 1;
228 | }
229 | }
230 | .Select-menu-outer {
231 | border-bottom-right-radius: 4px;
232 | border-bottom-left-radius: 4px;
233 | background-color: #fff;
234 | border: 1px solid #ccc;
235 | border-top-color: #e6e6e6;
236 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
237 | box-sizing: border-box;
238 | margin-top: -1px;
239 | max-height: 200px;
240 | position: absolute;
241 | top: 100%;
242 | width: 100%;
243 | z-index: 1;
244 | -webkit-overflow-scrolling: touch;
245 | }
246 | .Select-menu {
247 | max-height: 198px;
248 | overflow-y: auto;
249 | }
250 | .Select-option {
251 | box-sizing: border-box;
252 | background-color: #fff;
253 | color: #666666;
254 | cursor: pointer;
255 | display: block;
256 | padding: 8px 10px;
257 | }
258 | .Select-option:last-child {
259 | border-bottom-right-radius: 4px;
260 | border-bottom-left-radius: 4px;
261 | }
262 | .Select-option.is-selected {
263 | background-color: #f5faff;
264 | /* Fallback color for IE 8 */
265 | background-color: rgba(0, 126, 255, 0.04);
266 | color: #333;
267 | }
268 | .Select-option.is-focused {
269 | background-color: #ebf5ff;
270 | /* Fallback color for IE 8 */
271 | background-color: rgba(0, 126, 255, 0.08);
272 | color: #333;
273 | }
274 | .Select-option.is-disabled {
275 | color: #cccccc;
276 | cursor: default;
277 | }
278 | .Select-noresults {
279 | box-sizing: border-box;
280 | color: #999999;
281 | cursor: default;
282 | display: block;
283 | padding: 8px 10px;
284 | }
285 | .Select--multi .Select-input {
286 | vertical-align: middle;
287 | margin-left: 10px;
288 | padding: 0;
289 | }
290 | .Select--multi.has-value .Select-input {
291 | margin-left: 5px;
292 | }
293 | .Select--multi .Select-value {
294 | background-color: #ebf5ff;
295 | /* Fallback color for IE 8 */
296 | background-color: rgba(0, 126, 255, 0.08);
297 | border-radius: 2px;
298 | border: 1px solid #c2e0ff;
299 | /* Fallback color for IE 8 */
300 | border: 1px solid rgba(0, 126, 255, 0.24);
301 | color: #007eff;
302 | display: inline-block;
303 | font-size: 0.9em;
304 | line-height: 1.4;
305 | margin-left: 5px;
306 | margin-top: 5px;
307 | vertical-align: top;
308 | }
309 | .Select--multi .Select-value-icon,
310 | .Select--multi .Select-value-label {
311 | display: inline-block;
312 | vertical-align: middle;
313 | }
314 | .Select--multi .Select-value-label {
315 | border-bottom-right-radius: 2px;
316 | border-top-right-radius: 2px;
317 | cursor: default;
318 | padding: 2px 5px;
319 | }
320 | .Select--multi a.Select-value-label {
321 | color: #007eff;
322 | cursor: pointer;
323 | text-decoration: none;
324 | }
325 | .Select--multi a.Select-value-label:hover {
326 | text-decoration: underline;
327 | }
328 | .Select--multi .Select-value-icon {
329 | cursor: pointer;
330 | border-bottom-left-radius: 2px;
331 | border-top-left-radius: 2px;
332 | border-right: 1px solid #c2e0ff;
333 | /* Fallback color for IE 8 */
334 | border-right: 1px solid rgba(0, 126, 255, 0.24);
335 | padding: 1px 5px 3px;
336 | }
337 | .Select--multi .Select-value-icon:hover,
338 | .Select--multi .Select-value-icon:focus {
339 | background-color: #d8eafd;
340 | /* Fallback color for IE 8 */
341 | background-color: rgba(0, 113, 230, 0.08);
342 | color: #0071e6;
343 | }
344 | .Select--multi .Select-value-icon:active {
345 | background-color: #c2e0ff;
346 | /* Fallback color for IE 8 */
347 | background-color: rgba(0, 126, 255, 0.24);
348 | }
349 | .Select--multi.is-disabled .Select-value {
350 | background-color: #fcfcfc;
351 | border: 1px solid #e3e3e3;
352 | color: #333;
353 | }
354 | .Select--multi.is-disabled .Select-value-icon {
355 | cursor: not-allowed;
356 | border-right: 1px solid #e3e3e3;
357 | }
358 | .Select--multi.is-disabled .Select-value-icon:hover,
359 | .Select--multi.is-disabled .Select-value-icon:focus,
360 | .Select--multi.is-disabled .Select-value-icon:active {
361 | background-color: #fcfcfc;
362 | }
363 | @keyframes Select-animation-spin {
364 | to {
365 | transform: rotate(1turn);
366 | }
367 | }
368 | @-webkit-keyframes Select-animation-spin {
369 | to {
370 | -webkit-transform: rotate(1turn);
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | html {
4 | color: #454649;
5 | font-size: 16px;
6 | font-family: 'Questrial', sans-serif;
7 | line-height: 1.7;
8 | }
9 |
10 | body {
11 | min-width: 320px;
12 | }
13 |
14 | img {
15 | max-width: 100%;
16 | height: auto;
17 | vertical-align: top;
18 | }
19 |
20 | a {
21 | color: #454649;
22 | transition: color 280ms ease-out;
23 | text-decoration: none;
24 | }
25 |
26 | a:hover {
27 | color: #9fa2ab;
28 | }
29 |
30 | button:focus {
31 | outline: none;
32 | }
33 |
34 | ::selection {
35 | background-color: rgba(50, 95, 92, .4);
36 | }
37 |
38 | ::-moz-selection {
39 | background-color: rgba(50, 95, 92, .4);
40 | }
41 |
42 | ::-webkit-scrollbar {
43 | width: 8px;
44 | background: #efefef;
45 | z-index: 10;
46 | }
47 |
48 | ::-webkit-scrollbar:horizontal {
49 | height: 6px;
50 | }
51 |
52 | ::-webkit-scrollbar-track {
53 | border-radius: 2px;
54 | }
55 |
56 | ::-webkit-scrollbar-thumb {
57 | border-radius: 2px;
58 | background: #ccc;
59 | }
60 |
61 |
62 | /* Cutomize */
63 | .rc-slider-handle {
64 | margin-top: -6px;
65 | border: none;
66 | background: #fff;
67 | }
68 |
69 | .rc-slider-track {
70 | background: #fff;
71 | }
72 |
73 | .rc-slider-step {
74 | height: 2px;
75 | }
76 |
77 | .rc-slider-rail {
78 | height: 2px;
79 | background: #b1960a;
80 | }
81 |
82 |
83 | /* Header */
84 | .header {
85 | }
86 |
87 | .header h1 {
88 | position: fixed;
89 | top: 30px;
90 | left: 30px;
91 | z-index: 1000;
92 | width: 180px;
93 | margin: 0;
94 | }
95 |
96 | .header nav {
97 | position: fixed;
98 | top: 30px;
99 | left: 210px;
100 | z-index: 1000;
101 | }
102 |
103 | .header ul {
104 | margin: 0;
105 | padding: 0;
106 | list-style: none;
107 | font-size: 0;
108 | }
109 |
110 | .header li {
111 | display: inline-block;
112 | margin: 0 0 0 2em;
113 | font-size: 1rem;
114 | }
115 |
116 | .header .is-active {
117 | color: #ecc60b;
118 | }
119 |
120 |
121 | /* Button */
122 | .btn {
123 | display: inline-block;
124 | padding: 0 2em;
125 | background: #fff;
126 | border: none;
127 | color: #ebc60b;
128 | font-size: 1rem;
129 | font-weight: bold;
130 | text-align: center;
131 | line-height: 50px;
132 | white-space: nowrap;
133 | vertical-align: middle;
134 | cursor: pointer;
135 | transition: all 280ms ease-out;
136 | -webkit-user-select: none;
137 | user-select: none;
138 | }
139 |
140 | .btn:hover {
141 | background: #e0bc09;
142 | color: #fff;
143 | }
144 |
145 |
146 | /* Demo control */
147 | .demo-control {
148 | display: flex;
149 | position: fixed;
150 | right: 0;
151 | bottom: 0;
152 | left: 0;
153 | z-index: 1000;
154 | padding: 15px;
155 | overflow-x: auto;
156 | overflow-y: hidden;
157 | -webkit-overflow-scrolling : touch;
158 | background: #ebc60b;
159 | color: #fff;
160 | }
161 |
162 | .demo-control > div {
163 | padding-right: 15px;
164 | }
165 |
166 | .demo-control label {
167 | display: block;
168 | font-size: 12px;
169 | }
170 |
171 | .demo-control .rc-slider {
172 | width: 140px;
173 | margin: 10px 0 0;
174 | }
175 |
176 | .demo-control .btn {
177 | font-size: 12px;
178 | }
179 |
180 | .demo-control select {
181 | display: block;
182 | min-width: 140px;
183 | height: 35px;
184 | }
185 |
186 |
187 | /* Content */
188 | .content {
189 | padding: 120px 30px;
190 | }
191 |
192 |
193 | /* Item */
194 | .item {
195 | display: block;
196 | width: 100%;
197 | }
198 |
199 | .item--gray {
200 | background: #b8bbbe;
201 | }
202 |
203 | .item--gray-light {
204 | background: #dfe0df;
205 | }
206 |
207 | .item--gray-dark {
208 | background: #47484a;
209 | }
210 |
211 | .item--yellow {
212 | background: #e1c531;
213 | }
214 |
215 | .item--pink {
216 | background: #d59c90;
217 | }
218 |
219 | .item--purple {
220 | background: #c9cae3;
221 | }
222 |
223 | .item--pattern1 {
224 | background: url("../images/pattern1.png") repeat 0 0;
225 | background-size: 45px;
226 | }
227 |
228 | .item--pattern2 {
229 | background: url("../images/pattern2.png") repeat 0 0;
230 | background-size: 66px;
231 | }
232 |
233 | .item--pattern3 {
234 | background: url("../images/pattern3.png") repeat 0 0;
235 | background-size: 45px;
236 | }
237 |
238 |
239 | /* Image */
240 | .image {
241 | display: block;
242 | margin: 0;
243 | }
244 |
245 | .image figcaption {
246 | display: block;
247 | margin: .2em 0 0;
248 | font-weight: normal;
249 | }
250 |
251 |
252 | @media (max-width: 580px) {
253 | .header h1 {
254 | top: 15px;
255 | left: 15px;
256 | width: 120px;
257 | }
258 |
259 | .header nav {
260 | top: 15px;
261 | left: 135px;
262 | }
263 |
264 | .header li {
265 | display: block;
266 | margin-left: 1em;
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/logo.png
--------------------------------------------------------------------------------
/docs/images/pattern1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/pattern1.png
--------------------------------------------------------------------------------
/docs/images/pattern2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/pattern2.png
--------------------------------------------------------------------------------
/docs/images/pattern3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/pattern3.png
--------------------------------------------------------------------------------
/docs/images/photos/photo01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo01.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo02.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo03.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo04.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo05.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo06.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo07.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo08.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo09.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo10.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo11.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo12.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo13.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo14.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo15.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo16.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo17.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo18.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo19.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo20.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo21.jpg
--------------------------------------------------------------------------------
/docs/images/photos/photo22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tsuyoshiwada/react-stack-grid/13349baf5e12738359facfc126240e3633855752/docs/images/photos/photo22.jpg
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | React Stack Grid
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/docs/js/App.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 | import Header from './components/Header';
4 |
5 | const App = ({ children }) => (
6 |
7 |
8 |
9 | {children}
10 |
11 |
12 | );
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/docs/js/Root.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | import React from 'react';
4 | import { Router, hashHistory } from 'react-router';
5 |
6 | const Root = ({ routes }) => (
7 |
8 | {routes()}
9 |
10 | );
11 |
12 | export default Root;
13 |
--------------------------------------------------------------------------------
/docs/js/components/DemoControl.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | import React, { Component } from 'react';
4 | import Slider from 'rc-slider';
5 | import { easings, transitions } from '../../../src/';
6 |
7 | const selectEasingOptions = Object.keys(easings).map(k => ({
8 | label: k,
9 | value: easings[k],
10 | }));
11 |
12 | const selectTransitionOptions = Object.keys(transitions).map(k => ({
13 | label: k,
14 | value: k,
15 | }));
16 |
17 |
18 | export default class DemoControl extends Component {
19 | handleShuffle = () => {
20 | this.props.onShuffle();
21 | }
22 |
23 | handlePrepend = () => {
24 | this.props.onPrepend();
25 | }
26 |
27 | handleAppend = () => {
28 | this.props.onAppend();
29 | }
30 |
31 | handleMultipleAppend = () => {
32 | this.props.onMultipleAppend();
33 | }
34 |
35 | handleDurationChange = (value) => {
36 | this.props.onDurationChange(value);
37 | }
38 |
39 | handleColumnWidthChange = (value) => {
40 | this.props.onColumnWidthChange(value);
41 | }
42 |
43 | handleGutterChange = (value) => {
44 | this.props.onGutterChange(value);
45 | }
46 |
47 | handleEasingChange = (e) => {
48 | this.props.onEasingChange(e.target.value);
49 | }
50 |
51 | handleTransitionChange = (e) => {
52 | this.props.onTransitionChange(e.target.value);
53 | }
54 |
55 | handleRTLChange = (e) => {
56 | this.props.onRTLChange(e.target.checked);
57 | }
58 |
59 | render() {
60 | const {
61 | duration,
62 | columnWidth,
63 | gutter,
64 | easing,
65 | transition,
66 | rtl,
67 | } = this.props;
68 |
69 | return (
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
100 |
101 |
102 |
103 |
109 |
110 |
111 |
112 |
113 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
130 |
131 |
132 |
133 |
134 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | );
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/docs/js/components/Header.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | /* eslint-disable jsx-a11y/anchor-is-valid */
3 | /* eslint-disable import/no-extraneous-dependencies */
4 | import React from 'react';
5 | import { IndexLink, Link } from 'react-router';
6 |
7 | const Header = () => (
8 |
9 | 
10 |
19 |
20 | );
21 |
22 | export default Header;
23 |
--------------------------------------------------------------------------------
/docs/js/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | /* eslint-disable import/no-extraneous-dependencies */
3 | import { AppContainer } from 'react-hot-loader';
4 | import React from 'react';
5 | import ReactDOM from 'react-dom';
6 | import Root from './Root';
7 | import routes from './routes';
8 |
9 |
10 | const render = (nextRoutes) => {
11 | ReactDOM.render(
12 |
13 |
14 | ,
15 | document.getElementById('root')
16 | );
17 | };
18 |
19 | render(routes);
20 |
21 | if (module.hot) {
22 | module.hot.accept('./routes', () => {
23 | const nextRoutes = require('./routes').default;
24 | render(nextRoutes);
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/docs/js/pages/ChangeSize.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-return-assign */
2 | /* eslint-disable react/prop-types */
3 | /* eslint-disable jsx-a11y/no-static-element-interactions */
4 | /* eslint-disable jsx-a11y/click-events-have-key-events */
5 | import React, { Component } from 'react';
6 | import StackGrid from '../../../src/';
7 |
8 |
9 | const items = [];
10 |
11 | for (let i = 0; i < 30; i += 1) {
12 | const id = Math.random().toString(36).substr(2, 9);
13 | const height = Math.floor((Math.random() * (300 - 80)) + 80);
14 |
15 | items.push({
16 | id,
17 | height,
18 | active: false,
19 | actualHeight: height,
20 | });
21 | }
22 |
23 |
24 | export default class ChangeSize extends Component {
25 | state = { items };
26 |
27 | changeItemSize = (id) => {
28 | this.setState({
29 | items: this.state.items.map(o => (
30 | o.id !== id ? o : {
31 | ...o,
32 | active: !o.active,
33 | height: !o.active ? o.height * 1.5 : o.actualHeight,
34 | }
35 | )),
36 | }, () => {
37 | this.grid.updateLayout();
38 | });
39 | };
40 |
41 | render() {
42 | return (
43 | this.grid = grid}
45 | columnWidth={120}
46 | >
47 | {this.state.items.map(({ id, active, height }) => (
48 | this.changeItemSize(id)}
56 | />
57 | ))}
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/docs/js/pages/Home.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable class-methods-use-this */
2 | /* eslint-disable react/prop-types */
3 | /* eslint-disable jsx-a11y/no-static-element-interactions */
4 | /* eslint-disable jsx-a11y/click-events-have-key-events */
5 | import React, { Component } from 'react';
6 | import StackGrid, { transitions, easings } from '../../../src/';
7 | import DemoControl from '../components/DemoControl';
8 |
9 | const itemModifier = [
10 | 'pattern1',
11 | 'pattern2',
12 | 'pattern3',
13 | 'gray',
14 | 'gray-light',
15 | 'gray-dark',
16 | 'yellow',
17 | 'pink',
18 | 'purple',
19 | ];
20 |
21 | export default class Home extends Component {
22 | constructor(props) {
23 | super(props);
24 |
25 | const items = [];
26 |
27 | for (let i = 0; i < 10; i += 1) {
28 | items.push(this.createItem());
29 | }
30 |
31 | this.state = {
32 | items,
33 | duration: 480,
34 | columnWidth: 150,
35 | gutter: 5,
36 | easing: easings.quartOut,
37 | transition: 'fadeDown',
38 | rtl: false,
39 | };
40 | }
41 |
42 | createItem() {
43 | const id = Math.random().toString(36).substr(2, 9);
44 | const height = Math.floor((Math.random() * (300 - 80)) + 80);
45 | const modifier = itemModifier[Math.floor(Math.random() * itemModifier.length)];
46 |
47 | return { id, height, modifier };
48 | }
49 |
50 | shuffleItems = () => {
51 | const newItems = [...this.state.items];
52 | let i = newItems.length;
53 |
54 | while (i) {
55 | const j = Math.floor(Math.random() * i);
56 | const t = newItems[--i]; // eslint-disable-line no-plusplus
57 | newItems[i] = newItems[j];
58 | newItems[j] = t;
59 | }
60 |
61 | this.setState({ items: newItems });
62 | }
63 |
64 | prependItem = () => {
65 | this.setState({
66 | items: [this.createItem(), ...this.state.items],
67 | });
68 | }
69 |
70 | appendItem = () => {
71 | this.setState({
72 | items: [...this.state.items, this.createItem()],
73 | });
74 | }
75 |
76 | multipleAppendItem = () => {
77 | const newItems = [];
78 |
79 | for (let i = 0; i < 5; i += 1) {
80 | newItems.push(this.createItem());
81 | }
82 |
83 | this.setState({
84 | items: [...this.state.items, ...newItems],
85 | });
86 | }
87 |
88 | removeItem = (id) => {
89 | this.setState({
90 | items: this.state.items.filter(o => o.id !== id),
91 | });
92 | }
93 |
94 | handleDurationChange = (duration) => {
95 | this.setState({ duration });
96 | }
97 |
98 | handleColumnWidthChange = (columnWidth) => {
99 | this.setState({ columnWidth });
100 | }
101 |
102 | handleGutterChange = (gutter) => {
103 | this.setState({ gutter });
104 | }
105 |
106 | handleEasingChange = (easing) => {
107 | this.setState({ easing });
108 | }
109 |
110 | handleTransitionChange = (transition) => {
111 | this.setState({ transition });
112 | }
113 |
114 | handleRTLChange = (rtl) => {
115 | this.setState({ rtl });
116 | }
117 |
118 | render() {
119 | const {
120 | items,
121 | duration,
122 | columnWidth,
123 | gutter,
124 | easing,
125 | transition: transitionSelect,
126 | rtl,
127 | } = this.state;
128 |
129 | const transition = transitions[transitionSelect];
130 | return (
131 |
132 |
150 |
151 |
{
164 | console.log('[DEMO] `onLayout()` has been called.'); // eslint-disable-line
165 | }}
166 | >
167 | {items.map(item =>
168 | ( this.removeItem(item.id)}
173 | />)
174 | )}
175 |
176 |
177 | );
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/docs/js/pages/HorizontalFlow.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable class-methods-use-this */
2 | /* eslint-disable react/prop-types */
3 | /* eslint-disable jsx-a11y/no-static-element-interactions */
4 | /* eslint-disable jsx-a11y/click-events-have-key-events */
5 | import React, { Component } from 'react';
6 | import StackGrid, { transitions, easings } from '../../../src/';
7 | import DemoControl from '../components/DemoControl';
8 |
9 | const itemModifier = [
10 | 'pattern1',
11 | 'pattern2',
12 | 'pattern3',
13 | 'gray',
14 | 'gray-light',
15 | 'gray-dark',
16 | 'yellow',
17 | 'pink',
18 | 'purple',
19 | ];
20 |
21 | export default class HorizontalFlow extends Component {
22 | constructor(props) {
23 | super(props);
24 |
25 | const items = [];
26 |
27 | for (let i = 0; i < 10; i += 1) {
28 | items.push(this.createItem());
29 | }
30 |
31 | this.state = {
32 | items,
33 | duration: 480,
34 | columnWidth: 150,
35 | gutter: 5,
36 | easing: easings.quartOut,
37 | transition: 'fadeDown',
38 | };
39 | }
40 |
41 | createItem() {
42 | const id = Math.random().toString(36).substr(2, 9);
43 | const height = Math.floor((Math.random() * (300 - 80)) + 80);
44 | const modifier = itemModifier[Math.floor(Math.random() * itemModifier.length)];
45 |
46 | return { id, height, modifier };
47 | }
48 |
49 | shuffleItems = () => {
50 | const newItems = [...this.state.items];
51 | let i = newItems.length;
52 |
53 | while (i) {
54 | const j = Math.floor(Math.random() * i);
55 | const t = newItems[--i]; // eslint-disable-line no-plusplus
56 | newItems[i] = newItems[j];
57 | newItems[j] = t;
58 | }
59 |
60 | this.setState({ items: newItems });
61 | }
62 |
63 | prependItem = () => {
64 | this.setState({
65 | items: [this.createItem(), ...this.state.items],
66 | });
67 | }
68 |
69 | appendItem = () => {
70 | this.setState({
71 | items: [...this.state.items, this.createItem()],
72 | });
73 | }
74 |
75 | multipleAppendItem = () => {
76 | const newItems = [];
77 |
78 | for (let i = 0; i < 5; i += 1) {
79 | newItems.push(this.createItem());
80 | }
81 |
82 | this.setState({
83 | items: [...this.state.items, ...newItems],
84 | });
85 | }
86 |
87 | removeItem = (id) => {
88 | this.setState({
89 | items: this.state.items.filter(o => o.id !== id),
90 | });
91 | }
92 |
93 | handleDurationChange = (duration) => {
94 | this.setState({ duration });
95 | }
96 |
97 | handleColumnWidthChange = (columnWidth) => {
98 | this.setState({ columnWidth });
99 | }
100 |
101 | handleGutterChange = (gutter) => {
102 | this.setState({ gutter });
103 | }
104 |
105 | handleEasingChange = (easing) => {
106 | this.setState({ easing });
107 | }
108 |
109 | handleTransitionChange = (transition) => {
110 | this.setState({ transition });
111 | }
112 |
113 | render() {
114 | const {
115 | items,
116 | duration,
117 | columnWidth,
118 | gutter,
119 | easing,
120 | transition: transitionSelect,
121 | } = this.state;
122 |
123 | const transition = transitions[transitionSelect];
124 |
125 | return (
126 |
127 |
143 |
144 |
{
157 | console.log('[DEMO] `onLayout()` has been called.'); // eslint-disable-line
158 | }}
159 | >
160 | {items.map(item =>
161 | ( this.removeItem(item.id)}
166 | />)
167 | )}
168 |
169 |
170 | );
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/docs/js/pages/RealWorld.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React from 'react';
3 | import StackGrid, { transitions, easings } from '../../../src/';
4 |
5 |
6 | const transition = transitions.scaleDown;
7 |
8 | const images = [
9 | { src: './images/photos/photo01.jpg', label: 'Sample image 1' },
10 | { src: './images/photos/photo02.jpg', label: 'Sample image 2' },
11 | { src: './images/photos/photo03.jpg', label: 'Sample image 3' },
12 | { src: './images/photos/photo04.jpg', label: 'Sample image 4' },
13 | { src: './images/photos/photo05.jpg', label: 'Sample image 5' },
14 | { src: './images/photos/photo06.jpg', label: 'Sample image 6' },
15 | { src: './images/photos/photo07.jpg', label: 'Sample image 7' },
16 | { src: './images/photos/photo08.jpg', label: 'Sample image 8' },
17 | { src: './images/photos/photo09.jpg', label: 'Sample image 9' },
18 | { src: './images/photos/photo10.jpg', label: 'Sample image 10' },
19 | { src: './images/photos/photo11.jpg', label: 'Sample image 11' },
20 | { src: './images/photos/photo12.jpg', label: 'Sample image 12' },
21 | { src: './images/photos/photo13.jpg', label: 'Sample image 13' },
22 | { src: './images/photos/photo14.jpg', label: 'Sample image 14' },
23 | { src: './images/photos/photo15.jpg', label: 'Sample image 15' },
24 | { src: './images/photos/photo16.jpg', label: 'Sample image 16' },
25 | { src: './images/photos/photo17.jpg', label: 'Sample image 17' },
26 | { src: './images/photos/photo18.jpg', label: 'Sample image 18' },
27 | { src: './images/photos/photo19.jpg', label: 'Sample image 19' },
28 | { src: './images/photos/photo20.jpg', label: 'Sample image 20' },
29 | { src: './images/photos/photo21.jpg', label: 'Sample image 21' },
30 | { src: './images/photos/photo22.jpg', label: 'Sample image 22' },
31 | ];
32 |
33 |
34 | const RealWorld = () => (
35 |
49 | {images.map(obj => (
50 |
54 |
55 | {obj.label}
56 |
57 | ))}
58 |
59 | );
60 |
61 | export default RealWorld;
62 |
--------------------------------------------------------------------------------
/docs/js/routes.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | import React from 'react';
3 | import { Route, IndexRoute } from 'react-router';
4 | import App from './App';
5 | import Home from './pages/Home';
6 | import ChangeSize from './pages/ChangeSize';
7 | import RealWorld from './pages/RealWorld';
8 | import HorizontalFlow from './pages/HorizontalFlow';
9 |
10 | const routes = () => (
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
19 | export default routes;
20 |
--------------------------------------------------------------------------------
/docs/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: [
6 | 'react-hot-loader/patch',
7 | 'webpack-dev-server/client?http://localhost:3000',
8 | 'webpack/hot/only-dev-server',
9 | path.resolve(__dirname, 'js/index.js'),
10 | ],
11 |
12 | output: {
13 | filename: 'bundle.js',
14 | path: __dirname,
15 | publicPath: '/',
16 | },
17 |
18 | devtool: 'inline-source-map',
19 |
20 | module: {
21 | rules: [
22 | {
23 | test: /\.jsx?$/,
24 | use: [
25 | 'react-hot-loader/webpack',
26 | 'babel-loader',
27 | ],
28 | exclude: /node_modules/,
29 | },
30 | ],
31 | },
32 |
33 | plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NamedModulesPlugin(),
36 | new webpack.NoEmitOnErrorsPlugin(),
37 | ],
38 |
39 | devServer: {
40 | contentBase: __dirname,
41 | host: '0.0.0.0',
42 | port: 3000,
43 | historyApiFallback: true,
44 | hot: true,
45 | },
46 | };
47 |
--------------------------------------------------------------------------------
/docs/webpack.config.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: [
6 | path.resolve(__dirname, 'js/index.js'),
7 | ],
8 |
9 | output: {
10 | filename: 'bundle.js',
11 | path: __dirname,
12 | publicPath: '/',
13 | },
14 |
15 | devtool: false,
16 |
17 | module: {
18 | rules: [
19 | {
20 | test: /\.jsx?$/,
21 | use: [
22 | 'babel-loader',
23 | ],
24 | exclude: /node_modules/,
25 | },
26 | ],
27 | },
28 |
29 | plugins: [
30 | new webpack.NamedModulesPlugin(),
31 | new webpack.LoaderOptionsPlugin({
32 | minimize: true,
33 | debug: false,
34 | }),
35 | new webpack.DefinePlugin({
36 | 'process.env.NODE_ENV': JSON.stringify('production'),
37 | }),
38 | new webpack.optimize.UglifyJsPlugin({
39 | beautify: false,
40 | mangle: {
41 | screw_ie8: true,
42 | keep_fnames: true,
43 | },
44 | compress: {
45 | screw_ie8: true,
46 | },
47 | comments: false,
48 | }),
49 | ],
50 | };
51 |
--------------------------------------------------------------------------------
/jest/setup.js:
--------------------------------------------------------------------------------
1 | global.window.requestAnimationFrame = callback => (
2 | setTimeout(callback, 1)
3 | );
4 |
5 | global.window.cancelAnimationFrame = () => {};
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-stack-grid",
3 | "version": "0.7.1",
4 | "description": "Pinterest like layout components for React.js",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "start": "npm run docs",
8 | "clean": "rimraf lib",
9 | "build": "babel src/ -d lib/ --ignore \"**/__tests__/*\"",
10 | "test": "npm run test:lint && npm run test:typecheck && npm run test:unit",
11 | "test:lint": "eslint \"src/**/*.js\" \"docs/js/**/*.js\"",
12 | "test:typecheck": "flow",
13 | "test:unit": "jest",
14 | "test:watch": "npm run test:unit -- --watch",
15 | "docs": "webpack-dev-server --config docs/webpack.config.js",
16 | "docs:build": "webpack -p --config docs/webpack.config.production.js",
17 | "docs:deploy": "npm run docs:build && gh-pages -d docs",
18 | "prebuild": "npm run clean",
19 | "prepublish": "npm run build"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/tsuyoshiwada/react-stack-grid.git"
24 | },
25 | "keywords": [
26 | "react",
27 | "react-component",
28 | "layout",
29 | "grid",
30 | "pinterest",
31 | "masonry"
32 | ],
33 | "author": "tsuyoshiwada",
34 | "license": "MIT",
35 | "bugs": {
36 | "url": "https://github.com/tsuyoshiwada/react-stack-grid/issues"
37 | },
38 | "homepage": "https://github.com/tsuyoshiwada/react-stack-grid#readme",
39 | "files": [
40 | "lib",
41 | "test"
42 | ],
43 | "browserify": {
44 | "transform": [
45 | "babelify"
46 | ]
47 | },
48 | "devDependencies": {
49 | "babel-cli": "^6.18.0",
50 | "babel-eslint": "^8.0.3",
51 | "babel-jest": "^21.2.0",
52 | "babel-loader": "^7.1.1",
53 | "babel-preset-es2015": "^6.18.0",
54 | "babel-preset-flow": "^6.23.0",
55 | "babel-preset-power-assert": "^1.0.0",
56 | "babel-preset-react": "^6.16.0",
57 | "babel-preset-stage-1": "^6.16.0",
58 | "babel-register": "^6.18.0",
59 | "enzyme": "^2.9.1",
60 | "eslint": "^4.2.0",
61 | "eslint-config-airbnb": "^16.1.0",
62 | "eslint-plugin-babel": "^4.0.0",
63 | "eslint-plugin-flowtype": "^2.34.0",
64 | "eslint-plugin-flowtype-errors": "^3.3.0",
65 | "eslint-plugin-import": "^2.3.0",
66 | "eslint-plugin-jest": "^21.4.2",
67 | "eslint-plugin-jsx-a11y": "^6.0.2",
68 | "eslint-plugin-react": "^7.0.1",
69 | "flow-bin": "^0.50.0",
70 | "gh-pages": "^1.0.0",
71 | "jest": "^21.2.1",
72 | "rc-slider": "^8.2.0",
73 | "react": "^15.5.4",
74 | "react-addons-test-utils": "^15.6.0",
75 | "react-dom": "^15.5.4",
76 | "react-hot-loader": "^3.1.3",
77 | "react-router": "^3.0.0",
78 | "react-test-renderer": "^15.6.1",
79 | "rimraf": "^2.6.1",
80 | "sinon": "^2.3.2",
81 | "webpack": "^3.3.0",
82 | "webpack-dev-server": "^2.5.1"
83 | },
84 | "peerDependencies": {
85 | "react": ">=15.3.0",
86 | "react-dom": ">=15.3.0"
87 | },
88 | "dependencies": {
89 | "easy-css-transform-builder": "^0.0.2",
90 | "exenv": "^1.2.1",
91 | "imagesloaded": "^4.1.1",
92 | "inline-style-prefixer": "^3.0.6",
93 | "invariant": "^2.2.2",
94 | "prop-types": "^15.5.10",
95 | "react-sizeme": "^2.2.0",
96 | "react-transition-group": "^1.2.0",
97 | "shallowequal": "^1.0.1"
98 | },
99 | "jest": {
100 | "testMatch": [
101 | "**/?(*.)spec.js"
102 | ],
103 | "testPathIgnorePatterns": [
104 | "/node_modules/",
105 | "/lib/"
106 | ],
107 | "setupFiles": [
108 | "./jest/setup.js"
109 | ]
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/animations/easings.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | // http://easings.net/
3 | export const linear = 'linear';
4 | export const easeIn = 'ease-in';
5 | export const easeOut = 'ease-out';
6 | export const easeInOut = 'ease-in-out';
7 | export const sineIn = 'cubic-bezier(0.47, 0, 0.745, 0.715)';
8 | export const sineOut = 'cubic-bezier(0.39, 0.575, 0.565, 1)';
9 | export const sineInOut = 'cubic-bezier(0.445, 0.05, 0.55, 0.95)';
10 | export const quadIn = 'cubic-bezier(0.55, 0.085, 0.68, 0.53)';
11 | export const quadOut = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)';
12 | export const quadInOut = 'cubic-bezier(0.455, 0.03, 0.515, 0.955)';
13 | export const cubicIn = 'cubic-bezier(0.55, 0.055, 0.675, 0.19)';
14 | export const cubicOut = 'cubic-bezier(0.215, 0.61, 0.355, 1)';
15 | export const cubicInOut = 'cubic-bezier(0.645, 0.045, 0.355, 1)';
16 | export const quartIn = 'cubic-bezier(0.895, 0.03, 0.685, 0.22)';
17 | export const quartOut = 'cubic-bezier(0.165, 0.84, 0.44, 1)';
18 | export const quartInOut = 'cubic-bezier(0.77, 0, 0.175, 1)';
19 | export const quintIn = 'cubic-bezier(0.755, 0.05, 0.855, 0.06)';
20 | export const quintOut = 'cubic-bezier(0.23, 1, 0.32, 1)';
21 | export const quintInOut = 'cubic-bezier(0.86, 0, 0.07, 1)';
22 | export const expoIn = 'cubic-bezier(0.95, 0.05, 0.795, 0.035)';
23 | export const expoOut = 'cubic-bezier(0.19, 1, 0.22, 1)';
24 | export const expoInOut = 'cubic-bezier(1, 0, 0, 1)';
25 | export const circIn = 'cubic-bezier(0.6, 0.04, 0.98, 0.335)';
26 | export const circOut = 'cubic-bezier(0.075, 0.82, 0.165, 1)';
27 | export const circInOut = 'cubic-bezier(0.785, 0.135, 0.15, 0.86)';
28 | export const backIn = 'cubic-bezier(0.6, -0.28, 0.735, 0.045)';
29 | export const backOut = 'cubic-bezier(0.175, 0.885, 0.32, 1.275)';
30 | export const backInOut = 'cubic-bezier(0.68, -0.55, 0.265, 1.55)';
31 |
--------------------------------------------------------------------------------
/src/animations/request-animation-frame.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import ExecutionEnvironment from 'exenv';
3 |
4 | const vendors = ['ms', 'moz', 'webkit'];
5 | let tmpRaf = null;
6 | let tmpCaf = null;
7 |
8 | if (ExecutionEnvironment.canUseDOM) {
9 | tmpRaf = window.requestAnimationFrame;
10 | tmpCaf = window.cancelAnimationFrame;
11 |
12 | // eslint-disable-next-line no-plusplus
13 | for (let x = 0; x < vendors.length && !tmpRaf; ++x) {
14 | tmpRaf = window[`${vendors[x]}RequestAnimationFrame`];
15 | tmpCaf = window[`${vendors[x]}CancelAnimationFrame`] ||
16 | window[`${vendors[x]}CancelRequestAnimationFrame`];
17 | }
18 | } else {
19 | tmpRaf = (callback: Function): number => callback();
20 | tmpCaf = (id: number): void => {}; // eslint-disable-line no-unused-vars
21 | }
22 |
23 | export const raf = tmpRaf;
24 | export const caf = tmpCaf;
25 |
--------------------------------------------------------------------------------
/src/animations/transitions/fade-down.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Rect } from '../../types';
3 |
4 | export const appear = (rect: Rect) => ({
5 | translateY: rect.top - 10,
6 | opacity: 0,
7 | });
8 |
9 | export const appeared = () => ({ opacity: 1 });
10 |
11 | export const enter = appeared;
12 |
13 | export const entered = appeared;
14 |
15 | export const leaved = (rect: Rect) => ({
16 | translateY: rect.top + 10,
17 | opacity: 0,
18 | });
19 |
--------------------------------------------------------------------------------
/src/animations/transitions/fade-up.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Rect } from '../../types/';
3 |
4 | export const appear = (rect: Rect) => ({
5 | translateY: rect.top + 10,
6 | opacity: 0,
7 | });
8 |
9 | export const appeared = () => ({ opacity: 1 });
10 |
11 | export const enter = appeared;
12 |
13 | export const entered = appeared;
14 |
15 | export const leaved = appear;
16 |
--------------------------------------------------------------------------------
/src/animations/transitions/fade.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export const appear = () => ({ opacity: 0 });
3 |
4 | export const appeared = () => ({ opacity: 1 });
5 |
6 | export const enter = appear;
7 |
8 | export const entered = appeared;
9 |
10 | export const leaved = appear;
11 |
--------------------------------------------------------------------------------
/src/animations/transitions/flip.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | const common = {
3 | perspective: 1000,
4 | transformStyle: 'preserve-3d',
5 | backfaceVisibility: 'hidden',
6 | };
7 |
8 | export const appear = () => ({
9 | ...common,
10 | rotateX: -180,
11 | opacity: 0,
12 | });
13 |
14 | export const appeared = () => ({
15 | ...common,
16 | rotateX: 0,
17 | opacity: 1,
18 | });
19 |
20 | export const enter = appeared;
21 |
22 | export const entered = appeared;
23 |
24 | export const leaved = appear;
25 |
--------------------------------------------------------------------------------
/src/animations/transitions/helix.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { Rect } from '../../types/';
3 |
4 | const common = {
5 | transformStyle: 'preserve-3d',
6 | backfaceVisibility: 'hidden',
7 | };
8 |
9 | export const appear = (rect: Rect) => ({
10 | ...common,
11 | perspective: rect.height,
12 | rotateY: -180,
13 | opacity: 0,
14 | });
15 |
16 | export const appeared = () => ({
17 | ...common,
18 | perspective: 0,
19 | rotateY: 0,
20 | opacity: 1,
21 | });
22 |
23 | export const enter = appeared;
24 |
25 | export const entered = appeared;
26 |
27 | export const leaved = (rect: Rect) => ({
28 | ...common,
29 | perspective: rect.height,
30 | rotateY: 180,
31 | opacity: 0,
32 | });
33 |
--------------------------------------------------------------------------------
/src/animations/transitions/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export * as fade from './fade';
3 | export * as fadeDown from './fade-down';
4 | export * as fadeUp from './fade-up';
5 | export * as scaleDown from './scale-down';
6 | export * as scaleUp from './scale-up';
7 | export * as flip from './flip';
8 | export * as helix from './helix';
9 |
--------------------------------------------------------------------------------
/src/animations/transitions/scale-down.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export const appear = () => ({
3 | scale: 1.1,
4 | opacity: 0,
5 | });
6 |
7 | export const appeared = () => ({
8 | scale: 1,
9 | opacity: 1,
10 | });
11 |
12 | export const enter = appear;
13 |
14 | export const entered = appeared;
15 |
16 | export const leaved = () => ({
17 | scale: 0.95,
18 | opacity: 0,
19 | });
20 |
--------------------------------------------------------------------------------
/src/animations/transitions/scale-up.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export const appear = () => ({
3 | scale: 0.9,
4 | opacity: 0,
5 | });
6 |
7 | export const appeared = () => ({
8 | scale: 1,
9 | opacity: 1,
10 | });
11 |
12 | export const enter = appear;
13 |
14 | export const entered = appeared;
15 |
16 | export const leaved = () => ({
17 | scale: 1.05,
18 | opacity: 0,
19 | });
20 |
--------------------------------------------------------------------------------
/src/components/GridItem.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import PropTypes from 'prop-types';
4 | import shallowequal from 'shallowequal';
5 | import { transition, buildStyles } from '../utils/style-helper';
6 | import { raf } from '../animations/request-animation-frame';
7 |
8 | import type { Units, Rect } from '../types/';
9 |
10 | type Props = {
11 | itemKey: string;
12 | index: number;
13 | component: string,
14 | rect: Rect;
15 | containerSize: {
16 | width: number;
17 | height: number;
18 | actualWidth: number;
19 | };
20 | duration: number;
21 | easing: string;
22 | appearDelay: number;
23 | appear: Function;
24 | appeared: Function;
25 | enter: Function;
26 | entered: Function;
27 | leaved: Function;
28 | units: Units;
29 | vendorPrefix: boolean;
30 | userAgent: ?string;
31 | onMounted: Function;
32 | onUnmount: Function;
33 | rtl: boolean;
34 | };
35 |
36 | type State = Object;
37 |
38 | const getTransitionStyles = (type: string, props: Props): Object => {
39 | const { rect, containerSize, index } = props;
40 |
41 | return props[type](rect, containerSize, index);
42 | };
43 |
44 | const getPositionStyles = (rect: Rect, zIndex: number, rtl: boolean): Object => ({
45 | translateX: `${rtl ? -Math.round(rect.left) : Math.round(rect.left)}px`,
46 | translateY: `${Math.round(rect.top)}px`,
47 | zIndex,
48 | });
49 |
50 |
51 | export default class GridItem extends Component {
52 | props: Props;
53 | state: State;
54 | node: ?HTMLElement;
55 | mounted: boolean;
56 | appearTimer: ?number;
57 |
58 | static propTypes = {
59 | itemKey: PropTypes.string,
60 | index: PropTypes.number,
61 | component: PropTypes.string,
62 | rect: PropTypes.shape({
63 | top: PropTypes.number,
64 | left: PropTypes.number,
65 | width: PropTypes.number,
66 | height: PropTypes.number,
67 | }),
68 | containerSize: PropTypes.shape({
69 | width: PropTypes.number,
70 | height: PropTypes.number,
71 | actualWidth: PropTypes.number,
72 | }),
73 | duration: PropTypes.number,
74 | easing: PropTypes.string,
75 | appearDelay: PropTypes.number,
76 | appear: PropTypes.func,
77 | appeared: PropTypes.func,
78 | enter: PropTypes.func,
79 | entered: PropTypes.func,
80 | leaved: PropTypes.func,
81 | units: PropTypes.shape({
82 | length: PropTypes.string,
83 | angle: PropTypes.string,
84 | }),
85 | vendorPrefix: PropTypes.bool,
86 | userAgent: PropTypes.string,
87 | onMounted: PropTypes.func,
88 | onUnmount: PropTypes.func,
89 | rtl: PropTypes.bool,
90 | };
91 |
92 | constructor(props: Props) {
93 | super(props);
94 |
95 | this.mounted = false;
96 | this.appearTimer = null;
97 | this.node = null;
98 |
99 | this.state = {
100 | ...getPositionStyles(props.rect, 1, props.rtl),
101 | ...getTransitionStyles('appear', props),
102 | };
103 | }
104 |
105 | componentDidMount() {
106 | this.mounted = true;
107 | this.props.onMounted(this);
108 | }
109 |
110 | componentWillUnmount() {
111 | this.mounted = false;
112 | clearTimeout(this.appearTimer);
113 | this.appearTimer = null;
114 | this.props.onUnmount(this);
115 | }
116 |
117 | componentWillReceiveProps(nextProps: Props) {
118 | if (!shallowequal(nextProps, this.props)) {
119 | raf(() => {
120 | this.setStateIfNeeded({
121 | ...this.state,
122 | ...getPositionStyles(nextProps.rect, 2, nextProps.rtl),
123 | });
124 | });
125 | }
126 | }
127 |
128 | shouldComponentUpdate(nextProps: Props, nextState: State) {
129 | return (
130 | !shallowequal(nextProps, this.props) ||
131 | !shallowequal(nextState, this.state)
132 | );
133 | }
134 |
135 | componentWillAppear(callback: Function) {
136 | this.appearTimer = setTimeout(callback, this.props.appearDelay * this.props.index);
137 | }
138 |
139 | componentDidAppear() {
140 | this.setAppearedStyles();
141 | }
142 |
143 | componentWillEnter(callback: Function) {
144 | this.setEnterStyles();
145 | this.forceUpdate(callback);
146 | }
147 |
148 | componentDidEnter() {
149 | this.setEnteredStyles();
150 | }
151 |
152 | componentWillLeave(callback: Function) {
153 | this.setLeaveStyles();
154 | setTimeout(callback, this.props.duration);
155 | }
156 |
157 | setStateIfNeeded(state: Object) {
158 | if (this.mounted) {
159 | this.setState(state);
160 | }
161 | }
162 |
163 | setAppearedStyles() {
164 | this.setStateIfNeeded({
165 | ...this.state,
166 | ...getTransitionStyles('appeared', this.props),
167 | ...getPositionStyles(this.props.rect, 1, this.props.rtl),
168 | });
169 | }
170 |
171 | setEnterStyles() {
172 | this.setStateIfNeeded({
173 | ...this.state,
174 | ...getPositionStyles(this.props.rect, 2, this.props.rtl),
175 | ...getTransitionStyles('enter', this.props),
176 | });
177 | }
178 |
179 | setEnteredStyles() {
180 | this.setStateIfNeeded({
181 | ...this.state,
182 | ...getTransitionStyles('entered', this.props),
183 | ...getPositionStyles(this.props.rect, 1, this.props.rtl),
184 | });
185 | }
186 |
187 | setLeaveStyles() {
188 | this.setStateIfNeeded({
189 | ...this.state,
190 | ...getPositionStyles(this.props.rect, 2, this.props.rtl),
191 | ...getTransitionStyles('leaved', this.props),
192 | });
193 | }
194 |
195 | render() {
196 | const {
197 | /* eslint-disable no-unused-vars */
198 | index,
199 | component: Element,
200 | containerSize,
201 | appearDelay,
202 | appear,
203 | appeared,
204 | enter,
205 | entered,
206 | leaved,
207 | onMounted,
208 | onUnmount,
209 | itemKey,
210 | /* eslint-enable no-unused-vars */
211 | rect,
212 | duration,
213 | easing,
214 | units,
215 | vendorPrefix,
216 | userAgent,
217 | rtl,
218 | ...rest
219 | } = this.props;
220 |
221 | const style = buildStyles({
222 | ...this.state,
223 | display: 'block',
224 | position: 'absolute',
225 | top: 0,
226 | ...(rtl ? { right: 0 } : { left: 0 }),
227 | width: rect.width,
228 | transition: transition(['opacity', 'transform'], duration, easing),
229 | }, units, vendorPrefix, userAgent);
230 |
231 | /* eslint-disable no-return-assign */
232 | return (
233 | this.node = node}
236 | style={style}
237 | />
238 | );
239 | /* eslint-enable no-return-assign */
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/components/StackGrid.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component, isValidElement } from 'react';
3 | import ReactDOM from 'react-dom';
4 | import PropTypes from 'prop-types';
5 | import TransitionGroup from 'react-transition-group/TransitionGroup';
6 | import sizeMe from 'react-sizeme';
7 | import shallowequal from 'shallowequal';
8 | import ExecutionEnvironment from 'exenv';
9 | import invariant from 'invariant';
10 | import GridItem from './GridItem';
11 | import { transition } from '../utils/style-helper';
12 | import { raf } from '../animations/request-animation-frame';
13 | import * as easings from '../animations/easings';
14 | import * as transitions from '../animations/transitions/';
15 |
16 | import type { Units } from '../types/';
17 |
18 | const imagesLoaded = ExecutionEnvironment.canUseDOM ? require('imagesloaded') : null;
19 |
20 |
21 | const isNumber = (v: any): boolean => typeof v === 'number' && isFinite(v); // eslint-disable-line no-restricted-globals
22 | const isPercentageNumber = (v: any): boolean => typeof v === 'string' && /^\d+(\.\d+)?%$/.test(v);
23 |
24 | // eslint-disable-next-line arrow-parens
25 | const createArray = (v: T, l: number): T[] => {
26 | const array = [];
27 | for (let i = 0; i < l; i += 1) array.push(v);
28 | return array;
29 | };
30 |
31 | /* eslint-disable consistent-return */
32 | const getColumnLengthAndWidth = (
33 | width: number,
34 | value: number | string,
35 | gutter: number
36 | ): [number, number] => {
37 | if (isNumber(value)) {
38 | const columnWidth = parseFloat(value);
39 |
40 | return [
41 | Math.floor((width - (((width / columnWidth) - 1) * gutter)) / columnWidth),
42 | columnWidth,
43 | ];
44 | } else if (isPercentageNumber(value)) {
45 | const columnPercentage = parseFloat(value) / 100;
46 | const maxColumn = Math.floor(1 / columnPercentage);
47 | const columnWidth = (width - (gutter * (maxColumn - 1))) / maxColumn;
48 |
49 | return [
50 | maxColumn,
51 | columnWidth,
52 | ];
53 | }
54 |
55 | invariant(false, 'Should be columnWidth is a number or percentage string.');
56 | };
57 | /* eslint-enable consistent-return */
58 |
59 |
60 | type Props = {
61 | children: React$Element;
62 | className?: string;
63 | style: Object;
64 | gridRef?: Function;
65 | component: string;
66 | itemComponent: string;
67 | columnWidth: number | string;
68 | gutterWidth: number;
69 | gutterHeight: number;
70 | duration: number;
71 | easing: string;
72 | appearDelay: number;
73 | appear: Function;
74 | appeared: Function;
75 | enter: Function;
76 | entered: Function;
77 | leaved: Function;
78 | units: Units;
79 | monitorImagesLoaded: boolean;
80 | vendorPrefix: boolean;
81 | userAgent: ?string;
82 | enableSSR: boolean;
83 | onLayout: Function;
84 | horizontal: boolean;
85 | rtl: boolean;
86 | };
87 |
88 | type InlineState = {
89 | rects: Array<{
90 | top: number;
91 | left: number;
92 | width: number;
93 | height: number;
94 | }>;
95 | actualWidth: number;
96 | height: number;
97 | columnWidth: number;
98 | };
99 |
100 | type InlineProps = Props & {
101 | refCallback: Function;
102 | size: {
103 | width: number;
104 | height: number;
105 | }
106 | };
107 |
108 | /* eslint-disable react/no-unused-prop-types */
109 | const propTypes = {
110 | children: PropTypes.node,
111 | className: PropTypes.string,
112 | style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
113 | gridRef: PropTypes.func,
114 | component: PropTypes.string,
115 | itemComponent: PropTypes.string,
116 | columnWidth: PropTypes.oneOfType([
117 | PropTypes.number,
118 | PropTypes.string,
119 | ]).isRequired,
120 | gutterWidth: PropTypes.number,
121 | gutterHeight: PropTypes.number,
122 | duration: PropTypes.number,
123 | easing: PropTypes.string,
124 | appearDelay: PropTypes.number,
125 | appear: PropTypes.func,
126 | appeared: PropTypes.func,
127 | enter: PropTypes.func,
128 | entered: PropTypes.func,
129 | leaved: PropTypes.func,
130 | units: PropTypes.shape({
131 | length: PropTypes.string,
132 | angle: PropTypes.string,
133 | }),
134 | monitorImagesLoaded: PropTypes.bool,
135 | vendorPrefix: PropTypes.bool,
136 | userAgent: PropTypes.string,
137 | enableSSR: PropTypes.bool,
138 | onLayout: PropTypes.func,
139 | horizontal: PropTypes.bool,
140 | rtl: PropTypes.bool,
141 | };
142 | /* eslint-enable react/no-unused-prop-types */
143 |
144 | export class GridInline extends Component {
145 | props: InlineProps;
146 | state: InlineState;
147 | items: { [key: string]: GridItem; };
148 | imgLoad: Object;
149 | mounted: boolean;
150 |
151 | static propTypes = {
152 | ...propTypes,
153 | size: PropTypes.shape({
154 | width: PropTypes.number,
155 | height: PropTypes.number,
156 | }),
157 | };
158 |
159 | constructor(props: InlineProps) {
160 | super(props);
161 |
162 | this.items = {};
163 | this.imgLoad = {};
164 | this.mounted = false;
165 | this.state = this.doLayout(props);
166 | }
167 |
168 | componentDidMount() {
169 | this.mounted = true;
170 | this.updateLayout(this.props);
171 | }
172 |
173 | componentWillReceiveProps(nextProps: InlineProps) {
174 | if (!shallowequal(nextProps, this.props)) {
175 | this.updateLayout(nextProps);
176 | }
177 | }
178 |
179 | shouldComponentUpdate(nextProps: InlineProps, nextState: InlineState) {
180 | return (
181 | !shallowequal(nextProps, this.props) ||
182 | !shallowequal(nextState, this.state)
183 | );
184 | }
185 |
186 | componentWillUnmount() {
187 | this.mounted = false;
188 | }
189 |
190 | setStateIfNeeded(state: Object) {
191 | if (this.mounted) {
192 | this.setState(state);
193 | }
194 | }
195 |
196 | getItemHeight(item: any): number {
197 | if (item.key && this.items.hasOwnProperty(item.key)) {
198 | const component = this.items[item.key];
199 | const el = (ReactDOM.findDOMNode(component): any);
200 | const candidate = [el.scrollHeight, el.clientHeight, el.offsetHeight, 0].filter(isNumber);
201 |
202 | return Math.max(...candidate);
203 | }
204 |
205 | return 0;
206 | }
207 |
208 |
209 | doLayout(props: InlineProps): InlineState {
210 | if (!ExecutionEnvironment.canUseDOM) {
211 | return this.doLayoutForSSR(props);
212 | }
213 |
214 | const results = this.doLayoutForClient(props);
215 |
216 | if (this.mounted && typeof this.props.onLayout === 'function') {
217 | this.props.onLayout();
218 | }
219 |
220 | return results;
221 | }
222 |
223 | doLayoutForClient(props: InlineProps): InlineState {
224 | const {
225 | size: { width: containerWidth },
226 | columnWidth: rawColumnWidth,
227 | gutterWidth,
228 | gutterHeight,
229 | horizontal,
230 | } = props;
231 |
232 | const childArray = React.Children.toArray(props.children);
233 | const [maxColumn, columnWidth] = getColumnLengthAndWidth(
234 | containerWidth,
235 | rawColumnWidth,
236 | gutterWidth
237 | );
238 | const columnHeights = createArray(0, maxColumn);
239 |
240 | let rects;
241 | if (!horizontal) {
242 | rects = childArray.map((child) => {
243 | const column = columnHeights.indexOf(Math.min(...columnHeights));
244 | const height = this.getItemHeight(child);
245 | const left = (column * columnWidth) + (column * gutterWidth);
246 | const top = columnHeights[column];
247 |
248 | columnHeights[column] += Math.round(height) + gutterHeight;
249 |
250 | return { top, left, width: columnWidth, height };
251 | });
252 | } else {
253 | const sumHeights = childArray.reduce(
254 | (sum, child) => sum + Math.round(this.getItemHeight(child)) + gutterHeight, 0);
255 | const maxHeight = sumHeights / maxColumn;
256 |
257 | let currentColumn = 0;
258 | rects = childArray.map((child) => {
259 | const column = currentColumn >= maxColumn - 1 ? maxColumn - 1 : currentColumn;
260 | const height = this.getItemHeight(child);
261 | const left = (column * columnWidth) + (column * gutterWidth);
262 | const top = columnHeights[column];
263 |
264 | columnHeights[column] += Math.round(height) + gutterHeight;
265 | if (columnHeights[column] >= maxHeight) {
266 | currentColumn += 1;
267 | }
268 |
269 | return { top, left, width: columnWidth, height };
270 | });
271 | }
272 |
273 | const width = (maxColumn * columnWidth) + ((maxColumn - 1) * gutterWidth);
274 | const height = Math.max(...columnHeights) - gutterHeight;
275 | const finalRects = rects.map(o => ({
276 | ...o,
277 | left: o.left + ((containerWidth - width) / 2),
278 | }));
279 |
280 | return { rects: finalRects, actualWidth: width, height, columnWidth };
281 | }
282 |
283 | // eslint-disable-next-line class-methods-use-this
284 | doLayoutForSSR(props: InlineProps): InlineState {
285 | return {
286 | rects: React.Children.toArray(props.children).map(() => ({
287 | top: 0, left: 0, width: 0, height: 0,
288 | })),
289 | actualWidth: 0,
290 | height: 0,
291 | columnWidth: 0,
292 | };
293 | }
294 |
295 | updateLayout(props: ?InlineProps): void {
296 | if (!props) {
297 | this.setStateIfNeeded(this.doLayout(this.props));
298 | } else {
299 | this.setStateIfNeeded(this.doLayout(props));
300 | }
301 | }
302 |
303 | handleItemMounted = (item: GridItem) => {
304 | const { itemKey: key } = item.props;
305 | this.items[key] = item;
306 |
307 | if (this.props.monitorImagesLoaded && typeof imagesLoaded === 'function') {
308 | const node = ReactDOM.findDOMNode(item);
309 | const imgLoad = imagesLoaded(node);
310 |
311 | imgLoad.once('always', () => raf(() => {
312 | this.updateLayout(this.props);
313 | }));
314 |
315 | this.imgLoad[key] = imgLoad;
316 | }
317 |
318 | this.updateLayout(this.props);
319 | }
320 |
321 | handleItemUnmount = (item: GridItem) => {
322 | const { itemKey: key } = item.props;
323 |
324 | if (this.items.hasOwnProperty(key)) {
325 | delete this.items[key];
326 | }
327 |
328 | if (this.imgLoad.hasOwnProperty(key)) {
329 | this.imgLoad[key].off('always');
330 | delete this.imgLoad[key];
331 | }
332 | }
333 |
334 | handleRef = () => {
335 | this.props.refCallback(this);
336 | };
337 |
338 | render() {
339 | const {
340 | /* eslint-disable no-unused-vars */
341 | gutterWidth,
342 | gutterHeight,
343 | columnWidth: rawColumnWidth,
344 | monitorImagesLoaded,
345 | enableSSR,
346 | onLayout,
347 | horizontal,
348 | rtl,
349 | refCallback,
350 | /* eslint-enable no-unused-vars */
351 | className,
352 | style,
353 | size,
354 | component,
355 | itemComponent,
356 | children,
357 | ...rest
358 | } = this.props;
359 |
360 | const { rects, actualWidth, height } = this.state;
361 | const containerSize = {
362 | actualWidth,
363 | width: size.width == null ? 0 : size.width,
364 | height,
365 | };
366 | const validChildren = React.Children
367 | .toArray(children)
368 | .filter(child => isValidElement(child));
369 |
370 |
371 | /* eslint-disable no-return-assign */
372 | return (
373 |
384 | {validChildren.map((child, i) => (
385 |
397 | {child}
398 |
399 | ))}
400 |
401 | );
402 | /* eslint-enable no-return-assign */
403 | }
404 | }
405 |
406 | const SizeAwareGridInline = sizeMe({
407 | monitorWidth: true,
408 | monitorHeight: false,
409 | })(GridInline);
410 |
411 |
412 | export default class StackGrid extends Component {
413 | static propTypes = propTypes;
414 |
415 | static defaultProps = {
416 | style: {},
417 | gridRef: null,
418 | component: 'div',
419 | itemComponent: 'span',
420 | columnWidth: 150,
421 | gutterWidth: 5,
422 | gutterHeight: 5,
423 | duration: 480,
424 | easing: easings.quartOut,
425 | appearDelay: 30,
426 | appear: transitions.fadeUp.appear,
427 | appeared: transitions.fadeUp.appeared,
428 | enter: transitions.fadeUp.enter,
429 | entered: transitions.fadeUp.entered,
430 | leaved: transitions.fadeUp.leaved,
431 | units: { length: 'px', angle: 'deg' },
432 | monitorImagesLoaded: false,
433 | vendorPrefix: true,
434 | userAgent: null,
435 | enableSSR: false,
436 | onLayout: null,
437 | horizontal: false,
438 | rtl: false,
439 | };
440 |
441 | props: Props;
442 | grid: GridInline;
443 |
444 | updateLayout() {
445 | this.grid.updateLayout();
446 | }
447 |
448 | handleRef = (grid: GridInline) => {
449 | this.grid = grid;
450 |
451 | if (typeof this.props.gridRef === 'function') {
452 | this.props.gridRef(this);
453 | }
454 | };
455 |
456 | render() {
457 | const {
458 | enableSSR,
459 | gridRef,
460 | ...rest
461 | } = this.props;
462 |
463 | sizeMe.enableSSRBehaviour = enableSSR;
464 |
465 | return (
466 |
470 | );
471 | }
472 | }
473 |
--------------------------------------------------------------------------------
/src/components/__tests__/GridItem.spec.js:
--------------------------------------------------------------------------------
1 | import sinon from 'sinon';
2 | import React from 'react';
3 | import { mount } from 'enzyme';
4 | import { easings, transitions } from '../../';
5 | import GridItem from '../GridItem';
6 |
7 |
8 | const mockProps = {
9 | component: 'span',
10 | rect: {
11 | top: 0,
12 | left: 0,
13 | width: 0,
14 | height: 0,
15 | },
16 | containerSize: {
17 | width: 960,
18 | height: 0,
19 | },
20 | index: 0,
21 | easing: easings.quartOut,
22 | appearDelay: 30,
23 | appear: transitions.fadeUp.appear,
24 | appeared: transitions.fadeUp.appeared,
25 | enter: transitions.fadeUp.enter,
26 | entered: transitions.fadeUp.entered,
27 | leaved: transitions.fadeUp.leaved,
28 | units: { length: 'px', angle: 'deg' },
29 | vendorPrefix: true,
30 | userAgent: null,
31 | onMounted: () => {},
32 | onUnmount: () => {},
33 | };
34 |
35 | let clock = null;
36 |
37 |
38 | describe('', () => {
39 | beforeEach(() => {
40 | clock = sinon.useFakeTimers();
41 | });
42 |
43 | afterEach(() => {
44 | clock.restore();
45 | });
46 |
47 |
48 | test('Should be change component', () => {
49 | const wrapper = mount(
50 |
54 | );
55 |
56 | expect(wrapper.find('li')).toHaveLength(1);
57 | });
58 |
59 |
60 | test('Should be call handleMounted/handleUnmount', () => {
61 | const handleMounted = sinon.spy();
62 | const handleUnmount = sinon.spy();
63 |
64 | expect(handleMounted.called).toBe(false);
65 |
66 | const wrapper = mount(
67 |
72 | );
73 |
74 | expect(handleMounted.called).toBe(true);
75 |
76 | wrapper.unmount();
77 | expect(handleUnmount.called).toBe(true);
78 | });
79 |
80 |
81 | test('Should be call transition style function', () => {
82 | const spyFunctions = {
83 | appear: sinon.spy(),
84 | appeared: sinon.spy(),
85 | enter: sinon.spy(),
86 | entered: sinon.spy(),
87 | leaved: sinon.spy(),
88 | };
89 |
90 | const wrapper = mount(
91 |
96 | Item
97 |
98 | );
99 |
100 | const gridItem = wrapper.instance();
101 | const noop = () => {};
102 |
103 | expect(spyFunctions.appear.called).toBe(true);
104 |
105 | gridItem.componentDidAppear(() => {});
106 | clock.tick(300);
107 | expect(spyFunctions.appeared.called).toBe(true);
108 |
109 | clock.tick(300);
110 | gridItem.componentWillEnter(noop);
111 | expect(spyFunctions.enter.called).toBe(true);
112 |
113 | clock.tick(300);
114 | gridItem.componentDidEnter();
115 | expect(spyFunctions.entered.called).toBe(true);
116 |
117 | clock.tick(300);
118 | gridItem.componentWillLeave(noop);
119 | expect(spyFunctions.leaved.called).toBe(true);
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/src/components/__tests__/StackGrid.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount } from 'enzyme';
3 | import StackGrid, { easings, transitions } from '../../';
4 | import { GridInline } from '../StackGrid';
5 |
6 |
7 | const mockProps = {
8 | size: {
9 | width: 960,
10 | height: 0,
11 | },
12 | style: {},
13 | refCallback: () => {},
14 | component: 'div',
15 | itemComponent: 'span',
16 | columnWidth: 150,
17 | gutterWidth: 5,
18 | gutterHeight: 5,
19 | duration: 480,
20 | easing: easings.quartOut,
21 | appearDelay: 30,
22 | appear: transitions.fadeUp.appear,
23 | appeared: transitions.fadeUp.appeared,
24 | enter: transitions.fadeUp.enter,
25 | entered: transitions.fadeUp.entered,
26 | leaved: transitions.fadeUp.leaved,
27 | units: { length: 'px', angle: 'deg' },
28 | monitorImagesLoaded: false,
29 | vendorPrefix: true,
30 | userAgent: null,
31 | onLayout: () => {},
32 | };
33 |
34 |
35 | describe(' (GridInline)', () => {
36 | test('Should not be render children', () => {
37 | const wrapper = mount(
38 |
39 | );
40 |
41 | expect(wrapper.children()).toHaveLength(0);
42 | });
43 |
44 |
45 | test('Should be pass the base props', () => {
46 | const wrapper = mount(
47 |
54 | A
55 |
56 | );
57 |
58 | expect(wrapper.props().className).toBe('rsg-grid');
59 | expect(wrapper.props().style).toEqual({
60 | width: 960,
61 | background: '#fff',
62 | });
63 | });
64 |
65 |
66 | test('Should be render with specify component', () => {
67 | const wrapper = mount(
68 |
74 | A
75 | B
76 | C
77 |
78 | );
79 |
80 | expect(wrapper.find('ul.rsg-grid')).toHaveLength(1);
81 | expect(wrapper.find('li')).toHaveLength(3);
82 | });
83 |
84 |
85 | test('Should be render children', () => {
86 | const wrapper = mount(
87 |
88 | ITEM 1
89 | ITEM 2
90 | ITEM 3
91 |
92 | );
93 |
94 | expect(wrapper.find('div.item')).toHaveLength(3);
95 | });
96 |
97 |
98 | test('Should be get grid ref', () => {
99 | const callback = jest.fn();
100 | const wrapper = mount(
101 |
105 | Foo
106 |
107 | );
108 |
109 | expect(callback.mock.calls).toHaveLength(1);
110 | expect(callback.mock.calls[0]).toEqual([
111 | wrapper.instance(),
112 | ]);
113 | });
114 |
115 |
116 | test('Should be call onLayout', () => {
117 | const callback = jest.fn();
118 | const wrapper = mount(
119 |
123 | Foo
124 |
125 | );
126 |
127 | expect(callback.mock.calls).toHaveLength(1);
128 |
129 | wrapper.setProps({
130 | ...mockProps,
131 | size: { width: 300, height: 0 },
132 | });
133 |
134 | expect(callback.mock.calls).toHaveLength(2);
135 | });
136 |
137 | test('Should be horizontal', () => {
138 | const testMockProps = Object.assign({}, mockProps, {
139 | size: {
140 | width: 320,
141 | height: 0,
142 | },
143 | horizontal: true,
144 | });
145 |
146 | const wrapper = mount(
147 |
148 | ITEM 1
149 | ITEM 2
150 | ITEM 3
151 | ITEM 4
152 |
153 | );
154 |
155 | expect(wrapper.find('span').at(0).prop('style').transform).toBe('translateX(8px) translateY(10px)');
156 | expect(wrapper.find('span').at(1).prop('style').transform).toBe('translateX(8px) translateY(15px)');
157 | expect(wrapper.find('span').at(2).prop('style').transform).toBe('translateX(163px) translateY(10px)');
158 | expect(wrapper.find('span').at(3).prop('style').transform).toBe('translateX(163px) translateY(15px)');
159 | });
160 |
161 | test('Should be horizontal RTL', () => {
162 | const testMockProps = Object.assign({}, mockProps, {
163 | size: {
164 | width: 320,
165 | height: 0,
166 | },
167 | rtl: true,
168 | horizontal: true,
169 | });
170 |
171 | const wrapper = mount(
172 |
173 | ITEM 1
174 | ITEM 2
175 | ITEM 3
176 | ITEM 4
177 |
178 | );
179 | expect(wrapper.find('span').at(0).prop('style').right).toBe(0);
180 | expect(wrapper.find('span').at(1).prop('style').right).toBe(0);
181 | expect(wrapper.find('span').at(2).prop('style').right).toBe(0);
182 | expect(wrapper.find('span').at(3).prop('style').right).toBe(0);
183 |
184 | expect(wrapper.find('span').at(0).prop('style').transform).toBe('translateX(-8px) translateY(10px)');
185 | expect(wrapper.find('span').at(1).prop('style').transform).toBe('translateX(-8px) translateY(15px)');
186 | expect(wrapper.find('span').at(2).prop('style').transform).toBe('translateX(-163px) translateY(10px)');
187 | expect(wrapper.find('span').at(3).prop('style').transform).toBe('translateX(-163px) translateY(15px)');
188 | });
189 | });
190 |
--------------------------------------------------------------------------------
/src/declares/vendor.js.flow:
--------------------------------------------------------------------------------
1 | declare module "prop-types" {
2 | declare export default any;
3 | }
4 |
5 | declare module "imagesloaded" {
6 | declare export default any;
7 | }
8 |
9 | declare module "react-transition-group/TransitionGroup" {
10 | declare export default any;
11 | }
12 |
13 | declare module "inline-style-prefixer" {
14 | declare export default any;
15 | }
16 |
17 | declare module "shallowequal" {
18 | declare export default any;
19 | }
20 |
21 | declare module "react-sizeme" {
22 | declare export default any;
23 | }
24 |
25 | declare module "throttle-debounce" {
26 | declare export function debounce(): Function;
27 | }
28 |
29 | declare module "exenv" {
30 | declare export default any;
31 | }
32 |
33 | declare module "invariant" {
34 | declare export default any;
35 | }
36 |
37 | declare module "easy-css-transform-builder" {
38 | declare export function createCSSTransformBuilder(units: any): Function;
39 | declare export var properties: Array;
40 | }
41 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import StackGrid from './components/StackGrid';
3 |
4 | export * as easings from './animations/easings';
5 | export * as transitions from './animations/transitions/';
6 |
7 | export default StackGrid;
8 |
--------------------------------------------------------------------------------
/src/types/index.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export type Units = {
3 | length: string;
4 | angle: string;
5 | };
6 |
7 | export type Rect = {
8 | top: number;
9 | left: number;
10 | width: number;
11 | height: number;
12 | };
13 |
--------------------------------------------------------------------------------
/src/utils/__tests__/style-helper.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-len */
2 | import { transition, buildStyles } from '../style-helper';
3 |
4 |
5 | describe('style-helper', () => {
6 | test('Should be build a transition string', () => {
7 | expect(transition(['opacity'], 1000, 'ease-in')).toBe('opacity 1000ms ease-in');
8 |
9 | const easing = 'cubic-bezier(0.215, 0.61, 0.355, 1)';
10 | expect(transition(['transform', 'opacity', 'background'], 200, easing)).toBe([
11 | `transform 200ms ${easing}`,
12 | `opacity 200ms ${easing}`,
13 | `background 200ms ${easing}`,
14 | ].join(','));
15 | });
16 |
17 |
18 | test('Should be build a style object', () => {
19 | const ios8 = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A365 Safari/600.1.4';
20 | const units = {
21 | length: 'px',
22 | angle: 'deg',
23 | };
24 |
25 | expect(buildStyles({
26 | opacity: 1,
27 | fontSize: 16,
28 | }, units, false)).toEqual({
29 | opacity: 1,
30 | fontSize: 16,
31 | });
32 |
33 | expect(buildStyles({
34 | opacity: 1,
35 | translateX: 900,
36 | translateY: 200,
37 | scale: 0.8,
38 | perspective: 1000,
39 | }, units, false)).toEqual({
40 | opacity: 1,
41 | perspective: 1000,
42 | transform: 'translateX(900px) translateY(200px) scale(0.8) perspective(1000px)',
43 | });
44 |
45 | expect(buildStyles({
46 | opacity: 1,
47 | translateX: 900,
48 | translateY: 200,
49 | scale: 0.8,
50 | perspective: 1000,
51 | }, units, true, ios8)).toEqual({
52 | opacity: 1,
53 | WebkitPerspective: 1000,
54 | WebkitTransform: 'translateX(900px) translateY(200px) scale(0.8) perspective(1000px)',
55 | });
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/src/utils/style-helper.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import Prefixer from 'inline-style-prefixer';
3 | import { createCSSTransformBuilder, properties } from 'easy-css-transform-builder';
4 |
5 | export type Units = {
6 | length: string;
7 | angle: string;
8 | };
9 |
10 |
11 | const isTransformProp = v => properties.indexOf(v) > -1;
12 |
13 | export const transition = (props: Array, duration: number, easing: string) => (
14 | props.map(prop =>
15 | `${prop} ${duration}ms ${easing}`
16 | ).join(',')
17 | );
18 |
19 |
20 | export const buildStyles = (
21 | styles: Object,
22 | units: Units,
23 | vendorPrefix: boolean,
24 | userAgent: ?string
25 | ) => {
26 | const builder = createCSSTransformBuilder(units);
27 | const finalStyles = {};
28 | const transformStyles = {};
29 |
30 | Object.keys(styles).forEach((key) => {
31 | const value = styles[key];
32 |
33 | if (isTransformProp(key)) {
34 | transformStyles[key] = value;
35 |
36 | if (key === 'perspective') {
37 | finalStyles[key] = value;
38 | }
39 | } else {
40 | finalStyles[key] = value;
41 | }
42 | });
43 |
44 | const transform = builder(transformStyles, units);
45 | if (transform !== '') {
46 | finalStyles.transform = transform;
47 | }
48 |
49 | if (vendorPrefix) {
50 | const prefixer = new Prefixer({ userAgent });
51 | return prefixer.prefix(finalStyles);
52 | }
53 |
54 | return finalStyles;
55 | };
56 |
--------------------------------------------------------------------------------