├── .babelrc ├── .eslintrc ├── .gitignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples ├── App.tsx ├── index.html └── index.tsx ├── package.json ├── src ├── components │ ├── FullsizePicture.tsx │ └── Picture.tsx └── index.ts ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "loose": true, 7 | "modules": false 8 | } 9 | ], 10 | "@babel/react", 11 | "@babel/typescript" 12 | ], 13 | "env": { 14 | "commonjs": { 15 | "plugins": ["@babel/plugin-transform-modules-commonjs"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb-base", 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:@typescript-eslint/eslint-recommended", 7 | "plugin:@typescript-eslint/recommended", 8 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 9 | "plugin:prettier/recommended", 10 | "prettier/react", 11 | "prettier/@typescript-eslint" 12 | ], 13 | "parser": "@typescript-eslint/parser", 14 | "parserOptions": { 15 | "project": "./tsconfig.json" 16 | }, 17 | "plugins": ["@typescript-eslint", "react", "prettier"], 18 | "rules": { 19 | "class-methods-use-this": "off", 20 | "dot-notation": "off" 21 | }, 22 | "settings": { 23 | "react": { 24 | "version": "detect" 25 | }, 26 | "import/resolver": { 27 | "node": { 28 | "extensions": [".js", ".tsx", ".ts"] 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Directory for instrumented libs generated by jscoverage/JSCover 7 | lib-cov 8 | 9 | # Coverage directory used by tools like istanbul 10 | coverage 11 | 12 | # Compiled binary addons (http://nodejs.org/api/addons.html) 13 | build/Release 14 | 15 | # Dependency directory 16 | node_modules 17 | 18 | # Optional npm cache directory 19 | .npm 20 | 21 | # Optional REPL history 22 | .node_repl_history 23 | 24 | dist/ 25 | es/ 26 | lib/ 27 | examples/cache/ 28 | examples/dist/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "trailingComma": "es5", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": false, 10 | "fluid": false 11 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hi@bernardoraposo.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We are open to, and grateful for, any contributions made by the community. 4 | 5 | ## Reporting Issues 6 | 7 | Before opening an issue, please search the [issue tracker](https://github.com/braposo/react-responsive-picture/issues) to make sure your issue hasn't already been reported. 8 | 9 | ## Development 10 | 11 | Visit the [Issue tracker](https://github.com/braposo/react-responsive-picture/issues) to find a list of open issues that need attention. 12 | 13 | Start by forking and then cloning the repo: 14 | 15 | ``` 16 | git clone https://github.com/your-username/react-responsive-picture.git 17 | ``` 18 | 19 | Run the examples, which will automatically watch any changes in `src/` folder: 20 | 21 | ``` 22 | yarn start 23 | ``` 24 | 25 | ### Building and testing 26 | 27 | Build package: 28 | 29 | ``` 30 | yarn build 31 | ``` 32 | 33 | To run the tests: 34 | 35 | ``` 36 | yarn test 37 | ``` 38 | 39 | To perform linting with `eslint`, run the following: 40 | 41 | ``` 42 | yarn lint 43 | ``` 44 | 45 | ### New Features 46 | 47 | Please open an issue with a proposal for a new feature or refactoring before starting on the work. We don't want you to waste your efforts on a pull request that we won't want to accept. 48 | 49 | ## Submitting Changes 50 | 51 | - Open a new issue in the [Issue tracker](https://github.com/braposo/react-responsive-picture/issues). 52 | - Fork the repo. 53 | - Create a new feature branch based off the `master` branch. 54 | - Make sure all tests pass and there are no linting errors. 55 | - Submit a pull request, referencing any issues it addresses. 56 | 57 | Please try to keep your pull request focused in scope and avoid including unrelated commits. 58 | 59 | After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or improvements. 60 | 61 | Thank you for contributing! 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 EDITED 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## react-responsive-picture 2 | 3 | A future-proof responsive image component that supports latest `` specification. Uses [picturefill](https://github.com/scottjehl/picturefill) for backward compatibility from IE9+. 4 | 5 | [![npm version][version-badge]][npm] 6 | [![npm downloads][downloads-badge]][npm] 7 | [![gzip size][size-badge]][size] 8 | [![MIT License][license-badge]][license] 9 | [![PRs Welcome][prs-badge]][prs] 10 | 11 | --- 12 | 13 | ## Installation 14 | 15 | `npm install react-responsive-picture` or `yarn add react-responsive-picture` 16 | 17 | ## Playground 18 | 19 | [![Edit react-responsive-picture][codesandbox-badge]][codesandbox] 20 | 21 | You can also run the examples by cloning the repo and running `yarn start`. 22 | 23 | ### Usage 24 | 25 | ```jsx 26 | import { Picture } from 'react-responsive-picture'; 27 | 28 | function SomeComponent() { 29 | return ( 30 | 45 | ); 46 | } 47 | ``` 48 | 49 | ### Props 50 | 51 | | Prop | Type | Default | Definition | 52 | | --- | --- | --- | --- | 53 | | sources | array | | The array of source objects. Check Sources section for more information. | 54 | | src | string | (transparent pixel) | Source for standalone/fallback image. To prevent issues in some browsers, by default `src` is set to a 1x1 transparent pixel data image. | 55 | | sizes | string | | Sizes attribute to be used with `src` for determing best image for user's viewport. | 56 | | alt | string | | Alternative text for image | 57 | | className | string | | Any additional CSS classes you might want to use to style the image | 58 | 59 | ## Examples 60 | 61 | ### Simple image 62 | Normal `` like behaviour. The same image is displayed on every device/viewport. 63 | 64 | ```jsx 65 | 66 | ``` 67 | 68 | will render: 69 | 70 | ```html 71 | 72 | ``` 73 | 74 | ### Image with different resolutions 75 | Different images for specific devices (usually retina). 76 | 77 | ```jsx 78 | 79 | ``` 80 | 81 | will render: 82 | 83 | ```html 84 | 85 | ``` 86 | 87 | ### Image with sizes 88 | When you want to let the browser determine the best image for user's current viewport. More information about `size` attribute on this great [blog post](http://ericportis.com/posts/2014/srcset-sizes/). 89 | 90 | ```jsx 91 | 95 | ``` 96 | 97 | will render: 98 | 99 | ```html 100 | 101 | ``` 102 | 103 | ### Image with art direction 104 | When you want to explicitly control which image is displayed at specific viewport sizes. 105 | 106 | ```jsx 107 | 122 | ``` 123 | 124 | will render: 125 | 126 | ```html 127 | 128 | 129 | 130 | 131 | 132 | 133 | ``` 134 | 135 | The `sources` prop is where you can determine the behaviour of the `` component and which images will show for the specific screens. 136 | 137 | For each source you can send an object containing `srcSet`, `media` and `type` although the last two are optional. 138 | 139 | ### Styling 140 | 141 | You can use your favourite styling library and style the `Picture` component using the `className` prop. 142 | 143 | ```jsx 144 | import { css } from "emotion"; 145 | 146 | 152 | ``` 153 | 154 | ## Fullsize images 155 | 156 | There's also a `` component that you can use to display full-size images using the same benefits of `` for art direction. 157 | 158 | ```jsx 159 |
160 | 171 |
172 | ``` 173 | 174 | It will automatically fill the parent element maintaining the original image ratio. Please note that the parent element needs to have a defined height as you would expect for any background image as well. 175 | 176 | ### Props 177 | 178 | `FullsizePicture` accepts the same props as `Picture` plus a few more for styling and positioning. 179 | 180 | | Prop | Type | Default | Definition | 181 | | --- | --- | --- | --- | 182 | | sources | array | | The array of source objects. Check Sources section for more information. | 183 | | src | string | (transparent pixel) | Source for standalone/fallback image. To prevent issues in some browsers, by default `src` is set to a 1x1 transparent pixel data image. | 184 | | sizes | string | | Sizes attribute to be used with `src` for determing best image for user's viewport. | 185 | | alt | string | | Alternative text for image | 186 | | className | string | | Any additional CSS classes you might want to use to style the image | 187 | | wrapperClassName | string | | Any additional CSS classes you might want to use to style the wrapper of the `Picture` component | 188 | | cover | "both" \| "width" \| "height" | "both" | Decides the fullsize behaviour of the `Picture` component. By default it covers the entire parent, but can be changed to cover just the height or width instead. | 189 | | center | boolean | true | Helper prop to horizontally and vertically center the image. | 190 | 191 | ### Use as background image 192 | 193 | If you want to use `FullsizePicture` as a background image for other components, you can pass them as children too. 194 | 195 | ```jsx 196 |
197 | 208 | This is the section title 209 | 210 |
211 | ``` 212 | 213 | ## Contributing 214 | 215 | Please follow our [contributing guidelines](https://github.com/braposo/react-responsive-picture/blob/master/CONTRIBUTING.md). 216 | 217 | ## License 218 | 219 | [MIT](https://github.com/braposo/react-responsive-picture/blob/master/LICENSE) 220 | 221 | [npm]: https://www.npmjs.com/package/react-responsive-picture 222 | [license]: https://github.com/braposo/react-responsive-picture/blob/master/LICENSE 223 | [prs]: http://makeapullrequest.com 224 | [size]: https://unpkg.com/react-responsive-picture/dist/react-responsive-picture.min.js 225 | [version-badge]: https://img.shields.io/npm/v/react-responsive-picture.svg?style=flat-square 226 | [downloads-badge]: https://img.shields.io/npm/dm/react-responsive-picture.svg?style=flat-square 227 | [license-badge]: https://img.shields.io/npm/l/react-responsive-picture.svg?style=flat-square 228 | [size-badge]: http://img.badgesize.io/https://unpkg.com/react-responsive-picture/dist/react-responsive-picture.min.js?compression=gzip&style=flat-square 229 | [modules-badge]: https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20esm-green.svg?style=flat-square 230 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 231 | [codesandbox-badge]: https://codesandbox.io/static/img/play-codesandbox.svg 232 | [codesandbox]: https://codesandbox.io/s/react-responsive-picture-playground-e6kbv 233 | -------------------------------------------------------------------------------- /examples/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import cxs from "cxs/component"; 3 | import cxsStyles from "cxs"; 4 | import { Picture, FullsizePicture } from "../src"; 5 | 6 | const styles = cxsStyles({ 7 | border: "1px solid red", 8 | boxShadow: "2px 2px 10px", 9 | }); 10 | 11 | const Heading1 = cxs("h1")({ 12 | position: "relative", 13 | textAlign: "center", 14 | color: "white", 15 | marginTop: 50, 16 | }); 17 | 18 | const App = (): JSX.Element => ( 19 |
20 |

Picture with art direction

21 |
22 | 35 |
36 |

Full-height Picture with art direction

37 |
38 | 53 |
54 |

Full-width Picture with content

55 |
56 | 57 | This is a heading 58 | 59 |
60 |

Styled Picture

61 |
62 | 66 |
67 |
68 | ); 69 | 70 | export default App; 71 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "react-dom"; 3 | import App from "./App"; 4 | 5 | render(, document.getElementById("root")); 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-responsive-picture", 3 | "version": "3.2.2", 4 | "description": "A future-proof responsive image component that supports latest Picture specification", 5 | "main": "lib/index.js", 6 | "module": "es/index.js", 7 | "types": "lib/index.d.ts", 8 | "scripts": { 9 | "build": "run-p build:commonjs build:es build:umd build:umd:min build:ts build:ts-es", 10 | "build:es": "babel src -d es --extensions .ts,.tsx", 11 | "build:commonjs": "cross-env BABEL_ENV=commonjs babel src -d lib --extensions .ts,.tsx", 12 | "build:umd": "cross-env NODE_ENV=development webpack src/index.ts -o dist/react-responsive-picture.js", 13 | "build:umd:min": "cross-env NODE_ENV=production webpack src/index.ts -o dist/react-responsive-picture.min.js", 14 | "build:ts": "tsc", 15 | "build:ts-es": "tsc -m es6 --outDir es", 16 | "clean": "rimraf lib dist es", 17 | "start": "parcel examples/index.html -d examples/dist --cache-dir examples/cache --open", 18 | "dev": "yarn run clean && cross-env BABEL_ENV=commonjs babel src -d lib --watch --extensions .ts,.tsx", 19 | "lint": "eslint src/ --ext .js,.ts,.tsx", 20 | "prepack": "yarn run clean && yarn run build", 21 | "test": "echo \"No tests available\" && exit 0", 22 | "ts:check": "tsc -p tsconfig.json --allowJs false --noEmit --emitDeclarationOnly false" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/braposo/react-responsive-picture.git" 27 | }, 28 | "files": [ 29 | "es", 30 | "dist", 31 | "lib", 32 | "src" 33 | ], 34 | "keywords": [ 35 | "image", 36 | "picture", 37 | "responsive", 38 | "react" 39 | ], 40 | "author": { 41 | "name": "Bernardo Raposo", 42 | "email": "hi@bernardoraposo.com", 43 | "url": "http://bernardoraposo.com" 44 | }, 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/braposo/react-responsive-picture/issues" 48 | }, 49 | "homepage": "https://github.com/braposo/react-responsive-picture#readme", 50 | "devDependencies": { 51 | "@babel/cli": "^7.6.0", 52 | "@babel/core": "^7.6.0", 53 | "@babel/plugin-proposal-object-rest-spread": "^7.5.5", 54 | "@babel/plugin-transform-modules-commonjs": "^7.6.0", 55 | "@babel/preset-env": "^7.6.0", 56 | "@babel/preset-react": "^7.0.0", 57 | "@babel/preset-typescript": "^7.6.0", 58 | "@types/node": "^12.7.5", 59 | "@types/react": "^16.9.2", 60 | "@typescript-eslint/eslint-plugin": "^2.2.0", 61 | "@typescript-eslint/parser": "^2.2.0", 62 | "babel-eslint": "^10.0.3", 63 | "babel-loader": "^8.0.6", 64 | "cross-env": "^5.2.1", 65 | "eslint": "^6.3.0", 66 | "eslint-config-airbnb-base": "^14.0.0", 67 | "eslint-config-prettier": "^6.3.0", 68 | "eslint-plugin-import": "^2.18.2", 69 | "eslint-plugin-prettier": "^3.1.0", 70 | "eslint-plugin-react": "^7.14.3", 71 | "npm-run-all": "^4.1.5", 72 | "parcel-bundler": "^1.12.3", 73 | "prettier": "^1.18.2", 74 | "react": "^16.9.0", 75 | "react-dom": "^16.9.0", 76 | "rimraf": "^3.0.0", 77 | "source-map-loader": "^0.2.4", 78 | "typescript": "^3.6.3", 79 | "webpack": "^4.39.3", 80 | "webpack-cli": "^3.3.8" 81 | }, 82 | "peerDependencies": { 83 | "react": "^15.0.1 || ^16.0.1", 84 | "react-dom": "^15.0.1 || ^16.0.1" 85 | }, 86 | "dependencies": { 87 | "can-use-dom": "0.1.0", 88 | "cxs": "6.2.0", 89 | "picturefill": "3.0.3" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/components/FullsizePicture.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import cxs from "cxs/component"; 3 | import Picture, { Props as PictureProps } from "./Picture"; 4 | 5 | type Props = { 6 | className?: string; 7 | wrapperClassName?: string; 8 | children?: JSX.Element | JSX.Element[]; 9 | } & StyledPictureProps; 10 | 11 | type StyledPictureProps = { 12 | center?: boolean; 13 | cover?: "width" | "height" | "both"; 14 | } & PictureProps; 15 | 16 | const Wrapper: React.FunctionComponent<{ className?: string }> = cxs("div")({ 17 | width: "100%", 18 | height: "100%", 19 | position: "relative", 20 | }); 21 | 22 | const PictureWrapper: React.FunctionComponent = cxs("div")({ 23 | overflow: "hidden", 24 | top: 0, 25 | left: 0, 26 | right: 0, 27 | bottom: 0, 28 | position: "absolute", 29 | }); 30 | 31 | const StyledPicture: React.FunctionComponent = cxs(Picture)( 32 | ({ cover = "both", center = true }) => ({ 33 | position: "absolute", 34 | top: center ? "50%" : 0, 35 | left: center ? "50%" : 0, 36 | transform: center ? "translate(-50%, -50%)" : "none", 37 | width: cover === "width" ? "100%" : "auto", 38 | height: cover === "height" ? "100%" : "auto", 39 | minHeight: cover === "both" ? "100%" : "none", 40 | minWidth: cover === "both" ? "100%" : "none", 41 | }) 42 | ); 43 | 44 | const Fullsized: React.FunctionComponent = ({ 45 | className, 46 | wrapperClassName, 47 | children, 48 | ...rest 49 | }: Props) => { 50 | return ( 51 | 52 | 53 | 54 | {children} 55 | 56 | 57 | ); 58 | }; 59 | 60 | export default Fullsized; 61 | -------------------------------------------------------------------------------- /src/components/Picture.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import canUseDom from "can-use-dom"; 3 | 4 | export type Props = { 5 | sources?: { 6 | srcSet: string; 7 | media?: string; 8 | type?: string; 9 | }[]; 10 | src?: string; 11 | alt?: string; 12 | className?: string; 13 | sizes?: string; 14 | }; 15 | 16 | class Picture extends React.PureComponent { 17 | componentDidMount(): void { 18 | // c.f. https://github.com/scottjehl/picturefill/pull/556 19 | let picturefill; 20 | try { 21 | picturefill = require("picturefill"); // eslint-disable-line global-require 22 | } catch (x) { 23 | return; 24 | } 25 | 26 | if (picturefill) { 27 | picturefill(); // browser 28 | } 29 | // else node 30 | } 31 | 32 | renderSources(): JSX.Element | (JSX.Element | null)[] | null { 33 | const ieVersion = 34 | canUseDom && document["documentMode"] 35 | ? document["documentMode"] 36 | : -1; 37 | const { sources } = this.props; 38 | 39 | if (sources == null) { 40 | return null; 41 | } 42 | 43 | const mappedSources = sources.map((source, index) => { 44 | if (source.srcSet == null) { 45 | return null; 46 | } 47 | 48 | return ( 49 | 55 | ); 56 | }); 57 | 58 | // IE9 requires the sources to be wrapped around an