├── demo ├── dist │ ├── CNAME │ └── index.html ├── .babelrc ├── src │ ├── app.css │ ├── crumb-route.jsx │ ├── index.jsx │ ├── locations.jsx │ ├── events.jsx │ ├── friends.jsx │ └── app.jsx ├── webpack.dev.js ├── webpack.prod.js ├── package.json └── README.md ├── .gitignore ├── .travis.yml ├── src ├── index.js ├── style.css ├── store.js ├── breadcrumb.jsx └── breadcrumbs.jsx ├── .babelrc ├── bin └── authors ├── .eslintrc ├── LICENSE ├── webpack.config.js ├── package.json ├── AUTHORS ├── CHANGELOG.md ├── README.md └── dist └── react-breadcrumbs.min.js /demo/dist/CNAME: -------------------------------------------------------------------------------- 1 | breadcrumbs.surge.sh 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "8" 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export Breadcrumb from './breadcrumb.jsx' 2 | export Breadcrumbs from './breadcrumbs.jsx' -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "env", { "modules": false } ], 4 | "stage-0", 5 | "react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /demo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "env", { "modules": false } ], 4 | "stage-0", 5 | "react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /demo/src/app.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Titillium+Web'); 2 | 3 | body { 4 | font-size: 16px; 5 | margin: 0; 6 | font-family: 'Titillium Web', sans-serif; 7 | } 8 | 9 | .demo__main { 10 | max-width: 1280px; 11 | margin: 0 auto; 12 | padding: 0 1.5em; 13 | } -------------------------------------------------------------------------------- /bin/authors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Generate an AUTHORS file based on the output of git shortlog. It uses ABC 4 | # order, strips out leading spaces and numbers, then filters out specific 5 | # authors. 6 | 7 | git shortlog -se \ 8 | | perl -spe 's/^\s+\d+\s+//' \ 9 | | sed -e '/^CommitSyncScript.*$/d' \ 10 | > AUTHORS 11 | 12 | -------------------------------------------------------------------------------- /demo/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | React Breadcrumbs Demo 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:react/recommended" 5 | ], 6 | "plugins": [ 7 | "react" 8 | ], 9 | "parser": "babel-eslint", 10 | "parserOptions": { 11 | "ecmaVersion": 6, 12 | "sourceType": "module", 13 | "ecmaFeatures": { 14 | "jsx": true 15 | } 16 | }, 17 | "env": { 18 | "browser": false, 19 | "node": true, 20 | "es6": true 21 | }, 22 | "rules": { 23 | "semi": [ 2, "never" ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /demo/src/crumb-route.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react'; 3 | import { Route } from 'react-router-dom'; 4 | 5 | // Import Components 6 | import { Breadcrumb } from '../../src/index.js'; 7 | 8 | // Create and export the component 9 | export default ({ 10 | component: Component, 11 | includeSearch = false, 12 | render, 13 | ...props 14 | }) => ( 15 | ( 16 | 21 | { Component ? : render(routeProps) } 22 | 23 | )} /> 24 | ) -------------------------------------------------------------------------------- /demo/src/index.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { BrowserRouter } from 'react-router-dom'; 5 | 6 | // Import Components 7 | import App from './app.jsx'; 8 | import CrumbRoute from './crumb-route.jsx'; 9 | 10 | // Define element and render method 11 | let element = document.getElementById('app'), 12 | render = Root => { 13 | ReactDOM.render(( 14 | 15 | 16 | 17 | ), element); 18 | } 19 | 20 | // Initial render 21 | render(App) 22 | 23 | // Subsequent HMR renders 24 | if ( module.hot ) { 25 | module.hot.accept('./app.jsx', () => { 26 | render(App) 27 | }) 28 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License (ISC) 2 | 3 | Copyright (c) 2015, Sven Anders Robbestad 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | .breadcrumbs { 2 | background: #dedede; 3 | } 4 | 5 | .breadcrumbs__inner { 6 | display: flex; 7 | height: 2em; 8 | max-width: 1280px; 9 | margin: 0 auto; 10 | padding: 0 1.5em; 11 | align-items: center; 12 | } 13 | 14 | .breadcrumbs__crumb { 15 | margin-right: 1em; 16 | text-transform: uppercase; 17 | text-decoration: none; 18 | color: #3498db; 19 | vertical-align: middle; 20 | transition: color 250ms; 21 | } 22 | 23 | .breadcrumbs__crumb:hover { 24 | color: #2980b9; 25 | } 26 | 27 | .breadcrumbs__crumb--active { 28 | pointer-events: none; 29 | color: #666; 30 | } 31 | 32 | .breadcrumbs__separator { 33 | margin-right: 1em; 34 | color: #e67e22; 35 | pointer-events: none; 36 | } 37 | 38 | .breadcrumbs--hidden { 39 | display: none; 40 | } 41 | -------------------------------------------------------------------------------- /demo/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | entry: './src/index.jsx', 7 | module: { 8 | loaders: [ 9 | { 10 | test: /\.jsx?$/, 11 | use: 'babel-loader', 12 | exclude: /node_modules/ 13 | }, 14 | { 15 | test: /\.css/, 16 | use: [ 17 | 'style-loader', 18 | 'css-loader' 19 | ] 20 | } 21 | ] 22 | }, 23 | plugins: [ 24 | new webpack.HotModuleReplacementPlugin() 25 | ], 26 | output: { 27 | path: path.resolve(__dirname, 'dist'), 28 | filename: 'bundle.js' 29 | }, 30 | devServer: { 31 | port: 3030, 32 | hot: true, 33 | inline: true, 34 | compress: true, 35 | historyApiFallback: true, 36 | contentBase: 'dist/' 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /demo/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | entry: './src/index.jsx', 6 | module: { 7 | loaders: [ 8 | { 9 | test: /\.jsx?$/, 10 | use: 'babel-loader', 11 | exclude: /node_modules/ 12 | }, 13 | { 14 | test: /\.css/, 15 | use: [ 16 | 'style-loader', 17 | 'css-loader' 18 | ] 19 | } 20 | ] 21 | }, 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env': { NODE_ENV: '"production"' } 25 | }), 26 | new webpack.optimize.UglifyJsPlugin({ 27 | compressor: { 28 | screw_ie8: true, 29 | drop_console: true, 30 | warnings: false 31 | } 32 | }) 33 | ], 34 | output: { 35 | path: path.resolve(__dirname, 'dist'), 36 | filename: 'bundle.js' 37 | } 38 | } -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import { createStore } from 'redux' 3 | 4 | // Create the reducer 5 | let crumbs = (state = [], action) => { 6 | switch (action.type) { 7 | case 'ADD_CRUMB': 8 | return [ 9 | ...state, 10 | action.payload 11 | ] 12 | 13 | case 'UPDATE_CRUMB': 14 | return state.map(crumb => { 15 | return crumb.id === action.payload.id ? action.payload : crumb 16 | }) 17 | 18 | case 'REMOVE_CRUMB': 19 | return state.filter(crumb => { 20 | return crumb.id !== action.payload.id 21 | }) 22 | 23 | default: 24 | return state 25 | } 26 | } 27 | 28 | // Create the store 29 | let store = createStore(crumbs) 30 | 31 | // Export store and Dispatch method 32 | export default store 33 | export var Dispatch = store.dispatch 34 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-breadcrumbs-demo", 3 | "version": "0.1.0", 4 | "description": "A small demo of the react-breadcrumbs package.", 5 | "main": "n/a", 6 | "scripts": { 7 | "start": "webpack-dev-server --config webpack.dev.js", 8 | "build": "webpack --config webpack.prod.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/svenanders/react-breadcrumbs.git" 13 | }, 14 | "keywords": [ 15 | "react-breadcrumbs", 16 | "demo" 17 | ], 18 | "author": "Greg Venech", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/svenanders/react-breadcrumbs/issues" 22 | }, 23 | "homepage": "https://github.com/svenanders/react-breadcrumbs#readme", 24 | "devDependencies": { 25 | "babel-preset-env": "^1.6.1", 26 | "babel-preset-stage-0": "^6.24.1", 27 | "css-loader": "^0.28.7", 28 | "style-loader": "^0.18.2", 29 | "webpack": "^3.5.6", 30 | "webpack-dev-server": "^2.8.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demo/src/locations.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react' 3 | import { Switch, NavLink } from 'react-router-dom' 4 | 5 | // Import Components 6 | import CrumbRoute from './crumb-route.jsx' 7 | 8 | // Define a small event page 9 | const Location = props => ( 10 |
11 |

