神刀安全网

Build a Music Player with React & Electron II: Making the UI

In theprevious article we setup an Electron project, discussed the basic concepts of React and created a useless example to get us started. Now we will start making something – a music app.

We also discussed about the types of components which are presentation and container components. In this tutorial we will focus on our presentation component and talk about container in the next.

Revisit app Directory Structure

|---app #All React projects goes here |----components # Presentation Component Directory |------details.component.js |------footer.component.js |------player.component.js |------progress.component.js |------search.component.js |----containers # Container Component Directory |------app.container.js |----app.js

The app folder has two sub folders and an app.js at its root. Our only concern in this section of the series is app/components where our presentation component will live and app.js which will put the components together and tie them to the DOM.

Components

We will build the presentation component one after the other. The idea of components makes it easier for designers to work with wireframes and transform the wire-frames into components. Below is a wireframe for Scotch Player:

Build a Music Player with React & Electron II: Making the UI

With the above wireframe, it becomes really simple to fish out the components and reason around them. We have 5 UI (presentation) components:

  1. Search
  2. Details
  3. Player
  4. Progress
  5. Footer

A presentation component can also be referred to as UI component

Search (UI) Component

This component lives in app/components/search.component.js and has the same basic React component skeleton:

// Import React import React from 'react';  // Create Search component class class Search extends React.Component{    render() {     // Return JSX via render()     return (       <div className="search">        </div>     );   }  }  // Export Search export default Search

What will be cool is that instead of using the usual search form with a submit button, we could make use of an auto-complete component. The React team has an awesome auto-complete component which we aready included in the package.json ; no need to re-invent the wheel:

// Import React import React from 'react';  // Import React's Autocomplete component import Autocomplete from 'react-autocomplete';  // Create Search component class class Search extends React.Component{    render() {     // Return JSX via render()     return (       <div className="search">         {/*Autocomplete usage with value and behavior handled via this.props*/}         <Autocomplete          ref="autocomplete"          inputProps={{title: "Title"}}          value={this.props.autoCompleteValue}          {/*Array of tracks is passed in to items*/}          items={this.props.tracks}          {/*Single value selected*/}          getItemValue={(item) => item.title}          {/*What happens when an item is selected*/}          onSelect={this.props.handleSelect}          {/*What happens when keystrokes are received*/}          onChange={this.props.handleChange}          {/*How items are redered.*/}          renderItem={this.handleRenderItem.bind(this)}        />       </div>     );   }  }  // Export Search export default Search

Although {/* */} is the proper way to comment in JSX, React will not allow those comments between properties, therefore, remove in your usage.

renderitem is the only property that does not receive props but value which is handleRenderitem and is created as a method in the Search class:

// ... //class Search extends React.Component{   handleRenderItem(item, isHighlighted){     // Some basic style     const listStyles = {       item: {         padding: '2px 6px',         cursor: 'default'       },        highlightedItem: {         color: 'white',         background: '#F38B72',         padding: '2px 6px',         cursor: 'default'       }     };      // Render list items     return (       <div         style={isHighlighted ? listStyles.highlightedItem : listStyles.item}         key={item.id}         id={item.id}       >{item.title}</div>     )   } //  render() { // ...

Notice how styles are pased in too. In React there is room for global styles and inline dynamic styles as seen above.

Note that the Search component has no constructor method. If all a component does is receive props (which is exactly what UI components do), then the constructor is not needed.

To see what we have done so far, update the app.js with the following:

// ES6 Component // Import React and ReactDOM import React from 'react'; import ReactDOM from 'react-dom';  // Import Search Component import Search from './components/search.component';  // Component Class class App extends React.Component {     // render method is most important     // render method returns JSX template     render() {         return (           <Search />         );     } }  // Render to ID content in the DOM ReactDOM.render(     <App/ >,     document.getElementById('content') );

See how Search is imported and used inside App . No functionality attached yet, remember this section is just about UI

Build a Music Player with React &amp; Electron II: Making the UI

Yeah! So darn ugly! We will fix that later.

Details (UI) Component

This is the simplest component we will create as it just has a h3 tag with the track title:

