Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div id="root"></div>
              
            
!

CSS

              
                @import 'https://fonts.googleapis.com/css?family=Dancing+Script:400,700|Open+Sans'

*
	box-sizing: border-box

body
	background-color: #f9f9f9
	font-family: 'Open Sans', sans-serif
	margin: 0
header
	font-family: Helvetica,
	text-align: center
	color: #888
	font-family: 'Dancing Script', cursive
	h1
		font-size: 60px
		margin-bottom: 20px
	.back
		color: blue
		height: 40px
		opacity: 1
		pointer-events: auto
		transition: all 0.2s
		font-size: 22px
		&:hover
			color: teal
		&--active
			opacity: 0
			pointer-events: none
	@media(max-width: 480px)
		position: fixed
		top: 0
		width: 100%
		left: 50%
		z-index: 2
		transform: translateX(-50%)
		background: #f9f9f9
		box-shadow: 1px 2px 4px #888
		padding: 10px 0
		h1
			margin: 0
			font-size: 30px
			i
				color: #222
		.back
			position: absolute
			top: 50%
			left: 10px
			transform: translateY(-20%)
			font-size: 18px
			
a
	text-decoration: none
	font-weight: bold
	color: #888
	transition: all 0.2s
	&:hover
		color: #666
	&.active
		color: #444
	@media(max-width: 4080px)
		&:focus
			outline: none
			border: none
	
ul
	list-style: none
	display: flex
	padding: 0
	li
		margin: 0 20px

main
	width: 60%
	margin: 0 auto
	@media(max-width: 780px)
		width: 80%
	@media(max-width: 480px)
		width: 100%
		padding-top: 60px

.home
	position: relative
	padding: 0px 0px
	.m-new
		display: none
	.new
		position: absolute
		z-index: 3
		right: 10px
		top: -30px
		background: #888
		color: #fff
		padding: 10px
		box-shadow: 1px 2px 4px #888
		border-radius: 4px
		transition: all 0.2s
		&:hover
			background-color: #fff
			box-shadow: 4px 4px 8px #888
			color: #888
		&--active
			opacity: 0
	@media(max-width: 480px)
		.new
			display: none
		.m-new
			display: block
			position: fixed
			bottom: 20px
			right: 15px
			font-size: 38px
			background: #fff
			width: 50px
			height: 50px
			text-align: center
			line-height: 50px
			border-radius: 50%
			box-shadow: 2px 2px 8px #888
			transition: all 0.2s
			z-index: 10
			&:hover
				box-shadow: 4px 4px 10px #888
.new-recipe
	padding: 20px 0
	padding-bottom: 0
	width: 60%
	margin: 0 auto
	h3
		text-align: center
		margin: 0
		color: #444
	@media(max-width: 740px)
		width: 80%
	@media(max-width: 480px)
		width: 100%
		h3
			text-align: left
			margin-left: 20px
			font-size: 18px
	.input-block
		margin: 10px 20px
		display: flex
		align-items: center
		input
			flex: 1
			color: #444
			font-weight: bold
			border: none
			outline: none
			padding: 10px 0 5px 0
			border-bottom: 2px solid #888
			background-color: transparent
			transition: all 0.2s
			&:focus
				border-bottom-color: blue
		a
			display: inline-block
			margin-left: 20px
			width: 30px
			height: 30px
			line-height: 25px
			font-weight: bold
			font-size: 25px
			color: red
			text-align: center
			border: 1px solid red
			border-radius: 50%
			transition: all 0.2s
			&:hover
				color: orange
				border-color: orange
	.add-btn-block
		display: flex
		justify-content: center
		a
			display: inline-block
			width: 40px
			height: 40px
			border: 1px solid green
			border-radius: 50%
			color: green
			text-align: center
			font-size: 30px
			line-height: 40px
			transition: all 0.2s
			&:hover
				color: teal
				border-color: teal
	.cta
		margin: 20px 20px
		display: flex
		justify-content: center
		a
			padding: 10px 20px
			margin-left: 20px
			background: rgba(0,0,0,0.6)
			color: #fff
			border-radius: 4px
			box-shadow: 2px 2px 4px #888
			transition: all 0.2s
			&:hover
				color: #888
				background: #fff
				bax-shadow: 4px 4px 8px #888
	
.section
	.section-head
		background: #fff
		padding: 10px 0
		display: flex
		align-items: baseline
		box-shadow: 1px 2px 4px #888, 1px -2px 4px #fff
		cursor: pointer
		position: relative
		& > i
			position: absolute
			right: 20px
			font-size: 28px
			top: 50%
			transform: translateY(-50%)
		.icon
			font-size: 24px
			margin-left: 10px
			margin-right: 15px
		.name
			font-weight: bold
	.details
		height: 0
		overflow: hidden
		border-bottom: 1px solid #ccc
		transition: all 0.5s
		opacity: 0
		h3
			text-align: center
			margin-bottom: 0
			color: #888
		.ingrediants
			display: flex
			flex-wrap: wrap
			justify-content: center
			.ing
				color: #666
				text-align: center
				margin: 10px
				padding: 5px
				background: #fff
				border-radius: 7px
				box-shadow: 1px 2px 4px #888
		.cta
			padding: 10px
			display: flex
			justify-content: center
			a
				margin: 0 20px
				font-size: 20px
				border: 1px solid #fff
				padding: 5px
				border-radius: 7px
				i
					font-size: 20px
				@media(max-width: 480px)
					font-size: 14px
					i
						font-size: 16px
				&:first-child
					color: teal
					border-color: teal
				&:last-child
					color: red
					border-color: red
				
	&.open
		.details
			height: auto
			opacity: 1