{ props.name }

12 |

More information about { props.name }...

13 |
14 | ) 15 | 16 | // Create and export the component 17 | export default ({ 18 | location, 19 | match, 20 | ...props 21 | }) => ( 22 |
23 |

Locations

24 |

Some locations...

25 | 29 | 30 | 31 | } /> 32 | } /> 33 | 34 |
35 | ) -------------------------------------------------------------------------------- /demo/src/events.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react' 3 | import { Switch, NavLink } from 'react-router-dom' 4 | 5 | // Import Components 6 | import CrumbRoute from './crumb-route.jsx' 7 | 8 | // Define a small event page 9 | const Event = props => ( 10 |
11 |

{ props.name }

12 |

More information about the { props.name }...

13 |
14 | ) 15 | 16 | // Create and export the component 17 | export default ({ 18 | location, 19 | match, 20 | ...props 21 | }) => ( 22 |
23 |

Upcoming Events

24 |

These events are coming up soon...

25 | 29 | 30 | 31 | } /> 32 | } /> 33 | 34 |
35 | ) -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # React Breadcrumbs Demo 2 | 3 | This simple demo includes a variety of routes and very basic content to 4 | demonstrate clearly how `react-breadcrumbs` can easily be integrated into 5 | your application. It is compiled via `webpack` and the following scripts 6 | are available: 7 | 8 | - `npm start`: Start a local `webpack-dev-server` to test the site. 9 | - `npm run build`: Build a production version of the demo. 10 | 11 | Note that making an "enhanced route" component is only one method of using 12 | the `` component. It can also be used directly for throughout 13 | the application, dynamically at the top-level using the `hidden` prop, or 14 | in a variety of other ways. 15 | 16 | See the [`/src`][1] directory for all code. The `/dist` directory is auto 17 | generated and should not be edited. 18 | 19 | 20 | ## TODOs 21 | 22 | Note that we kept the site extremely simple intentionally. Obviously it 23 | would be better to dynamically create routes and links using data. 24 | 25 | We should also create a `webpack.common.js` file and use `webpack-merge` 26 | to dedupe the configurations. I think we could also simplify management 27 | of the HTML file either via the `HTMLWebpackPlugin` or the `CopyWebpackPlugin` 28 | so everything in `/dist` is truly auto-generated. 29 | 30 | 31 | [1]: https://github.com/svenanders/react-breadcrumbs/tree/master/demo/src -------------------------------------------------------------------------------- /demo/src/friends.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react' 3 | import { Switch, NavLink } from 'react-router-dom' 4 | 5 | // Import Components 6 | import CrumbRoute from './crumb-route.jsx' 7 | 8 | // Define a small friend page 9 | const Friend = props => ( 10 |
11 |

