├── .babelrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── docs ├── components │ ├── Head.js │ └── Page.js └── index.html ├── lib └── .gitkeep ├── package-lock.json ├── package.json ├── src └── index.js └── test └── index.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/index.js 2 | node_modules 3 | *.log 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oli Evans 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 ResponsiveEmbed 2 | 3 | You want to embed a YouTube or other `iframe` style embedded content, and you'd 4 | like it to take up the available width, and retain it's aspect ratio. 5 | 6 | Much like the [Bootstrap responsive embed helpers] of old, but for react. 7 | 8 | ## Usage 9 | 10 | ```sh 11 | npm install --save react-responsive-embed 12 | ``` 13 | 14 | Then in your app import `ResponsiveEmbed` and in JSX flavour you might do: 15 | 16 | ```html 17 | 18 | ``` 19 | 20 | Which'd give you an `iframe` element pointing at the `src` and any other props. 21 | It's wrapped in a `div` with just the right amount of `padding-bottom` to 22 | preserve a **16:9** aspect ratio like so: 23 | 24 | ```html 25 |
26 | 30 |
31 | ``` 32 | 33 | Pass in a `ratio` prop to pick a different one. Any ratio will do: 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | `frameborder="0"` is applied by default. 40 | 41 | 42 | ## Browserify / Webpack / Other? 43 | 44 | **This module is just a function**. It's been babelified so it'll work with 45 | whatever your set up is. Use browserify or webpack or any other npm module 46 | consuming bundle whizzbang; ResponsiveEmbed don't mind. 47 | 48 | --- 49 | 50 | A [(╯°□°)╯︵TABLEFLIP] side project. 51 | 52 | 53 | [Bootstrap responsive embed helpers]: https://v4-alpha.getbootstrap.com/utilities/responsive-helpers/#responsive-embeds 54 | [(╯°□°)╯︵TABLEFLIP]: https://tableflip.io 55 | -------------------------------------------------------------------------------- /docs/components/Head.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const style = ` 4 | html { background-color: #50E3C2;} 5 | body { 6 | color: #4A4A4A; 7 | background-color: #fff; 8 | padding: 2em 1em; 9 | margin:0px 0 6px; 10 | line-height: 1.6; 11 | font-size:20px; 12 | letter-spacing: 0.2px; 13 | min-height:100vh; 14 | font-family: -apple-system, BlinkMacSystemFont, 15 | "avenir next", avenir, 16 | helvetica, "helvetica neue", 17 | ubuntu, 18 | roboto, noto, 19 | "segoe ui", arial, 20 | sans-serif; 21 | } 22 | code { 23 | font-size: .875rem; 24 | font-family: Consolas, monaco, monospace; 25 | } 26 | a { 27 | color: #333; 28 | } 29 | a:hover { 30 | color: #319C84; 31 | } 32 | blockquote { 33 | color: #888; 34 | padding: .5rem 1.2rem; 35 | margin: 1rem 0; 36 | font-size: 1.1rem; 37 | border-left: .25rem solid #eceeef; 38 | } 39 | blockquote p:first-child { 40 | margin-top: 0.25rem; 41 | } 42 | blockquote p:last-child { 43 | margin-bottom: 0.25rem; 44 | } 45 | h1 { font-size: 3rem; } 46 | h2 { font-size: 2.25rem; } 47 | h3 { font-size: 1.5rem; } 48 | h4 { font-size: 1.25rem; } 49 | h5 { font-size: 1rem; } 50 | h6 { font-size: .875rem; } 51 | img { max-width: 100%; } 52 | .m-b-1 { 53 | margin-bottom: 1rem !important; 54 | } 55 | .m-t-3 { 56 | margin-top: 3rem !important; 57 | } 58 | .max-width-md { 59 | max-width: 700px; 60 | margin:0 auto; 61 | } 62 | .text-decoration-none { 63 | text-decoration: none; 64 | } 65 | .tracked { 66 | letter-spacing: 0.5px; 67 | } 68 | ` 69 | 70 | const Head = () => ( 71 | 72 | 73 | React ResponsiveEmbed 74 | 75 |
TABLEFLIP

React ResponsiveEmbed

You want to embed a YouTube or other `iframe` style embedded content, and you'd like it to take up the available width, and retain it's aspect ratio.

Much like the Bootstrap responsive embed helpers of old, but for react.

<ResponsiveEmbed ratio='16:9' src='https://www.youtube.com/embed/2yqz9zgoC-U' />
<ResponsiveEmbed ratio='4:3' src='https://www.youtube.com/embed/mM5_T-F1Yn4' />

https://github.com/tableflip/react-responsive-embed

-------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tableflip/react-responsive-embed/60519bd5112de7b00b8eb8f882e4ab2affa741df/lib/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-responsive-embed", 3 | "version": "2.1.0", 4 | "description": "Embed iframes responsively", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "ava", 8 | "prepublish": "npm run build", 9 | "build": "babel src -d lib", 10 | "docs": "static-react docs/components/Page.js > docs/index.html" 11 | }, 12 | "author": "oilzilla ", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "ava": "^0.23.0", 16 | "babel-cli": "^6.26.0", 17 | "babel-preset-env": "^1.6.1", 18 | "prop-types": "^15.6.0", 19 | "react": "^16.0", 20 | "react-dom": "^16.0.0", 21 | "react-test-renderer": "^16.0.0", 22 | "standard": "^10.0.3", 23 | "static-react": "^3.2.0" 24 | }, 25 | "peerDependencies": { 26 | "prop-types": ">=15", 27 | "react": ">=15" 28 | }, 29 | "engines": { 30 | "node": ">=6" 31 | }, 32 | "directories": { 33 | "doc": "docs", 34 | "test": "test" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "git+https://github.com/tableflip/react-responsive-embed.git" 39 | }, 40 | "keywords": [ 41 | "react", 42 | "responsive", 43 | "iframe", 44 | "aspect", 45 | "ratio" 46 | ], 47 | "bugs": { 48 | "url": "https://github.com/tableflip/react-responsive-embed/issues" 49 | }, 50 | "homepage": "https://github.com/tableflip/react-responsive-embed#readme" 51 | } 52 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const React = require('react') 2 | const PropTypes = require('prop-types') 3 | const div = React.createElement.bind(React, 'div') 4 | const iframe = React.createElement.bind(React, 'iframe') 5 | 6 | const divStyle = { 7 | position: 'relative', 8 | height: 0, 9 | overflow: 'hidden', 10 | maxWidth: '100%' 11 | } 12 | 13 | const iframeStyle = { 14 | position: 'absolute', 15 | top: 0, 16 | left: 0, 17 | width: '100%', 18 | height: '100%' 19 | } 20 | 21 | /* 22 | * Turn `16:9` into `9 / 16` into `56.25%` 23 | * Turn `4:3` into `3 / 4` into `75%` 24 | */ 25 | const ratioToPercent = (ratio) => { 26 | const [w, h] = ratio.split(':').map((num) => Number(num)) 27 | return `${(h / w) * 100}%` 28 | } 29 | 30 | /* 31 | * Usage: 32 | */ 33 | const ResponsiveEmbed = (props) => { 34 | const paddingBottom = ratioToPercent(props.ratio) 35 | const style = Object.assign({}, divStyle, {paddingBottom}) 36 | const iframeProps = Object.assign({frameBorder: 0}, props, {style: iframeStyle}) 37 | delete iframeProps.ratio 38 | return div({style}, 39 | iframe(iframeProps) 40 | ) 41 | } 42 | 43 | ResponsiveEmbed.defaultProps = { 44 | src: 'https://www.youtube.com/embed/dQw4w9WgXcQ', 45 | ratio: '16:9' 46 | } 47 | 48 | ResponsiveEmbed.propTypes = { 49 | src: PropTypes.string, 50 | ratio: (props, propName, componentName) => { 51 | if (!/\d+:\d+/.test(props[propName])) { 52 | return new Error( 53 | 'Invalid ratio supplied to ResponsiveEmbed. Expected a string like "16:9" or any 2 numbers seperated by a colon' 54 | ) 55 | } 56 | } 57 | } 58 | 59 | module.exports = ResponsiveEmbed 60 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava') 2 | const React = require('react') 3 | const ShallowRenderer = require('react-test-renderer/shallow') 4 | const ResponsiveEmbed = require('../src/index') 5 | 6 | test('ResponsiveEmbed renders OK', (t) => { 7 | const src = 'https://www.youtube.com/embed/zc1g_hSuxVE' 8 | const element = React.createElement(ResponsiveEmbed, {src}) 9 | const renderer = new ShallowRenderer() 10 | renderer.render(element) 11 | const root = renderer.getRenderOutput() 12 | const child = root.props.children 13 | 14 | t.plan(4) 15 | t.is(root.type, 'div', 'Root element is a div') 16 | t.is(root.props.style.paddingBottom, '56.25%', 'Aspect ratio of 16x9 is manifest as a paddingBottom style of 56.25%') 17 | t.is(child.type, 'iframe', 'Child element is an iframe') 18 | t.is(child.props.src, src, 'Provided `src` is used') 19 | }) 20 | 21 | test('ResponsiveEmbed renders other ratios', (t) => { 22 | const src = 'https://www.youtube.com/embed/zc1g_hSuxVE' 23 | const ratio = '1.43:1' // imax 24 | const element = React.createElement(ResponsiveEmbed, {src, ratio}) 25 | const renderer = new ShallowRenderer() 26 | renderer.render(element) 27 | const root = renderer.getRenderOutput() 28 | const child = root.props.children 29 | 30 | t.plan(4) 31 | t.is(root.type, 'div', 'Root element is a div') 32 | t.is(root.props.style.paddingBottom, '69.93006993006993%', 'IMAX aspect ratio is preserved') 33 | t.is(child.type, 'iframe', 'Child element is an iframe') 34 | t.is(child.props.src, src, 'Provided `src` is used') 35 | }) 36 | 37 | test('ResponsiveEmbed renders with default props', (t) => { 38 | const element = React.createElement(ResponsiveEmbed) 39 | const renderer = new ShallowRenderer() 40 | renderer.render(element) 41 | const root = renderer.getRenderOutput() 42 | const child = root.props.children 43 | 44 | t.plan(4) 45 | t.is(root.type, 'div', 'Root element is a div') 46 | t.is(root.props.style.paddingBottom, '56.25%', 'Aspect ratio of 16x9 is manifest as a paddingBottom style of 56.25%') 47 | t.is(child.type, 'iframe', 'Child element is an iframe') 48 | t.is(child.props.src, 'https://www.youtube.com/embed/dQw4w9WgXcQ', 'Default `src` is used') 49 | }) --------------------------------------------------------------------------------