footer
	text-align: center
	font-size: 14px
	color: #888
              
            
!

JS

              
                const LS_KEY = '_vinaypuppal_recipes';
let recipes = [{
	name: 'Upma',
	ingredients: ['Bombay rava', 'urad dal', 'chana dal', 'cashews', 'oil', 'sliced onions', '1 slit green chili', 'chopped ginger', 'water', 'salt']
}, {
	name: 'Lemon rice',
	ingredients: ['1.5 cups rice', 'lemon juice', 'Olive Oil', 'Salt', 'Cashews', 'curry leaves', 'green chilies']
}, {
	name: 'Gulab jamun',
	ingredients: ['1 cup milk powder', 'maida', 'ghee/ oil', '1 tbsp milk', '1 pinch of Baking soda', '1 ¼ to 1 ½ cups Sugar', '1 ½ cup water']
}];

let {
	Router,
	Route,
	Link,
	IndexLink,
	IndexRoute,
	hashHistory
} = ReactRouter;

const Header = () => {
	return (
		<header>
			<h1><i className='sb-bistro-chef-hat
'></i> Recipe Book</h1>
			<IndexLink to='/' className='back' activeClassName='back--active'> {'<'} Back 	</IndexLink>
		</header>
	);
}

const Footer = () => {
	return (
		<footer>
			<p>
				Made with &hearts; By VinayPuppal
			</p>
		</footer>
	);
}

class Section extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			open: false
		}
	}
	handelClick(e) {
		e.preventDefault();
		this.setState({
			open: !this.state.open
		});
	}
	render() {
		const stateClass = this.state.open ? 'section open' : 'section';
		const stateIcon = this.state.open ? 'pe-7s-angle-down' : 'pe-7s-angle-right';
		return (
			<div className={stateClass}>
				<div onClick={this.handelClick.bind(this)} className='section-head'>
					<div className='icon'><i className='sb-bistro-appetizer'></i></div>
					<div className='name'>{this.props.recipe.name}</div>
					<i className={stateIcon}></i>
				</div>
				<div className='details'>
					<h3>Ingrediants</h3>
					<div className='ingrediants'>
						{this.props.recipe.ingredients.map((ing)=>{
							return(
								<div className='ing'>{ing}</div>
							);
						})}
					</div>
					<div className='cta'>
						<Link to={`newrecipe/${this.props.recipe.name}`}>
							<i className='pe-7s-note'></i>
							Edit
						</Link>
						<a onClick={
								(e)=>{
									e.preventDefault();
									this.props.onDelete(this.props.recipe);
								}
							}
							href='#'>
							<i className='pe-7s-trash'></i>
							Delete
						</a>
					</div>
				</div>
			</div>
		);
	}
}

const Home = (props) => {
		return (
				<div className='home'>
			<Link to='/newrecipe' className='new' activeClassName='new--active'>New Recipe</Link>
			<Link to='/newrecipe' className='m-new' activeClassName='m-new--active'>+</Link>
			<div className="content">
				{props.recipes.map((recipe)=>{
					return (
						<Section onDelete={props.handelDeleteRecipe} key={recipe.name} recipe={recipe}/>
						);
						})
				}
			</div>
		</div>
	);
}