{ props.name }

12 |

More information about { props.name }...

13 |
14 | ) 15 | 16 | // Create and export the component 17 | export default ({ 18 | location, 19 | match, 20 | ...props 21 | }) => ( 22 |
23 |

Your Friends

24 |

Here are your friends...

25 |
    26 |
  • Alice
  • 27 |
  • Frank
  • 28 |
  • Jane
  • 29 |
  • Matt
  • 30 |
31 | 32 | 33 | } /> 34 | } /> 35 | } /> 36 | } /> 37 | 38 |
39 | ) -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | entry: './src/index.js', 7 | module: { 8 | loaders: [ 9 | { 10 | test: /\.jsx?$/, 11 | exclude: /node_modules/, 12 | use: [ 13 | 'babel-loader', 14 | 'eslint-loader' 15 | ] 16 | } 17 | ] 18 | }, 19 | plugins: [ 20 | new webpack.DefinePlugin({ 21 | 'process.env': { NODE_ENV: '"production"' } 22 | }), 23 | new webpack.optimize.UglifyJsPlugin({ 24 | sourceMap: true, 25 | compressor: { 26 | screw_ie8: true, 27 | drop_console: true, 28 | warnings: false 29 | } 30 | }) 31 | ], 32 | externals: { 33 | react: { 34 | root: 'React', 35 | commonjs2: 'react', 36 | commonjs: 'react', 37 | amd: 'react' 38 | }, 39 | 'prop-types': { 40 | root: 'PropTypes', 41 | commonjs2: 'prop-types', 42 | commonjs: 'prop-types', 43 | amd: 'prop-types' 44 | }, 45 | 'react-router-dom': { 46 | root: 'ReactRouterDOM', 47 | commonjs2: 'react-router-dom', 48 | commonjs: 'react-router-dom', 49 | amd: 'react-router-dom' 50 | } 51 | }, 52 | output: { 53 | path: path.resolve(__dirname, 'dist'), 54 | filename: 'react-breadcrumbs.min.js', 55 | library: 'react-breadcrumbs', 56 | libraryTarget: 'umd' 57 | } 58 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-breadcrumbs", 3 | "version": "2.1.7", 4 | "description": "Automatic breadcrumbs for react-router", 5 | "author": "Sven Anders Robbestad ", 6 | "homepage": "https://github.com/svenanders/react-breadcrumbs", 7 | "license": "ISC", 8 | "main": "dist/react-breadcrumbs.min.js", 9 | "scripts": { 10 | "build": "webpack", 11 | "release": "standard-version" 12 | }, 13 | "npmFileMap": [ 14 | { 15 | "basePath": "/dist/", 16 | "files": [ 17 | "*.js" 18 | ] 19 | } 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/svenanders/react-breadcrumbs.git" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/svenanders/react-breadcrumbs/issues" 27 | }, 28 | "keywords": [ 29 | "react-component", 30 | "breadcrumbs", 31 | "react", 32 | "trail" 33 | ], 34 | "peerDependencies": { 35 | "react": "^15.0.0 || ^16.0.0", 36 | "react-router-dom": "^4.0.0 || ^5.0.0", 37 | "prop-types": "^15.5.7" 38 | }, 39 | "devDependencies": { 40 | "babel-core": "^6.26.3", 41 | "babel-eslint": "^8.0.0", 42 | "babel-loader": "^7.1.2", 43 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 44 | "babel-preset-env": "^1.7.0", 45 | "babel-preset-react": "^6.5.0", 46 | "babel-preset-stage-0": "^6.24.1", 47 | "eslint": "^3.17.0", 48 | "eslint-loader": "^1.9.0", 49 | "eslint-plugin-react": "^7.3.0", 50 | "prop-types": "^15.5.8", 51 | "react": "^15.3.2", 52 | "react-dom": "^15.5.4", 53 | "react-router-dom": "^4.1.1", 54 | "standard-version": "^6.0.1", 55 | "webpack": "^3.5.6" 56 | }, 57 | "dependencies": { 58 | "lodash.isequal": "^4.5.0", 59 | "redux": "^3.6.0", 60 | "uuid": "^3.0.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/breadcrumb.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | import UUID from 'uuid' 5 | import IsEqual from 'lodash.isequal' 6 | 7 | // Import Utilities 8 | import { Dispatch } from './store' 9 | 10 | // Create and export the component 11 | export default class Breadcrumb extends React.Component { 12 | static propTypes = { 13 | data: PropTypes.object.isRequired, 14 | hidden: PropTypes.bool, 15 | children: PropTypes.element 16 | } 17 | 18 | static defaultProps = { 19 | hidden: false, 20 | children: null 21 | } 22 | 23 | state = { 24 | id: UUID.v4() 25 | } 26 | 27 | render() { 28 | return this.props.children 29 | } 30 | 31 | componentDidMount() { 32 | let { data, hidden } = this.props 33 | 34 | if ( !hidden ) this._dispatch('ADD_CRUMB', data) 35 | } 36 | 37 | componentWillReceiveProps(nextProps) { 38 | let { data, hidden } = nextProps 39 | 40 | // Update the crumb if its data has changed 41 | if ( !IsEqual(data, this.props.data) ) { 42 | this._dispatch('UPDATE_CRUMB', data) 43 | } 44 | 45 | // Remove/add crumb based on `hidden` prop 46 | if ( hidden && !this.props.hidden ) { 47 | this._dispatch('REMOVE_CRUMB', data) 48 | 49 | } else if ( !hidden && this.props.hidden ) { 50 | this._dispatch('ADD_CRUMB', data) 51 | } 52 | } 53 | 54 | componentWillUnmount() { 55 | this._dispatch( 56 | 'REMOVE_CRUMB', 57 | this.props.data 58 | ) 59 | } 60 | 61 | /** 62 | * Dispatch the given `action` 63 | * 64 | * @param {string} action - A valid action name accepted by the store 65 | * @param {object} data - The breadcrumb data to pass 66 | */ 67 | _dispatch(action, data) { 68 | let { id } = this.state 69 | 70 | Dispatch({ 71 | type: action, 72 | payload: { id, ...data } 73 | }) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alasdair 2 | Andrew Sullivan 3 | BALEHOK 4 | Benjamin Flesch 5 | Dany Ellement 6 | Derek Yau 7 | Dmitry Erman 8 | Erik Anderson 9 | FoxxMD 10 | Greg Venech 11 | Guilherme Caminha 12 | Jack Allan 13 | Jack Coulter 14 | Jeremy Greer 15 | Jonas Matser 16 | Joshua 17 | Konstantin Alekseev 18 | Lance Batson 19 | Lee Siong Chan 20 | Loris Guignard 21 | Luca Fabbri 22 | Mateusz Nowotyński 23 | Mateusz Zatorski 24 | Michael Tsyganov 25 | Noah Hall 26 | Pavel Vanecek 27 | Rahav lussato 28 | Ricardo Filipe Gonçalves Ribeiro 29 | Ricardo Ribeiro 30 | Ricky Miller 31 | Sharon Rolel 32 | Steve Fuller 33 | Sven A Robbestad 34 | Sven Anders Robbestad 35 | Sven Anders Robbestad 36 | Sven Anders Robbestad 37 | TheClark 38 | Thomas Wilson 39 | Tom Panier 40 | Varun Arora 41 | Vincent 42 | bakatrouble 43 | clodal 44 | dignifiedquire 45 | -------------------------------------------------------------------------------- /demo/src/app.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react'; 3 | import { Switch, NavLink, Route } from 'react-router-dom'; 4 | 5 | // Import Components 6 | import { Breadcrumbs } from '../../src/index.js'; 7 | import CrumbRoute from './crumb-route.jsx' 8 | import Friends from './friends.jsx' 9 | import Events from './events.jsx' 10 | import Locations from './locations.jsx' 11 | 12 | // Load Styling 13 | import '../../src/style.css'; 14 | import './app.css'; 15 | 16 | // Create and export the component 17 | export default class App extends React.Component { 18 | /** 19 | * Handle breadcrumb render 20 | * @param {[{}]} crumbs 21 | * @return {[{}]} 22 | */ 23 | handleCrumbs = (crumbs) => { 24 | // Remove first crumb 25 | return crumbs.filter((c, i) => i !== 0) 26 | } 27 | render() { 28 | return ( 29 |
30 | 31 |
32 |

