├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── demo-app │ ├── App.tsx │ ├── MyBasketDataProvider.ts │ ├── Products.tsx │ ├── index.css │ ├── index.html │ ├── index.tsx │ └── webpack.config.js ├── index.html └── js │ └── main.min.js ├── package-lock.json ├── package.json ├── src ├── basket-context │ └── index.tsx ├── basket-data │ └── index.ts ├── basket-provider │ └── index.tsx ├── components │ └── basket.tsx └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - 10 5 | install: 6 | - npm install 7 | script: 8 | - npm i --no-save @material-ui/icons 9 | - npm run build 10 | deploy: 11 | provider: npm 12 | email: "mehmetbaran@mehmetbaran.net" 13 | api_key: "$NPM_TOKEN" 14 | skip_cleanup: true 15 | on: 16 | branch: master -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mehmet Baran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-basket 2 | 3 | [![Build Status](https://travis-ci.org/mbrn/react-basket.svg?branch=master)](https://travis-ci.org/mbrn/react-basket) 4 | [![npm package](https://img.shields.io/npm/v/react-basket/latest.svg)](https://www.npmjs.com/package/react-basket) 5 | [![NPM Downloads](https://img.shields.io/npm/dt/react-basket.svg?style=flat)](https://npmcharts.com/compare/react-basket?minimal=true) 6 | [![Install Size](https://packagephobia.now.sh/badge?p=react-basket)](https://packagephobia.now.sh/result?p=react-basket) 7 | [![Follow on Twitter](https://img.shields.io/twitter/follow/baranmehmet.svg?label=follow+baranmehmet)](https://twitter.com/baranmehmet) [![Join the chat at https://gitter.im/mbrn-react-basket/community](https://badges.gitter.im/mbrn-react-basket/community.svg)](https://gitter.im/mbrn-react-basket/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 8 | 9 | A shopping basket components library for React based on material-ui components. 10 | 11 | ## Key features 12 | 13 | - Shopping Cart Component 14 | - Realtime shopping cart support 15 | - Connect to shopping cart data anywhere in app 16 | 17 | ## Demo 18 | 19 | You can access the demo page from [__demo site__](https://mbrn.github.io/react-basket/). 20 | 21 | If you have any sort of doubt, idea or just want to talk about the project, feel free to join [our chat on Gitter](https://gitter.im/mbrn-react-basket/community) :) 22 | 23 | ## Installation 24 | npm install react-basket 25 | 26 | ## Usage 27 | 28 | ### 1. Implement a DataProvider 29 | 30 | ```javascript 31 | import { DataProvider, BasketItem } from "react-basket"; 32 | 33 | export class MyBasketDataProvider implements DataProvider { 34 | 35 | registerToChanges(callback: (items: BasketItem[]) => void) { 36 | // You can call callback functions if you socket.io/pusher or something like it. 37 | } 38 | 39 | products = [ 40 | { id: "1", name: 'Computer', price: 1722.44, quantity: 1 }, 41 | { id: "2", name: 'Phone', price: 522.14, quantity: 1 } 42 | ]; 43 | 44 | items = [ 45 | { id: "1", name: 'Computer', price: 1722.44, quantity: 1 }, 46 | { id: "2", name: 'Phone', price: 522.14, quantity: 2 } 47 | ]; 48 | 49 | getInitialData = (): Promise => { 50 | return new Promise((resolve, reject) => { 51 | setTimeout(() => { 52 | resolve(this.items) 53 | }, 1000) 54 | }); 55 | } 56 | 57 | onAllItemsDeleted(): Promise { 58 | return new Promise((resolve, reject) => { 59 | setTimeout(() => { 60 | this.items = []; 61 | 62 | resolve(this.items) 63 | }, 1000) 64 | }); 65 | } 66 | 67 | onItemAdded(id: string): Promise { 68 | return new Promise((resolve, reject) => { 69 | setTimeout(() => { 70 | let index = this.items.findIndex(item => item.id === id); 71 | if (index > -1) { 72 | this.items[index].quantity++; 73 | } 74 | else { 75 | const index = this.products.findIndex(item => item.id === id); 76 | if (index > -1) { 77 | const item = {...this.products[index]}; 78 | this.items.push(item); 79 | } 80 | } 81 | 82 | resolve(this.items) 83 | }, 1000) 84 | }); 85 | } 86 | 87 | onItemDeleted = (id: string): Promise => { 88 | return new Promise((resolve, reject) => { 89 | setTimeout(() => { 90 | 91 | const index = this.items.findIndex(item => item.id === id); 92 | if (index > -1) { 93 | this.items.splice(index, 1); 94 | } 95 | 96 | resolve(this.items) 97 | }, 1000) 98 | }); 99 | } 100 | } 101 | ``` 102 | 103 | > I used Typescript, but you can use pure javascript. 104 | 105 | ### 2. Adding Basket Provider to app 106 | You should add BasketProvider component in root of your application with a data provider implementation. 107 | 108 | ```javascript 109 | class App extends React.Component { 110 | render() { 111 | const { classes } = this.props; 112 | 113 | return ( 114 | 115 |
your all components will be here
116 | BasketProvider> 117 | ); 118 | } 119 | } 120 | ``` 121 | 122 | ### 3. Use Basket component 123 | 124 | It connects to BasketProvider and take data from it. 125 | 126 | ```javascript 127 | import { Basket } from 'react-basket'; 128 | 129 | 130 | ``` 131 | 132 | ### 4. Connect to basket data anywhere 133 | 134 | 135 | ```javascript 136 | import { withBasketData } from 'react-basket'; 137 | import { IconButton, Badge } from '@material-ui/core'; 138 | import ShoppingCart from '@material-ui/icons/ShoppingCart'; 139 | 140 | const MyComponent = (props) => ( 141 | 142 | 143 | 144 | 145 | 146 | ) 147 | 148 | export default withBasketData(MyComponent); 149 | ``` 150 | 151 | 152 | ## Licence 153 | 154 | This project is licensed under the terms of the [MIT license](/LICENSE). 155 | 156 | 157 | -------------------------------------------------------------------------------- /docs/demo-app/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { BasketProvider, Basket, BasketContext, BasketData, DataProvider } from '../../src'; 3 | import { createStyles, Theme, WithStyles, withStyles } from '@material-ui/core/styles'; 4 | import { AppBar, Toolbar, Typography, IconButton, Badge } from '@material-ui/core'; 5 | import ShoppingCart from '@material-ui/icons/ShoppingCart'; 6 | import { MyBasketDataProvider } from './MyBasketDataProvider'; 7 | import { Products } from './Products'; 8 | 9 | export interface AppProps extends WithStyles { } 10 | 11 | class App extends React.Component { 12 | render() { 13 | const { classes } = this.props; 14 | 15 | return ( 16 | 17 |
18 | 19 | 20 | 21 | react-basket demo 22 | 23 |
24 | 25 | {(basketData: BasketData) => ( 26 | 27 | 28 | 29 | 30 | 31 | )} 32 | 33 | 34 | 35 | 36 |
37 | 38 |
39 | 40 |
41 |
42 | 43 | ); 44 | } 45 | } 46 | 47 | const styles = (theme: Theme) => 48 | createStyles({ 49 | root: { 50 | 51 | }, 52 | grow: { 53 | flexGrow: 1, 54 | }, 55 | title: { 56 | display: 'none', 57 | [theme.breakpoints.up('sm')]: { 58 | display: 'block', 59 | }, 60 | }, 61 | }); 62 | 63 | 64 | export default withStyles(styles)(App); -------------------------------------------------------------------------------- /docs/demo-app/MyBasketDataProvider.ts: -------------------------------------------------------------------------------- 1 | import { DataProvider, BasketItem } from "../../src"; 2 | 3 | export class MyBasketDataProvider implements DataProvider { 4 | 5 | registerToChanges(callback: (items: BasketItem[]) => void) { 6 | } 7 | 8 | products = [ 9 | { id: "1", name: 'Computer', price: 1722.44, quantity: 1 }, 10 | { id: "2", name: 'Phone', price: 522.14, quantity: 1 } 11 | ]; 12 | 13 | items = [ 14 | { id: "1", name: 'Computer', price: 1722.44, quantity: 1 }, 15 | { id: "2", name: 'Phone', price: 522.14, quantity: 2 } 16 | ]; 17 | 18 | getInitialData = (): Promise => { 19 | return new Promise((resolve, reject) => { 20 | setTimeout(() => { 21 | resolve(this.items) 22 | }, 1000) 23 | }); 24 | } 25 | 26 | onAllItemsDeleted(): Promise { 27 | return new Promise((resolve, reject) => { 28 | setTimeout(() => { 29 | this.items = []; 30 | 31 | resolve(this.items) 32 | }, 1000) 33 | }); 34 | } 35 | 36 | onItemAdded(id: string): Promise { 37 | return new Promise((resolve, reject) => { 38 | setTimeout(() => { 39 | let index = this.items.findIndex(item => item.id === id); 40 | if (index > -1) { 41 | this.items[index].quantity++; 42 | } 43 | else { 44 | const index = this.products.findIndex(item => item.id === id); 45 | if (index > -1) { 46 | const item = {...this.products[index]}; 47 | this.items.push(item); 48 | } 49 | } 50 | 51 | resolve(this.items) 52 | }, 1000) 53 | }); 54 | } 55 | 56 | onItemDeleted = (id: string): Promise => { 57 | return new Promise((resolve, reject) => { 58 | setTimeout(() => { 59 | 60 | const index = this.items.findIndex(item => item.id === id); 61 | if (index > -1) { 62 | this.items.splice(index, 1); 63 | } 64 | 65 | resolve(this.items) 66 | }, 1000) 67 | }); 68 | } 69 | } -------------------------------------------------------------------------------- /docs/demo-app/Products.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { CircularProgress, Card, CardContent, CardHeader, CardActions, Button, Typography } from '@material-ui/core'; 3 | import { BasketItem, withBasketData, BasketData } from '../../src'; 4 | 5 | class ProductsInner extends React.Component<{ basketData: BasketData }, any> { 6 | 7 | constructor(props: any) { 8 | super(props); 9 | 10 | this.state = { 11 | isLoading: false, 12 | items: [ 13 | { id: "1", name: 'Computer', price: 1722.44, quantity: 1 }, 14 | { id: "2", name: 'Phone', price: 522.14, quantity: 2 } 15 | ] 16 | } 17 | } 18 | 19 | public render() { 20 | if (this.state.isLoading) { 21 | return ( 22 |
23 | 24 |
25 | ); 26 | } 27 | 28 | if (this.state.error) { 29 | return ( 30 |
31 | Error: {this.state.error} 32 |
33 | ); 34 | } 35 | 36 | return ( 37 |
38 | {this.state.items.map((item: BasketItem) => ( 39 | 40 | 41 | 42 | {item.name} 43 | 44 | 45 | Price ${item.price} 46 | 47 | 48 | 49 | 56 | 57 | 58 | ))} 59 |
60 | ) 61 | } 62 | } 63 | 64 | export const Products = withBasketData(ProductsInner) -------------------------------------------------------------------------------- /docs/demo-app/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | background-color: #e8eaf5; 10 | } 11 | 12 | code { 13 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 14 | monospace; 15 | } 16 | -------------------------------------------------------------------------------- /docs/demo-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | react-basket demo 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /docs/demo-app/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from 'react-dom'; 3 | // @ts-ignore 4 | import { AppContainer } from 'react-hot-loader'; 5 | import App from './App'; 6 | import './index.css'; 7 | 8 | const rootEl = document.getElementById('root'); 9 | 10 | render( 11 | 12 | 13 | , 14 | rootEl 15 | ); 16 | 17 | // Hot Module Replacement API 18 | declare let module: { hot: any }; 19 | 20 | if (module.hot) { 21 | module.hot.accept('./App', () => { 22 | const NewApp = require('./App').default; 23 | 24 | render( 25 | 26 | 27 | , 28 | rootEl 29 | ); 30 | }); 31 | } -------------------------------------------------------------------------------- /docs/demo-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | // shared config (dev and prod) 2 | const { resolve } = require('path'); 3 | const { CheckerPlugin } = require('awesome-typescript-loader'); 4 | const StyleLintPlugin = require('stylelint-webpack-plugin'); 5 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | const webpack = require('webpack'); 7 | 8 | module.exports = { 9 | mode: 'development', 10 | entry: [ 11 | 'react-hot-loader/patch', // activate HMR for React 12 | 'webpack-dev-server/client?http://localhost:8080', // bundle the client for webpack-dev-server and connect to the provided endpoint 13 | 'webpack/hot/only-dev-server', // bundle the client for hot reloading, only- means to only hot reload for successful updates 14 | './index.tsx' // the entry point of our app 15 | ], 16 | output: { 17 | filename: 'js/main.min.js', 18 | path: resolve(process.cwd(), './docs'), 19 | publicPath: './', 20 | }, 21 | devServer: { 22 | hot: true, // enable HMR on the server 23 | headers: { 24 | 'Access-Control-Allow-Origin': '*', 25 | 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept', 26 | 'Access-Control-Allow-Methods': 'POST, PUT, GET, OPTIONS' 27 | } 28 | }, 29 | devtool: 'cheap-module-eval-source-map', 30 | plugins: [ 31 | 32 | ], 33 | resolve: { 34 | extensions: ['.ts', '.tsx', '.js', '.jsx'] 35 | }, 36 | context: resolve(__dirname), 37 | module: { 38 | rules: [ 39 | { 40 | test: /\.js$/, 41 | use: ['babel-loader', 'source-map-loader'], 42 | exclude: /node_modules/ 43 | }, 44 | { 45 | test: /\.tsx?$/, 46 | use: ['babel-loader', 'awesome-typescript-loader'] 47 | }, 48 | { 49 | test: /\.css$/, 50 | use: [ 51 | 'style-loader', 52 | { loader: 'css-loader', options: { importLoaders: 1 } } 53 | ] 54 | }, 55 | { 56 | test: /\.scss$/, 57 | loaders: [ 58 | 'style-loader', 59 | { loader: 'css-loader', options: { importLoaders: 1 } }, 60 | 'sass-loader' 61 | ] 62 | }, 63 | { 64 | test: /\.(jpe?g|png|gif|svg)$/i, 65 | loaders: [ 66 | 'file-loader?hash=sha512&digest=hex&name=img/[hash].[ext]', 67 | 'image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false' 68 | ] 69 | } 70 | ] 71 | }, 72 | plugins: [ 73 | new CheckerPlugin(), 74 | new StyleLintPlugin(), 75 | new HtmlWebpackPlugin({ template: './index.html' }), 76 | new webpack.HotModuleReplacementPlugin(), // enable HMR globally 77 | new webpack.NamedModulesPlugin() // prints more readable module names in the browser console on HMR updates 78 | ], 79 | performance: { 80 | hints: false 81 | } 82 | }; -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | react-basket demo 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-basket", 3 | "version": "0.2.1", 4 | "description": "A shopping basket components library for React based on material-ui components.", 5 | "main": "./dist/index.js", 6 | "homepage": "https://github.com/mbrn/react-basket#readme", 7 | "author": "Mehmet Baran", 8 | "license": "MIT", 9 | "files": [ 10 | "dist" 11 | ], 12 | "scripts": { 13 | "start": "webpack-dev-server --config=docs/demo-app/webpack.config.js", 14 | "build": "tsc", 15 | "build:docs": "webpack --config=docs/demo-app/webpack.config.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/mbrn/react-basket.git" 20 | }, 21 | "keywords": [ 22 | "javascript", 23 | "shopping", 24 | "cart", 25 | "basket", 26 | "react", 27 | "typescript" 28 | ], 29 | "bugs": { 30 | "url": "https://github.com/mbrn/react-basket/issues" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "^24.0.11", 34 | "@types/node": "^11.11.3", 35 | "@types/react": "^16.8.8", 36 | "@types/react-dom": "^16.8.2", 37 | "awesome-typescript-loader": "^5.2.1", 38 | "babel-loader": "^8.0.2", 39 | "css-loader": "^1.0.0", 40 | "html-webpack-plugin": "^3.2.0", 41 | "react-dom": "16.5.2", 42 | "react-hot-loader": "^4.3.11", 43 | "stylelint": "^9.5.0", 44 | "stylelint-webpack-plugin": "^0.10.5", 45 | "style-loader": "^0.23.0", 46 | "typescript": "^3.1.3", 47 | "webpack": "4.19.1", 48 | "webpack-cli": "^3.1.0", 49 | "webpack-dev-server": "^3.1.8" 50 | }, 51 | "dependencies": { 52 | "material-table": "^1.23.6" 53 | }, 54 | "peerDependencies": { 55 | "@material-ui/core": "^3.9.2", 56 | "@material-ui/icons": "^3.0.2", 57 | "react": "16.5.2" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/basket-context/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { BasketData, BasketItem } from '../'; 3 | 4 | const BasketContext = React.createContext({ 5 | items: [], 6 | onAllItemsDeleted: () => {}, 7 | onItemAdded: (id: string) => {}, 8 | onItemDeleted: (id: string) => {} 9 | }); 10 | 11 | const withBasketData = (Wrap: any) => (props: T) => { 12 | return ( 13 | 14 | {(basketData: BasketData) => ( 15 | 16 | )} 17 | 18 | ) 19 | } 20 | 21 | export { BasketContext, withBasketData } -------------------------------------------------------------------------------- /src/basket-data/index.ts: -------------------------------------------------------------------------------- 1 | export interface BasketData { 2 | isLoading?: boolean; 3 | items: BasketItem[]; 4 | 5 | 6 | onAllItemsDeleted: () => void; 7 | onItemAdded: (id: string) => void; 8 | onItemDeleted: (id: string) => void; 9 | } 10 | 11 | export interface BasketItem { 12 | id: string; 13 | name: string; 14 | image?: string; 15 | link?: string; 16 | quantity: number; 17 | price: number; 18 | maxCount?: number 19 | } -------------------------------------------------------------------------------- /src/basket-provider/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { BasketContext } from '../'; 3 | import { BasketData, BasketItem } from '../basket-data'; 4 | 5 | 6 | export interface DataProvider { 7 | getInitialData(): Promise; 8 | onAllItemsDeleted(): Promise; 9 | onItemAdded(id: string): Promise; 10 | onItemDeleted(id: string): Promise; 11 | registerToChanges(callback: (items: BasketItem[]) => void): any; 12 | } 13 | 14 | export interface BasketProviderProps { 15 | dataProvider: DataProvider; 16 | } 17 | 18 | export class BasketProvider extends React.Component { 19 | 20 | constructor(props: BasketProviderProps) { 21 | super(props); 22 | 23 | this.state = { 24 | isLoading: false, 25 | items: [], 26 | onAllItemsDeleted: this.onAllItemsAdded, 27 | onItemAdded: this.onItemAdded, 28 | onItemDeleted: this.onItemDeleted, 29 | } 30 | } 31 | 32 | componentWillMount() { 33 | this.props.dataProvider.registerToChanges((items: BasketItem[]) => { 34 | this.setState({ items }); 35 | }) 36 | } 37 | 38 | componentDidMount() { 39 | this.setState({ isLoading: true }, () => { 40 | this.props.dataProvider.getInitialData() 41 | .then(items => { 42 | this.setState({ items, isLoading: false }) 43 | }); 44 | }) 45 | } 46 | 47 | onAllItemsAdded = () => { 48 | this.setState({ isLoading: true }, () => { 49 | this.props.dataProvider.onAllItemsDeleted() 50 | .then(items => { 51 | this.setState({ items, isLoading: false }) 52 | }); 53 | }) 54 | } 55 | 56 | onItemAdded = (id: string) => { 57 | this.setState({ isLoading: true }, () => { 58 | this.props.dataProvider.onItemAdded(id) 59 | .then(items => { 60 | this.setState({ items, isLoading: false }) 61 | }); 62 | }) 63 | } 64 | 65 | onItemDeleted = (id: string) => { 66 | this.setState({ isLoading: true }, () => { 67 | this.props.dataProvider.onItemDeleted(id) 68 | .then(items => { 69 | this.setState({ items, isLoading: false }) 70 | }); 71 | }) 72 | } 73 | 74 | public render() { 75 | return ( 76 | 77 | {this.props.children} 78 | 79 | ); 80 | } 81 | } -------------------------------------------------------------------------------- /src/components/basket.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Paper, Card, CardContent, CardActions, Button, Typography } from '@material-ui/core'; 3 | import MaterialTable from 'material-table'; 4 | import DeleteOutline from '@material-ui/icons/DeleteOutline'; 5 | import { withBasketData } from '../basket-context'; 6 | import { BasketData } from '../basket-data'; 7 | import { Omit } from '@material-ui/core'; 8 | 9 | 10 | export interface BasketProps { 11 | basketData: BasketData, 12 | showPaymentButton?: boolean 13 | } 14 | 15 | class BasketInner extends React.Component { 16 | static defaultProps = { 17 | basketData: null, 18 | showPaymentButton: true 19 | } 20 | 21 | public render() { 22 | return ( 23 | 24 | 25 | 26 | 29 | }} 30 | isLoading={this.props.basketData.isLoading} 31 | title="Shopping Cart" 32 | data={this.props.basketData.items} 33 | actions={[ 34 | { 35 | icon: () => , 36 | tooltip: 'Delete Item(s)', 37 | onClick: (e:any, rowData:any) => { 38 | this.props.basketData.onItemDeleted(rowData.id); 39 | } 40 | }, 41 | { 42 | icon: () => , 43 | tooltip: 'Delete All Item', 44 | onClick: () => { 45 | this.props.basketData.onAllItemsDeleted(); 46 | }, 47 | isFreeAction: true 48 | } 49 | ]} 50 | columns={[ 51 | { title: 'Product', field: 'name' }, 52 | { title: 'Quantity', field: 'quantity', type: 'numeric' }, 53 | { title: 'Price', field: 'price', type: 'currency' }, 54 | ]} 55 | options={{ 56 | actionsColumnIndex: -1, 57 | emptyRowsWhenPaging: false, 58 | paging: false, 59 | search: false 60 | }} 61 | localization={{ 62 | body: { 63 | emptyDataSourceMessage: 'No item in your shopping cart' 64 | }, 65 | header: { 66 | actions: '' 67 | } 68 | }} 69 | /> 70 | 71 |
72 | 73 |
74 |
75 | {this.props.showPaymentButton && 76 | 77 | 78 | Total: $1987.22 79 | 80 | 87 | 88 | } 89 |
90 | ); 91 | } 92 | } 93 | 94 | export const Basket = withBasketData>(BasketInner); -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { Basket } from './components/basket'; 2 | 3 | 4 | export { BasketData, BasketItem } from './basket-data'; 5 | export { BasketProvider, DataProvider } from './basket-provider'; 6 | export { BasketContext, withBasketData } from './basket-context'; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "jsx": "react", 5 | "outDir": "./dist", 6 | "target": "es2015", 7 | "module": "commonjs", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true 11 | }, 12 | "include": [ 13 | "src/**/*" 14 | ] 15 | } --------------------------------------------------------------------------------