// Import React import React from 'react';  class Details extends React.Component {   // Render   render(){     return(       <div className="details">         <h3>{this.props.title}</h3>       </div>     )   }  } // Export export default Details

We update app.js with Details :

// ES6 Component // Import React and ReactDOM import React from 'react'; import ReactDOM from 'react-dom';  // Import Search Component import Search from './components/search.component';  // Import Details Component import Details from './components/details.component';  // Component Class class App extends React.Component {     // render method is most important     // render method returns JSX template     render() {         return (           <div>             <Search />             {/* Added Details Component */}             <Details title={'Track title'} />           </div>         );     } }  // Render to ID content in the DOM ReactDOM.render(     <App/ > ,     document.getElementById('content') ); 

We import the Details component and add it to the App .

JSX will fail to compile if we do not wrap and expose the content using only one tag. That’s why we wrapped Search and Details with div

Build a Music Player with React &amp; Electron II: Making the UI

Player (UI) Component

The player component is made of the controls for our music player:

// Import React import React from 'react';  // Import ClassNames import ClassNames from 'classnames';  // Player component class class Player extends React.Component {    render(){     // Dynamic class names with ClassNames     const playPauseClass = ClassNames({       'fa fa-play': this.props.playStatus == 'PLAYING' ? false : true,       'fa fa-pause': this.props.playStatus == 'PLAYING' ? true : false     });      // Return JSX     return(       <div className="player">         {/*Rewind Button*/}         <div className="player__backward">           <button onClick={this.props.backward}><i className="fa fa-backward"></i></button>         </div>         <div className="player__main">           {/*Play/Pause Button*/}           <button onClick={this.props.togglePlay}><i className={playPauseClass}></i></button>           {/*Stop Button*/}           <button onClick={this.props.stop}><i className="fa fa-stop"></i></button>           {/*Random Track Button*/}           <button onClick={this.props.random}><i className="fa fa-random"></i></button>         </div>         {/*Forward Button*/}         <div className="player__forward">           <button onClick={this.props.forward}><i className="fa fa-forward"></i></button>         </div>       </div>     )   }  }  // Export Player export default Player

The new stuff to observe in this component is that we imported a classnames utility library which helps us dynamically set classes. It is useful here to swith the play/pause button to pause or play depending on the current state of the track.

The events callbacks will be handled in the container component which we will discuss in the next section. Next, include Player component in app.js :

// ES6 Component // Import React and ReactDOM import React from 'react'; import ReactDOM from 'react-dom';  // Import Search Component import Search from './components/search.component';  // Import Details Component import Details from './components/details.component';  // Import Player Component import Player from './components/player.component';  // Import Progress Component // Component Class class App extends React.Component {      // render method is most important     // render method returns JSX template     render() {         return (           <div>             <Search />             <Details title={'Track title'} />             {/* Added Player component*/}             <Player  />           </div>         );     }  }  // Render to ID content in the DOM ReactDOM.render(     <App/ > ,     document.getElementById('content') );

We need to add font-awesome to index.html for the control icons to look as expected:

<!-- ... -->   <title>Scotch Player</title>     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css"> <!-- ... -->

Build a Music Player with React &amp; Electron II: Making the UI

You will end up dissapointed if you try clicking them – no behavior or state yet. Relax!

Progress (UI) Component

This guy is a simple one also – just a:

  • progress bar
  • total play time
  • elapsed time:
// Import React import React from 'react';  // Create Progress component class class Progress extends React.Component {    // Render method   render() {      return(       <div className="progress">         {/* Elapsed time */}         <span className="player__time-elapsed">{this.props.elapsed}</span>         {/* Progress Bar */}         <progress            value={this.props.position}            max="1"></progress>          {/* Total time */}          <span className="player__time-total">{this.props.total}</span>       </div>     )   }  }  //Export Progress export default Progress

And now to include this component in app.js like we did for the others:

// ES6 Component // Import React and ReactDOM import React from 'react'; import ReactDOM from 'react-dom';  // Import Search Component import Search from './components/search.component';  // Import Details Component import Details from './components/details.component';  // Import Player Component import Player from './components/player.component';  // Import Progress Component import Progress from './components/progress.component';  // Component Class class App extends React.Component {      // render method is most important     // render method returns JSX template     render() {         return (           <div>             <Search />             <Details title={'Track title'} />             <Player  />             {/* Added Progress component*/}             <Progress               position={'0.3'}               elapsed={'00:00'}               total={'0:40'}/>           </div>         );     }  }  // Render to ID content in the DOM ReactDOM.render(     <App/ > ,     document.getElementById('content') ); 

