diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/actions/app.js | 93 | ||||
-rw-r--r-- | src/actions/counter.js | 24 | ||||
-rw-r--r-- | src/actions/shop.js | 79 | ||||
-rw-r--r-- | src/components/button-shared-styles.js | 26 | ||||
-rw-r--r-- | src/components/counter-element.js | 66 | ||||
-rw-r--r-- | src/components/my-app.js | 268 | ||||
-rw-r--r-- | src/components/my-icons.js | 17 | ||||
-rw-r--r-- | src/components/my-view1.js | 37 | ||||
-rw-r--r-- | src/components/my-view2.js | 72 | ||||
-rw-r--r-- | src/components/my-view3.js | 111 | ||||
-rw-r--r-- | src/components/my-view404.js | 31 | ||||
-rw-r--r-- | src/components/page-view-element.js | 24 | ||||
-rw-r--r-- | src/components/shared-styles.js | 60 | ||||
-rw-r--r-- | src/components/shop-cart.js | 67 | ||||
-rw-r--r-- | src/components/shop-item.js | 33 | ||||
-rw-r--r-- | src/components/shop-products.js | 68 | ||||
-rw-r--r-- | src/components/snack-bar.js | 54 | ||||
-rw-r--r-- | src/reducers/app.js | 51 | ||||
-rw-r--r-- | src/reducers/counter.js | 30 | ||||
-rw-r--r-- | src/reducers/shop.js | 199 | ||||
-rw-r--r-- | src/store.js | 39 |
21 files changed, 1449 insertions, 0 deletions
diff --git a/src/actions/app.js b/src/actions/app.js new file mode 100644 index 0000000..50d1529 --- /dev/null +++ b/src/actions/app.js @@ -0,0 +1,93 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +export const UPDATE_PAGE = 'UPDATE_PAGE'; +export const UPDATE_OFFLINE = 'UPDATE_OFFLINE'; +export const UPDATE_DRAWER_STATE = 'UPDATE_DRAWER_STATE'; +export const OPEN_SNACKBAR = 'OPEN_SNACKBAR'; +export const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR'; + +export const navigate = (path) => (dispatch) => { + // Extract the page name from path. + const page = path === '/' ? 'view1' : path.slice(1); + + // Any other info you might want to extract from the path (like page type), + // you can do here + dispatch(loadPage(page)); + + // Close the drawer - in case the *path* change came from a link in the drawer. + dispatch(updateDrawerState(false)); +}; + +const loadPage = (page) => (dispatch) => { + switch(page) { + case 'view1': + import('../components/my-view1.js').then((module) => { + // Put code in here that you want to run every time when + // navigating to view1 after my-view1.js is loaded. + }); + break; + case 'view2': + import('../components/my-view2.js'); + break; + case 'view3': + import('../components/my-view3.js'); + break; + default: + page = 'view404'; + import('../components/my-view404.js'); + } + + dispatch(updatePage(page)); +}; + +const updatePage = (page) => { + return { + type: UPDATE_PAGE, + page + }; +}; + +let snackbarTimer; + +export const showSnackbar = () => (dispatch) => { + dispatch({ + type: OPEN_SNACKBAR + }); + clearTimeout(snackbarTimer); + snackbarTimer = setTimeout(() => + dispatch({ type: CLOSE_SNACKBAR }), 3000); +}; + +export const updateOffline = (offline) => (dispatch, getState) => { + // Show the snackbar, unless this is the first load of the page. + if (getState().app.offline !== undefined) { + dispatch(showSnackbar()); + } + dispatch({ + type: UPDATE_OFFLINE, + offline + }); +}; + +export const updateLayout = (wide) => (dispatch, getState) => { + if (getState().app.drawerOpened) { + dispatch(updateDrawerState(false)); + } +}; + +export const updateDrawerState = (opened) => (dispatch, getState) => { + if (getState().app.drawerOpened !== opened) { + dispatch({ + type: UPDATE_DRAWER_STATE, + opened + }); + } +}; diff --git a/src/actions/counter.js b/src/actions/counter.js new file mode 100644 index 0000000..4ca5254 --- /dev/null +++ b/src/actions/counter.js @@ -0,0 +1,24 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +export const INCREMENT = 'INCREMENT'; +export const DECREMENT = 'DECREMENT'; + +export const increment = () => { + return { + type: INCREMENT + }; +}; + +export const decrement = () => { + return { + type: DECREMENT + }; +}; diff --git a/src/actions/shop.js b/src/actions/shop.js new file mode 100644 index 0000000..4542186 --- /dev/null +++ b/src/actions/shop.js @@ -0,0 +1,79 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +export const GET_PRODUCTS = 'GET_PRODUCTS'; +export const ADD_TO_CART = 'ADD_TO_CART'; +export const REMOVE_FROM_CART = 'REMOVE_FROM_CART'; +export const CHECKOUT_SUCCESS = 'CHECKOUT_SUCCESS'; +export const CHECKOUT_FAILURE = 'CHECKOUT_FAILURE'; + +const PRODUCT_LIST = [ + {"id": 1, "title": "Cabot Creamery Extra Sharp Cheddar Cheese", "price": 10.99, "inventory": 2}, + {"id": 2, "title": "Cowgirl Creamery Mt. Tam Cheese", "price": 29.99, "inventory": 10}, + {"id": 3, "title": "Tillamook Medium Cheddar Cheese", "price": 8.99, "inventory": 5}, + {"id": 4, "title": "Point Reyes Bay Blue Cheese", "price": 24.99, "inventory": 7}, + {"id": 5, "title": "Shepherd's Halloumi Cheese", "price": 11.99, "inventory": 3} +]; + +export const getAllProducts = () => (dispatch, getState) => { + // Here you would normally get the data from the server. We're simulating + // that by dispatching an async action (that you would dispatch when you + // succesfully got the data back) + + // You could reformat the data in the right format as well: + const products = PRODUCT_LIST.reduce((obj, product) => { + obj[product.id] = product + return obj + }, {}); + + dispatch({ + type: GET_PRODUCTS, + products: products + }); +}; + +export const checkout = (productId) => (dispatch) => { + // Here you could do things like credit card validation, etc. + // If that fails, dispatch CHECKOUT_FAILURE. We're simulating that + // by flipping a coin :) + const flip = Math.floor(Math.random() * 2); + if (flip === 0) { + dispatch({ + type: CHECKOUT_FAILURE + }); + } else { + dispatch({ + type: CHECKOUT_SUCCESS + }); + } +}; + +export const addToCart = (productId) => (dispatch, getState) =>{ + const state = getState(); + // Just because the UI thinks you can add this to the cart + // doesn't mean it's in the inventory (user could've fixed it); + if (state.shop.products[productId].inventory > 0) { + dispatch(addToCartUnsafe(productId)); + } +}; + +export const removeFromCart = (productId) => { + return { + type: REMOVE_FROM_CART, + productId + }; +}; + +export const addToCartUnsafe = (productId) => { + return { + type: ADD_TO_CART, + productId + }; +}; diff --git a/src/components/button-shared-styles.js b/src/components/button-shared-styles.js new file mode 100644 index 0000000..2f167e4 --- /dev/null +++ b/src/components/button-shared-styles.js @@ -0,0 +1,26 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; + +export const ButtonSharedStyles = html` +<style> + button { + font-size: inherit; + vertical-align: middle; + background: transparent; + border: none; + cursor: pointer; + } + button:hover svg { + fill: var(--app-primary-color); + } +</style> +`; diff --git a/src/components/counter-element.js b/src/components/counter-element.js new file mode 100644 index 0000000..680c077 --- /dev/null +++ b/src/components/counter-element.js @@ -0,0 +1,66 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement, html } from '@polymer/lit-element'; + +// These are the elements needed by this element. +import { plusIcon, minusIcon } from './my-icons.js'; + +// These are the shared styles needed by this element. +import { ButtonSharedStyles } from './button-shared-styles.js'; + +// This is a reusable element. It is not connected to the store. You can +// imagine that it could just as well be a third-party element that you +// got from someone else. +class CounterElement extends LitElement { + _render(props) { + return html` + ${ButtonSharedStyles} + <style> + span { width: 20px; display: inline-block; text-align: center; font-weight: bold;} + </style> + <div> + <p> + Clicked: <span>${props.clicks}</span> times. + Value is <span>${props.value}</span>. + <button on-click="${() => this._onIncrement()}" title="Add 1">${plusIcon}</button> + <button on-click="${() => this._onDecrement()}" title="Minus 1">${minusIcon}</button> + </p> + </div> + `; + } + + static get properties() { return { + /* The total number of clicks you've done. */ + clicks: Number, + /* The current value of the counter. */ + value: Number + }}; + + constructor() { + super(); + this.clicks = 0; + this.value = 0; + } + + _onIncrement() { + this.value++; + this.clicks++; + this.dispatchEvent(new CustomEvent('counter-incremented')); + } + + _onDecrement() { + this.value--; + this.clicks++; + this.dispatchEvent(new CustomEvent('counter-decremented')); + } +} + +window.customElements.define('counter-element', CounterElement); diff --git a/src/components/my-app.js b/src/components/my-app.js new file mode 100644 index 0000000..cb5cf19 --- /dev/null +++ b/src/components/my-app.js @@ -0,0 +1,268 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement, html } from '@polymer/lit-element'; +import { setPassiveTouchGestures } from '@polymer/polymer/lib/utils/settings.js'; +import { connect } from 'pwa-helpers/connect-mixin.js'; +import { installMediaQueryWatcher } from 'pwa-helpers/media-query.js'; +import { installOfflineWatcher } from 'pwa-helpers/network.js'; +import { installRouter } from 'pwa-helpers/router.js'; +import { updateMetadata } from 'pwa-helpers/metadata.js'; + +// This element is connected to the Redux store. +import { store } from '../store.js'; + +// These are the actions needed by this element. +import { + navigate, + updateOffline, + updateDrawerState, + updateLayout +} from '../actions/app.js'; + +// These are the elements needed by this element. +import '@polymer/app-layout/app-drawer/app-drawer.js'; +import '@polymer/app-layout/app-header/app-header.js'; +import '@polymer/app-layout/app-scroll-effects/effects/waterfall.js'; +import '@polymer/app-layout/app-toolbar/app-toolbar.js'; +import { menuIcon } from './my-icons.js'; +import './snack-bar.js'; + +class MyApp extends connect(store)(LitElement) { + _render({appTitle, _page, _drawerOpened, _snackbarOpened, _offline}) { + // Anything that's related to rendering should be done in here. + return html` + <style> + :host { + --app-drawer-width: 256px; + display: block; + + --app-primary-color: #E91E63; + --app-secondary-color: #293237; + --app-dark-text-color: var(--app-secondary-color); + --app-light-text-color: white; + --app-section-even-color: #f7f7f7; + --app-section-odd-color: white; + + --app-header-background-color: white; + --app-header-text-color: var(--app-dark-text-color); + --app-header-selected-color: var(--app-primary-color); + + --app-drawer-background-color: var(--app-secondary-color); + --app-drawer-text-color: var(--app-light-text-color); + --app-drawer-selected-color: #78909C; + } + + app-header { + position: fixed; + top: 0; + left: 0; + width: 100%; + text-align: center; + background-color: var(--app-header-background-color); + color: var(--app-header-text-color); + border-bottom: 1px solid #eee; + } + + .toolbar-top { + background-color: var(--app-header-background-color); + } + + [main-title] { + font-family: 'Pacifico'; + text-transform: lowercase; + font-size: 30px; + /* In the narrow layout, the toolbar is offset by the width of the + drawer button, and the text looks not centered. Add a padding to + match that button */ + padding-right: 44px; + } + + .toolbar-list { + display: none; + } + + .toolbar-list > a { + display: inline-block; + color: var(--app-header-text-color); + text-decoration: none; + line-height: 30px; + padding: 4px 24px; + } + + .toolbar-list > a[selected] { + color: var(--app-header-selected-color); + border-bottom: 4px solid var(--app-header-selected-color); + } + + .menu-btn { + background: none; + border: none; + fill: var(--app-header-text-color); + cursor: pointer; + height: 44px; + width: 44px; + } + + .drawer-list { + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 24px; + background: var(--app-drawer-background-color); + position: relative; + } + + .drawer-list > a { + display: block; + text-decoration: none; + color: var(--app-drawer-text-color); + line-height: 40px; + padding: 0 24px; + } + + .drawer-list > a[selected] { + color: var(--app-drawer-selected-color); + } + + /* Workaround for IE11 displaying <main> as inline */ + main { + display: block; + } + + .main-content { + padding-top: 64px; + min-height: 100vh; + } + + .page { + display: none; + } + + .page[active] { + display: block; + } + + footer { + padding: 24px; + background: var(--app-drawer-background-color); + color: var(--app-drawer-text-color); + text-align: center; + } + + /* Wide layout: when the viewport width is bigger than 460px, layout + changes to a wide layout. */ + @media (min-width: 460px) { + .toolbar-list { + display: block; + } + + .menu-btn { + display: none; + } + + .main-content { + padding-top: 107px; + } + + /* The drawer button isn't shown in the wide layout, so we don't + need to offset the title */ + [main-title] { + padding-right: 0px; + } + } + </style> + + <!-- Header --> + <app-header condenses reveals effects="waterfall"> + <app-toolbar class="toolbar-top"> + <button class="menu-btn" title="Menu" on-click="${_ => store.dispatch(updateDrawerState(true))}">${menuIcon}</button> + <div main-title>${appTitle}</div> + </app-toolbar> + + <!-- This gets hidden on a small screen--> + <nav class="toolbar-list"> + <a selected?="${_page === 'view1'}" href="/view1">View One</a> + <a selected?="${_page === 'view2'}" href="/view2">View Two</a> + <a selected?="${_page === 'view3'}" href="/view3">View Three</a> + </nav> + </app-header> + + <!-- Drawer content --> + <app-drawer opened="${_drawerOpened}" + on-opened-changed="${e => store.dispatch(updateDrawerState(e.target.opened))}"> + <nav class="drawer-list"> + <a selected?="${_page === 'view1'}" href="/view1">View One</a> + <a selected?="${_page === 'view2'}" href="/view2">View Two</a> + <a selected?="${_page === 'view3'}" href="/view3">View Three</a> + </nav> + </app-drawer> + + <!-- Main content --> + <main role="main" class="main-content"> + <my-view1 class="page" active?="${_page === 'view1'}"></my-view1> + <my-view2 class="page" active?="${_page === 'view2'}"></my-view2> + <my-view3 class="page" active?="${_page === 'view3'}"></my-view3> + <my-view404 class="page" active?="${_page === 'view404'}"></my-view404> + </main> + + <footer> + <p>Made with ♥ by the Polymer team.</p> + </footer> + + <snack-bar active?="${_snackbarOpened}"> + You are now ${_offline ? 'offline' : 'online'}.</snack-bar> + `; + } + + static get properties() { + return { + appTitle: String, + _page: String, + _drawerOpened: Boolean, + _snackbarOpened: Boolean, + _offline: Boolean + } + } + + constructor() { + super(); + // To force all event listeners for gestures to be passive. + // See https://www.polymer-project.org/3.0/docs/devguide/settings#setting-passive-touch-gestures + setPassiveTouchGestures(true); + } + + _firstRendered() { + installRouter((location) => store.dispatch(navigate(window.decodeURIComponent(location.pathname)))); + installOfflineWatcher((offline) => store.dispatch(updateOffline(offline))); + installMediaQueryWatcher(`(min-width: 460px)`, + (matches) => store.dispatch(updateLayout(matches))); + } + + _didRender(properties, changeList) { + if ('_page' in changeList) { + const pageTitle = properties.appTitle + ' - ' + changeList._page; + updateMetadata({ + title: pageTitle, + description: pageTitle + // This object also takes an image property, that points to an img src. + }); + } + } + + _stateChanged(state) { + this._page = state.app.page; + this._offline = state.app.offline; + this._snackbarOpened = state.app.snackbarOpened; + this._drawerOpened = state.app.drawerOpened; + } +} + +window.customElements.define('my-app', MyApp); diff --git a/src/components/my-icons.js b/src/components/my-icons.js new file mode 100644 index 0000000..76d5834 --- /dev/null +++ b/src/components/my-icons.js @@ -0,0 +1,17 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; + +export const menuIcon = html`<svg height="24" viewBox="0 0 24 24" width="24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg>`; +export const addToCartIcon = html`<svg height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0zm18.31 6l-2.76 5z" fill="none"/><path id="cart-path" d="M11 9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4 9c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zm10 0c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zm-9.83-3.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.86-7.01L19.42 4h-.01l-1.1 2-2.76 5H8.53l-.13-.27L6.16 6l-.95-2-.94-2H1v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.13 0-.25-.11-.25-.25z"/></svg>`; +export const removeFromCartIcon = html`<svg height="24" viewBox="0 0 24 24" width="24"><path d="M22.73 22.73L2.77 2.77 2 2l-.73-.73L0 2.54l4.39 4.39 2.21 4.66-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h7.46l1.38 1.38c-.5.36-.83.95-.83 1.62 0 1.1.89 2 1.99 2 .67 0 1.26-.33 1.62-.84L21.46 24l1.27-1.27zM7.42 15c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h2.36l2 2H7.42zm8.13-2c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H6.54l9.01 9zM7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2z"/><path d="M0 0h24v24H0z" fill="none"/></svg>`; +export const minusIcon = html`<svg height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>`; +export const plusIcon = html`<svg height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg>`; diff --git a/src/components/my-view1.js b/src/components/my-view1.js new file mode 100644 index 0000000..6ab10ac --- /dev/null +++ b/src/components/my-view1.js @@ -0,0 +1,37 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; +import { PageViewElement } from './page-view-element.js'; + +// These are the shared styles needed by this element. +import { SharedStyles } from './shared-styles.js'; + +class MyView1 extends PageViewElement { + _render(props) { + return html` + ${SharedStyles} + <section> + <h2>Static page</h2> + <p>This is a text-only page.</p> + <p>It doesn't do anything other than display some static text.</p> + </section> + <section> + <h2>Welcome</h2> + <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac nisi orci. Maecenas sollicitudin diam in diam efficitur cursus. Morbi sollicitudin in justo tincidunt placerat. Integer tincidunt elementum nisi, eu ornare dolor lacinia eget. Fusce pulvinar massa eget odio placerat, commodo molestie ipsum tempus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse porttitor id purus eu cursus. Suspendisse arcu nulla, mattis vel hendrerit et, malesuada a elit. Nam at diam ornare, aliquet est sed, malesuada metus. Cras nec enim vel nibh tincidunt euismod ut et enim. Etiam pharetra eros in sodales iaculis. Duis sagittis urna et cursus mollis. Cras tempor rutrum est. Praesent sollicitudin ligula at laoreet placerat. Praesent tortor dui, semper in sapien non, pharetra luctus turpis.</p> + </section> + <section> + <p>Vestibulum at est ex. Aenean id ligula id nibh dictum laoreet. Etiam non semper erat. Pellentesque eu justo rhoncus diam vulputate facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam feugiat metus ex, vel fringilla massa tincidunt sit amet. Nunc facilisis bibendum tristique. Mauris commodo, dolor vitae dapibus fermentum, odio nibh viverra lorem, eu cursus diam turpis et sapien. Nunc suscipit tortor a ligula tincidunt, id hendrerit tellus sollicitudin.</p> + </section> + `; + } +} + +window.customElements.define('my-view1', MyView1); diff --git a/src/components/my-view2.js b/src/components/my-view2.js new file mode 100644 index 0000000..0dee7ee --- /dev/null +++ b/src/components/my-view2.js @@ -0,0 +1,72 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; +import { PageViewElement } from './page-view-element.js'; +import { connect } from 'pwa-helpers/connect-mixin.js'; + +// This element is connected to the Redux store. +import { store } from '../store.js'; + +// These are the actions needed by this element. +import { increment, decrement } from '../actions/counter.js'; + +// We are lazy loading its reducer. +import counter from '../reducers/counter.js'; +store.addReducers({ + counter +}); + +// These are the elements needed by this element. +import './counter-element.js'; + +// These are the shared styles needed by this element. +import { SharedStyles } from './shared-styles.js'; + +class MyView2 extends connect(store)(PageViewElement) { + _render(props) { + return html` + ${SharedStyles} + <section> + <h2>Redux example: simple counter</h2> + <div class="circle">${props._value}</div> + <p>This page contains a reusable <code><counter-element></code>. The + element is not built in a Redux-y way (you can think of it as being a + third-party element you got from someone else), but this page is connected to the + Redux store. When the element updates its counter, this page updates the values + in the Redux store, and you can see the current value of the counter reflected in + the bubble above.</p> + <br><br> + </section> + <section> + <p> + <counter-element value="${props._value}" clicks="${props._clicks}" + on-counter-incremented="${() => store.dispatch(increment())}" + on-counter-decremented="${() => store.dispatch(decrement())}"> + </counter-element> + </p> + </section> + `; + } + + static get properties() { return { + // This is the data from the store. + _clicks: Number, + _value: Number + }} + + // This is called every time something is updated in the store. + _stateChanged(state) { + this._clicks = state.counter.clicks; + this._value = state.counter.value; + } +} + +window.customElements.define('my-view2', MyView2); diff --git a/src/components/my-view3.js b/src/components/my-view3.js new file mode 100644 index 0000000..e226eaa --- /dev/null +++ b/src/components/my-view3.js @@ -0,0 +1,111 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; +import { PageViewElement } from './page-view-element.js'; +import { connect } from 'pwa-helpers/connect-mixin.js'; + +// This element is connected to the Redux store. +import { store } from '../store.js'; + +// These are the actions needed by this element. +import { checkout } from '../actions/shop.js'; + +// We are lazy loading its reducer. +import shop, { cartQuantitySelector } from '../reducers/shop.js'; +store.addReducers({ + shop +}); + +// These are the elements needed by this element. +import './shop-products.js'; +import './shop-cart.js'; + +// These are the shared styles needed by this element. +import { SharedStyles } from './shared-styles.js'; +import { ButtonSharedStyles } from './button-shared-styles.js'; +import { addToCartIcon } from './my-icons.js'; + +class MyView3 extends connect(store)(PageViewElement) { + _render({_quantity, _error}) { + return html` + ${SharedStyles} + ${ButtonSharedStyles} + <style> + /* Add more specificity (.checkout) to workaround an issue in lit-element: + https://github.com/PolymerLabs/lit-element/issues/34 */ + button.checkout { + border: 2px solid var(--app-dark-text-color); + border-radius: 3px; + padding: 8px 16px; + } + button.checkout:hover { + border-color: var(--app-primary-color); + color: var(--app-primary-color); + } + .cart, .cart svg { + fill: var(--app-primary-color); + width: 64px; + height: 64px; + } + .circle.small { + margin-top: -72px; + width: 28px; + height: 28px; + font-size: 16px; + font-weight: bold; + line-height: 30px; + } + </style> + + <section> + <h2>Redux example: shopping cart</h2> + <div class="cart">${addToCartIcon}<div class="circle small">${_quantity}</div></div> + <p>This is a slightly more advanced Redux example, that simulates a + shopping cart: getting the products, adding/removing items to the + cart, and a checkout action, that can sometimes randomly fail (to + simulate where you would add failure handling). </p> + <p>This view, as well as its 2 child elements, <code><shop-products></code> and + <code><shop-cart></code> are connected to the Redux store.</p> + </section> + <section> + <h3>Products</h3> + <shop-products></shop-products> + + <br> + <h3>Your Cart</h3> + <shop-cart></shop-cart> + + <div>${_error}</div> + <br> + <p> + <button class="checkout" hidden="${_quantity == 0}" + on-click="${() => store.dispatch(checkout())}"> + Checkout + </button> + </p> + </section> + `; + } + + static get properties() { return { + // This is the data from the store. + _quantity: Number, + _error: String + }} + + // This is called every time something is updated in the store. + _stateChanged(state) { + this._quantity = cartQuantitySelector(state); + this._error = state.shop.error; + } +} + +window.customElements.define('my-view3', MyView3); diff --git a/src/components/my-view404.js b/src/components/my-view404.js new file mode 100644 index 0000000..38e39b8 --- /dev/null +++ b/src/components/my-view404.js @@ -0,0 +1,31 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; +import { PageViewElement } from './page-view-element.js'; + +// These are the shared styles needed by this element. +import { SharedStyles } from './shared-styles.js'; + +class MyView404 extends PageViewElement { + _render(props) { + return html` + ${SharedStyles} + <section> + <h2>Oops! You hit a 404</h2> + <p>The page you're looking for doesn't seem to exist. Head back + <a href="/">home</a> and try again? + </p> + </section> + ` + } +} + +window.customElements.define('my-view404', MyView404); diff --git a/src/components/page-view-element.js b/src/components/page-view-element.js new file mode 100644 index 0000000..dcb2192 --- /dev/null +++ b/src/components/page-view-element.js @@ -0,0 +1,24 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement } from '@polymer/lit-element'; + +export class PageViewElement extends LitElement { + // Only render this page if it's actually visible. + _shouldRender(props, changedProps, old) { + return props.active; + } + + static get properties() { + return { + active: Boolean + } + } +} diff --git a/src/components/shared-styles.js b/src/components/shared-styles.js new file mode 100644 index 0000000..0c91063 --- /dev/null +++ b/src/components/shared-styles.js @@ -0,0 +1,60 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { html } from '@polymer/lit-element'; + +export const SharedStyles = html` +<style> + :host { + display: block; + box-sizing: border-box; + } + + section { + padding: 24px; + background: var(--app-section-odd-color); + } + + section > * { + max-width: 600px; + margin-right: auto; + margin-left: auto; + } + + section:nth-of-type(even) { + background: var(--app-section-even-color); + } + + h2 { + font-size: 24px; + text-align: center; + color: var(--app-dark-text-color); + } + + @media (min-width: 460px) { + h2 { + font-size: 36px; + } + } + + .circle { + display: block; + width: 64px; + height: 64px; + margin: 0 auto; + text-align: center; + border-radius: 50%; + background: var(--app-primary-color); + color: var(--app-light-text-color); + font-size: 30px; + line-height: 64px; + } +</style> +`; diff --git a/src/components/shop-cart.js b/src/components/shop-cart.js new file mode 100644 index 0000000..1fd7f15 --- /dev/null +++ b/src/components/shop-cart.js @@ -0,0 +1,67 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement, html } from '@polymer/lit-element'; +import { connect } from 'pwa-helpers/connect-mixin.js'; + +// This element is connected to the Redux store. +import { store } from '../store.js'; + +// These are the elements needed by this element. +import { removeFromCartIcon } from './my-icons.js'; +import './shop-item.js'; + +// These are the actions needed by this element. +import { removeFromCart } from '../actions/shop.js'; + +// These are the reducers needed by this element. +import { cartItemsSelector, cartTotalSelector } from '../reducers/shop.js'; + +// These are the shared styles needed by this element. +import { ButtonSharedStyles } from './button-shared-styles.js'; + +class ShopCart extends connect(store)(LitElement) { + _render({_items, _total}) { + return html` + ${ButtonSharedStyles} + <style> + :host { display: block; } + </style> + <p hidden="${_items.length !== 0}">Please add some products to cart.</p> + ${_items.map((item) => + html` + <div> + <shop-item name="${item.title}" amount="${item.amount}" price="${item.price}"></shop-item> + <button + on-click="${(e) => store.dispatch(removeFromCart(e.currentTarget.dataset['index']))}" + data-index$="${item.id}" + title="Remove from cart"> + ${removeFromCartIcon} + </button> + </div> + ` + )} + <p hidden="${!_items.length}"><b>Total:</b> ${_total}</p> + `; + } + + static get properties() { return { + _items: Array, + _total: Number + }} + + // This is called every time something is updated in the store. + _stateChanged(state) { + this._items = cartItemsSelector(state); + this._total = cartTotalSelector(state); + } +} + +window.customElements.define('shop-cart', ShopCart); diff --git a/src/components/shop-item.js b/src/components/shop-item.js new file mode 100644 index 0000000..6c6dcb9 --- /dev/null +++ b/src/components/shop-item.js @@ -0,0 +1,33 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement, html } from '@polymer/lit-element'; + +// This element is *not* connected to the Redux store. +class ShopItem extends LitElement { + _render(props) { + return html` + ${props.name}: + <span hidden="${props.amount === 0}">${props.amount} * </span> + $${props.price} + </span> + `; + } + + static get properties() { + return { + name: String, + amount: String, + price: String + } + } +} + +window.customElements.define('shop-item', ShopItem); diff --git a/src/components/shop-products.js b/src/components/shop-products.js new file mode 100644 index 0000000..b5b33e2 --- /dev/null +++ b/src/components/shop-products.js @@ -0,0 +1,68 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement, html } from '@polymer/lit-element'; +import { connect } from 'pwa-helpers/connect-mixin.js'; + +// This element is connected to the Redux store. +import { store } from '../store.js'; + +// These are the elements needed by this element. +import './shop-item.js'; + +// These are the actions needed by this element. +import { getAllProducts, addToCart } from '../actions/shop.js'; + +// These are the elements needed by this element. +import { addToCartIcon } from './my-icons.js'; + +// These are the shared styles needed by this element. +import { ButtonSharedStyles } from './button-shared-styles.js'; + +class ShopProducts extends connect(store)(LitElement) { + _render({_products}) { + return html` + ${ButtonSharedStyles} + <style> + :host { display: block; } + </style> + ${Object.keys(_products).map((key) => { + const item = _products[key]; + return html` + <div> + <shop-item name="${item.title}" amount="${item.inventory}" price="${item.price}"></shop-item> + <button + disabled="${item.inventory === 0}" + on-click="${(e) => store.dispatch(addToCart(e.currentTarget.dataset['index']))}" + data-index$="${item.id}" + title="${item.inventory === 0 ? 'Sold out' : 'Add to cart' }"> + ${item.inventory === 0 ? 'Sold out': addToCartIcon } + </button> + </div> + ` + })} + `; + } + + static get properties() { return { + _products: Object + }} + + _firstRendered() { + store.dispatch(getAllProducts()); + } + + // This is called every time something is updated in the store. + _stateChanged(state) { + this._products = state.shop.products; + } +} + +window.customElements.define('shop-products', ShopProducts); diff --git a/src/components/snack-bar.js b/src/components/snack-bar.js new file mode 100644 index 0000000..521c1e9 --- /dev/null +++ b/src/components/snack-bar.js @@ -0,0 +1,54 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { LitElement, html } from '@polymer/lit-element'; + +class SnackBar extends LitElement { + _render(props) { + return html` + <style> + :host { + display: block; + position: fixed; + bottom: 0; + left: 0; + right: 0; + padding: 12px; + background-color: var(--app-secondary-color); + color: white; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + text-align: center; + will-change: transform; + transform: translate3d(0, 100%, 0); + transition-property: visibility, transform; + transition-duration: 0.2s; + visibility: hidden; + } + :host([active]) { + visibility: visible; + transform: translate3d(0, 0, 0); + } + @media (min-width: 460px) { + :host { + width: 320px; + margin: auto; + } + } + </style> + <slot></slot> + `; + } + + static get properties() { return { + active: Boolean, + }} +} + +window.customElements.define('snack-bar', SnackBar); diff --git a/src/reducers/app.js b/src/reducers/app.js new file mode 100644 index 0000000..375e214 --- /dev/null +++ b/src/reducers/app.js @@ -0,0 +1,51 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { + UPDATE_PAGE, + UPDATE_OFFLINE, + OPEN_SNACKBAR, + CLOSE_SNACKBAR, + UPDATE_DRAWER_STATE +} from '../actions/app.js'; + +const app = (state = {drawerOpened: false}, action) => { + switch (action.type) { + case UPDATE_PAGE: + return { + ...state, + page: action.page + }; + case UPDATE_OFFLINE: + return { + ...state, + offline: action.offline + }; + case UPDATE_DRAWER_STATE: + return { + ...state, + drawerOpened: action.opened + }; + case OPEN_SNACKBAR: + return { + ...state, + snackbarOpened: true + }; + case CLOSE_SNACKBAR: + return { + ...state, + snackbarOpened: false + }; + default: + return state; + } +}; + +export default app; diff --git a/src/reducers/counter.js b/src/reducers/counter.js new file mode 100644 index 0000000..523a0f7 --- /dev/null +++ b/src/reducers/counter.js @@ -0,0 +1,30 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { INCREMENT, DECREMENT } from '../actions/counter.js'; + +const counter = (state = {clicks: 0, value: 0}, action) => { + switch (action.type) { + case INCREMENT: + return { + 'clicks': state.clicks + 1, + 'value': state.value + 1 + }; + case DECREMENT: + return { + 'clicks': state.clicks + 1, + 'value': state.value - 1 + }; + default: + return state; + } +}; + +export default counter; diff --git a/src/reducers/shop.js b/src/reducers/shop.js new file mode 100644 index 0000000..1005ca7 --- /dev/null +++ b/src/reducers/shop.js @@ -0,0 +1,199 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { + GET_PRODUCTS, + ADD_TO_CART, + REMOVE_FROM_CART, + CHECKOUT_SUCCESS, + CHECKOUT_FAILURE +} from '../actions/shop.js'; +import { createSelector } from 'reselect'; + +const INITIAL_CART = { + addedIds: [], + quantityById: {} +}; + +const UPDATED_CART = { + addedIds: ['1'], + quantityById: {'1': 1} +}; + +const shop = (state = {products: {}, cart: INITIAL_CART}, action) => { + switch (action.type) { + case GET_PRODUCTS: + return { + ...state, + products: action.products + }; + case ADD_TO_CART: + case REMOVE_FROM_CART: + case CHECKOUT_SUCCESS: + return { + ...state, + products: products(state.products, action), + cart: cart(state.cart, action), + error: '' + }; + case CHECKOUT_FAILURE: + return { + ...state, + error: 'Checkout failed. Please try again' + }; + default: + return state; + } +}; + +// Slice reducer: it only reduces the bit of the state it's concerned about. +const products = (state, action) => { + switch (action.type) { + case ADD_TO_CART: + case REMOVE_FROM_CART: + const productId = action.productId; + return { + ...state, + [productId]: product(state[productId], action) + }; + default: + return state; + } +}; + +const product = (state, action) => { + switch (action.type) { + case ADD_TO_CART: + return { + ...state, + inventory: state.inventory - 1 + }; + case REMOVE_FROM_CART: + return { + ...state, + inventory: state.inventory + 1 + }; + default: + return state; + } +}; + +const cart = (state = INITIAL_CART, action) => { + switch (action.type) { + case ADD_TO_CART: + case REMOVE_FROM_CART: + return { + addedIds: addedIds(state.addedIds, state.quantityById, action), + quantityById: quantityById(state.quantityById, action) + }; + case CHECKOUT_SUCCESS: + return INITIAL_CART; + default: + return state; + } +}; + +const addedIds = (state = INITIAL_CART.addedIds, quantityById, action) => { + const productId = action.productId; + switch (action.type) { + case ADD_TO_CART: + if (state.indexOf(productId) !== -1) { + return state; + } + return [ + ...state, + action.productId + ]; + case REMOVE_FROM_CART: + // This is called before the state is updated, so if you have 1 item in the + // cart during the remove action, you'll have 0. + if (quantityById[productId] <= 1) { + // This removes all items in this array equal to productId. + return state.filter(e => e !== productId); + } + return state; + default: + return state; + } +}; + +const quantityById = (state = INITIAL_CART.quantityById, action) => { + const productId = action.productId; + switch (action.type) { + case ADD_TO_CART: + return { + ...state, + [productId]: (state[productId] || 0) + 1 + }; + case REMOVE_FROM_CART: + return { + ...state, + [productId]: (state[productId] || 0) - 1 + }; + default: + return state; + } +}; + +export default shop; + +// Per Redux best practices, the shop data in our store is structured +// for efficiency (small size and fast updates). +// +// The _selectors_ below transform store data into specific forms that +// are tailored for presentation. Putting this logic here keeps the +// layers of our app loosely coupled and easier to maintain, since +// views don't need to know about the store's internal data structures. +// +// We use a tiny library called `reselect` to create efficient +// selectors. More info: https://github.com/reduxjs/reselect. + +const cartSelector = state => state.shop.cart; +const productsSelector = state => state.shop.products; + +// Return a flattened array representation of the items in the cart +export const cartItemsSelector = createSelector( + cartSelector, + productsSelector, + (cart, products) => { + const items = []; + for (let id of cart.addedIds) { + const item = products[id]; + items.push({id: item.id, title: item.title, amount: cart.quantityById[id], price: item.price}); + } + return items; + } +); + +// Return the total cost of the items in the cart +export const cartTotalSelector = createSelector( + cartSelector, + productsSelector, + (cart, products) => { + let total = 0; + for (let id of cart.addedIds) { + const item = products[id]; + total += item.price * cart.quantityById[id]; + } + return parseFloat(Math.round(total * 100) / 100).toFixed(2); + } +); + +// Return the number of items in the cart +export const cartQuantitySelector = createSelector( + cartSelector, + cart => { + let num = 0; + for (let id of cart.addedIds) { + num += cart.quantityById[id]; + } + return num; + } +); diff --git a/src/store.js b/src/store.js new file mode 100644 index 0000000..87ca9f8 --- /dev/null +++ b/src/store.js @@ -0,0 +1,39 @@ +/** +@license +Copyright (c) 2018 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +import { + createStore, + compose as origCompose, + applyMiddleware, + combineReducers +} from 'redux'; +import thunk from 'redux-thunk'; +import { lazyReducerEnhancer } from 'pwa-helpers/lazy-reducer-enhancer.js'; + +import app from './reducers/app.js'; + +// Sets up a Chrome extension for time travel debugging. +// See https://github.com/zalmoxisus/redux-devtools-extension for more information. +const compose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || origCompose; + +// Initializes the Redux store with a lazyReducerEnhancer (so that you can +// lazily add reducers after the store has been created) and redux-thunk (so +// that you can dispatch async actions). See the "Redux and state management" +// section of the wiki for more details: +// https://github.com/Polymer/pwa-starter-kit/wiki/4.-Redux-and-state-management +export const store = createStore( + (state, action) => state, + compose(lazyReducerEnhancer(combineReducers), applyMiddleware(thunk)) +); + +// Initially loaded reducers. +store.addReducers({ + app +}); |