Breadcrumbs Demo

33 |

Use the links below to jump around the site and watch the breadcrumbs update...

34 |
    35 |
  • Friends
  • 36 |
  • Events
  • 37 |
  • Locations
  • 38 |
39 | 40 |
41 | 42 | Home content... } /> 43 | 44 | 45 | 46 | Page not found... } /> 47 | 48 |
49 |
50 |
51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [2.1.7](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.6...v2.1.7) (2019-06-14) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * allow usage with latest react-router version ([d4258e9](https://github.com/svenanders/react-breadcrumbs/commit/d4258e9)) 11 | 12 | 13 | 14 | 15 | ## [2.1.6](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.5...v2.1.6) (2018-07-05) 16 | 17 | 18 | 19 | 20 | ## [2.1.5](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.4...v2.1.5) (2018-01-18) 21 | 22 | 23 | 24 | 25 | ## [2.1.4](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.3...v2.1.4) (2018-01-11) 26 | 27 | 28 | 29 | 30 | ## [2.1.3](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.2...v2.1.3) (2018-01-04) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * **breadcrumb:** allow no children to be passed ([2122618](https://github.com/svenanders/react-breadcrumbs/commit/2122618)) 36 | * **demo:** update crumb-route ([0a0bd31](https://github.com/svenanders/react-breadcrumbs/commit/0a0bd31)) 37 | * **deps:** allow the latest version of react ([0815c17](https://github.com/svenanders/react-breadcrumbs/commit/0815c17)) 38 | 39 | 40 | 41 | 42 | ## [2.1.2](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.1...v2.1.2) (2017-10-24) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * fix “Cannot read property 'Component' of undefined” issue ([275ded1](https://github.com/svenanders/react-breadcrumbs/commit/275ded1)) 48 | 49 | 50 | 51 | 52 | ## [2.1.1](https://github.com/svenanders/react-breadcrumbs/compare/v2.1.0...v2.1.1) (2017-10-19) 53 | 54 | 55 | 56 | 57 | # [2.1.0](https://github.com/svenanders/react-breadcrumbs/compare/v1.6.6...v2.1.0) (2017-10-14) 58 | 59 | 60 | ### Features 61 | 62 | * rewrite library to be compatible with react-router v4 ([0637464](https://github.com/svenanders/react-breadcrumbs/commit/0637464)) 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Breadcrumbs 2 | 3 | [React][1] component use to generate a breadcrumb trail (compatible with 4 | [React Router][2]). 5 | 6 | ## Installation 7 | 8 | ```sh 9 | npm install --save react-breadcrumbs 10 | ``` 11 | 12 | Note: this version is only compatible with React-Router v4 and up. If you 13 | need a version that is compatible with React-Router v3 and below, use 14 | ``` 15 | npm install --save react-breadcrumbs@1.6.x 16 | ``` 17 | 18 | ## Demo 19 | 20 | The `/demo` directory provide one example of how this 21 | package can be used. See the [`/demo`][3] for the code powering the small 22 | site. 23 | 24 | ## Usage 25 | 26 | This package exposes two components, a `` component to wrap 27 | the entire application and a `` component to use throughout 28 | the different sections (e.g. ``s) within the application. 29 | 30 | ### Breadcrumbs 31 | 32 | The top-level `` component accepts the following `props`: 33 | 34 | - `className` (string): A class name for the outer wrapper element. 35 | - `hidden` (bool): Hide the inner breadcrumbs wrapper. 36 | - `setCrumbs` (func): A `function(crumbs: [Object]): [Object]` which will be called before crumbs are rendered. 37 | - `wrapper` (func|class): A react component to use for the inner wrapper. 38 | 39 | ### Breadcrumb 40 | 41 | - `data` (object): An extended [location descriptor][5]. See below... 42 | - `hidden` (bool): Hide an individual breadcrumb (rarely needed). 43 | 44 | The `data` object allows any valid [location descriptor][5] key (e.g. 45 | `pathname` or `search`) as well as a `title` prop: 46 | 47 | ``` js 48 | { 49 | title: 'Home', // Any valid `PropTypes.node` 50 | pathname: '/', 51 | // ... any other location descriptor values 52 | } 53 | ``` 54 | 55 | The fact that the `title` can be any valid `PropTypes.node` allows for a huge 56 | amount of customization. The following values are all valid: 57 | 58 | ``` jsx 59 | title: 'Home' 60 | title: Home 61 | title: 62 | ``` 63 | 64 | ### Authors 65 | 66 | This project would not have been where it is today without massive contributions from 67 | a whole lot of people ([`AUTHORS`][6]). Suport for React Router v4 support was written 68 | entirely by ([`@skipjack`][7]). 69 | 70 | [1]: https://facebook.github.io/react 71 | [2]: https://github.com/rackt/react-router 72 | [3]: https://github.com/svenanders/react-breadcrumbs/tree/master/demo 73 | [4]: http://breadcrumbs.surge.sh/index.html 74 | [5]: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/location.md 75 | [6]: https://github.com/svenanders/react-breadcrumbs/tree/master/AUTHORS 76 | [7]: https://github.com/skipjack 77 | -------------------------------------------------------------------------------- /src/breadcrumbs.jsx: -------------------------------------------------------------------------------- 1 | // Import External Dependencies 2 | import React from 'react' 3 | import PropTypes from 'prop-types' 4 | 5 | // TODO: Use imitation and allow it to be passed as a prop 6 | import { NavLink } from 'react-router-dom' 7 | 8 | // Import Utilities 9 | import Store from './store' 10 | 11 | // Specify BEM block name 12 | const block = 'breadcrumbs' 13 | 14 | // Create and export the component 15 | export default class Breadcrumbs extends React.Component { 16 | static propTypes = { 17 | className: PropTypes.string, 18 | hidden: PropTypes.bool, 19 | separator: PropTypes.node, 20 | setCrumbs: PropTypes.func, 21 | wrapper: PropTypes.oneOfType([ 22 | PropTypes.func, 23 | PropTypes.instanceOf( 24 | React.Component 25 | ) 26 | ]), 27 | children: PropTypes.oneOfType([ 28 | PropTypes.node, 29 | PropTypes.arrayOf( 30 | PropTypes.node 31 | ) 32 | ]) 33 | } 34 | 35 | static defaultProps = { 36 | className: '', 37 | hidden: false, 38 | separator: '>', 39 | setCrumbs: undefined, 40 | wrapper: props => ( 41 | 44 | ) 45 | } 46 | 47 | _unsubscribe = null 48 | 49 | render() { 50 | let { className, hidden, wrapper: Wrapper, setCrumbs } = this.props, 51 | hiddenMod = hidden ? `${block}--hidden` : '', 52 | crumbs = Store.getState() 53 | 54 | crumbs = crumbs.sort((a, b) => { 55 | return a.pathname.length - b.pathname.length 56 | }) 57 | 58 | if (setCrumbs) crumbs = setCrumbs(crumbs) 59 | 60 | return ( 61 |
62 | 63 |
64 | { 65 | crumbs.map((crumb, i) => ( 66 | 67 | 76 | { crumb.title } 77 | 78 | 79 | { i < crumbs.length - 1 ? ( 80 | 81 | { this.props.separator } 82 | 83 | ) : null } 84 | 85 | )) 86 | } 87 |
88 |
89 | 90 | { this.props.children } 91 |
92 | ) 93 | } 94 | 95 | componentWillMount() { 96 | this._unsubscribe = Store.subscribe(() => { 97 | this.forceUpdate() 98 | }) 99 | } 100 | 101 | componentWillUnmount() { 102 | this._unsubscribe() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /dist/react-breadcrumbs.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react"),require("prop-types"),require("react-router-dom")):"function"==typeof define&&define.amd?define(["react","prop-types","react-router-dom"],e):"object"==typeof exports?exports["react-breadcrumbs"]=e(require("react"),require("prop-types"),require("react-router-dom")):t["react-breadcrumbs"]=e(t.React,t.PropTypes,t.ReactRouterDOM)}(this,function(t,e,r){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=12)}([function(t,e){var r;r=function(){return this}();try{r=r||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(r=window)}t.exports=r},function(e,r){e.exports=t},function(t,r){t.exports=e},function(t,e,r){(function(e){var r,n=e.crypto||e.msCrypto;if(n&&n.getRandomValues){var o=new Uint8Array(16);r=function(){return n.getRandomValues(o),o}}if(!r){var i=new Array(16);r=function(){for(var t,e=0;e<16;e++)0==(3&e)&&(t=4294967296*Math.random()),i[e]=t>>>((3&e)<<3)&255;return i}}t.exports=r}).call(e,r(0))},function(t,e){function r(t,e){var r=e||0,o=n;return o[t[r++]]+o[t[r++]]+o[t[r++]]+o[t[r++]]+"-"+o[t[r++]]+o[t[r++]]+"-"+o[t[r++]]+o[t[r++]]+"-"+o[t[r++]]+o[t[r++]]+"-"+o[t[r++]]+o[t[r++]]+o[t[r++]]+o[t[r++]]+o[t[r++]]+o[t[r++]]}for(var n=[],o=0;o<256;++o)n[o]=(o+256).toString(16).substr(1);t.exports=r},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,r){"use strict";function n(t){if(Array.isArray(t)){for(var e=0,r=Array(t.length);e0&&void 0!==arguments[0]?arguments[0]:[],e=arguments[1];switch(e.type){case"ADD_CRUMB":return[].concat(n(t),[e.payload]);case"UPDATE_CRUMB":return t.map(function(t){return t.id===e.payload.id?e.payload:t});case"REMOVE_CRUMB":return t.filter(function(t){return t.id!==e.payload.id});default:return t}},a=Object(o.a)(i);e.b=a;var c=a.dispatch},function(t,e,r){"use strict";function n(t,e,r){function i(){y===v&&(y=v.slice())}function u(){return b}function s(t){if("function"!=typeof t)throw new Error("Expected listener to be a function.");var e=!0;return i(),y.push(t),function(){if(e){e=!1,i();var r=y.indexOf(t);y.splice(r,1)}}}function f(t){if(!Object(o.a)(t))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===t.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(_)throw new Error("Reducers may not dispatch actions.");try{_=!0,b=h(b,t)}finally{_=!1}for(var e=v=y,r=0;rs)&&void 0===t.nsecs&&(p=0),p>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");s=l,f=p,u=a,l+=122192928e5;var h=(1e4*(268435455&l)+p)%4294967296;o[n++]=h>>>24&255,o[n++]=h>>>16&255,o[n++]=h>>>8&255,o[n++]=255&h;var b=l/4294967296*1e4&268435455;o[n++]=b>>>8&255,o[n++]=255&b,o[n++]=b>>>24&15|16,o[n++]=b>>>16&255,o[n++]=a>>>8|128,o[n++]=255&a;for(var v=t.node||c,y=0;y<6;++y)o[n+y]=v[y];return e||i(o)}var o=r(3),i=r(4),a=o(),c=[1|a[0],a[1],a[2],a[3],a[4],a[5]],u=16383&(a[6]<<8|a[7]),s=0,f=0;t.exports=n},function(t,e,r){function n(t,e,r){var n=e&&r||0;"string"==typeof t&&(e="binary"==t?new Array(16):null,t=null),t=t||{};var a=t.random||(t.rng||o)();if(a[6]=15&a[6]|64,a[8]=63&a[8]|128,e)for(var c=0;c<16;++c)e[n+c]=a[c];return e||i(a)}var o=r(3),i=r(4);t.exports=n},function(t,e,r){(function(t,r){function n(t,e){for(var r=-1,n=null==t?0:t.length,o=0,i=[];++r-1}function w(t,e){var r=this.__data__,n=I(r,t);return n<0?(++this.size,r.push([t,e])):r[n][1]=e,this}function O(t){var e=-1,r=null==t?0:t.length;for(this.clear();++es))return!1;var l=a.get(t);if(l&&a.get(e))return l==e;var p=-1,d=!0,h=r&mt?new M:void 0;for(a.set(t,e),a.set(e,t);++p-1&&t%1==0&&t-1&&t%1==0&&t<=gt}function pt(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function dt(t){return null!=t&&"object"==typeof t}function ht(t){return ut(t)?D(t):H(t)}function bt(){return[]}function vt(){return!1}var yt=200,_t="__lodash_hash_undefined__",jt=1,mt=2,gt=9007199254740991,wt="[object Arguments]",Ot="[object Array]",Et="[object AsyncFunction]",xt="[object Boolean]",At="[object Date]",Pt="[object Error]",kt="[object Function]",Mt="[object GeneratorFunction]",Rt="[object Map]",zt="[object Number]",Tt="[object Null]",St="[object Object]",Ut="[object Proxy]",Ct="[object RegExp]",Nt="[object Set]",Bt="[object String]",Dt="[object Symbol]",It="[object Undefined]",qt="[object ArrayBuffer]",Ft="[object DataView]",Wt=/[\\^$.*+?()[\]{}|]/g,Vt=/^\[object .+?Constructor\]$/,$t=/^(?:0|[1-9]\d*)$/,Lt={};Lt["[object Float32Array]"]=Lt["[object Float64Array]"]=Lt["[object Int8Array]"]=Lt["[object Int16Array]"]=Lt["[object Int32Array]"]=Lt["[object Uint8Array]"]=Lt["[object Uint8ClampedArray]"]=Lt["[object Uint16Array]"]=Lt["[object Uint32Array]"]=!0,Lt[wt]=Lt[Ot]=Lt[qt]=Lt[xt]=Lt[Ft]=Lt[At]=Lt[Pt]=Lt[kt]=Lt[Rt]=Lt[zt]=Lt[St]=Lt[Ct]=Lt[Nt]=Lt[Bt]=Lt["[object WeakMap]"]=!1;var Gt="object"==typeof t&&t&&t.Object===Object&&t,Ht="object"==typeof self&&self&&self.Object===Object&&self,Jt=Gt||Ht||Function("return this")(),Kt="object"==typeof e&&e&&!e.nodeType&&e,Qt=Kt&&"object"==typeof r&&r&&!r.nodeType&&r,Xt=Qt&&Qt.exports===Kt,Yt=Xt&&Gt.process,Zt=function(){try{return Yt&&Yt.binding&&Yt.binding("util")}catch(t){}}(),te=Zt&&Zt.isTypedArray,ee=Array.prototype,re=Function.prototype,ne=Object.prototype,oe=Jt["__core-js_shared__"],ie=re.toString,ae=ne.hasOwnProperty,ce=function(){var t=/[^.]+$/.exec(oe&&oe.keys&&oe.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}(),ue=ne.toString,se=RegExp("^"+ie.call(ae).replace(Wt,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),fe=Xt?Jt.Buffer:void 0,le=Jt.Symbol,pe=Jt.Uint8Array,de=ne.propertyIsEnumerable,he=ee.splice,be=le?le.toStringTag:void 0,ve=Object.getOwnPropertySymbols,ye=fe?fe.isBuffer:void 0,_e=function(t,e){return function(r){return t(e(r))}}(Object.keys,Object),je=Z(Jt,"DataView"),me=Z(Jt,"Map"),ge=Z(Jt,"Promise"),we=Z(Jt,"Set"),Oe=Z(Jt,"WeakMap"),Ee=Z(Object,"create"),xe=at(je),Ae=at(me),Pe=at(ge),ke=at(we),Me=at(Oe),Re=le?le.prototype:void 0,ze=Re?Re.valueOf:void 0;l.prototype.clear=p,l.prototype.delete=d,l.prototype.get=h,l.prototype.has=b,l.prototype.set=v,y.prototype.clear=_,y.prototype.delete=j,y.prototype.get=m,y.prototype.has=g,y.prototype.set=w,O.prototype.clear=E,O.prototype.delete=x,O.prototype.get=A,O.prototype.has=P,O.prototype.set=k,M.prototype.add=M.prototype.push=R,M.prototype.has=z,T.prototype.clear=S,T.prototype.delete=U,T.prototype.get=C,T.prototype.has=N,T.prototype.set=B;var Te=ve?function(t){return null==t?[]:(t=Object(t),n(ve(t),function(e){return de.call(t,e)}))}:bt,Se=F;(je&&Se(new je(new ArrayBuffer(1)))!=Ft||me&&Se(new me)!=Rt||ge&&"[object Promise]"!=Se(ge.resolve())||we&&Se(new we)!=Nt||Oe&&"[object WeakMap]"!=Se(new Oe))&&(Se=function(t){var e=F(t),r=e==St?t.constructor:void 0,n=r?at(r):"";if(n)switch(n){case xe:return Ft;case Ae:return Rt;case Pe:return"[object Promise]";case ke:return Nt;case Me:return"[object WeakMap]"}return e});var Ue=W(function(){return arguments}())?W:function(t){return dt(t)&&ae.call(t,"callee")&&!de.call(t,"callee")},Ce=Array.isArray,Ne=ye||vt,Be=te?function(t){return function(e){return t(e)}}(te):G;r.exports=st}).call(e,r(0),r(5)(t))},function(t,e,r){"use strict";var n=r(7);r(30),r(31),r(32),r(11),r(10);r.d(e,"a",function(){return n.b})},function(t,e,r){"use strict";function n(t){return null==t?void 0===t?u:c:s&&s in Object(t)?Object(i.a)(t):Object(a.a)(t)}var o=r(9),i=r(22),a=r(23),c="[object Null]",u="[object Undefined]",s=o.a?o.a.toStringTag:void 0;e.a=n},function(t,e,r){"use strict";var n=r(21),o="object"==typeof self&&self&&self.Object===Object&&self,i=n.a||o||Function("return this")();e.a=i},function(t,e,r){"use strict";(function(t){var r="object"==typeof t&&t&&t.Object===Object&&t;e.a=r}).call(e,r(0))},function(t,e,r){"use strict";function n(t){var e=a.call(t,u),r=t[u];try{t[u]=void 0;var n=!0}catch(t){}var o=c.call(t);return n&&(e?t[u]=r:delete t[u]),o}var o=r(9),i=Object.prototype,a=i.hasOwnProperty,c=i.toString,u=o.a?o.a.toStringTag:void 0;e.a=n},function(t,e,r){"use strict";function n(t){return i.call(t)}var o=Object.prototype,i=o.toString;e.a=n},function(t,e,r){"use strict";var n=r(25),o=Object(n.a)(Object.getPrototypeOf,Object);e.a=o},function(t,e,r){"use strict";function n(t,e){return function(r){return t(e(r))}}e.a=n},function(t,e,r){"use strict";function n(t){return null!=t&&"object"==typeof t}e.a=n},function(t,e,r){t.exports=r(28)},function(t,e,r){"use strict";(function(t,n){Object.defineProperty(e,"__esModule",{value:!0});var o,i=r(29),a=function(t){return t&&t.__esModule?t:{default:t}}(i);o="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==t?t:n;var c=(0,a.default)(o);e.default=c}).call(e,r(0),r(5)(t))},function(t,e,r){"use strict";function n(t){var e,r=t.Symbol;return"function"==typeof r?r.observable?e=r.observable:(e=r("observable"),r.observable=e):e="@@observable",e}Object.defineProperty(e,"__esModule",{value:!0}),e.default=n},function(t,e,r){"use strict";r(7),r(8),r(10)},function(t,e,r){"use strict"},function(t,e,r){"use strict";r(11),Object.assign},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var a=r(1),c=r.n(a),u=r(2),s=r.n(u),f=r(34),l=(r.n(f),r(6)),p=function(){function t(t,e){for(var r=0;r",setCrumbs:void 0,wrapper:function(t){return c.a.createElement("nav",t,t.children)}},e.a=h},function(t,e){t.exports=r}])}); 2 | //# sourceMappingURL=react-breadcrumbs.min.js.map --------------------------------------------------------------------------------