class NewRecipe extends React.Component{
	constructor(props){
		super(props);
		this.state={
			newRecipe:{
				name:'',
				ingredients:['','','']
			},
		}
	}
	componentWillMount(){
		if(this.props.params.name){
			let newRecipe = this.props.recipes.find((recipe)=>{
				return recipe.name === this.props.params.name;
			});
			this.setState({newRecipe});
		}
	}
	handelAddIng(e){
		e.preventDefault();
		this.setState({
			newRecipe:{...this.state.newRecipe,ingredients:[...this.state.newRecipe.ingredients,'']}
		});
	}
	handelRemoveIng(index){
		this.setState({
			newRecipe:{...this.state.newRecipe,ingredients:[...this.state.newRecipe.ingredients.slice(0,index),...this.state.newRecipe.ingredients.slice(index+1)]}
		});
	}
	updateName(value){
		this.setState({
			newRecipe:{...this.state.newRecipe,name:value}
		});
	}
	updateIng(value,index){
		this.setState({
			newRecipe:{...this.state.newRecipe,ingredients:[...this.state.newRecipe.ingredients.slice(0,index),value,...this.state.newRecipe.ingredients.slice(index+1)]}
		});
		console.log(this.state.newRecipe);
	}
	handelReset(e){
		e.preventDefault();
		this.setState({
			newRecipe:{
				name:'',
				ingredients:['','','']
			},
		});
	}
	handelSave(e){
		e.preventDefault();
		let recipe = this.state.newRecipe
		recipe.ingredients = recipe.ingredients.filter((item)=>{
			return item !== ''
		});
		if(recipe.name==='' || recipe.ingredients.length === 0){
			alert('Please Fill Name and Ingredients Of Recipe To Save')	
		}else{
			this.props.handelSaveRecipe(recipe);	
		}
	}
	render(){
		return(
			<div className='new-recipe'>
				<form>
					<h3>Recipe Name</h3>
					<div className='input-block'>
						<input type='text' 
							placeholder='name' 
							value={this.state.newRecipe.name} 
							onChange={(e)=>{this.updateName(e.target.value)}}/>
					</div>
					<h3>Ingredients</h3>
					{this.state.newRecipe.ingredients.map((item,i)=>{
						if(i===0){
							return(
							<div className='input-block' key={i}>
								<input type='text' 
									placeholder='ingredient'
									value={item}
									onChange={(e)=>{this.updateIng(e.target.value,i)}}/>
							</div>
						);	
						}
						return(
							<div className='input-block' key={i}>
								<input type='text' 
									placeholder='ingredient'
									value={item}
									onChange={(e)=>{this.updateIng(e.target.value,i)}}/>
								<a href='#' 
									onClick={(e)=>{e.preventDefault();
																 this.handelRemoveIng(i)}}>
									{'-'}
								</a>
							</div>
						);
					})}
					<div className="add-btn-block">
						<a href='#' onClick={this.handelAddIng.bind(this)}>{'+'}</a>
					</div>
					<div className='cta'>
						<a href='#' onClick={this.handelReset.bind(this)}>Reset</a>
						<a href='#' onClick={this.handelSave.bind(this)}>Save</a>
					</div>
				</form>
			</div>
		);
	}
}

class Layout extends React.Component{
	constructor(props){
		super(props);
		this.state = {
			recipes:[],
		}
	}
	componentWillMount(){
		if (typeof(Storage) !== "undefined") {
    // Store
			if(localStorage.getItem(LS_KEY)){
				let storedRecipes = JSON.parse(localStorage.getItem(LS_KEY));
					this.setState({
						recipes: [...this.state.recipes,...storedRecipes]
					});
			}else{
				console.log(recipes);
				localStorage.setItem(LS_KEY,JSON.stringify(recipes));
				this.setState({
						recipes: recipes
					});
			}
    // Retrieve
		} else {
    	alert('Local Storage Not Supported!.')
		}
	}
	handelSaveRecipe(recipe){
		let recipes = JSON.parse(localStorage.getItem(LS_KEY));
		let recipeIndex = recipes.findIndex((item)=>{
			return item.name === recipe.name;
		});
		if(~recipeIndex){
			recipes = [...recipes.slice(0,recipeIndex),recipe,...recipes.slice(recipeIndex+1)];
		}else{
			recipes.push(recipe)	
		}
		
		recipes = JSON.stringify(recipes);
		localStorage.setItem(LS_KEY,recipes);
		if(~recipeIndex){
		this.setState({
			recipes: [...this.state.recipes.slice(0,recipeIndex),recipe,...this.state.recipes.slice(recipeIndex+1)]
		});
		}else{
			this.setState({
			recipes: [...this.state.recipes,recipe]
		});
		}
		hashHistory.push('/');
	}
	handelDeleteRecipe(recipe){
		let recipes = JSON.parse(localStorage.getItem(LS_KEY));
		let recipeIndex = recipes.findIndex((item)=>{
			return item.name === recipe.name;
		});
		if(~recipeIndex){
			recipes = [...recipes.slice(0,recipeIndex),...recipes.slice(recipeIndex+1)];
		this.setState({
			recipes: recipes
		});
		recipes = JSON.stringify(recipes);
		localStorage.setItem(LS_KEY,recipes);
		}else{
			alert('no recipe found');
		}
	}
	render(){
		let passProps ={
			recipes: this.state.recipes,
			handelSaveRecipe: this.handelSaveRecipe.bind(this),
			handelDeleteRecipe: this.handelDeleteRecipe.bind(this),
		}
		let childrenWithProps = React.cloneElement(this.props.children, passProps);
		return(
			<div>
				<Header />
				<main>
					{childrenWithProps}
				</main>
				<Footer />
			</div>
		);
	}
}

const App = ()=>{
	return(
		<Router>
			<Route path='/' component={Layout}>
				<IndexRoute component={Home}/>
				<Route path='newrecipe(/:name)' component={NewRecipe}/>
			</Route>
		</Router>
	);
}

ReactDOM.render(<App />,document.getElementById('root'));
              
            
!
999px

Console