├── .browserslistrc
├── .lintstagedrc
├── .commitlintrc.json
├── postcss.config.js
├── assets
└── 480w
│ └── breadstick-logo.png
├── docs
├── .vuepress
│ ├── public
│ │ └── breadstick-logo.png
│ ├── config.js
│ └── enhanceApp.js
├── support
│ └── README.md
├── guide
│ └── getting-started.md
├── README.md
├── examples
│ └── README.md
├── api
│ └── README.md
└── advanced
│ └── README.md
├── .editorconfig
├── src
├── index.js
├── plugin
│ └── index.js
└── components
│ ├── Alert
│ ├── alert.test.js
│ ├── index.js
│ └── styles.css
│ ├── Breadstick
│ └── index.js
│ ├── Message
│ └── index.js
│ └── BreadstickManager
│ └── index.js
├── .huskyrc
├── babel.config.js
├── jest.config.js
├── .gitignore
├── .eslintrc.js
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── CONTRIBUTING.md
├── PULL_REQUEST_TEMPLATE.md
└── CODE_OF_CONDUCT.md
├── .all-contributorsrc
├── LICENSE
├── rollup.config.js
├── package.json
└── README.md
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js,vue}": "eslint --fix"
3 | }
--------------------------------------------------------------------------------
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@commitlint/config-conventional"
4 | ]
5 | }
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/assets/480w/breadstick-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codebender828/breadstick/HEAD/assets/480w/breadstick-logo.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/breadstick-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codebender828/breadstick/HEAD/docs/.vuepress/public/breadstick-logo.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Breadstick from './components/Breadstick'
2 | export default Breadstick
3 | export { default as BreadstickBakery } from './plugin'
4 |
--------------------------------------------------------------------------------
/.huskyrc:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": {
3 | "pre-commit": "lint-staged",
4 | "pre-push": "yarn test",
5 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@babel/preset-env'
4 | ],
5 | plugins: [
6 | '@babel/plugin-proposal-class-properties'
7 | ],
8 | exclude: ['node_modules']
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugin/index.js:
--------------------------------------------------------------------------------
1 | import Breadstick from '../components/Breadstick'
2 |
3 | /**
4 | * Breadstick plugin
5 | */
6 | export default {
7 | install (Vue, options = {}) {
8 | Vue.prototype.$breadstick = new Breadstick()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: {
3 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
4 | '^.+\\.(js|jsx)?$': 'babel-jest'
5 | },
6 | testMatch: [
7 | '**/src/**/*.test.(js|jsx|ts|tsx)'
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/src/components/Alert/alert.test.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import { Alert } from '.'
3 |
4 | describe('===== Alert Component Tests =====', () => {
5 | let alert
6 | beforeEach(() => {
7 | alert = shallowMount(Alert, {
8 | propsData: {
9 | id: 123,
10 | title: 'Test notification',
11 | close: jest.fn(),
12 | clear: jest.fn(),
13 | reset: jest.fn()
14 | }
15 | })
16 | })
17 | describe('Instance tests', () => {
18 | it('should be a Vue instance', () => {
19 | expect(alert.isVueInstance()).toBeTruthy()
20 | })
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | jest: true
6 | },
7 | 'extends': [
8 | 'plugin:vue/essential',
9 | '@vue/standard'
10 | ],
11 | rules: {
12 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
14 | },
15 | parserOptions: {
16 | parser: 'babel-eslint'
17 | },
18 | overrides: [
19 | {
20 | files: [
21 | '**/__tests__/*.{j,t}s?(x)',
22 | '**/tests/unit/**/*.spec.{j,t}s?(x)'
23 | ],
24 | env: {
25 | jest: true
26 | }
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/docs/support/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Support
3 | ---
4 |
5 | ## ❤️ Support this project
6 | If you like this project, please consider supporting it by buying my a coffee!
7 |
8 |
9 |
10 | Buy me a coffee
11 |
12 |
13 |
14 |
15 | Made with ❤️ by Jonathan Bakebwa 🇺🇬
16 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "codebender828",
10 | "name": "Jonathan Bakebwa",
11 | "avatar_url": "https://avatars2.githubusercontent.com/u/21237954?v=4",
12 | "profile": "https://jbakebwa.dev",
13 | "contributions": [
14 | "code",
15 | "doc",
16 | "test"
17 | ]
18 | },
19 | {
20 | "login": "DominusKelvin",
21 | "name": "Omereshone Kelvin Oghenerhoro",
22 | "avatar_url": "https://avatars0.githubusercontent.com/u/24433274?v=4",
23 | "profile": "http://bit.ly/becomeworldclass",
24 | "contributions": [
25 | "doc"
26 | ]
27 | }
28 | ],
29 | "contributorsPerLine": 7,
30 | "projectName": "breadstick",
31 | "projectOwner": "codebender828",
32 | "repoType": "github",
33 | "repoHost": "https://github.com",
34 | "skipCi": true
35 | }
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a build.
11 | 2. Update the README.md with details of changes to the interface, this includes new environment
12 | variables, exposed ports, useful file locations and container parameters.
13 | 3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
14 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you.
15 |
--------------------------------------------------------------------------------
/docs/guide/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | ---
4 |
5 | ## :speaking_head: Introduction
6 | Breadstick is a simple and flexible implementation of toast style notifications for Vue.js.
7 |
8 | _🌟 Inspired by [toasted-notes](https://github.com/bmcmahen/toasted-notes) for React - which I think is really cool. ❤️_
9 |
10 | ## Demo
11 | See a live [demo](https://breadstick.now.sh/) of breadstick
12 |
13 | ## ⚡️ Installation
14 | Install `breadstick` and its peer dependency, `animejs`, using yarn or npm.
15 |
16 | ```bash
17 | npm install breadstick animejs --save
18 | ```
19 |
20 | You can then register `BreadstickBakery` as a Vue plugin.
21 | ```js
22 | import Vue from 'vue'
23 | import { BreadstickBakery } from 'breadstick'
24 |
25 | // This exposes `this.$breadstick` in your Vue template.
26 | Vue.use(BreadstickBakery)
27 | ```
28 |
29 | #### Installing with Nuxt
30 | After installing Breadstick, we import it and register it as a **client-side plugin**. This is because Breadstick it makes use of some DOM APIs. Code is similiar to the Vue plugin shown above.
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Jonathan Bakebwa
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 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | actionText: Get Started →
4 | actionLink: /guide/getting-started
5 | heroImage: /breadstick-logo.png
6 | features:
7 | - title: An imperative API
8 | details: This means that you don't need to set component state or render elements to trigger notifications. Instead, just call a function. This makes it very user friendly for component library authors.
9 | - title: Render whatever you want
10 | details: utilize the render callback to create entirely custom notifications.
11 | - title: Functional default styles
12 | details: Import the provided css for some nice styling defaults or write your own styles.
13 | - title: JS agnostic notifications
14 | details: Breadstick can be incrementally adopted to your application since it uses the already progressive Vue.js under the hood.
15 | footer: Made with 💚 by Jonathan Bakebwa
16 | ---
17 |
18 | ## 🥳 Getting Started
19 | Here are a few Codesandbox starters you can use to get started with Breadstick in your Vue or Nuxt App.
20 |
21 | * Vue Starter: [http://bit.ly/breadstick-vue](http://bit.ly/breadstick-vue)
22 | * Nuxt.js Starter: [http://bit.ly/breadstick-nuxt](http://bit.ly/breadstick-nuxt)
--------------------------------------------------------------------------------
/src/components/Alert/index.js:
--------------------------------------------------------------------------------
1 | import './styles.css'
2 |
3 | export const Alert = {
4 | name: 'Alert',
5 | props: {
6 | id: [String, Number],
7 | title: [String, Object],
8 | close: Function,
9 | clear: Function,
10 | reset: Function
11 | },
12 | render (h) {
13 | return h('span', {
14 | class: ['Breadstick__alert'],
15 | attrs: {
16 | id: this.id
17 | },
18 | on: {
19 | mouseenter: this.clear,
20 | mouseleave: this.reset
21 | }
22 | }, [
23 | typeof title === 'string'
24 | ? h('div', {
25 | class: ['Breadstick__alert_text']
26 | }, this.title) : this.title,
27 | !this.title && this.$slots.default,
28 | this.close && h(Close, {
29 | props: {
30 | close: this.close
31 | }
32 | })
33 | ])
34 | }
35 |
36 | }
37 |
38 | export const Close = {
39 | name: 'Close',
40 | props: {
41 | close: {
42 | type: Function,
43 | default: () => null
44 | }
45 | },
46 | render (h) {
47 | return h('button', {
48 | class: ['Breadstick__alert_close'],
49 | attrs: {
50 | type: 'button',
51 | 'aria-label': this.close
52 | },
53 | on: {
54 | click: this.close
55 | }
56 | }, [h('span', { attrs: { 'aria-hidden': true } }, '×')])
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/Alert/styles.css:
--------------------------------------------------------------------------------
1 | .Breadstick__alert {
2 | margin: 2.5px;
3 | background-color: white;
4 | overflow: hidden;
5 | max-width: 650px;
6 | position: relative;
7 | border-radius: 0.4rem;
8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
9 | display: flex;
10 | padding: 1rem;
11 | padding-right: 48px;
12 | box-shadow: rgba(52, 58, 64, 0.15) 0px 1px 10px 0px,
13 | rgba(52, 58, 64, 0.1) 0px 6px 12px 0px,
14 | rgba(52, 58, 64, 0.12) 0px 6px 15px -2px;
15 | }
16 |
17 | .Breadstick__alert_text {
18 | box-sizing: border-box;
19 | -webkit-font-smoothing: antialiased;
20 | font-weight: 500;
21 | line-height: 1.5;
22 | font-size: 1rem;
23 | margin: 0px;
24 | }
25 |
26 | .Breadstick__alert_close {
27 | padding: 12px;
28 | outline: none;
29 | cursor: pointer;
30 | background-color: transparent;
31 | position: absolute;
32 | top: 7px;
33 | right: 4px;
34 | border-radius: 0.4rem;
35 | border: 0;
36 | -webkit-appearance: none;
37 | font-size: 1rem;
38 | font-weight: 700;
39 | line-height: 1;
40 | text-shadow: 0 1px 0 #fff;
41 | opacity: 0.5;
42 | }
43 |
44 | .Breadstick__alert_close:focus {
45 | box-shadow: rgba(52, 58, 64, 0.15) 0px 0px 0px 3px;
46 | }
47 |
48 | .Breadstick__message-wrapper {
49 | padding: 8px;
50 | }
51 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 | ## Motivation and Context
7 |
8 |
9 |
10 | ## How Has This Been Tested?
11 |
12 |
13 |
14 |
15 | ## Screenshots (if appropriate):
16 |
17 | ## Types of changes
18 |
19 | - [ ] Bug fix (non-breaking change which fixes an issue)
20 | - [ ] New feature (non-breaking change which adds functionality)
21 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
22 |
23 | ## Checklist:
24 |
25 |
26 | - [ ] My code follows the code style of this project.
27 | - [ ] My change requires a change to the documentation.
28 | - [ ] I have updated the documentation accordingly.
29 | - [ ] I have added tests to cover my changes.
30 | - [ ] All new and existing tests passed.
31 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | title: 'Breadstick',
3 | description: "A simple but flexible implementation of toast style notifications for Vue.js.",
4 | themeConfig: {
5 | nav: [
6 | {
7 | text: 'Guide',
8 | link: '/guide/getting-started.html'
9 | },
10 | {
11 | text: 'API',
12 | link: '/api/'
13 | },
14 | {
15 | text: 'Examples',
16 | link: '/examples/'
17 | },
18 | {
19 | text: 'Github',
20 | link: 'https://github.com/codebender828/breadstick'
21 | },
22 | ],
23 | repo: 'codebender828/breadstick',
24 | docsDir: 'docs',
25 | docsBranch: 'dev',
26 | sidebar: [
27 | {
28 | title: 'Guide',
29 | collapsable: false,
30 | children: [
31 | '/guide/getting-started',
32 | ]
33 | },
34 | {
35 | title: 'Examples',
36 | collapsable: false,
37 | children: [
38 | '/examples/'
39 | ]
40 | },
41 | {
42 | title: 'Advanced Usage',
43 | collapsable: false,
44 | children: [
45 | '/advanced/'
46 | ]
47 | },
48 | {
49 | title: 'API',
50 | collapsable: false,
51 | children: [
52 | '/api/'
53 | ]
54 | },
55 | {
56 | title: '❤️ Support',
57 | collapsable: false,
58 | children: [
59 | '/support/'
60 | ]
61 | }
62 | ],
63 | editLinks: true,
64 | editLinkText: 'Help us improve this page!'
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/docs/examples/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Using Breadstick
3 | name: Using Breadstick
4 | ---
5 |
6 | ## 🥳 Starters
7 | Here are a few Codesandbox starters you can use to get started with Breadstick in your Vue or Nuxt App.
8 | - **[Vue.js Starter](http://bit.ly/breadstick-vue)**
9 | - **[Nuxt.js Starter](http://bit.ly/breadstick-nuxt)**
10 |
11 | Breadstick can be used to render different types of notifications out of the box. You can render simple string notifications as well as custom styled notifications. This makes it really convenient.
12 |
13 | ## 🍊 Basic usage
14 | Invoke a notification using the `notify` method to display a message to the user.
15 | Breadstick defaults the notification duration to 5 seconds in the `top` position.
16 |
17 | ```js
18 | this.$breadstick.notify('🥞 Show me the pancakes')
19 | ```
20 |
21 | ## 📚 Using different positions
22 | You can display notifications in different positions, including top-left, top, top-right, bottom-left, bottom, and bottom-right.
23 |
24 | ```js
25 | [
26 | 'top-left',
27 | 'top',
28 | 'top-right',
29 | 'bottom-left',
30 | 'bottom',
31 | 'bottom-right'
32 | ].forEach(position => {
33 | this.$breadstick.notify("Using position " + position, {
34 | position
35 | })
36 | })
37 | ```
38 |
39 | ## 🏠 Using custom element
40 | With JSX or Vue's render function, breadstick is able to render a custom element or Vue component
41 |
42 | ```jsx
43 | this.$breadstick.notify(
44 | I am a custom HTML element
45 | )
46 | ```
47 |
48 | ## 📭 Close all notifications
49 | You can clear all notifications by calling breadstick's `closeAll` method
50 |
51 | ```jsx
52 | this.$breadstick.closeAll()
53 | ```
--------------------------------------------------------------------------------
/docs/api/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: API
3 | ---
4 |
5 | ## `notify(String|VNode|Function, options)`
6 | - **Arguments**
7 | - `{ String | VNode | Function }` Message
8 | - `{ Object }` options
9 |
10 | Breadstick's `notify` method accepts two parameters. The first parameter can be a `String`, `VNode` (Object), or `Function` and the second is the options object.
11 |
12 | If a string is passed in the first argument, breadstick will render a notificiation with the string in the top center position with it's default internal component.
13 | ```js
14 | this.$breadstick.notify('Simple notification.')
15 | ```
16 |
17 | If a `VNode` is passed, Breadstick treats it like a rendered component and renders it instead.
18 | ```jsx
19 | this.$breadstick.notify(
20 | I am a custom HTML element
21 | )
22 | ```
23 |
24 | If a callback `Function` is passed in the first argument, it will expose an object with two parameters: `h` and the `onClose` which are both functions. Using a render callback allows you to tap into the close function. It's your best option if you want to completely re-style your toast notification
25 |
26 | ```js
27 | this.$breadstick.notify(({ h, onClose }) => {
28 | return h('div', 'My custom notification')
29 | })
30 | ```
31 |
32 | ## Options
33 | Option | Type | Default | Values
34 | --- | --- | --- | --
35 | `position` | `String` | `top` | `top`, `right`, `bottom`, `left`, `top-left`, `top-right`, `bottom-right`, `bottom-left`
36 | `duration` | `Number` | 5000 | Any number in milliseconds
37 |
38 |
39 | ## `closeAll()`
40 | - Type: `Function`
41 | The `closeAll` method closes all toast notifications that are visible in the UI at the time of invocation. Nice a succinct way to dismiss all notifications
42 |
--------------------------------------------------------------------------------
/docs/advanced/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | Advanced Usage
3 | ---
4 |
5 | # Advanced Usage
6 |
7 | Whereas breadstick shines in making simple notifications for your Vue app, it's real strength is shown in allowing you to create custom notifications through it's render function callback.
8 |
9 | This is particularly useful if you want use custom themed elements or Vue components inside of your toast notification. In the following snippet, we render a custom `Alert` component to display a toast notification.
10 |
11 | This is particularly useful for building your own themed notification component library.
12 |
13 | [Here are some examples](#) of how to use breadstick to render you own custom element.
14 |
15 | ## 🌮 With Vue's `render` function callback
16 | Breadstick exposes Vue's `createElement` function in the render callback that you can use to render your own components in a toast notification. This can be useful in a context where Vue's `this` context may not be available.
17 |
18 | In a Vue component, you can even use that component's `this.$createElement` to render your own element/component and return it in the render function callback so breadstick can display it.
19 |
20 | ```js
21 | // Import your custom `Alert` component and render it in breadstick
22 | import Alert from './components/Alert'
23 |
24 | export default {
25 | mounted () {
26 | this.$breadstick.notify(({ h, onClose }) => {
27 | return h(Alert, {
28 | on: {
29 | click: onClose
30 | }
31 | }, 'A render function Alert notification')
32 | })
33 | }
34 | }
35 | ```
36 |
37 | ## 🚚 With JSX
38 | You can also use JSX if you like :).
39 | ```jsx
40 | // Import your custom `Alert` component and render it in breadstick
41 | import Alert from './components/Alert'
42 |
43 | export default {
44 | mounted () {
45 | breadstick.notify(({ onClose }) => {
46 | return (
47 |
48 | An JSX Alert notification
49 |
50 | )
51 | }
52 | }
53 | }
54 | ```
--------------------------------------------------------------------------------
/src/components/Breadstick/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import BreadstickManager from '../BreadstickManager'
3 |
4 | const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'
5 | const PORTAL_ID = 'breadstick-kitchen'
6 | const ROOT_ID = 'breadstick-chef'
7 | const VM = typeof window !== 'undefined' && window.Vue ? window.Vue : Vue
8 | /**
9 | * @description Create Toast Portal
10 | * @returns {HTMLElement}
11 | */
12 | function createPortal () {
13 | if (!isBrowser) {
14 | return
15 | }
16 |
17 | let portalElement
18 | const existingPortalElement = document.getElementById(PORTAL_ID)
19 |
20 | if (existingPortalElement) {
21 | portalElement = existingPortalElement
22 | } else {
23 | const el = document.createElement('div')
24 | el.id = PORTAL_ID
25 | el.className = 'Breadstick'
26 | if (document.body != null) {
27 | document.body.appendChild(el)
28 | }
29 | portalElement = el
30 | }
31 |
32 | // Create toaster-kitchen root instance
33 | const root = document.createElement('div')
34 | root.id = ROOT_ID
35 | portalElement.appendChild(root)
36 | return portalElement
37 | }
38 |
39 | function createBreadstick (boundNotify) {
40 | new VM({
41 | el: `#${ROOT_ID}`,
42 | render (h) {
43 | return h(BreadstickManager, {
44 | props: {
45 | notify: boundNotify
46 | }
47 | }, this.$slots.default)
48 | }
49 | }).$mount()
50 | }
51 |
52 | /**
53 | * Toaster returns breadstick instance
54 | */
55 | class Breadstick {
56 | constructor () {
57 | createPortal()
58 | createBreadstick(this.bindNotify)
59 | }
60 |
61 | closeAll = () => {
62 | if (this.removeAll) {
63 | this.removeAll()
64 | }
65 | };
66 |
67 | bindNotify = (fn, removeAll) => {
68 | this.createNotification = fn
69 | this.removeAll = removeAll
70 | };
71 |
72 | /**
73 | * @description Calls notification method
74 | * @param {Function|String} Message object
75 | * @param {Object} options
76 | */
77 | notify = (message, options = {}) => {
78 | if (this.createNotification) {
79 | this.createNotification(message, options)
80 | }
81 | };
82 | }
83 |
84 | export default Breadstick
85 |
--------------------------------------------------------------------------------
/docs/.vuepress/enhanceApp.js:
--------------------------------------------------------------------------------
1 | const MIXPANEL_TOKEN = 'dbca4e68375c43b7ed8b199eeffb1cdf'
2 |
3 | let mixpanelConfig = {
4 | token: MIXPANEL_TOKEN,
5 | debug: true
6 | }
7 |
8 | export default ({ router }) => {
9 | (function(c,a){if(!a.__SV){var b=window;try{var d,m,j,k=b.location,f=k.hash;d=function(a,b){return(m=a.match(RegExp(b+'=([^&]*)')))?m[1]:null};f&&d(f,'state')&&(j=JSON.parse(decodeURIComponent(d(f,'state'))),'mpeditor'===j.action&&(b.sessionStorage.setItem('_mpcehash',f),history.replaceState(j.desiredHash||'',c.title,k.pathname+k.search)))}catch(n){}var l,h;window.mixpanel=a;a._i=[];a.init=function(b,d,g){function c(b,i){var a=i.split('.');2==a.length&&(b=b[a[0]],i=a[1]);b[i]=function(){b.push([i].concat(Array.prototype.slice.call(arguments,
10 | 0)))}}var e=a;'undefined'!==typeof g?e=a[g]=[]:g='mixpanel';e.people=e.people||[];e.toString=function(b){var a='mixpanel';'mixpanel'!==g&&(a+='.'+g);b||(a+=' (stub)');return a};e.people.toString=function(){return e.toString(1)+'.people (stub)'};l='disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove'.split(' ');
11 | for(h=0;h {
17 | (process.env.NODE_ENV !== 'production') && console.log('Route changed to', to)
18 | (process.env.NODE_ENV === 'production') && mixpanel.track('Route Viewed', { 'page': to.path })
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve'
2 | import cjs from 'rollup-plugin-commonjs'
3 | import babel from 'rollup-plugin-babel'
4 | import scss from 'rollup-plugin-scss'
5 | import { terser } from 'rollup-plugin-terser'
6 | import pkg from './package.json'
7 |
8 | const name = 'Breadstick'
9 | const banner = `/*!
10 | * ${pkg.name} v${pkg.version}
11 | * (c) ${new Date().getFullYear()} Jonathan Bakebwa Mugaju
12 | * @license MIT
13 | */`
14 |
15 | const production = !process.env.ROLLUP_WATCH
16 |
17 | // Externals
18 | const externals = [
19 | ...Object.keys(pkg.peerDependencies || {})
20 | ]
21 |
22 | const commons = {
23 | external: externals,
24 | plugins: [
25 | resolve(),
26 | babel({
27 | exclude: 'node_modules/**',
28 | runtimeHelpers: true
29 | }),
30 | cjs({
31 | include: /node_modules/
32 | }),
33 | scss({
34 | output: 'dist/breadstick.css'
35 | }),
36 | production && terser()
37 | ]
38 | }
39 |
40 | /**
41 | * Configurations
42 | */
43 | export default [
44 | {
45 | input: 'src/index.js',
46 | output: [
47 | {
48 | banner,
49 | name,
50 | file: `dist/esm/index.js`,
51 | format: 'esm',
52 | exports: 'named',
53 | globals: {
54 | 'vue': 'Vue',
55 | 'animejs': 'anime'
56 | }
57 | }
58 | ],
59 | ...commons
60 | },
61 | {
62 | input: 'src/index.js',
63 | output: [
64 | {
65 | banner,
66 | name,
67 | file: `dist/es/index.js`,
68 | format: 'es',
69 | exports: 'named',
70 | globals: {
71 | 'vue': 'Vue',
72 | 'animejs': 'anime'
73 | }
74 | }
75 | ],
76 | ...commons
77 | },
78 | {
79 | input: 'src/index.js',
80 | output: [
81 | {
82 | banner,
83 | name,
84 | file: `dist/umd/index.js`,
85 | format: 'umd',
86 | exports: 'named',
87 | globals: {
88 | 'vue': 'Vue',
89 | 'animejs': 'anime'
90 | }
91 | }
92 | ],
93 | ...commons
94 | },
95 | {
96 | input: 'src/index.js',
97 | output: [
98 | {
99 | banner,
100 | name,
101 | file: `dist/cjs/index.js`,
102 | format: 'cjs',
103 | exports: 'named',
104 | globals: {
105 | 'vue': 'Vue',
106 | 'animejs': 'anime'
107 | }
108 | }
109 | ],
110 | ...commons
111 | }
112 | ]
113 |
--------------------------------------------------------------------------------
/src/components/Message/index.js:
--------------------------------------------------------------------------------
1 | import { Alert } from '../Alert/'
2 |
3 | /**
4 | * Message component
5 | */
6 | const Message = {
7 | name: 'Message',
8 | props: {
9 | id: {
10 | type: Number,
11 | default: null
12 | },
13 | message: {
14 | type: [String, Function, Object],
15 | default: null
16 | },
17 | position: {
18 | type: String
19 | },
20 | requestClose: {
21 | type: Boolean,
22 | default: false
23 | },
24 | duration: {
25 | type: Number,
26 | default: 5000
27 | }
28 | },
29 | data () {
30 | return {
31 | timeout: undefined
32 | }
33 | },
34 | mounted () {
35 | this.createTimeout()
36 | },
37 | methods: {
38 | close () {
39 | this.$emit('remove', { id: this.id, position: this.position })
40 | clearTimeout(this.timeout)
41 | },
42 | onMouseEnter () {
43 | if (this.timeout) {
44 | clearTimeout(this.timeout)
45 | }
46 | },
47 | onMouseLeave () {
48 | this.createTimeout()
49 | },
50 | createTimeout () {
51 | this.timeout = setTimeout(this.close, this.duration)
52 | return this.timeout
53 | },
54 | renderMessage (h) {
55 | // The returned message is a string
56 | if (typeof this.message === 'string') {
57 | return h(Alert, {
58 | attrs: {
59 | id: this.id
60 | },
61 | props: {
62 | clear: this.onMouseEnter,
63 | reset: this.onMouseLeave,
64 | title: this.message,
65 | close: this.close
66 | }
67 | }, this.message)
68 | }
69 |
70 | // The returned message is a function with Vue's render function callback
71 | if (typeof this.message === 'function') {
72 | const message = this.message({
73 | h,
74 | id: this.id,
75 | onClose: this.close
76 | })
77 | return h('span', {
78 | on: {
79 | mouseenter: this.onMouseEnter,
80 | mouseleave: this.onMouseLeave
81 | }
82 | }, [message])
83 | }
84 |
85 | // The returned message is a component VNode
86 | if (this.message.constructor && this.message.constructor.name === 'VNode') {
87 | return h('span', {
88 | on: {
89 | 'mouseenter': this.onMouseEnter,
90 | 'mouseleave': this.onMouseLeave
91 | }
92 | }, [this.message])
93 | }
94 | return null
95 | }
96 | },
97 | render (h) {
98 | return h('span', {
99 | }, [this.renderMessage(h)])
100 | }
101 | }
102 |
103 | export default Message
104 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "breadstick",
3 | "version": "0.2.15",
4 | "private": false,
5 | "sideEffects": false,
6 | "main": "dist/index.js",
7 | "module": "dist/es/index.js",
8 | "unpkg": "dist/umd/index.js",
9 | "jsdelivr": "dist/umd/index.js",
10 | "author": {
11 | "name": "Jonathan Bakebwa",
12 | "url": "https://github.com/codebender828"
13 | },
14 | "license": "MIT",
15 | "keywords": [
16 | "front-end",
17 | "vue",
18 | "notifications",
19 | "toast",
20 | "snackbar",
21 | "breadstick",
22 | "stacking",
23 | "position",
24 | "dismissible",
25 | "functional"
26 | ],
27 | "files": [
28 | "dist",
29 | "src"
30 | ],
31 | "repository": {
32 | "type": "git",
33 | "url": "https://github.com/codebender828/breadstick"
34 | },
35 | "scripts": {
36 | "dev": "watch 'yarn build:es' src",
37 | "build": "yarn build:cjs && yarn build:es",
38 | "build:cjs": "cross-env NODE_ENV=production BABEL_ENV=cjs babel --root-mode upward src -d dist --copy-files",
39 | "build:es": "cross-env NODE_ENV=production BABEL_ENV=es babel --root-mode upward src -d dist/es --copy-files",
40 | "docs:dev": "vuepress dev docs",
41 | "docs:build": "vuepress build docs",
42 | "test": "jest --passWithNoTests",
43 | "test:watch": "jest --passWithNoTests --watchAll",
44 | "lint": "vue-cli-service lint"
45 | },
46 | "peerDependencies": {
47 | "animejs": "^3.1.0",
48 | "vue": "^2.6.10"
49 | },
50 | "devDependencies": {
51 | "@babel/cli": "^7.8.3",
52 | "@babel/core": "^7.8.3",
53 | "@babel/plugin-proposal-class-properties": "^7.8.3",
54 | "@babel/plugin-syntax-jsx": "^7.8.3",
55 | "@babel/preset-env": "^7.8.3",
56 | "@commitlint/cli": "^8.2.0",
57 | "@commitlint/config-conventional": "^8.2.0",
58 | "@vue/cli-plugin-babel": "^4.0.0",
59 | "@vue/cli-plugin-eslint": "^4.0.0",
60 | "@vue/cli-plugin-unit-jest": "^4.0.0",
61 | "@vue/cli-service": "^4.0.0",
62 | "@vue/eslint-config-standard": "^4.0.0",
63 | "@vue/test-utils": "1.0.0-beta.29",
64 | "babel-eslint": "^10.0.3",
65 | "babel-jest": "^25.1.0",
66 | "babel-plugin-transform-class-properties": "^6.24.1",
67 | "breadstick": "^0.1.21",
68 | "core-js": "^3.3.2",
69 | "cross-env": "^6.0.3",
70 | "eslint": "^5.16.0",
71 | "eslint-plugin-vue": "^5.0.0",
72 | "husky": "^3.0.9",
73 | "jest": "^25.1.0",
74 | "lint-staged": "^9.4.2",
75 | "rollup": "^1.27.12",
76 | "rollup-plugin-babel": "^4.3.3",
77 | "rollup-plugin-commonjs": "^10.1.0",
78 | "rollup-plugin-node-resolve": "^5.2.0",
79 | "rollup-plugin-scss": "^1.0.2",
80 | "rollup-plugin-terser": "^5.1.3",
81 | "rollup-plugin-uglify": "^6.0.4",
82 | "vue": "^2.6.11",
83 | "vue-template-compiler": "^2.6.11",
84 | "vuepress": "^1.3.0",
85 | "watch": "^1.0.2"
86 | },
87 | "publishConfig": {
88 | "registry": "https://registry.npmjs.org/"
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at excellence@jbakebwa.dev. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/src/components/BreadstickManager/index.js:
--------------------------------------------------------------------------------
1 | import anime from 'animejs'
2 | import Message from '../Message'
3 |
4 | const positions = {
5 | top: [],
6 | 'top-left': [],
7 | 'top-right': [],
8 | 'bottom-left': [],
9 | bottom: [],
10 | 'bottom-right': []
11 | }
12 |
13 | /**
14 | * @description Compute styles for specific position
15 | * @param {String} position
16 | * @returns {Object} Styles object
17 | */
18 | const computeBreadstickStyle = (position) => {
19 | let style = {
20 | display: 'flex',
21 | flexDirection: 'column',
22 | alignItems: 'center'
23 | }
24 |
25 | if (position.includes('right')) {
26 | style.alignItems = 'flex-end'
27 | } else if (position.includes('left')) {
28 | style.alignItems = 'flex-start'
29 | }
30 |
31 | return style
32 | }
33 |
34 | /**
35 | * Breadstick Animations
36 | */
37 | const animations = {
38 | enter: (el) => {
39 | var height = el.clientHeight
40 | return {
41 | height: [0, height],
42 | opacity: [0, 1],
43 | scale: [0.9, 1]
44 | }
45 | },
46 | leave: {
47 | height: 0,
48 | opacity: [1, 0],
49 | scale: [1, 0.9]
50 | }
51 | }
52 |
53 | /**
54 | * Breadstick component
55 | */
56 | const BreadstickManager = {
57 | name: 'BreadstickManager',
58 | data () {
59 | return {
60 | idCounter: 0,
61 | positions
62 | }
63 | },
64 | props: {
65 | notify: Function,
66 | default: () => null
67 | },
68 | created () {
69 | this.notify(this._notify, this.closeAll)
70 | },
71 | methods: {
72 | /**
73 | * @description Creates toast state for single toast notification
74 | * @param {Object|Vue.Component|String} message
75 | * @param {Object} options
76 | * @returns {Object} Toast state object
77 | */
78 | createToastState (message, options) {
79 | const id = ++this.idCounter
80 | // a bit messy, but object.position returns a number because
81 | // it's a method argument.
82 | const position =
83 | options.hasOwnProperty('position') && typeof options.position === 'string'
84 | ? options.position
85 | : 'top'
86 |
87 | return {
88 | id,
89 | message,
90 | position,
91 | showing: true,
92 | duration:
93 | typeof options.duration === 'undefined' ? 5000 : options.duration,
94 | onRequestRemove: () => this.removeToast(String(id), position),
95 | type: options.type
96 | }
97 | },
98 |
99 | /**
100 | * @description Shows notification
101 | * @param {Object|Vue.Component|String} message
102 | * @param {Object} options
103 | */
104 | _notify (message, options) {
105 | const toast = this.createToastState(message, options)
106 | const { position } = toast
107 |
108 | // prepend the toast for toasts positioned at the top of
109 | // the screen, otherwise append it.
110 | const isTop = position.includes('top')
111 | isTop ? this.positions[position].unshift(toast)
112 | : this.positions[position].push(toast)
113 | },
114 |
115 | /**
116 | * @description Close all toast components
117 | */
118 | closeAll () {
119 | Object.keys(this.positions).forEach((pos) => {
120 | const position = this.positions[pos]
121 | position.forEach((toast) => {
122 | this.removeToast(toast.id, pos)
123 | })
124 | })
125 | },
126 |
127 | /**
128 | * @description Remove toast from position list
129 | * @param {String} id
130 | * @param {String} position
131 | */
132 | removeToast (id, position) {
133 | this.positions[position] = this.positions[position].filter(toast => toast.id !== id)
134 | return this.positions[position]
135 | },
136 |
137 | /**
138 | * @description Compute styles for toast component
139 | * @param {String} position
140 | */
141 | getStyle (position) {
142 | let style = {
143 | width: 'fit-content',
144 | position: 'fixed',
145 | zIndex: 5500
146 | }
147 |
148 | if (position === 'top' || position === 'bottom') {
149 | style.margin = '0 auto'
150 | style.textAlign = 'center'
151 | }
152 |
153 | if (position.includes('top')) {
154 | style.top = 0
155 | }
156 |
157 | if (position.includes('bottom')) {
158 | style.bottom = 0
159 | }
160 |
161 | if (!position.includes('left')) {
162 | style.right = 0
163 | }
164 |
165 | if (!position.includes('right')) {
166 | style.left = 0
167 | }
168 | return style
169 | },
170 | /**
171 | * @description Get animation for transition
172 | * @param {String} key Type of animation phase
173 | * @param {HTMLElement} el Element
174 | */
175 | getAnimation (key, el) {
176 | const animation = animations[key]
177 | return typeof animation === 'function'
178 | ? animation.call(this, el)
179 | : animation
180 | },
181 | /**
182 | * @description Calls enter animation
183 | * @param {{el: HTMLElement, complete: Function}} el
184 | */
185 | enter (el, complete) {
186 | const animation = this.getAnimation('enter', el)
187 | anime({
188 | targets: el,
189 | ...animation,
190 | complete,
191 | easing: 'spring(1, 100, 50, 0)'
192 | })
193 | },
194 | /**
195 | * @description Calls leave animation
196 | * @param {{el: HTMLElement, complete: Function}} el
197 | */
198 | leave (el, complete) {
199 | let animation = this.getAnimation('leave', el)
200 | anime({
201 | targets: el,
202 | ...animation,
203 | complete,
204 | easing: 'spring(1, 100, 70, 0)'
205 | })
206 | }
207 | },
208 | render (h) {
209 | return h('span', [
210 | Object.keys(this.positions).map((position) => {
211 | const pos = position
212 | const toasts = this.positions[pos]
213 | return h('TransitionGroup', {
214 | style: this.getStyle(pos),
215 | props: {
216 | css: false
217 | },
218 | on: {
219 | enter: this.enter,
220 | leave: this.leave
221 | },
222 | key: position,
223 | class: ['Breadstick__manager-' + pos],
224 | ref: `BreadstickManager_${pos}`
225 | }, [
226 | toasts.map((toast) => {
227 | return h(Message, {
228 | props: {
229 | position: pos,
230 | key: toast.id,
231 | message: toast.message,
232 | ...toast
233 | },
234 | style: {
235 | ...computeBreadstickStyle(pos)
236 | },
237 | on: {
238 | remove: ({ id, position }) => {
239 | this.removeToast(id, position)
240 | }
241 | },
242 | key: toast.id
243 | }, this.$slots.default)
244 | })
245 | ])
246 | })
247 | ])
248 | }
249 | }
250 |
251 | export default BreadstickManager
252 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 🚀 A simple and flexible implementation of toast style notifications for Vue.js.
2 |
3 |
4 | [](#contributors-)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Breadstick
13 | A simple and flexible implementation of toast style notifications for Vue.js.
14 | 🌟 Inspired by toasted-notes for React - which I think is really cool. ❤️
15 |
16 |
17 |
18 | ### 🌟 Features
19 | - **Functional Stacking, Positioning, Timing & Dismissal API.** Breadstick exposes a function that allows you to to control the stacking, positioning and dismissal of authored toast style notifications.
20 | - **Render whatever you want.** Utilize the render callback to create beautiful custom notifications.
21 | - **Functional default styles.** Import the provided css for some nice styling defaults or write your own styles.
22 | - **Vue framework-agnostic notification.** Breadstick is un-opininated about the styling of your Vue toast notifications. It's main strengths are in handling the business logic of stacking, positioning, timing & dismissal of your toast notifications.
23 |
24 | ## 📚 Table of Contents
25 | - [🥳 Breadstick Starters](#starters)
26 | - [⚡️ Installation](#installation)
27 | - [Installing with Nuxt](#installing-with-nuxt)
28 | - [🤖 Examples](#examples)
29 | - [🍊 Basic usage](#basic-usage)
30 | - [📚 Using different positions](#different-positions)
31 | - [🏠 Using custom element](#custom-notifications)
32 | - [📭 Close all notifications](#close-all)
33 | - [🏗 Advanced usage](#advanced)
34 | - [🌮 With Vue's `render` function callback](#render-callback)
35 | - [🚚 With JSX](#with-jsx)
36 | - [💼 API](#api)
37 | - [`notify(String|VNode|Function, options)`](#notify)
38 | - [Options](#options)
39 | - [`closeAll()`](#close-all)
40 | - [🔖 TODO:](#todo)
41 | - [🤝 Contributing](#contributing)
42 | - [❤️ Support this project](#support)
43 | - [Contributors ✨](#contributors)
44 |
45 |
46 | ### 🥳 Breadstick Starters
47 | Here are a few Codesandbox starters you can use to get started with Breadstick in your Vue or Nuxt App.
48 | - **Vue.js Starter**: http://bit.ly/breadstick-vue
49 | - **Nuxt.js Starter**: http://bit.ly/breadstick-nuxt
50 |
51 |
52 | ### ⚡️ Installation
53 | Install `breadstick` and its peer dependency, `animejs`, using yarn or npm.
54 |
55 | ```bash
56 | npm install breadstick animejs --save
57 | ```
58 |
59 | You can then register `BreadstickBakery` as a Vue plugin.
60 | ```js
61 | import Vue from 'vue'
62 | import { BreadstickBakery } from 'breadstick'
63 |
64 | // This exposes `this.$breadstick` in your Vue template.
65 | Vue.use(BreadstickBakery)
66 | ```
67 |
68 | #### Installing with Nuxt
69 | After installing Breadstick, we import it and register it as a **client-side plugin**. This is because Breadstick it makes use of some DOM APIs. Code is similiar to the Vue plugin shown above.
70 |
71 |
72 |
73 |
74 | ### 🤖 Examples
75 | Breadstick can be used to render different types of notifications out of the box. You can render simple string notifications as well as custom styled notifications. This makes it really convenient.
76 |
77 |
78 |
79 | #### 🍊 Basic usage
80 | Invoke a notification using the `notify` method to display a message to the user.
81 | Breadstick defaults the notification duration to 5 seconds in the `top` position.
82 |
83 | ```js
84 | this.$breadstick.notify('🥞 Show me the pancakes')
85 | ```
86 |
87 |
88 | #### 📚 Using different positions
89 | You can display notifications in different positions, including top-left, top, top-right, bottom-left, bottom, and bottom-right.
90 |
91 | ```js
92 | [
93 | 'top-left',
94 | 'top',
95 | 'top-right',
96 | 'bottom-left',
97 | 'bottom',
98 | 'bottom-right'
99 | ].forEach(position => {
100 | this.$breadstick.notify("Using position " + position, {
101 | position
102 | })
103 | })
104 | ```
105 |
106 |
107 |
108 | #### 🏠 Using custom element
109 | With JSX or Vue's render function, breadstick is able to render a custom element or Vue component
110 |
111 | ```jsx
112 | this.$breadstick.notify(
113 | I am a custom HTML element
114 | )
115 | ```
116 |
117 |
118 |
119 | #### 📭 Close all notifications
120 | You can clear all notifications by calling breadstick's `closeAll` method
121 |
122 | ```jsx
123 | this.$breadstick.closeAll()
124 | ```
125 |
126 |
127 |
128 | ### 🏗 Advanced usage
129 | Whereas breadstick shines in making simple notifications for your Vue app, it's real strength is shown in allowing you to create custom notifications through it's render function callback.
130 |
131 | This is particularly useful if you want use custom themed elements or Vue components inside of your toast notification. In the following snippet, we render a custom `Alert` component to display a toast notification.
132 |
133 | This is particularly useful for building your own themed notification component library.
134 |
135 | [Here is an advanced examples](https://github.com/codebender828/breadstick-examples) of how to use breadstick to render you own authored toast notifications.
136 |
137 |
138 |
139 | #### 🌮 With Vue's `render` function callback
140 | Breadstick exposes Vue's `createElement` function in the render callback that you can use to render your own components in a toast notification. This can be useful in a context where Vue's `this` context may not be available.
141 |
142 | In a Vue component, you can even use that component's `this.$createElement` to render your own element/component and return it in the render function callback so breadstick can display it.
143 |
144 | ```js
145 | // Import your custom `Alert` component and render it in breadstick
146 | import Alert from './components/Alert'
147 |
148 | export default {
149 | mounted () {
150 | this.$breadstick.notify(({ h, onClose }) => {
151 | return h(Alert, {
152 | on: {
153 | click: onClose
154 | }
155 | }, 'A render function Alert notification')
156 | })
157 | }
158 | }
159 | ```
160 |
161 |
162 |
163 | #### 🚚 With JSX
164 | You can also use JSX if you like :).
165 | ```jsx
166 | // Import your custom `Alert` component and render it in breadstick
167 | import Alert from './components/Alert'
168 |
169 | export default {
170 | mounted () {
171 | breadstick.notify(({ onClose }) => {
172 | return (
173 |
174 | An JSX Alert notification
175 |
176 | )
177 | }
178 | }
179 | }
180 | ```
181 |
182 |
183 |
184 | ## 💼 API
185 |
186 |
187 |
188 | ### `notify(String|VNode|Function, options)`
189 | - **Arguments**
190 | - `{ String | VNode | Function }` Message
191 | - `{ Object }` options
192 |
193 | Breadstick's `notify` method accepts two parameters. The first parameter can be a `String`, `VNode` (Object), or `Function` and the second is the options object.
194 |
195 | If a string is passed in the first argument, breadstick will render a notificiation with the string in the top center position with it's default internal component.
196 | ```js
197 | this.$breadstick.notify('Simple notification.')
198 | ```
199 |
200 | If a `VNode` is passed, Breadstick treats it like a rendered component and renders it instead.
201 | ```jsx
202 | this.$breadstick.notify(
203 | I am a custom HTML element
204 | )
205 | ```
206 |
207 | If a callback `Function` is passed in the first argument, it will expose an object with two parameters: `h` and the `onClose` which are both functions. Using a render callback allows you to tap into the close function. It's your best option if you want to completely re-style your toast notification
208 |
209 | ```js
210 | this.$breadstick.notify(({ h, onClose }) => {
211 | return h('div', 'My custom notification')
212 | })
213 | ```
214 |
215 |
216 | #### Options
217 | Option | Type | Default | Values
218 | --- | --- | --- | --
219 | `position` | `String` | `top` | `top`, `right`, `bottom`, `left`, `top-left`, `top-right`, `bottom-right`, `bottom-left`
220 | `duration` | `Number` | 5000 | Any number in milliseconds
221 |
222 |
223 |
224 | ### `closeAll()`
225 | - Type: `Function`
226 | The `closeAll` method closes all toast notifications that are visible in the UI at the time of invocation. Nice a succinct way to dismiss all notifications
227 |
228 |
229 |
230 | ## 🔖 TODO:
231 | Breadstick still has a few more features coming up. These include:
232 | - [ ] Indefinitely display toast notification
233 | - [ ] Allow sharing of same application Vue instance.
234 |
235 |
236 |
237 | ## 🤝 Contributing
238 | Here's our contribution [guide.](./.github/CONTRIBUTING.md)
239 |
240 |
241 |
242 | ## ❤️ Support this project
243 | If you like this project, please consider supporting it by buying my a coffee!
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 | Made with ❤️ by Jonathan Bakebwa 🇺🇬
252 |
253 |
254 |
255 | ## Contributors ✨
256 |
257 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
258 |
259 |
260 |
261 |
262 |
268 |
269 |
270 |
271 |
272 |
273 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
274 |
--------------------------------------------------------------------------------