After updating the app.js as we have been doing, you should have something like the image below:

Build a Music Player with React &amp; Electron II: Making the UI

Footer (UI) Component

The last is the footer component. This is a completely dumb static component:

import React from 'react';  class Footer extends React.Component {   render(){     return(       <div className="footer">         <p>Love from <img src="public/img/logo.png" className="logo"/>             & <img src="public/img/soundcloud.png" className="soundcloud"/></p>       </div>     )   }  }  export default Footer

Include in app.js , add the images in the public/img directory and the app should be looking like the image below:

Build a Music Player with React &amp; Electron II: Making the UI

Awful look, right? Let’s fix that.

Global Styles

React supports inline styles which we have seen. Inline styles are handy for dynamic functionality and to compose reusable components. This does not mean that we cannot use our global style and at the time of this writing, React has no rigid recommendation for how styles are added.

We won’t spend much time talking about the styles as it is out of scope. Just dump the following in public/css/globals.css and wait for the magic :

/* Box sizing resets*/ *, *:before, *:after {   box-sizing: border-box; }  /* Body resets and fancy font*/ body{   margin: 0;   font-family: 'Exo 2', sans-serif; }  .scotch_music{   position: relative; }  .search div, .search input {   width: 100%; }  .search input {   border: none;   border-bottom: 2px solid rgb(243, 139, 114);   outline: none;   background: rgba(255, 255, 255, 0.8);   padding: 5px; }  .details h3{   text-align: center;   padding: 50px 10px;   margin: 0;   color: white; }  .player{   text-align: center;   margin-top: 60px; }  .player div{   display: inline-block;   margin-left: 10px;   margin-right: 10px; }  .player .player__backward button,  .player .player__forward button{   background: transparent;   border: 1px solid rgb(243, 139, 114);   color: rgb(243, 139, 114);   width: 75px;   height: 75px;   border-radius: 100%;   font-size: 35px;   outline: none; }  .player .player__backward button{   border-left: none; }  .player .player__forward button{   border-right: none; }  .player .player__main button:hover,  .player .player__backward button:hover,  .player .player__forward button:hover{   color: rgba(243, 139, 114, 0.7);   border: 1px solid rgba(243, 139, 114, 0.7); }  .player .player__main {   border: 1px solid rgb(243, 139, 114); }  .player .player__main button {   color: rgb(243, 139, 114);   background: transparent;   width: 75px;   height: 75px;   border: none;   font-size: 35px;   outline: none; }  .progress{   text-align: center;   margin-top: 100px;     color: white; }  .progress progress[value] {   /* Reset the default appearance */   -webkit-appearance: none;    appearance: none;    width: 390px;   height: 20px;   margin-left: 4px;   margin-right: 4px; }  .progress progress[value]::-webkit-progress-bar {   background-color: #eee;   border-radius: 2px;   box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25) inset; }  .progress progress[value]::-webkit-progress-value {     background-color: rgb(243, 139, 114);     border-radius: 2px;     background-size: 35px 20px, 100% 100%, 100% 100%; }  .footer{   color: white;   position: absolute;   bottom: 0px;   width: 100%;   background: #524C4C; }  .footer p{   text-align: center; }  .footer .logo{   height: 25px;   width: auto; } .footer .soundcloud{   height: 25px;   width: auto; }

We need to import the global.css and Exo 2 font in the index.html :

<link rel="stylesheet" href="public/css/global.css">     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">     <link href='https://fonts.googleapis.com/css?family=Exo+2:500' rel='stylesheet' type='text/css'>

Build a Music Player with React &amp; Electron II: Making the UI

Looks better, right? We’re another big step closer to achieving our fully functional React and Electron music player!

Up Next…

Our presentation components are ready and styled (though needs some more touches). In the next section we will create our container component. See you soon…

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Build a Music Player with React & Electron II: Making the UI

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址