Repository: caljrimmer/portfolio-redux-app
Branch: master
Commit: 1c5297756eb1
Files: 39
Total size: 56.7 KB
Directory structure:
gitextract_hpkv_v1h/
├── .gitignore
├── README.md
├── package.json
├── src/
│ ├── client/
│ │ └── index.js
│ ├── common/
│ │ ├── actions/
│ │ │ ├── about.js
│ │ │ └── layout.js
│ │ ├── api/
│ │ │ ├── fetchComponentDataBeforeRender.js
│ │ │ ├── portfolio.js
│ │ │ └── promiseMiddleware.js
│ │ ├── components/
│ │ │ ├── 404.js
│ │ │ ├── About.js
│ │ │ ├── Home.js
│ │ │ ├── Portfolio.js
│ │ │ ├── Services.js
│ │ │ ├── about/
│ │ │ │ └── Repos.js
│ │ │ └── layout/
│ │ │ ├── Banner.js
│ │ │ ├── Header.js
│ │ │ ├── Loader.js
│ │ │ └── Sidebar.js
│ │ ├── containers/
│ │ │ ├── AboutPage.js
│ │ │ ├── App.js
│ │ │ └── HomePage.js
│ │ ├── reducers/
│ │ │ ├── about.js
│ │ │ ├── index.js
│ │ │ └── layout.js
│ │ ├── routes.js
│ │ └── store/
│ │ └── configureStore.js
│ └── server/
│ ├── devtools.js
│ ├── index.js
│ ├── server.js
│ └── webpack.js
├── styles/
│ ├── about.css
│ ├── base.css
│ ├── custom.css
│ ├── index.css
│ ├── layout.css
│ ├── portfolio.css
│ └── services.css
└── webpack.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
npm-debug.log
lib
.DS_Store
dist
================================================
FILE: README.md
================================================
# Portfolio Redux App
A Portfolio example of an isomorphic rendering application in React and Redux. [Live Example Here](http://www.callumrimmer.co.uk)
<img src="http://g.recordit.co/cO0pqgO8Fo.gif" width="728" />
## Development Installation
In the project's directory, run the following commands:
```
$ npm install
$ npm start
```
Then Visit
```
http://localhost:3002
```
## Releasing to Production
Production has Devtools, logging and hot reloading middleware removed and the scripts/css compressed.
In the project's directory, run the following commands:
```
$ npm run build
$ npm run start-prod
```
Then Visit
```
http://localhost:3002
```
## Other Boilerplate code
[General Redux App (with Undo)](https://github.com/caljrimmer/isomorphic-redux-app)
## Credit
App template was based on [Lanyon Theme](https://github.com/poole/lanyon) by [mdo](https://github.com/mdo)
================================================
FILE: package.json
================================================
{
"name": "portfolio-redux-app",
"version": "0.0.1",
"description": "A portfolio template built with Redux and React",
"scripts": {
"slate": "rm -rf node_modules && npm install",
"clean": "rm -rf dist",
"start": "node src/server/index.js --progress --colors --profile",
"build": "npm run clean && NODE_ENV=production webpack -p --progress --colors --profile",
"postinstall": "npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/caljrimmer/portfolio-redux-app.git"
},
"license": "MIT",
"dependencies": {
"express": "^4.13.3",
"react": "^0.13.3",
"react-router": "^1.0.0-rc1",
"redux-router": "^1.0.0-beta3",
"react-redux": "^2.1.2",
"redux": "^3.0.0",
"redux-thunk": "^0.1.0",
"history": "1.13.0",
"classnames" : "^2.1.5",
"axios" : "^0.7.0"
},
"devDependencies": {
"babel": "^5.8.21",
"babel-core": "^5.8.22",
"babel-loader": "^5.3.2",
"redux-devtools": "^2.1.5",
"redux-logger" : "^2.0.2",
"babel-plugin-react-transform": "^1.1.0",
"babel-runtime": "^5.8.20",
"webpack": "^1.11.0",
"style-loader": "^0.8.0",
"css-loader": "^0.9.0",
"url-loader": "^0.5.6",
"file-loader": "0.8.5",
"react-transform-hmr": "^1.0.0",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.2.0",
"merge":"^1.2.0",
"extract-text-webpack-plugin" : "^0.8.2"
}
}
================================================
FILE: src/client/index.js
================================================
import 'babel-core/polyfill';
import React from 'react';
import { Router } from 'react-router';
import { Provider } from 'react-redux';
import { ReduxRouter } from 'redux-router';
import createBrowserHistory from 'history/lib/createBrowserHistory'
import configureStore from '../common/store/configureStore';
import routes from '../common/routes';
import "../../styles/index.css";
const history = createBrowserHistory();
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
const rootElement = document.getElementById('root');
React.render(
<Provider store={store}>
{() =>
<ReduxRouter>
<Router children={routes} history={history} />
</ReduxRouter>
}
</Provider>,
document.getElementById('root')
);
if (process.env.NODE_ENV !== 'production') {
require('../server/devtools')(store);
}
================================================
FILE: src/common/actions/about.js
================================================
import request from 'axios';
export const INVALIDATE_REPOS = 'INVALIDATE_REPOS';
export const REPOS_GET = 'REPOS_GET';
export const REPOS_GET_REQUEST = 'REPOS_GET_REQUEST';
export const REPOS_GET_SUCCESS = 'REPOS_GET_SUCCESS';
export const REPOS_GET_FAILURE = 'REPOS_GET_FAILURE';
export function invalidateRepos(repos) {
return {
type: INVALIDATE_REPOS,
repos
};
}
export function fetchRepos() {
return {
type: REPOS_GET,
promise: request.get(`https://api.github.com/users/caljrimmer/repos`)
}
}
function shouldFetchRepos(state) {
const repos = state.repos.results;
if (!repos.length) {
return true;
} else if (repos.isFetching) {
return false;
} else {
return repos.didInvalidate;
}
}
export function fetchReposIfNeeded() {
return (dispatch, getState) => {
if (shouldFetchRepos(getState())) {
return dispatch(fetchRepos());
}
};
}
================================================
FILE: src/common/actions/layout.js
================================================
export const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR';
export function toggleSidebar(value) {
return {
type: TOGGLE_SIDEBAR,
value : value
};
}
================================================
FILE: src/common/api/fetchComponentDataBeforeRender.js
================================================
/**
* This looks at static needs parameter in components and waits for the promise to be fullfilled
* It is used to make sure server side rendered pages wait for APIs to resolve before returning res.end()
*/
export function fetchComponentDataBeforeRender(dispatch, components, params) {
const needs = components.reduce( (prev, current) => {
return (current.need || [])
.concat((current.WrappedComponent ? current.WrappedComponent.need : []) || [])
.concat(prev);
}, []);
const promises = needs.map(need => dispatch(need()));
return Promise.all(promises);
}
================================================
FILE: src/common/api/portfolio.js
================================================
export function getPortfolio() {
return [
{
link : 'http://www.goldmansachs.com',
title : 'Goldman Sachs',
classname : 'goldmans',
roles : [
{
title : 'Front End Developer',
skills : 'React, Backbone, D3'
},
{
title : 'Back End Developer',
skills : 'Node, MongoDB, Webpack'
}
]
},
{
link : 'http://www.libon.com/',
title : 'Orange (Libon)',
classname : 'orange',
roles : [
{
title : 'Front End Developer',
skills : 'Backbone, WebRTC, FireFoxOS'
},
{
title : 'Back End Developer',
skills : 'Node, MongoDB, Grunt'
}
]
},
{
link : 'http://www.pwul.net',
title : 'Pay What You Like',
classname : 'pwul',
roles : [
{
title : 'Front End Developer',
skills : 'React, Backbone, D3'
},
{
title : 'Back End Developer',
skills : 'Node, MongoDB, Gulp'
}
]
},
{
link : 'http://www.timeout.com/london/card',
title : 'Timeout',
classname : 'timeout',
roles : [
{
title : 'Front End Developer',
skills : 'Backbone, CSS3, HTML5'
}
]
},
{
link : 'http://www.tesco.com/direct',
title : 'Tesco Entertainment',
classname : 'tesco',
roles : [
{
title : 'Front End Developer',
skills : 'Backbone, CSS3, HTML5'
}
]
},
{
link : 'http://onlinelibrary.wiley.com',
title : 'John Wiley and Sons',
classname : 'wiley',
roles : [
{
title : 'Front End Developer',
skills : 'Backbone, CSS3, HTML5'
}
]
},
{
link : 'http://www.covestor.com',
title : 'Covestor',
classname : 'covestor',
roles : [
{
title : 'Front End Developer',
skills : 'OO Javascript, CSS3, HTML5'
}
]
},
{
link : 'http://www.shipserv.com',
title : 'ShipServ',
classname : 'shipserv',
roles : [
{
title : 'Front End Developer',
skills : 'OO Javascript, CSS3, HTML5'
}
]
}
]
}
================================================
FILE: src/common/api/promiseMiddleware.js
================================================
export default function promiseMiddleware() {
return next => action => {
const { promise, type, ...rest } = action;
if (!promise) return next(action);
const SUCCESS = type + '_SUCCESS';
const REQUEST = type + '_REQUEST';
const FAILURE = type + '_FAILURE';
next({ ...rest, type: REQUEST });
return promise
.then(req => {
next({ ...rest, req, type: SUCCESS });
return true;
})
.catch(error => {
next({ ...rest, error, type: FAILURE });
console.log(error);
return false;
});
};
}
================================================
FILE: src/common/components/404.js
================================================
import React, { Component } from 'react';
import { Link } from 'react-router';
class Error404 extends Component {
render() {
return (
<div className="page">
<h1 className="page-title">404: Page not found</h1>
<p className="lead">Sorry, we've misplaced that URL or it's pointing to something that does not exist.</p>
<p><Link to="/home" className="sidebar-nav-item" activeClassName="active">> Head back home</Link></p>
</div>
);
}
}
export default Error404;
================================================
FILE: src/common/components/About.js
================================================
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Repos from './about/Repos';
import Loader from './layout/Loader';
import Banner from './layout/Banner';
class About extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchReposIfNeeded();
}
render () {
const { results, isFetching, lastUpdated, error } = this.props;
return (
<div>
<Banner />
<div className="about">
<h3>About Me</h3>
<p>I have been developing web sites for over 15 years. Over the years, I have adopted, mastered and moved on from many languages, frameworks and architectures. I currently focus on Javascript heavy web application development.</p>
<p>I created my first start-up in 2004 which raised £30K pounds investment which sold custom built software to companies like News International, NHS and Guardian.</p>
<p>In 2007, I was the CTO of <a href="https://en.wikipedia.org/wiki/MyFootballClub" target="_blank">My Football Club</a>. MyFC crowdsourced over £1.5 Million to buy a British football club. In the following year, we got to Wembley and won the FA Trophy.</p>
<p>Over the years, I have developed web apps for start-ups (<a href="http://www.covestor.com" target="_blank">Covestor</a>, <a href="https://www.isubscribe.co.uk" target="_blank">iSubscribe</a>, <a href="http://www.shipserv.com/" target="_blank">Shipserv</a>), for multi-national bluechip companies (<a href="http://www.goldmansachs.com" target="_blank">Goldman Sachs</a>, <a href="http://www.tesco.com" target="_blank">Tesco</a>, <a href="http://www.timeout.com" target="_blank">Timeout</a>, <a href="http://www.wiley.com" target="_blank">John Wiley and Sons</a>) and created my own companies (<a href="http://www.pwul.net" target="_blank">PWUL</a>, <a href="http://www.muffleit.com/" target="_blank">Muffle</a>, <a href="http://pencil.training" target="_blank">Pencil Training</a>)</p>
<h3>About Site</h3>
<p>This site is a single page web app built with React and Redux. It is Isomorphic (all the code renders on the server and well as the browser) which has the advantage of initially rendering quicker and being indexed by search engines.</p>
<p>I built the site as a simple example of what can be built with React and Redux. You can get the code base forfrom <a href="https://github.com/caljrimmer/portfolio-redux-app">my github repo</a>.</p>
<p>Feel free to use this sites code for whatever you want. I hope it inspires you to build something awesome or learn something new</p>
</div>
<div className="repos">
<h3><a href="https://github.com/caljrimmer?tab=repositories" target="_blank">My GitHub</a> Repos</h3>
{isFetching && results.length === 0 &&
<Loader />
}
{!isFetching && error && results.length === 0 &&
<h3 className="post-error">There has been an Error</h3>
}
{!isFetching && !error && results.length === 0 &&
<h3>Empty</h3>
}
{results.length > 0 &&
<div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Repos results={results} />
</div>
}
</div>
</div>
);
}
}
About.propTypes = {
results: PropTypes.array.isRequired,
error: PropTypes.object.isRequired,
isFetching: PropTypes.bool.isRequired
};
export default About;
================================================
FILE: src/common/components/Home.js
================================================
import React, { Component } from 'react';
import Banner from './layout/Banner';
class Home extends Component {
constructor(props){
super(props);
this.eventToggleSidebar = this.eventToggleSidebar.bind(this)
}
eventToggleSidebar(e) {
e.preventDefault();
this.props.toggleSidebar(!this.props.layout.sidebarOpen);
}
render() {
return (
<div className="posts">
<div className="post banner">
<h1 className="post-title">I build <em>scalable</em>, <em>maintainable</em> and <em>secure</em> enterprise web applications.</h1>
<p>for <em>agencies</em>, <em>bluechips</em>, <em>start-ups</em> and sometimes, <em>myself</em>. <a href="#" onClick={this.eventToggleSidebar}> Find out More</a></p>
</div>
<div className="post clearfix">
<h2>Technologies I build with:</h2>
<div className="skill-item clearfix">
<h4>Client Side JS</h4>
<ul className="">
<li><em>Backbone</em></li>
<li><em><b>*</b><b>*</b>React</em>
<ul>
<li><em><b>*</b>Redux</em></li>
<li><em>Flux</em></li>
</ul>
</li>
<li><em>D3</em></li>
</ul>
</div>
<div className="skill-item clearfix">
<h4>Server Side JS</h4>
<ul>
<li><em>MongoDB</em></li>
<li><em><b>*</b>Node</em>
<ul>
<li><em><b>*</b>Express</em></li>
<li><em>Hapi</em></li>
</ul>
</li>
</ul>
</div>
<div className="skill-item clearfix">
<h4>Testing</h4>
<ul>
<li><em>Jasmine</em></li>
<li><em>Karma</em></li>
<li><em>Protractor</em></li>
<li><em>Jest</em></li>
</ul>
</div>
<div className="skill-item clearfix">
<h4>Deployment</h4>
<ul className="clearfix">
<li><em><b>*</b>Nginx</em></li>
<li><em><b>*</b>Webpack</em></li>
<li><em>Gulp</em></li>
<li><em>Grunt</em></li>
</ul>
</div>
<div className="exclaimation">
<em><b>*</b> This site is built with these technologies. View the <a href="https://github.com/caljrimmer/portfolio-redux-app">github repo here</a>.</em><br/>
<em><b>**</b> Yep, I know React can be used on the server side too. This site is Isomorphical rendered.</em>
</div>
</div>
<div className="post clearfix">
<h2>Companies I have worked with:</h2>
<ul className="clients">
<li className="goldmans-logo">Goldman Sachs</li>
<li className="orange-logo">Orange</li>
<li className="tesco-logo">Tesco</li>
<li className="timeout-logo">Timeout</li>
<li className="wiley-logo">John Wiley and Sons</li>
<li className="covestor-logo">Covestor</li>
<li className="shipserv-logo">Shipserv</li>
</ul>
</div>
<Banner />
</div>
);
}
}
export default Home;
================================================
FILE: src/common/components/Portfolio.js
================================================
import React, { Component } from 'react';
import { getPortfolio } from '../api/portfolio';
import classNames from 'classnames';
class Portfolio extends Component {
render() {
const portfolio = getPortfolio();
const RoleRows = (roles) => {
return roles.map((role) => {
return (
<div key={role.title} className="role_wrapper clearfix">
<p className="role">Role</p>
<p className="role_title">{role.title}<br />
<span className="role_skills">{role.skills}</span></p>
</div>
)
});
}
const PortfolioRows = portfolio.map((row) => {
const classname = classNames('portfolio_item','clearfix',row.classname);
return (
<div key={row.title} className={classname}>
<h2><a href={row.link} target="_blank">(visit site)</a> {row.title}</h2>
{RoleRows(row.roles)}
</div>
)
});
return (
<div className="posts">
{PortfolioRows}
</div>
);
}
}
export default Portfolio;
================================================
FILE: src/common/components/Services.js
================================================
import React, { Component, PropTypes } from 'react';
import Banner from './layout/Banner';
class Services extends Component {
render () {
return (
<div>
<Banner />
<div className="services">
<h3>Prototypes/MVPs</h3>
<p>I can build you a prototype or a minimal viable product (MVP)for your idea. An MVP can validate your idea and help you raise funding.
</p>
<p>The code I deliver will be the best possible foundations from which your application and idea can grow.</p>
<h3>Training</h3>
<p>I can help you or your employees develop their web development coding skills. I have been teaching code to both beginners and professionals for many years.</p>
<p>I have my own training company called <a href="http://pencil.training" target="_blank">Pencil Training</a>. More details can be found <a href="http://pencil.training" target="_blank">here</a>.</p>
</div>
<div className="services">
<h3>Enterprise Web Apps</h3>
<p>Whether you are a Start-up or Bluechip company, I can help you build you a production ready web application.
</p>
<p>The web application will be built with the best technologies and will be scalable, secure and maintainable.</p>
<h3>Hybridised Development</h3>
<p>Do you need a mobile app and web app with the same code base? I can code your web application so that it utilises Phonegap/Cordova to build both a web application and a cross-device mobile application.</p>
</div>
</div>
);
}
}
export default Services;
================================================
FILE: src/common/components/about/Repos.js
================================================
import React, { PropTypes, Component } from 'react';
export default class Repos extends Component {
render () {
return (
<div>
{this.props.results.map((repo, i) =>
<div className="repo-item" key={i}>
<a href={repo.html_url}>{repo.name}</a><br/>
{repo.description}
</div>
)}
</div>
);
}
}
Repos.propTypes = {
results: PropTypes.array.isRequired
};
================================================
FILE: src/common/components/layout/Banner.js
================================================
import React, { Component } from 'react';
class Banner extends Component {
render() {
return (
<div className="contact-banner">
<p>Contact me at <em>callum(at)deadtrendy.co.uk</em> or call <em>07919 411 405</em></p>
</div>
);
}
}
export default Banner;
================================================
FILE: src/common/components/layout/Header.js
================================================
import React, { Component } from 'react';
class Header extends Component {
render() {
return (
<div className="masthead">
<div className="container">
<h3 className="masthead-title">
<a href="/" title="Home">Callum Rimmer</a>
<small>Full Stack Web Developer based in London</small>
</h3>
</div>
</div>
);
}
}
export default Header;
================================================
FILE: src/common/components/layout/Loader.js
================================================
import React, { Component } from 'react';
class Loader extends Component {
render() {
return (
<section className="loader">
<div className="ldr">
<div className="ldr-blk"></div>
<div className="ldr-blk an_delay"></div>
<div className="ldr-blk an_delay"></div>
<div className="ldr-blk"></div>
</div>
</section>
);
}
}
export default Loader;
================================================
FILE: src/common/components/layout/Sidebar.js
================================================
import React, { Component } from 'react';
import { Link } from 'react-router';
import classNames from 'classnames';
class Sidebar extends Component {
constructor(props){
super(props);
this.eventCloseSidebar = this.eventCloseSidebar.bind(this)
}
eventCloseSidebar (e) {
this.props.toggleSidebar(!this.props.layout.sidebarOpen);
}
render() {
return (
<div className="sidebar">
<div className="sidebar-item sidebar-footer">
<p>I built this site with Redux and React. You can get the <a href="https://github.com/caljrimmer/portfolio-redux-app">source code here</a></p>
</div>
<nav className="sidebar-nav">
<Link to="/home" className="sidebar-nav-item" onClick={this.eventCloseSidebar} activeClassName="active">Home</Link>
<Link to="/portfolio" className="sidebar-nav-item" onClick={this.eventCloseSidebar} activeClassName="active">My Portfolio</Link>
<Link to="/services" className="sidebar-nav-item" onClick={this.eventCloseSidebar} activeClassName="active">My Services</Link>
<Link to="/about" className="sidebar-nav-item" onClick={this.eventCloseSidebar} activeClassName="active">About</Link>
</nav>
<div className="sidebar-item sidebar-footer">
<p>
Visit <a href="https://github.com/caljrimmer">My GitHub Repo</a><br/>
Visit <a href="https://www.linkedin.com/in/callumrimmer">My Linkedin</a><br/>
Visit <a href="https://twitter.com/caljrimmer">My Twitter</a><br/>
</p>
<p>
Design based on <a href="http://lanyon.getpoole.com/"> Lanyon Theme</a>
</p>
</div>
</div>
);
}
}
export default Sidebar;
================================================
FILE: src/common/containers/AboutPage.js
================================================
import { bindActionCreators } from 'redux';
import React, { Component} from 'react';
import { connect } from 'react-redux';
import About from '../components/About';
import * as AboutActions from '../actions/about';
//Data that needs to be called before rendering the component
//This is used for server side rending via the fetchComponentDataBeforeRending() method
About.need = [
AboutActions.fetchRepos
]
function mapStateToProps(state) {
const {
isFetching,
lastUpdated,
error,
results
} = state.repos || {
isFetching: true,
error:false,
results: []
};
return {
isFetching,
lastUpdated,
error,
results
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(AboutActions, dispatch);
}
export default connect(mapStateToProps,mapDispatchToProps)(About);
================================================
FILE: src/common/containers/App.js
================================================
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import classNames from 'classnames';
import * as LayoutActions from '../actions/layout';
import Home from '../components/Home'
import Header from '../components/layout/Header'
import Sidebar from '../components/layout/Sidebar'
class App extends Component {
constructor(props){
super(props);
this.eventToggleSidebar = this.eventToggleSidebar.bind(this)
}
eventToggleSidebar(e) {
e.preventDefault();
this.props.toggleSidebar(!this.props.layout.sidebarOpen);
}
render() {
const { layout, toggleSidebar } = this.props;
const { sidebarOpen } = layout;
const layoutClass = classNames({open : sidebarOpen});
return (
<div className={layoutClass}>
<Sidebar layout={layout} toggleSidebar={toggleSidebar} />
<div className="wrap">
<Header />
<div className="container content">
{!this.props.children && <Home layout={layout} toggleSidebar={toggleSidebar} />}
{this.props.children}
</div>
</div>
<label className="sidebar-toggle" onClick={this.eventToggleSidebar}></label>
</div>
);
}
}
function mapStateToProps(state) {
return {
layout : state.layout
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(LayoutActions,dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
================================================
FILE: src/common/containers/HomePage.js
================================================
import { bindActionCreators } from 'redux';
import React, { Component} from 'react';
import { connect } from 'react-redux';
import Home from '../components/Home';
import * as LayoutActions from '../actions/layout';
function mapStateToProps(state) {
return {
layout : state.layout
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(LayoutActions, dispatch);
}
export default connect(mapStateToProps,mapDispatchToProps)(Home);
================================================
FILE: src/common/reducers/about.js
================================================
import {
INVALIDATE_REPOS, REPOS_GET_REQUEST, REPOS_GET_SUCCESS, REPOS_GET_FAILURE
} from '../actions/about';
export function reposByUser(state = {
error: {},
isFetching: false,
didInvalidate: false,
results: []
}, action) {
switch (action.type) {
case INVALIDATE_REPOS:
return Object.assign({}, state, {
didInvalidate: true
});
case REPOS_GET_REQUEST:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false,
});
case REPOS_GET_SUCCESS:
let data = [];
if(action.req && action.req.data){
data = action.req.data.sort((a,b) => {
return new Date(b.pushed_at) - new Date(a.pushed_at);
});
}
return Object.assign({}, state, {
isFetching: false,
results: data,
lastUpdated: new Date()
});
case REPOS_GET_FAILURE:
return Object.assign({}, state, {
isFetching: false,
error : {
status: action.error.status,
statusText : action.error.statusText
}
});
default:
return state;
}
}
================================================
FILE: src/common/reducers/index.js
================================================
import { combineReducers } from 'redux';
import { routerStateReducer } from 'redux-router';
import layout from './layout';
import { reposByUser } from './about';
const rootReducer = combineReducers({
layout : layout,
repos : reposByUser,
router : routerStateReducer
});
export default rootReducer;
================================================
FILE: src/common/reducers/layout.js
================================================
import { TOGGLE_SIDEBAR } from '../actions/layout';
export default function layout(state = {sidebarOpen: false}, action) {
switch (action.type) {
case TOGGLE_SIDEBAR:
return {
sidebarOpen : action.value
};
default:
return state;
}
}
================================================
FILE: src/common/routes.js
================================================
import { Route } from "react-router";
import React from "react";
import App from "./containers/App";
//Redux Smart
import AboutPage from "./containers/AboutPage";
import HomePage from "./containers/HomePage";
//Redux Dumb
import PortfolioPage from "./components/Portfolio";
import ServicesPage from "./components/Services";
import error404 from "./components/404";
export default (
<Route name="app" path="/" component={App}>
<Route path="home" component={HomePage} />
<Route path="portfolio" component={PortfolioPage} />
<Route path="services" component={ServicesPage} />
<Route path="about" component={AboutPage} />
<Route path="*" component={error404}/>
</Route>
);
================================================
FILE: src/common/store/configureStore.js
================================================
import { createStore, applyMiddleware, compose } from 'redux';
import { devTools } from 'redux-devtools';
import { reduxReactRouter } from 'redux-router';
import thunk from 'redux-thunk';
import createHistory from 'history/lib/createBrowserHistory';
import createLogger from 'redux-logger';
import promiseMiddleware from '../api/promiseMiddleware';
import rootReducer from '../reducers';
const middlewareBuilder = () => {
let middleware = {};
let universalMiddleware = [thunk,promiseMiddleware];
let allComposeElements = [];
if(process.browser){
if(process.env.NODE_ENV === 'production'){
middleware = applyMiddleware(...universalMiddleware);
allComposeElements = [
middleware,
reduxReactRouter({
createHistory
})
]
}else{
middleware = applyMiddleware(...universalMiddleware,createLogger());
allComposeElements = [
middleware,
reduxReactRouter({
createHistory
}),
devTools()
]
}
}else{
middleware = applyMiddleware(...universalMiddleware);
allComposeElements = [
middleware
]
}
return allComposeElements;
}
const finalCreateStore = compose(...middlewareBuilder())(createStore);
export default function configureStore(initialState) {
const store = finalCreateStore(rootReducer, initialState);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
================================================
FILE: src/server/devtools.js
================================================
import React from 'react';
import { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';
/*
* Puts Redux DevTools into a separate window.
* Based on https://gist.github.com/tlrobinson/1e63d15d3e5f33410ef7#gistcomment-1560218.
*/
export default function createDevToolsWindow(store) {
// Give it a name so it reuses the same window
const name = 'Redux DevTools';
const win = window.open(
null,
name,
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no,width=450,height=600'
);
if (!win) {
console.error( // eslint-disable-line no-console
'Couldn\'t open Redux DevTools due to a popup blocker. ' +
'Please disable the popup blocker for the current page.'
);
return;
}
// Reload in case it's reusing the same window with the old content.
win.location.reload();
// Set visible Window title.
win.document.title = name;
// Wait a little bit for it to reload, then render.
setTimeout(() => React.render(
<DebugPanel top right bottom left>
<DevTools store={store} monitor={LogMonitor} />
</DebugPanel>,
win.document.body.appendChild(document.createElement('div'))
), 10);
}
================================================
FILE: src/server/index.js
================================================
require('babel/register');
require('./server');
================================================
FILE: src/server/server.js
================================================
import express from 'express';
import webpack from 'webpack';
import webpackConfig from '../../webpack.config';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import React from 'react';
import { RoutingContext, match } from 'react-router';
import { Provider } from 'react-redux';
import createLocation from 'history/lib/createLocation';
import { fetchComponentDataBeforeRender } from '../common/api/fetchComponentDataBeforeRender';
import configureStore from '../common/store/configureStore';
import routes from '../common/routes';
import packagejson from '../../package.json';
const app = express();
const renderFullPage = (html, initialState) => {
return `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Full Stack Web Developer based in London</title>
<link rel="stylesheet" type="text/css" href="/static/app.css">
</head>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
</script>
<script src="/static/bundle.js"></script>
</body>
</html>
`;
}
if(process.env.NODE_ENV !== 'production'){
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
}else{
app.use('/static', express.static(__dirname + '/../../dist'));
}
app.get('/*', function (req, res) {
const location = createLocation(req.url);
match({ routes, location }, (err, redirectLocation, renderProps) => {
if(err) {
console.error(err);
return res.status(500).end('Internal server error');
}
if(!renderProps)
return res.status(404).end('Not found');
const store = configureStore();
const InitialView = (
<Provider store={store}>
{() =>
<RoutingContext {...renderProps} />
}
</Provider>
);
//This method waits for all render component promises to resolve before returning to browser
fetchComponentDataBeforeRender(store.dispatch, renderProps.components, renderProps.params)
.then(html => {
const componentHTML = React.renderToString(InitialView);
const initialState = store.getState();
res.status(200).end(renderFullPage(componentHTML,initialState))
})
.catch(err => {
console.log(err)
res.end(renderFullPage("",{}))
});
});
});
const server = app.listen(process.env.PORT || 3002, function () {
const host = server.address().address;
const port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
================================================
FILE: src/server/webpack.js
================================================
// Webpack dev server
// Ran in parallel with the Express server
import WebpackDevServer from "webpack-dev-server";
import webpack from "webpack";
import config from "../../webpack.config.dev";
var server = new WebpackDevServer(webpack(config), {
// webpack-dev-server options
publicPath: config.output.publicPath,
hot: true,
stats: { colors: true },
});
server.listen(8080, "localhost", function() {});
================================================
FILE: styles/about.css
================================================
.repos {
width:46%;
float:right;
}
.about,
.services {
width:50%;
float:left;
font-size:16px;}
.repos h3 {
margin-bottom:20px;
}
.repo-item {
background:#fff;
padding:10px;
box-shadow:1px 1px 1px #eaeaea;
border:1px solid #eaeaea;
margin-bottom:10px;
font-size:16px;
}
@media (max-width: 40em) {
.about,
.repos {
width:100%;
}
}
================================================
FILE: styles/base.css
================================================
/*
* Contents
*
* Body resets
* Custom type
* Messages
* Container
* Masthead
* Posts and pages
* Pagination
* Reverse layout
* Themes
*/
/*
* Body resets
*
* Update the foundational and global aspects of the page.
*/
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
}
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
}
@media (min-width: 38em) {
html {
font-size: 20px;
}
}
body {
color: #515151;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
/* No `:visited` state is required by default (browsers will use `a`) */
a {
color: #1db7a9;
text-decoration: none;
}
a strong {
color: inherit;
}
/* `:focus` is linked to `:hover` for basic accessibility */
a:hover,
a:focus {
text-decoration: underline;
}
/* Headings */
h1, h2, h3, h4, h5, h6 {
margin-bottom: .5rem;
font-weight: bold;
line-height: 1.25;
color: #313131;
text-rendering: optimizeLegibility;
}
h1 {
font-size: 2.6rem;
line-height: 3.6rem;
}
h2 {
margin-top: 1rem;
font-size: 1.5rem;
}
h3 {
margin-top: 1.5rem;
font-size: 1.25rem;
}
h4, h5, h6 {
margin-top: 1rem;
font-size: 1rem;
}
/* Body text */
p {
margin-top: 0;
margin-bottom: 1rem;
}
p em {
font-weight:bold;
color:#000;
font-size:1.2em;
}
strong {
color: #303030;
}
/* Lists */
ul, ol, dl {
margin-top: 0;
margin-bottom: 1rem;
}
dt {
font-weight: bold;
}
dd {
margin-bottom: .5rem;
}
/* Misc */
hr {
position: relative;
margin: 1.5rem 0;
border: 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #fff;
}
abbr {
font-size: 85%;
font-weight: bold;
color: #555;
text-transform: uppercase;
}
abbr[title] {
cursor: help;
border-bottom: 1px dotted #e5e5e5;
}
/* Code */
code,
pre {
font-family: Menlo, Monaco, "Courier New", monospace;
}
code {
padding: .25em .5em;
font-size: 85%;
color: #bf616a;
background-color: #f9f9f9;
border-radius: 3px;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
padding: 1rem;
font-size: .8rem;
line-height: 1.4;
white-space: pre;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
background-color: #f9f9f9;
}
pre code {
padding: 0;
font-size: 100%;
color: inherit;
background-color: transparent;
}
/* Pygments via Jekyll */
.highlight {
margin-bottom: 1rem;
border-radius: 4px;
}
.highlight pre {
margin-bottom: 0;
}
/* Gist via GitHub Pages */
.gist .gist-file {
font-family: Menlo, Monaco, "Courier New", monospace !important;
}
.gist .markdown-body {
padding: 15px;
}
.gist pre {
padding: 0;
background-color: transparent;
}
.gist .gist-file .gist-data {
font-size: .8rem !important;
line-height: 1.4;
}
.gist code {
padding: 0;
color: inherit;
background-color: transparent;
border-radius: 0;
}
/* Quotes */
blockquote {
padding: .5rem 1rem;
margin: .8rem 0;
color: #7a7a7a;
border-left: .25rem solid #e5e5e5;
}
blockquote p:last-child {
margin-bottom: 0;
}
@media (min-width: 30em) {
blockquote {
padding-right: 5rem;
padding-left: 1.25rem;
}
}
img {
display: block;
max-width: 100%;
margin: 0 0 1rem;
border-radius: 5px;
}
/* Tables */
table {
margin-bottom: 1rem;
width: 100%;
border: 1px solid #e5e5e5;
border-collapse: collapse;
}
td,
th {
padding: .25rem .5rem;
border: 1px solid #e5e5e5;
}
tbody tr:nth-child(odd) td,
tbody tr:nth-child(odd) th {
background-color: #f9f9f9;
}
/*
* Custom type
*
* Extend paragraphs with `.lead` for larger introductory text.
*/
.lead {
font-size: 1.25rem;
font-weight: 300;
}
/*
* Messages
*
* Show alert messages to users. You may add it to single elements like a `<p>`,
* or to a parent if there are multiple elements to show.
*/
.message {
margin-bottom: 1rem;
padding: 1rem;
color: #717171;
background-color: #f9f9f9;
}
/*
* Container
*
* Center the page content.
*/
.container {
max-width: 38rem;
padding-left: 1rem;
padding-right: 1rem;
margin-left: auto;
margin-right: auto;
}
/*
* Masthead
*
* Super small header above the content for site name and short description.
*/
.masthead {
padding-top: 1rem;
padding-bottom: 1rem;
margin-bottom: 3rem;
}
.masthead-title {
margin-top: 0;
margin-bottom: 0;
color: #505050;
}
.masthead-title a {
color: #505050;
}
.masthead-title small {
font-size: 75%;
font-weight: 400;
color: #c0c0c0;
letter-spacing: 0;
}
/*
* Posts and pages
*
* Each post is wrapped in `.post` and is used on default and post layouts. Each
* page is wrapped in `.page` and is only used on the page layout.
*/
.page,
.post {
margin-bottom: 4em;
}
/* Blog post or page title */
.page-title,
.post-title,
.post-title a {
color: #303030;
}
.page-title,
.post-title {
margin-top: 0;
}
/* Meta data line below post title */
.post-date {
display: block;
margin-top: -.5rem;
margin-bottom: 1rem;
color: #9a9a9a;
}
/* Related posts */
.related {
padding-top: 2rem;
padding-bottom: 2rem;
border-top: 1px solid #eee;
}
.related-posts {
padding-left: 0;
list-style: none;
}
.related-posts h3 {
margin-top: 0;
}
.related-posts li small {
font-size: 75%;
color: #999;
}
.related-posts li a:hover {
color: #268bd2;
text-decoration: none;
}
.related-posts li a:hover small {
color: inherit;
}
/*
* Pagination
*
* Super lightweight (HTML-wise) blog pagination. `span`s are provide for when
* there are no more previous or next posts to show.
*/
.pagination {
overflow: hidden; /* clearfix */
margin-left: -1rem;
margin-right: -1rem;
font-family: "PT Sans", Helvetica, Arial, sans-serif;
color: #ccc;
text-align: center;
}
/* Pagination items can be `span`s or `a`s */
.pagination-item {
display: block;
padding: 1rem;
border: 1px solid #eee;
}
.pagination-item:first-child {
margin-bottom: -1px;
}
/* Only provide a hover state for linked pagination items */
a.pagination-item:hover {
background-color: #f5f5f5;
}
@media (min-width: 30em) {
.pagination {
margin: 3rem 0;
}
.pagination-item {
float: left;
width: 50%;
}
.pagination-item:first-child {
margin-bottom: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.pagination-item:last-child {
margin-left: -1px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.clearfix { display: inline-block; }
/* start commented backslash hack \*/
* html .clearfix { height: 1%; }
.clearfix { display: block; }
/* close commented backslash hack */
================================================
FILE: styles/custom.css
================================================
.sidebar-item p b {
color:#fff;
}
/**
* Loader
*/
@keyframes pulse {
0% { opacity: 1; }
100% { opacity: 0; }
}
body {
margin: 0;
}
.loader {
display: flex;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.ldr {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
margin: 50px auto 50px;
width: 2.5em;
height: 2.5em;
}
.ldr-blk {
height: 35%;
width: 35%;
animation: pulse 0.75s ease-in infinite alternate;
background-color: #1db7a9;
}
.an_delay {
animation-delay: 0.75s;
}
/**
* Clients on Home Page
*/
ul.clients {
margin:0;
padding:0;
}
ul.clients li {
list-style:none;
float:left;
margin:1em 1.6em 1em 0em;
height:70px;
text-indent:-5000px;
box-shadow:1px 1px 1px #ccc;
}
ul.clients li.goldmans-logo {
background: url('assets/goldmans-logo.jpg') no-repeat;
width:124px;
}
ul.clients li.orange-logo {
background: url('assets/orange-logo.jpg') no-repeat;
width:70px;
}
ul.clients li.tesco-logo {
background: url('assets/tesco-logo.jpg') no-repeat;
width:181px;
}
ul.clients li.timeout-logo {
background: url('assets/timeout-logo.jpg') no-repeat;
width:151px;
}
ul.clients li.wiley-logo {
background: url('assets/wiley-logo.jpg') no-repeat;
width:169px;
}
ul.clients li.covestor-logo {
background: url('assets/covestor-logo.jpg') no-repeat;
width:218px;
}
ul.clients li.shipserv-logo {
background: url('assets/shipserv-logo.jpg') no-repeat;
width:218px;
}
/**
* Skill Boxes on Home Page
*/
.skill-item {
float:left;
margin: 0 3.5em 2em 0;
font-size:0.8em;
}
.skill-item h4 {
font-size:20px;
font-weight:bold;
}
.posts em b {
font-weight:bold;
margin-right:5px;
margin-left:-5px;
text-shadow:1px 1px 1px #666;
}
.skill-item ul{
list-style:none;
padding-left:0;
font-size:16px;
}
.skill-item ul li {
display:inline-block;
float:left;
clear:both;
}
.skill-item ul ul {
padding-left:2em;
}
.skill-item ul li em {
background: #1db7a9;
display:inline-block;
float:left;
color:#fff;
padding:0.2em 1em 0.2em;
margin:0.2em;
clear:both;
font-style:normal;
box-shadow:1px 1px 1px #ccc;
}
.skill-item ul ul li em {
background: #d9ead3;
color: #1db7a9;
}
.exclaimation {
clear:both;
font-size:14px;
}
/**
* Banner
*/
.contact-banner {
background:#1db7a9;
color:#d9ead3;
text-align:center;
}
.contact-banner p {
padding:20px;
}
.contact-banner p em {
color:#fff;
font-weight:bold;
font-style:normal;
}
================================================
FILE: styles/index.css
================================================
@import 'https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700%7CPT+Sans:400';
@import 'base.css';
@import 'portfolio.css';
@import 'about.css';
@import 'services.css';
@import 'layout.css';
@import 'custom.css';
================================================
FILE: styles/layout.css
================================================
/*
* Contents
*
* Global resets
* Masthead
* Sidebar
* Slide effect
* Posts and pages
* Pagination
* Reverse layout
* Themes
*/
/*
* Global resets
*
* Update the foundational and global aspects of the page.
*/
/* Prevent scroll on narrow devices */
html,
body {
overflow-x: hidden;
}
html {
font-family: "PT Sans",Helvetica,Arial,sans-serif;
background:#fafafa;
}
h1, h2, h3, h4, h5, h6 {
font-family: "PT Sans", Helvetica, Arial, sans-serif;
font-weight: 400;
color: #313131;
letter-spacing: -.025rem;
}
/*
* Wrapper
*
* The wrapper is used to position site content when the sidebar is toggled. We
* use an outter wrap to position the sidebar without interferring with the
* regular page content.
*/
.wrap {
position: relative;
width: 100%;
}
/*
* Container
*
* Center the page content.
*/
.container {
max-width: 28rem;
}
@media (min-width: 38em) {
.container {
max-width: 32rem;
}
}
@media (min-width: 56em) {
.container {
max-width: 38rem;
}
}
/*
* Masthead
*
* Super small header above the content for site name and short description.
*/
.masthead {
padding-top: 1rem;
padding-bottom: 1rem;
margin-bottom: 3rem;
border-bottom: 1px solid #eee;
background:#fff;
}
.masthead-title {
margin-top: 0;
margin-bottom: 0;
color: #505050;
}
.masthead-title a {
color: #505050;
font-weight:bold;
}
.masthead-title small {
font-size: 75%;
font-weight: 400;
color: #c0c0c0;
letter-spacing: 0;
margin-left:0.6em;
}
@media (max-width: 48em) {
.masthead-title {
text-align: center;
}
.masthead-title small {
display: none;
}
}
/*
* Sidebar
*
* The sidebar is the drawer, the item we are toggling with our handy hamburger
* button in the corner of the page.
*
* This particular sidebar implementation was inspired by Chris Coyier's
* "Offcanvas Menu with CSS Target" article, and the checkbox variation from the
* comments by a reader. It modifies both implementations to continue using the
* checkbox (no change in URL means no polluted browser history), but this uses
* `position` for the menu to avoid some potential content reflow issues.
*
* Source: http://css-tricks.com/off-canvas-menu-with-css-target/#comment-207504
*/
/* Style and "hide" the sidebar */
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: -14rem;
width: 14rem;
visibility: hidden;
overflow-y: auto;
font-family: "PT Sans", Helvetica, Arial, sans-serif;
font-size: .875rem; /* 15px */
color: rgba(255,255,255,.6);
background-color: #202020;
-webkit-transition: all .3s ease-in-out;
transition: all .3s ease-in-out;
}
@media (min-width: 30em) {
.sidebar {
font-size: .75rem; /* 14px */
}
}
/* Sidebar content */
.sidebar a {
font-weight: normal;
color: #fff;
}
.sidebar-item {
padding: 1rem;
}
.sidebar-item p:last-child {
margin-bottom: 0;
}
/* Sidebar nav */
.sidebar-nav {
border-bottom: 1px solid rgba(255,255,255,.1);
}
.sidebar-nav-item {
display: block;
padding: .5rem 1rem;
border-top: 1px solid rgba(255,255,255,.1);
}
.sidebar-nav-item.active,
a.sidebar-nav-item:hover,
a.sidebar-nav-item:focus {
text-decoration: none;
background-color: rgba(255,255,255,.1);
border-color: transparent;
}
.sidebar-nav-item span.nav-note {
text-transform:uppercase;
font-size:0.6em;
opacity:0.5;
}
@media (min-width: 48em) {
.sidebar-item {
padding: 1.5rem;
}
.sidebar-nav-item {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
}
/* Style the `label` that we use to target the `.sidebar-checkbox` */
.sidebar-toggle {
position: absolute;
top: .8rem;
left: 1rem;
display: block;
padding: .25rem .75rem;
color: #505050;
background-color: #fff;
border-radius: .25rem;
cursor: pointer;
}
.sidebar-toggle:before {
display: inline-block;
width: 1rem;
height: .75rem;
content: "";
background-image: -webkit-linear-gradient(to bottom, #555, #555 20%, #fff 20%, #fff 40%, #555 40%, #555 60%, #fff 60%, #fff 80%, #555 80%, #555 100%);
background-image: -moz-linear-gradient(to bottom, #555, #555 20%, #fff 20%, #fff 40%, #555 40%, #555 60%, #fff 60%, #fff 80%, #555 80%, #555 100%);
background-image: -ms-linear-gradient(to bottom, #555, #555 20%, #fff 20%, #fff 40%, #555 40%, #555 60%, #fff 60%, #fff 80%, #555 80%, #555 100%);
background-image: linear-gradient(to bottom, #555, #555 20%, #fff 20%, #fff 40%, #555 40%, #555 60%, #fff 60%, #fff 80%, #555 80%, #555 100%);
}
.open .sidebar-toggle:active,
.open .sidebar-toggle,
.open .sidebar-toggle {
color: #fff;
background-color: #555;
}
.open .sidebar-toggle:active:before,
.open .sidebar-toggle:before,
.open .sidebar-toggle:before {
background-image: -webkit-linear-gradient(to bottom, #fff, #fff 20%, #555 20%, #555 40%, #fff 40%, #fff 60%, #555 60%, #555 80%, #fff 80%, #fff 100%);
background-image: -moz-linear-gradient(to bottom, #fff, #fff 20%, #555 20%, #555 40%, #fff 40%, #fff 60%, #555 60%, #555 80%, #fff 80%, #fff 100%);
background-image: -ms-linear-gradient(to bottom, #fff, #fff 20%, #555 20%, #555 40%, #fff 40%, #fff 60%, #555 60%, #555 80%, #fff 80%, #fff 100%);
background-image: linear-gradient(to bottom, #fff, #fff 20%, #555 20%, #555 40%, #fff 40%, #fff 60%, #555 60%, #555 80%, #fff 80%, #fff 100%);
}
@media (min-width: 30.1em) {
.sidebar-toggle {
position: fixed;
}
}
@media print {
.sidebar-toggle {
display: none;
}
}
/* Slide effect
*
* Handle the sliding effects of the sidebar and content in one spot, seperate
* from the default styles.
*
* As an a heads up, we don't use `transform: translate3d()` here because when
* mixed with `position: fixed;` for the sidebar toggle, it creates a new
* containing block. Put simply, the fixed sidebar toggle behaves like
* `position: absolute;` when transformed.
*
* Read more about it at http://meyerweb.com/eric/thoughts/2011/09/12/.
*/
.wrap,
.sidebar,
.sidebar-toggle {
-webkit-backface-visibility: hidden;
-ms-backface-visibility: hidden;
backface-visibility: hidden;
}
.wrap,
.sidebar-toggle {
-webkit-transition: -webkit-transform .3s ease-in-out;
transition: transform .3s ease-in-out;
}
.open .sidebar-toggle,
.open .sidebar,
.open .wrap{
z-index: 10;
visibility: visible;
-webkit-transform: translateX(14rem);
-ms-transform: translateX(14rem);
transform: translateX(14rem);
}
.sidebar-footer a{
color: #1db7a9;
opacity: 0.8;
border-bottom:1px dotted #1db7a9;
margin-right:1em;
}
.sidebar-footer a:hover {
opacity:1;
text-decoration:none;
}
/*
* Posts and pages
*
* Each post is wrapped in `.post` and is used on default and post layouts. Each
* page is wrapped in `.page` and is only used on the page layout.
*/
.page,
.post {
margin-bottom: 4em;
}
.banner {
margin-top:4em;
margin-bottom: 6em;
}
.banner a {
border-bottom: 1px dotted #1db7a9;
text-decoration:none;
}
.banner a:hover,
.banner a:active
.banner a:visited {
border-bottom: 2px dotted #1db7a9;
}
/* Blog post or page title */
.page-title,
.post-title,
.post-title a {
color: #303030;
}
.page-title,
.post-title {
margin-top: 0;
}
.post-title em {
background:#d9ead3;
color:#1db7a9;
padding:0em 0.3em 0em;
margin:0.1em;
font-style:normal;
}
/* Meta data line below post title */
.post-date {
display: block;
margin-top: -.5rem;
margin-bottom: 1rem;
color: #9a9a9a;
}
/* Related posts */
.related {
padding-top: 2rem;
padding-bottom: 2rem;
border-top: 1px solid #eee;
}
.related-posts {
padding-left: 0;
list-style: none;
}
.related-posts h3 {
margin-top: 0;
}
.related-posts li small {
font-size: 75%;
color: #999;
}
.related-posts li a:hover {
color: #1db7a9;
text-decoration: none;
}
.related-posts li a:hover small {
color: inherit;
}
/*
* Pagination
*
* Super lightweight (HTML-wise) blog pagination. `span`s are provide for when
* there are no more previous or next posts to show.
*/
.pagination {
overflow: hidden; /* clearfix */
margin-left: -1rem;
margin-right: -1rem;
font-family: "PT Sans", Helvetica, Arial, sans-serif;
color: #ccc;
text-align: center;
}
/* Pagination items can be `span`s or `a`s */
.pagination-item {
display: block;
padding: 1rem;
border: 1px solid #eee;
}
.pagination-item:first-child {
margin-bottom: -1px;
}
/* Only provide a hover state for linked pagination items */
a.pagination-item:hover {
background-color: #f5f5f5;
}
@media (min-width: 30em) {
.pagination {
margin: 3rem 0;
}
.pagination-item {
float: left;
width: 50%;
}
.pagination-item:first-child {
margin-bottom: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.pagination-item:last-child {
margin-left: -1px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
}
/*
* Reverse layout
*
* Flip the orientation of the page by placing the `.sidebar` and sidebar toggle
* on the right side.
*/
.layout-reverse .sidebar {
left: auto;
right: -14rem;
}
.layout-reverse .sidebar-toggle {
left: auto;
right: 1rem;
}
.layout-reverse #sidebar-checkbox:checked ~ .sidebar,
.layout-reverse #sidebar-checkbox:checked ~ .wrap,
.layout-reverse #sidebar-checkbox:checked ~ .sidebar-toggle {
-webkit-transform: translateX(-14rem);
-ms-transform: translateX(-14rem);
transform: translateX(-14rem);
}
/*
* Themes
*
* Apply custom color schemes by adding the appropriate class to the `body`.
* Based on colors from Base16: http://chriskempson.github.io/base16/#default.
*/
/* Red */
.theme-base-08 .sidebar,
.theme-base-08 .sidebar-toggle:active,
.theme-base-08 #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #ac4142;
}
.theme-base-08 .container a,
.theme-base-08 .sidebar-toggle,
.theme-base-08 .related-posts li a:hover {
color: #ac4142;
}
/* Orange */
.theme-base-09 .sidebar,
.theme-base-09 .sidebar-toggle:active,
.theme-base-09 #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #d28445;
}
.theme-base-09 .container a,
.theme-base-09 .sidebar-toggle,
.theme-base-09 .related-posts li a:hover {
color: #d28445;
}
/* Yellow */
.theme-base-0a .sidebar,
.theme-base-0a .sidebar-toggle:active,
.theme-base-0a #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #f4bf75;
}
.theme-base-0a .container a,
.theme-base-0a .sidebar-toggle,
.theme-base-0a .related-posts li a:hover {
color: #f4bf75;
}
/* Green */
.theme-base-0b .sidebar,
.theme-base-0b .sidebar-toggle:active,
.theme-base-0b #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #90a959;
}
.theme-base-0b .container a,
.theme-base-0b .sidebar-toggle,
.theme-base-0b .related-posts li a:hover {
color: #90a959;
}
/* Cyan */
.theme-base-0c .sidebar,
.theme-base-0c .sidebar-toggle:active,
.theme-base-0c #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #75b5aa;
}
.theme-base-0c .container a,
.theme-base-0c .sidebar-toggle,
.theme-base-0c .related-posts li a:hover {
color: #75b5aa;
}
/* Blue */
.theme-base-0d .sidebar,
.theme-base-0d .sidebar-toggle:active,
.theme-base-0d #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #6a9fb5;
}
.theme-base-0d .container a,
.theme-base-0d .sidebar-toggle,
.theme-base-0d .related-posts li a:hover {
color: #6a9fb5;
}
/* Magenta */
.theme-base-0e .sidebar,
.theme-base-0e .sidebar-toggle:active,
.theme-base-0e #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #aa759f;
}
.theme-base-0e .container a,
.theme-base-0e .sidebar-toggle,
.theme-base-0e .related-posts li a:hover {
color: #aa759f;
}
/* Brown */
.theme-base-0f .sidebar,
.theme-base-0f .sidebar-toggle:active,
.theme-base-0f #sidebar-checkbox:checked ~ .sidebar-toggle {
background-color: #8f5536;
}
.theme-base-0f .container a,
.theme-base-0f .sidebar-toggle,
.theme-base-0f .related-posts li a:hover {
color: #8f5536;
}
/*
* Overlay sidebar
*
* Make the sidebar content overlay the viewport content instead of pushing it
* aside when toggled.
*/
.sidebar-overlay #sidebar-checkbox:checked ~ .wrap {
-webkit-transform: translateX(0);
-ms-transform: translateX(0);
transform: translateX(0);
}
.sidebar-overlay #sidebar-checkbox:checked ~ .sidebar-toggle {
box-shadow: 0 0 0 .25rem #fff;
}
.sidebar-overlay #sidebar-checkbox:checked ~ .sidebar {
box-shadow: .25rem 0 .5rem rgba(0,0,0,.1);
}
/* Only one tweak for a reverse layout */
.layout-reverse.sidebar-overlay #sidebar-checkbox:checked ~ .sidebar {
box-shadow: -.25rem 0 .5rem rgba(0,0,0,.1);
}
================================================
FILE: styles/portfolio.css
================================================
/**
* Layout styles
*/
.portfolio_item {
left: right;
margin: 0 0 20px 0;
padding:20px 20px 0 20px;
box-shadow:1px 2px 2px #eaeaea;
border:1px solid #eaeaea;
}
.portfolio_item h2 {
font-size: 24px;
margin: 20px 0 20px 0;
text-align: right;
padding-top: 250px;
text-transform:uppercase;
font-weight:bold;
}
.portfolio_item h2 a {
font-size: 14px;
text-decoration: none;
font-weight: bold;
}
.portfolio_item .role_wrapper {
border-bottom: 1px dotted #ccc;
position: relative;
margin-bottom:20px;
}
.portfolio_item .role {
float: left;
color: #1db7a9;
font-size: 30px;
font-weight: bold;
text-transform:uppercase;
margin: 16px 10px 10px 10px;
opacity:0.5;
}
.portfolio_item .role_title {
float: right;
font-size: 22px;
font-weight: bold;
margin: 10px;
color: #1db7a9;
line-height: 24px;
text-align: right;
}
.portfolio_item span.role_skills {
font-size: 18px;
color: #a6a6a6;
}
/**
* Individual
*/
.goldmans {
background:#fff url(assets/goldmans.jpg) top no-repeat;
}
.orange {
background:#fff url(assets/orange.jpg) top no-repeat;
}
.tesco {
background:#fff url(assets/tesco.png) top no-repeat;
}
.timeout {
background:#fff url(assets/timeout.png) top no-repeat;
}
.wiley {
background:#fff url(assets/wiley.png) top no-repeat;
}
.hellas {
background:#fff url(assets/hellas.png) top no-repeat;
}
.shipserv {
background:#fff url(assets/shipserv.png) top no-repeat;
}
.covestor {
background:#fff url(assets/covestor.png) top no-repeat;
}
.pwul {
background:#fff url(assets/pwul.jpg) top no-repeat;
}
================================================
FILE: styles/services.css
================================================
.services {
width:46%;
float:left;
font-size:16px;
}
.services:last-child{
float:right;
}
@media (max-width: 40em) {
.services {
width:100%;
}
}
================================================
FILE: webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
var merge = require('merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpackConfig = {
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.NoErrorsPlugin()
]
};
if (process.env.NODE_ENV === 'production') {
webpackConfig = merge(webpackConfig,{
devtool: "source-map",
entry : [
'./src/client/index.js'
],
module: {
loaders: [{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
include: __dirname
},
{ test: /\.(png|jpg|gif|jpeg)$/, loader: 'url-loader?limit=8192'},
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap') }
]},
plugins : [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new ExtractTextPlugin("app.css"),
new webpack.optimize.UglifyJsPlugin({minimize: true})
]
});
}else{
webpackConfig = merge(webpackConfig,{
devtool: 'inline-source-map',
module: {
loaders: [{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
include: __dirname,
query: {
optional: ['runtime'],
stage: 2,
env: {
development: {
plugins: [
'react-transform'
],
extra: {
'react-transform': {
transforms: [{
transform: 'react-transform-hmr',
imports: ['react'],
locals: ['module']
}]
}
}
}
}
}
},
{ test: /\.(png|jpg|gif|jpeg)$/, loader: 'url-loader?limit=8192'},
{ test: /\.css$/, loader: 'style-loader!css-loader' }
]},
entry : [
'webpack-hot-middleware/client',
'./src/client/index.js'
],
plugins : [
new webpack.HotModuleReplacementPlugin()
]
});
}
module.exports = webpackConfig;
gitextract_hpkv_v1h/ ├── .gitignore ├── README.md ├── package.json ├── src/ │ ├── client/ │ │ └── index.js │ ├── common/ │ │ ├── actions/ │ │ │ ├── about.js │ │ │ └── layout.js │ │ ├── api/ │ │ │ ├── fetchComponentDataBeforeRender.js │ │ │ ├── portfolio.js │ │ │ └── promiseMiddleware.js │ │ ├── components/ │ │ │ ├── 404.js │ │ │ ├── About.js │ │ │ ├── Home.js │ │ │ ├── Portfolio.js │ │ │ ├── Services.js │ │ │ ├── about/ │ │ │ │ └── Repos.js │ │ │ └── layout/ │ │ │ ├── Banner.js │ │ │ ├── Header.js │ │ │ ├── Loader.js │ │ │ └── Sidebar.js │ │ ├── containers/ │ │ │ ├── AboutPage.js │ │ │ ├── App.js │ │ │ └── HomePage.js │ │ ├── reducers/ │ │ │ ├── about.js │ │ │ ├── index.js │ │ │ └── layout.js │ │ ├── routes.js │ │ └── store/ │ │ └── configureStore.js │ └── server/ │ ├── devtools.js │ ├── index.js │ ├── server.js │ └── webpack.js ├── styles/ │ ├── about.css │ ├── base.css │ ├── custom.css │ ├── index.css │ ├── layout.css │ ├── portfolio.css │ └── services.css └── webpack.config.js
SYMBOL INDEX (54 symbols across 22 files)
FILE: src/common/actions/about.js
constant INVALIDATE_REPOS (line 3) | const INVALIDATE_REPOS = 'INVALIDATE_REPOS';
constant REPOS_GET (line 4) | const REPOS_GET = 'REPOS_GET';
constant REPOS_GET_REQUEST (line 5) | const REPOS_GET_REQUEST = 'REPOS_GET_REQUEST';
constant REPOS_GET_SUCCESS (line 6) | const REPOS_GET_SUCCESS = 'REPOS_GET_SUCCESS';
constant REPOS_GET_FAILURE (line 7) | const REPOS_GET_FAILURE = 'REPOS_GET_FAILURE';
function invalidateRepos (line 10) | function invalidateRepos(repos) {
function fetchRepos (line 17) | function fetchRepos() {
function shouldFetchRepos (line 24) | function shouldFetchRepos(state) {
function fetchReposIfNeeded (line 35) | function fetchReposIfNeeded() {
FILE: src/common/actions/layout.js
constant TOGGLE_SIDEBAR (line 2) | const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR';
function toggleSidebar (line 4) | function toggleSidebar(value) {
FILE: src/common/api/fetchComponentDataBeforeRender.js
function fetchComponentDataBeforeRender (line 6) | function fetchComponentDataBeforeRender(dispatch, components, params) {
FILE: src/common/api/portfolio.js
function getPortfolio (line 1) | function getPortfolio() {
FILE: src/common/api/promiseMiddleware.js
function promiseMiddleware (line 1) | function promiseMiddleware() {
FILE: src/common/components/404.js
class Error404 (line 4) | class Error404 extends Component {
method render (line 6) | render() {
FILE: src/common/components/About.js
class About (line 8) | class About extends Component {
method constructor (line 9) | constructor(props) {
method componentDidMount (line 13) | componentDidMount() {
method render (line 17) | render () {
FILE: src/common/components/Home.js
class Home (line 4) | class Home extends Component {
method constructor (line 6) | constructor(props){
method eventToggleSidebar (line 11) | eventToggleSidebar(e) {
method render (line 16) | render() {
FILE: src/common/components/Portfolio.js
class Portfolio (line 5) | class Portfolio extends Component {
method render (line 7) | render() {
FILE: src/common/components/Services.js
class Services (line 5) | class Services extends Component {
method render (line 7) | render () {
FILE: src/common/components/about/Repos.js
class Repos (line 3) | class Repos extends Component {
method render (line 4) | render () {
FILE: src/common/components/layout/Banner.js
class Banner (line 3) | class Banner extends Component {
method render (line 5) | render() {
FILE: src/common/components/layout/Header.js
class Header (line 3) | class Header extends Component {
method render (line 5) | render() {
FILE: src/common/components/layout/Loader.js
class Loader (line 3) | class Loader extends Component {
method render (line 5) | render() {
FILE: src/common/components/layout/Sidebar.js
class Sidebar (line 5) | class Sidebar extends Component {
method constructor (line 8) | constructor(props){
method eventCloseSidebar (line 13) | eventCloseSidebar (e) {
method render (line 17) | render() {
FILE: src/common/containers/AboutPage.js
function mapStateToProps (line 13) | function mapStateToProps(state) {
function mapDispatchToProps (line 32) | function mapDispatchToProps(dispatch) {
FILE: src/common/containers/App.js
class App (line 13) | class App extends Component {
method constructor (line 15) | constructor(props){
method eventToggleSidebar (line 20) | eventToggleSidebar(e) {
method render (line 25) | render() {
function mapStateToProps (line 47) | function mapStateToProps(state) {
function mapDispatchToProps (line 53) | function mapDispatchToProps(dispatch) {
FILE: src/common/containers/HomePage.js
function mapStateToProps (line 7) | function mapStateToProps(state) {
function mapDispatchToProps (line 13) | function mapDispatchToProps(dispatch) {
FILE: src/common/reducers/about.js
function reposByUser (line 5) | function reposByUser(state = {
FILE: src/common/reducers/layout.js
function layout (line 3) | function layout(state = {sidebarOpen: false}, action) {
FILE: src/common/store/configureStore.js
function configureStore (line 48) | function configureStore(initialState) {
FILE: src/server/devtools.js
function createDevToolsWindow (line 8) | function createDevToolsWindow(store) {
Condensed preview — 39 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (64K chars).
[
{
"path": ".gitignore",
"chars": 45,
"preview": "node_modules\nnpm-debug.log\nlib\n.DS_Store\ndist"
},
{
"path": "README.md",
"chars": 889,
"preview": "# Portfolio Redux App\n\nA Portfolio example of an isomorphic rendering application in React and Redux. [Live Example Here"
},
{
"path": "package.json",
"chars": 1438,
"preview": "\n{\n \"name\": \"portfolio-redux-app\",\n \"version\": \"0.0.1\",\n \"description\": \"A portfolio template built with Redux and Re"
},
{
"path": "src/client/index.js",
"chars": 871,
"preview": "import 'babel-core/polyfill';\n\nimport React from 'react';\nimport { Router } from 'react-router';\nimport { Provider } fro"
},
{
"path": "src/common/actions/about.js",
"chars": 902,
"preview": "import request from 'axios';\n\nexport const INVALIDATE_REPOS = 'INVALIDATE_REPOS';\nexport const REPOS_GET = 'REPOS_GET';\n"
},
{
"path": "src/common/actions/layout.js",
"chars": 152,
"preview": "\nexport const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR';\n\nexport function toggleSidebar(value) {\n return {\n type: TOGGLE_SID"
},
{
"path": "src/common/api/fetchComponentDataBeforeRender.js",
"chars": 587,
"preview": "/**\n* This looks at static needs parameter in components and waits for the promise to be fullfilled\n* It is used to make"
},
{
"path": "src/common/api/portfolio.js",
"chars": 2164,
"preview": "export function getPortfolio() {\n return [\n {\n \tlink : 'http://www.goldmansachs.com',\n \ttitle : 'Goldman Sachs"
},
{
"path": "src/common/api/promiseMiddleware.js",
"chars": 581,
"preview": "export default function promiseMiddleware() {\n return next => action => {\n const { promise, type, ...rest } = action"
},
{
"path": "src/common/components/404.js",
"chars": 498,
"preview": "import React, { Component } from 'react';\nimport { Link } from 'react-router';\n\nclass Error404 extends Component {\n\n re"
},
{
"path": "src/common/components/About.js",
"chars": 3504,
"preview": "import React, { Component, PropTypes } from 'react';\nimport { connect } from 'react-redux';\nimport Repos from './about/R"
},
{
"path": "src/common/components/Home.js",
"chars": 2804,
"preview": "import React, { Component } from 'react';\nimport Banner from './layout/Banner';\n\nclass Home extends Component {\n\n const"
},
{
"path": "src/common/components/Portfolio.js",
"chars": 981,
"preview": "import React, { Component } from 'react';\nimport { getPortfolio } from '../api/portfolio';\nimport classNames from 'class"
},
{
"path": "src/common/components/Services.js",
"chars": 1640,
"preview": "import React, { Component, PropTypes } from 'react';\nimport Banner from './layout/Banner';\n\n\nclass Services extends Comp"
},
{
"path": "src/common/components/about/Repos.js",
"chars": 435,
"preview": "import React, { PropTypes, Component } from 'react';\n\nexport default class Repos extends Component {\n render () {\n r"
},
{
"path": "src/common/components/layout/Banner.js",
"chars": 288,
"preview": "import React, { Component } from 'react';\n\nclass Banner extends Component {\n\n render() {\n\n return (\n\t <div classN"
},
{
"path": "src/common/components/layout/Header.js",
"chars": 381,
"preview": "import React, { Component } from 'react';\n\nclass Header extends Component {\n\n render() {\n return (\n <div classNam"
},
{
"path": "src/common/components/layout/Loader.js",
"chars": 393,
"preview": "import React, { Component } from 'react';\n\nclass Loader extends Component {\n\n render() {\n return (\n\t <section cla"
},
{
"path": "src/common/components/layout/Sidebar.js",
"chars": 1639,
"preview": "import React, { Component } from 'react';\nimport { Link } from 'react-router';\nimport classNames from 'classnames';\n\ncla"
},
{
"path": "src/common/containers/AboutPage.js",
"chars": 830,
"preview": "import { bindActionCreators } from 'redux';\nimport React, { Component} from 'react';\nimport { connect } from 'react-redu"
},
{
"path": "src/common/containers/App.js",
"chars": 1533,
"preview": "import React, { Component } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from 'react-red"
},
{
"path": "src/common/containers/HomePage.js",
"chars": 456,
"preview": "import { bindActionCreators } from 'redux';\nimport React, { Component} from 'react';\nimport { connect } from 'react-redu"
},
{
"path": "src/common/reducers/about.js",
"chars": 1116,
"preview": "import {\n INVALIDATE_REPOS, REPOS_GET_REQUEST, REPOS_GET_SUCCESS, REPOS_GET_FAILURE\n} from '../actions/about';\n\nexport "
},
{
"path": "src/common/reducers/index.js",
"chars": 306,
"preview": "import { combineReducers } from 'redux';\nimport { routerStateReducer } from 'redux-router';\n\nimport layout from './layou"
},
{
"path": "src/common/reducers/layout.js",
"chars": 259,
"preview": "import { TOGGLE_SIDEBAR } from '../actions/layout';\n\nexport default function layout(state = {sidebarOpen: false}, action"
},
{
"path": "src/common/routes.js",
"chars": 707,
"preview": "import { Route } from \"react-router\";\nimport React from \"react\";\n\nimport App from \"./containers/App\";\n\n//Redux Smart\nimp"
},
{
"path": "src/common/store/configureStore.js",
"chars": 1613,
"preview": "import { createStore, applyMiddleware, compose } from 'redux';\nimport { devTools } from 'redux-devtools';\nimport { redux"
},
{
"path": "src/server/devtools.js",
"chars": 1177,
"preview": "import React from 'react';\nimport { DevTools, DebugPanel, LogMonitor } from 'redux-devtools/lib/react';\n\n/*\n * Puts Redu"
},
{
"path": "src/server/index.js",
"chars": 47,
"preview": "require('babel/register');\nrequire('./server');"
},
{
"path": "src/server/server.js",
"chars": 2764,
"preview": "import express from 'express';\n\nimport webpack from 'webpack';\nimport webpackConfig from '../../webpack.config';\nimport "
},
{
"path": "src/server/webpack.js",
"chars": 414,
"preview": "// Webpack dev server\n// Ran in parallel with the Express server\n\nimport WebpackDevServer from \"webpack-dev-server\";\nimp"
},
{
"path": "styles/about.css",
"chars": 349,
"preview": ".repos {\n\twidth:46%;\n\tfloat:right;\n}\n\n.about,\n.services {\n\twidth:50%;\n\tfloat:left;\n\tfont-size:16px;}\n\n.repos h3 {\n\tmargi"
},
{
"path": "styles/base.css",
"chars": 6818,
"preview": "/*\n * Contents\n *\n * Body resets\n * Custom type\n * Messages\n * Container\n * Masthead\n * Posts and pages\n * Pagination\n *"
},
{
"path": "styles/custom.css",
"chars": 2585,
"preview": ".sidebar-item p b {\n color:#fff;\n}\n\n/**\n* Loader\n*/\n\n@keyframes pulse {\n 0% { opacity: 1; }\n 100% { opacity: 0; }\n"
},
{
"path": "styles/index.css",
"chars": 225,
"preview": "@import 'https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700%7CPT+Sans:400';\n@import 'base.css';\n@import "
},
{
"path": "styles/layout.css",
"chars": 12649,
"preview": "/*\n * Contents\n *\n * Global resets\n * Masthead\n * Sidebar\n * Slide effect\n * Posts and pages\n * Pagination\n * Reverse la"
},
{
"path": "styles/portfolio.css",
"chars": 1649,
"preview": "/**\n* Layout styles\n*/\n\n.portfolio_item {\n\tleft: right;\n margin: 0 0 20px 0;\n padding:20px 20px 0 20px;\n box-sh"
},
{
"path": "styles/services.css",
"chars": 154,
"preview": ".services {\n\twidth:46%;\n\tfloat:left;\n\tfont-size:16px;\n}\n\n.services:last-child{\n\tfloat:right;\n}\n\n@media (max-width: 40em)"
},
{
"path": "webpack.config.js",
"chars": 2231,
"preview": "var path = require('path');\nvar webpack = require('webpack');\nvar merge = require('merge');\nvar ExtractTextPlugin = requ"
}
]
About this extraction
This page contains the full source code of the caljrimmer/portfolio-redux-app GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 39 files (56.7 KB), approximately 17.1k tokens, and a symbol index with 54 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.