├── testem.yml ├── .gitignore ├── .travis.yml ├── .flowconfig ├── flow ├── lib.js └── type.js ├── .eslintrc.yml ├── examples ├── basic │ ├── main.js │ ├── js │ │ ├── store.js │ │ └── App.vue │ ├── index.html │ └── main.scss └── webpack.config.js ├── src ├── config.js ├── utils.js ├── index.js ├── module.js └── Toast.vue ├── .babelrc ├── LICENSE ├── rollup.config.js ├── package.json └── README.md /testem.yml: -------------------------------------------------------------------------------- 1 | --- 2 | framework: mocha 3 | src_files: 4 | - .tmp/test.js 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | /dist/ 4 | /examples/*/__build__.js 5 | /.tmp/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | script: 5 | - npm run test 6 | - npm run build 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/ 3 | 4 | [include] 5 | 6 | [libs] 7 | flow 8 | 9 | [options] 10 | module.file_ext=.js 11 | module.file_ext=.vue 12 | -------------------------------------------------------------------------------- /flow/lib.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | declare module vue { 4 | declare module.exports: any; 5 | } 6 | 7 | declare module vuex { 8 | declare module.exports: any; 9 | } 10 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: eslint-config-ktsn 3 | parser: babel-eslint 4 | plugins: 5 | - html 6 | - flowtype 7 | rules: 8 | flowtype/define-flow-type: 1 9 | flowtype/use-flow-type: 1 10 | -------------------------------------------------------------------------------- /examples/basic/main.js: -------------------------------------------------------------------------------- 1 | import './main.scss' 2 | 3 | import Vue from 'vue' 4 | 5 | import store from './js/store' 6 | import App from './js/App' 7 | 8 | new Vue({ 9 | el: '#app', 10 | store, 11 | render: h => h(App) 12 | }) 13 | -------------------------------------------------------------------------------- /examples/basic/js/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import { createModule } from 'vuex-toast' 4 | 5 | Vue.use(Vuex) 6 | 7 | export default new Vuex.Store({ 8 | modules: { 9 | toast: createModule() 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /examples/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vuex Toast basic example 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | export const DefaultTransition = { 2 | functional: true, 3 | render(h, { children }) { 4 | const data = { 5 | attrs: { tag: 'div', name: 'toast', type: 'transition' } 6 | } 7 | return h('transition-group', data, children) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ], 5 | "plugins": [ 6 | "transform-flow-strip-types", 7 | "transform-object-rest-spread" 8 | ], 9 | "env": { 10 | "test": { 11 | "presets": ["power-assert"] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /flow/type.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | interface ToastMessage { 4 | id: number; 5 | text: string; 6 | type: string; 7 | dismissAfter: number; 8 | } 9 | 10 | interface ToastState { 11 | messages: ToastMessage[]; 12 | } 13 | 14 | interface ToastOptions { 15 | dismissInterval?: number; 16 | } 17 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /** 4 | * Simple update without mutation 5 | */ 6 | export function update(obj: Object, updater: Object): Object { 7 | const res = {} 8 | Object.keys(obj).forEach(key => { 9 | res[key] = updater[key] === undefined ? obj[key] : updater[key] 10 | }) 11 | return res 12 | } 13 | -------------------------------------------------------------------------------- /examples/basic/main.scss: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background-color: #caf0f5; 9 | } 10 | 11 | body, 12 | input, 13 | select { 14 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; 15 | font-weight: 300; 16 | } 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Toast from './Toast.vue' 4 | import { update } from './utils' 5 | 6 | export function createComponent(options = {}) { 7 | const { 8 | transition 9 | } = options 10 | 11 | return update(Toast, { 12 | components: { 13 | toastTransition: transition 14 | } 15 | }) 16 | } 17 | 18 | export { Toast } 19 | export * from './module' 20 | -------------------------------------------------------------------------------- /examples/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const path = require('path') 3 | 4 | module.exports = { 5 | entry: createEntry(['basic']), 6 | output: { 7 | path: __dirname, 8 | filename: '[name]/__build__.js' 9 | }, 10 | resolve: { 11 | alias: { 12 | 'vuex-toast': path.resolve(__dirname, '../src/index.js') 13 | }, 14 | extensions: ['.js', '.vue'] 15 | }, 16 | module: { 17 | rules: [ 18 | { enforce: 'pre', test: /\.scss$/, loader: 'sass-loader' }, 19 | { test: /\.vue$/, loader: 'vue-loader', options: { 20 | loaders: { 21 | scss: 'style-loader!css-loader!sass-loader' 22 | } 23 | }}, 24 | { test: /\.s?css$/, loader: 'style-loader!css-loader' }, 25 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } 26 | ] 27 | } 28 | } 29 | 30 | function createEntry(names) { 31 | const res = {} 32 | names.forEach(name => { 33 | res[name] = `./${name}/main.js` 34 | }) 35 | return res 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 katashin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/module.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | const PREFIX = '@@toast/' 4 | 5 | const ADD = `${PREFIX}ADD_TOAST_MESSAGE` 6 | const REMOVE = `${PREFIX}REMOVE_TOAST_MESSAGE` 7 | 8 | export { 9 | ADD as ADD_TOAST_MESSAGE, 10 | REMOVE as REMOVE_TOAST_MESSAGE 11 | } 12 | 13 | function createMessage(id: number, text: string, type: string, dismissAfter: number): ToastMessage { 14 | return { 15 | id, 16 | text, 17 | type, 18 | dismissAfter 19 | } 20 | } 21 | 22 | export function createModule(options: ToastOptions = {}) { 23 | const { 24 | dismissInterval = 5000 25 | } = options 26 | 27 | let maxToastId = 0 28 | 29 | const state: ToastState = { 30 | messages: [] 31 | } 32 | 33 | const getters = { 34 | toastMessages: (state: ToastState) => state.messages 35 | } 36 | 37 | const actions = { 38 | [ADD] ({ commit }, { text, type = 'info', dismissAfter = dismissInterval }) { 39 | const id = ++maxToastId 40 | 41 | commit(ADD, createMessage(id, text, type, dismissAfter)) 42 | setTimeout(() => commit(REMOVE, id), dismissAfter) 43 | }, 44 | 45 | [REMOVE] ({ commit }, id) { 46 | commit(REMOVE, id) 47 | } 48 | } 49 | 50 | const mutations = { 51 | [ADD] (state: ToastState, data: ToastMessage) { 52 | state.messages.push(data) 53 | }, 54 | 55 | [REMOVE] (state: ToastState, id: number) { 56 | state.messages = state.messages.filter(m => m.id !== id) 57 | } 58 | } 59 | 60 | return { 61 | state, 62 | getters, 63 | actions, 64 | mutations 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | const fs = require('fs') 3 | const path = require('path') 4 | const babel = require('rollup-plugin-babel') 5 | const vue = require('rollup-plugin-vue') 6 | const replace = require('rollup-plugin-replace') 7 | const sass = require('node-sass') 8 | const autoprefixer = require('autoprefixer') 9 | const postcss = require('postcss') 10 | const meta = require('./package.json') 11 | 12 | if (!fs.existsSync('dist')) { 13 | fs.mkdirSync('dist') 14 | } 15 | 16 | const prefixer = postcss([ 17 | autoprefixer({ 18 | browsers: ['> 1%', 'last 2 versions', 'IE >= 9'] 19 | }) 20 | ]) 21 | 22 | const banner = `/*! 23 | * ${meta.name} v${meta.version} 24 | * ${meta.homepage} 25 | * 26 | * @license 27 | * Copyright (c) 2016 ${meta.author} 28 | * Released under the MIT license 29 | * ${meta.homepage}/blob/master/LICENSE 30 | */` 31 | 32 | const name = 'VuexToast' 33 | 34 | const plugins = [ 35 | vue({ 36 | compileTemplate: true, 37 | css: !process.env.NODE_ENV && (styles => { 38 | const out = path.resolve(__dirname, './dist/vuex-toast.css') 39 | 40 | // compile scss 41 | sass.render({ 42 | data: styles, 43 | outputStyle: 'expanded', 44 | outFile: out 45 | }, (error, result) => { 46 | if (error) { 47 | console.error(formatSassError(error)) 48 | return 49 | } 50 | 51 | // autoprefixer 52 | prefixer.process(result.css, { from: undefined }).then(result => { 53 | result.warnings().forEach(warn => { 54 | console.warn(warn.toString()) 55 | }) 56 | fs.writeFile(out, result.css) 57 | }) 58 | }) 59 | }) 60 | }), 61 | babel({ 62 | exclude: 'node_modules/**' 63 | }) 64 | ] 65 | if (process.env.NODE_ENV) { 66 | plugins.push( 67 | replace({ 68 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) 69 | }) 70 | ) 71 | } 72 | 73 | module.exports = { 74 | input: 'src/index.js', 75 | plugins, 76 | output: { 77 | name, 78 | banner, 79 | globals: { 80 | vuex: 'Vuex' 81 | } 82 | }, 83 | external: [ 84 | 'vuex' 85 | ] 86 | } 87 | 88 | function formatSassError(e) { 89 | return `[${e.line}:${e.column}] ${e.message} (${e.file})` 90 | } 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex-toast", 3 | "version": "0.1.3", 4 | "author": "katashin", 5 | "description": "Simple toast notification using Vuex", 6 | "keywords": [ 7 | "UI", 8 | "Flux", 9 | "Vuex", 10 | "Vue", 11 | "toast" 12 | ], 13 | "license": "MIT", 14 | "main": "dist/vuex-toast.cjs.js", 15 | "files": [ 16 | "dist" 17 | ], 18 | "homepage": "https://github.com/ktsn/vuex-toast", 19 | "bugs": "https://github.com/ktsn/vuex-toast/issues", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/ktsn/vuex-toast.git" 23 | }, 24 | "scripts": { 25 | "prepublishOnly": "npm run flow && npm run lint && npm run build", 26 | "build": "npm run build:cjs && npm run build:dev && npm run build:prod", 27 | "build:cjs": "rollup -c rollup.config.js -f cjs -o dist/vuex-toast.cjs.js", 28 | "build:dev": "cross-env NODE_ENV=development rollup -c rollup.config.js -f umd -o dist/vuex-toast.js", 29 | "build:prod": "cross-env NODE_ENV=production rollup -c rollup.config.js -f umd | uglifyjs -mc warnings=false --comments -o dist/vuex-toast.min.js", 30 | "build:example": "cd examples && webpack", 31 | "dev:example": "cd examples && webpack-dev-server --inline --hot", 32 | "test": "npm run flow && npm run lint", 33 | "flow": "flow check", 34 | "lint": "eslint --ext .js,.vue src test flow example", 35 | "lint:fix": "npm run lint -- --fix" 36 | }, 37 | "devDependencies": { 38 | "autoprefixer": "^8.0.0", 39 | "babel-core": "^6.26.0", 40 | "babel-eslint": "^8.2.1", 41 | "babel-loader": "^7.1.2", 42 | "babel-plugin-transform-flow-strip-types": "^6.22.0", 43 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 44 | "babel-preset-es2015": "^6.24.1", 45 | "babel-preset-power-assert": "^2.0.0", 46 | "cross-env": "^5.1.3", 47 | "css-loader": "^0.28.9", 48 | "eslint": "^4.17.0", 49 | "eslint-config-ktsn": "^1.0.3", 50 | "eslint-plugin-flowtype": "^2.44.0", 51 | "eslint-plugin-html": "^4.0.2", 52 | "flow-bin": "^0.65.0", 53 | "node-sass": "^4.7.2", 54 | "postcss": "^6.0.18", 55 | "power-assert": "^1.4.4", 56 | "rollup": "^0.56.0", 57 | "rollup-plugin-babel": "^3.0.3", 58 | "rollup-plugin-replace": "^2.0.0", 59 | "rollup-plugin-vue": "^3.0.0", 60 | "sass-loader": "^6.0.6", 61 | "style-loader": "^0.20.2", 62 | "uglifyjs": "^2.4.11", 63 | "vue": "^2.5.13", 64 | "vue-loader": "^14.1.1", 65 | "vue-template-compiler": "^2.5.13", 66 | "vuex": "^3.0.1", 67 | "webpack": "^3.11.0", 68 | "webpack-dev-server": "^2.11.1" 69 | }, 70 | "peerDependencies": { 71 | "vue": "^2.0.0", 72 | "vuex": "^2.0.0 || ^3.0.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/basic/js/App.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 62 | 63 | 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vuex Toast 2 | 3 | Simple toast notification using Vuex 4 | 5 | ## Requirements 6 | 7 | - Vue >= 2.0 8 | - Vuex >= 2.0 9 | 10 | ## Demo 11 | 12 | http://codepen.io/ktsn/pen/Bzxkjd 13 | 14 | ## Install 15 | 16 | ```npm install vuex-toast --save``` 17 | 18 | 19 | ## Example 20 | 21 | First, you should register a toast module to your Vuex store. You can use a default style at `dist/vuex-toast.css`. 22 | 23 | ```js 24 | import Vue from 'vue' 25 | import Vuex from 'vuex' 26 | import { createModule } from 'vuex-toast' 27 | 28 | // If you want to use the default style (with webpack css-loader) 29 | import 'vuex-toast/dist/vuex-toast.css' 30 | 31 | Vue.use(Vuex) 32 | 33 | export default new Vuex.Store({ 34 | modules: { 35 | // ... 36 | toast: createModule({ 37 | dismissInterval: 8000 38 | }) 39 | // ... 40 | } 41 | }) 42 | ``` 43 | 44 | Put `Toast` component anywhere in your application. 45 | 46 | ```html 47 | 53 | 54 | 65 | ``` 66 | 67 | You can send notifications to the toast component with toast type. 68 | 69 | ```js 70 | import { mapActions } from 'vuex' 71 | import { ADD_TOAST_MESSAGE } from 'vuex-toast' 72 | 73 | export default { 74 | methods: { 75 | ...mapActions({ 76 | addToast: ADD_TOAST_MESSAGE 77 | }), 78 | 79 | sendNotification(text) { 80 | this.addToast({ 81 | text, 82 | type: 'success', 83 | dismissAfter: 10000 84 | }) 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | ## API 91 | ### `Toast` 92 | A Vue component that shows toast messages. 93 | 94 | - props 95 | - `position` 96 | - `html` 97 | - `namespace` 98 | - Vuex module's namespace if you install toast module into some namespaced module. 99 | 100 | ### `createModule(options): VuexModule` 101 | Create Vuex module for managing toast messages. 102 | 103 | - options 104 | - dismissInterval 105 | 106 | ### `createComponent(options): VueComponent` 107 | Create customized toast component. 108 | 109 | - options 110 | - transition 111 | 112 | ### Action Types 113 | - ADD_TOAST_MESSAGE 114 | - `dispatch(ADD_TOAST_MESSAGE, { text, type, dismissAfter })` 115 | - REMOVE_TOAST_MESSAGE 116 | - `dispatch(REMOVE_TOAST_MESSAGE, messageId)` 117 | 118 | ### Getters 119 | - toastMessage 120 | - get all toast messages. 121 | 122 | ### Toast Message Type 123 | - `id` Auto generated message ID 124 | - `text` Text of the toast message 125 | - `type` Type of the toast message 126 | - You can use any value for styling purpose. 127 | - There are default styles for `info`, `success`, `warning`, and `danger` 128 | - `dismissAfter` Milli-second that indicates the message dismiss after this time 129 | 130 | ## License 131 | 132 | MIT 133 | -------------------------------------------------------------------------------- /src/Toast.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 74 | 75 | 206 | --------------------------------------------------------------------------------