sajjad 2 months ago
commit 1cb67ddba8

2
.gitignore vendored

@ -0,0 +1,2 @@
node_modules
dist

@ -0,0 +1,18 @@
import commonjs from '@rollup/plugin-commonjs'; // Convert CommonJS modules to ES6
import vue from 'rollup-plugin-vue'; // Handle .vue SFC files
import buble from '@rollup/plugin-buble'; // Transpile/polyfill with reasonable browser support
export default {
input: 'src/main.js', // Path relative to package.json
output: {
name: 'MeshkeeToast',
exports: 'named',
},
plugins: [
commonjs(),
vue({
css: true, // Dynamically inject css as a <style> tag
compileTemplate: true, // Explicitly convert template to render function
}),
buble(), // Transpile to ES5
],
};

9011
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,36 @@
{
"name": "meshkeetoast",
"version": "0.1.0",
"main": "dist/MeshkeeToast.umd.js",
"style": "dist/theme-bootstrap.css",
"module": "dist/MeshkeeToast.esm.js",
"unpkg": "dist/MeshkeeToast.min.js",
"browser": {
"./sfc": "src/MeshkeeToast.vue"
},
"scripts": {
"build": "npm run build:umd & npm run build:es & npm run build:unpkg",
"postbuild": "cp -r src/theme dist/",
"build:umd": "rollup --config build/rollup.config.js --format umd --file dist/MeshkeeToast.umd.js",
"build:es": "rollup --config build/rollup.config.js --format es --file dist/MeshkeeToast.esm.js",
"build:unpkg": "rollup --config build/rollup.config.js --format iife --file dist/MeshkeeToast.min.js"
},
"devDependencies": {
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-commonjs": "^11.1.0",
"rollup": "^1.17.0",
"rollup-plugin-vue": "^5.0.1",
"vue": "^2.6.10",
"vue-template-compiler": "^2.6.10"
},
"dependencies": {
"@vue/cli-service": "^5.0.8",
"mitt": "^3.0.1",
"sass": "^1.72.0",
"sass-loader": "^14.1.1",
"style-loader": "^3.3.4",
"svg-url-loader": "^8.0.0",
"vue-loader": "^17.4.2"
}
}

@ -0,0 +1,192 @@
<template>
<transition :enter-active-class="transition.enter" :leave-active-class="transition.leave">
<div ref="root" role="alert" v-show="isActive" class="v-toast__item"
:class="[`v-toast__item--${type}`, `v-toast__item--${position}`]" @mouseover="toggleTimer(true)"
@mouseleave="toggleTimer(false)" @click="whenClicked">
<div class="v-toast__icon"></div>
<p class="v-toast__text" v-html="message"></p>
</div>
</transition>
</template>
<script>
import { defineComponent, h } from 'vue';
import { removeElement } from './helpers.js';
import Timer from "./timer.js";
import Positions from './positions.js'
import eventBus from './bus.js'
export default defineComponent({
name: 'Toast',
props: {
message: {
type: String,
required: true
},
type: {
type: String,
default: 'success'
},
position: {
type: String,
default: Positions.BOTTOM_RIGHT,
validator(value) {
return Object.values(Positions).includes(value)
}
},
duration: {
type: Number,
default: 3000
},
dismissible: {
type: Boolean,
default: true
},
onDismiss: {
type: Function,
default: () => {
}
},
onClick: {
type: Function,
default: () => {
}
},
queue: Boolean,
pauseOnHover: {
type: Boolean,
default: true
},
},
data() {
return {
isActive: false,
parentTop: null,
parentBottom: null,
isHovered: false,
}
},
computed: {
correctParent() {
switch (this.position) {
case Positions.TOP:
case Positions.TOP_RIGHT:
case Positions.TOP_LEFT:
return this.parentTop;
case Positions.BOTTOM:
case Positions.BOTTOM_RIGHT:
case Positions.BOTTOM_LEFT:
return this.parentBottom;
}
},
transition() {
switch (this.position) {
case Positions.TOP:
case Positions.TOP_RIGHT:
case Positions.TOP_LEFT:
return {
enter: 'v-toast--fade-in-down',
leave: 'v-toast--fade-out'
};
case Positions.BOTTOM:
case Positions.BOTTOM_RIGHT:
case Positions.BOTTOM_LEFT:
return {
enter: 'v-toast--fade-in-up',
leave: 'v-toast--fade-out'
}
}
},
},
methods: {
setupContainer() {
this.parentTop = document.querySelector('.v-toast.v-toast--top');
this.parentBottom = document.querySelector('.v-toast.v-toast--bottom');
// No need to create them, they already exists
if (this.parentTop && this.parentBottom) return;
if (!this.parentTop) {
this.parentTop = document.createElement('div');
this.parentTop.className = 'v-toast v-toast--top';
}
if (!this.parentBottom) {
this.parentBottom = document.createElement('div');
this.parentBottom.className = 'v-toast v-toast--bottom'
}
const container = document.body;
container.appendChild(this.parentTop);
container.appendChild(this.parentBottom);
},
shouldQueue() {
if (!this.queue) return false;
return (
this.parentTop.childElementCount > 0 ||
this.parentBottom.childElementCount > 0
)
},
dismiss() {
if (this.timer) this.timer.stop();
clearTimeout(this.queueTimer);
this.isActive = false;
// Timeout for the animation complete before destroying
setTimeout(() => {
this.onDismiss.apply(null, arguments);
const wrapper = this.$refs.root;
// unmount the component
removeElement(wrapper)
}, 150)
},
showNotice() {
if (this.shouldQueue()) {
// Call recursively if it should queue
this.queueTimer = setTimeout(this.showNotice, 250);
return
}
console.log(this.correctParent);
this.correctParent.insertAdjacentElement('afterbegin', this.$refs.root);
this.$options.elem = this.correctParent;
this.isActive = true;
if (this.duration) {
this.timer = new Timer(this.dismiss, this.duration);
}
},
whenClicked() {
if (!this.dismissible) return;
this.onClick.apply(null, arguments);
this.dismiss()
},
toggleTimer(newVal) {
if (!this.pauseOnHover || !this.timer) return;
newVal ? this.timer.pause() : this.timer.resume();
}
},
beforeUnmount() {
eventBus.off('toast-clear', this.dismiss)
},
beforeMount() {
this.setupContainer()
},
mounted() {
this.showNotice();
eventBus.on('toast-clear', this.dismiss)
},
})
</script>
<style lang="scss">
@import './theme/bootstrap';
</style>

@ -0,0 +1,55 @@
import MeshkeeToast from './MeshkeeToast.vue'
import { createComponent } from './helpers';
import eventBus from './bus.js';
export const useToast = (globalProps = {}) => {
return {
open(options) {
let message = null;
if (typeof options === 'string') message = options;
const defaultProps = {
message
};
const propsData = Object.assign({}, defaultProps, globalProps, options);
const instance = createComponent(MeshkeeToast, propsData, document.body);
return {
dismiss: instance.dismiss
}
},
clear() {
eventBus.emit('toast-clear')
},
success(message, options = {}) {
return this.open(Object.assign({}, {
message,
type: 'success'
}, options))
},
error(message, options = {}) {
return this.open(Object.assign({}, {
message,
type: 'error'
}, options))
},
info(message, options = {}) {
return this.open(Object.assign({}, {
message,
type: 'info'
}, options))
},
warning(message, options = {}) {
return this.open(Object.assign({}, {
message,
type: 'warning'
}, options))
},
default(message, options = {}) {
return this.open(Object.assign({}, {
message,
type: 'default'
}, options))
}
}
};

@ -0,0 +1,3 @@
import mitt from 'mitt'
const eventBus = mitt();
export default eventBus;

@ -0,0 +1,49 @@
import Vue from 'vue';
export const removeElement = (el) => {
console.log(el);
if (typeof el.remove !== 'undefined') {
el.remove()
} else {
if (el.parentNode) el.parentNode.removeChild(el)
}
}
export const createComponent = (component, props, parentContainer, slots = {}) => {
// const vNode = h(component, props, slots)
// const container = document.createElement('div');
// console.log(parentContainer, container);
// container.classList.add('v-toast--pending')
// parentContainer.appendChild(container);
// h(vNode, container);
// return vNode.component
// return Vue.component('MeshkeeToast', {
// render: (createElement) => {
// return createElement(
// 'div',
// { class: { 'v-toast--pending': true } },
// MeshkeeToast
// )
// }
// });
const toastTpl = Vue.extend({
data() {
return {
}
},
render(h) {
return h(
component,
{
class: ['lx-toast', 'lx-toast-lx-word-wrap'],
style: this.extStyle,
props
}
)
}
});
let toastVM = new toastTpl();
const tpl = toastVM.$mount().$el;
parentContainer.appendChild(tpl);
return toastVM.$children[0];
}

@ -0,0 +1,51 @@
// Import vue component
import Vue from 'vue';
import MeshkeeToast from './MeshkeeToast.vue';
import { useToast } from './api';
// Vue.prototype.$meshkeeToast = function () {
// console.log(324);
// return 321;
// }
// // Declare install function executed by Vue.use()
// export function install(Vue) {
// console.log(43);
// if (install.installed) return;
// install.installed = true;
// Vue.prototype.$meshkeeToast = function () {
// console.log(324);
// return 321;
// }
// Vue.component('MeshkeeToast', MeshkeeToast);
// }
// // Create module definition for Vue.use()
// const plugin = {
// install,
// };
// // Auto-install when vue is found (eg. in browser via <script> tag)
// let GlobalVue = null;
// if (typeof window !== 'undefined') {
// GlobalVue = window.Vue;
// } else if (typeof global !== 'undefined') {
// GlobalVue = global.Vue;
// }
// console.log(global, GlobalVue);
// if (GlobalVue) {
// GlobalVue.use(plugin);
// }
// // To allow use as module (npm/webpack/etc.) export component
// export default MeshkeeToast;
export default {
install(Vue, options) {
// Let's register our component globally
// https://vuejs.org/v2/guide/components-registration.html
Vue.prototype.$meshkeeToast = function () {
const { open } = useToast(options);
open('Vue');
}
}
};

@ -0,0 +1,8 @@
export default Object.freeze({
TOP_RIGHT: 'top-right',
TOP: 'top',
TOP_LEFT: 'top-left',
BOTTOM_RIGHT: 'bottom-right',
BOTTOM: 'bottom',
BOTTOM_LEFT: 'bottom-left',
})

@ -0,0 +1,59 @@
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.v-toast--fade-out {
animation-name: fadeOut;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translate3d(0, -100%, 0);
}
to {
opacity: 1;
transform: none;
}
}
.v-toast--fade-in-down {
animation-name: fadeInDown;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translate3d(0, 100%, 0);
}
to {
opacity: 1;
transform: none;
}
}
.v-toast--fade-in-up {
animation-name: fadeInUp;
}
/**
* Vue Transitions
*/
.fade-enter-active,
.fade-leave-active {
transition: opacity 150ms ease-out;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}

@ -0,0 +1 @@
$toast-icons-path: "../sugar/icons" !default;

@ -0,0 +1,5 @@
@import '../default/variables';
@import 'variables';
@import '../animations';
@import '../default/main';
@import '../sugar/icons';

@ -0,0 +1,78 @@
.v-toast {
position: fixed;
display: flex;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 2em;
overflow: hidden;
z-index: 1090;
pointer-events: none;
&__item {
display: inline-flex;
align-items: center;
animation-duration: 150ms;
margin: 0.5em 0;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
border-radius: 0.25em;
pointer-events: auto;
opacity: 0.92;
color: #fff;
min-height: 3em;
cursor: pointer;
// Colors
@each $color, $value in $toast-colors {
&--#{$color} {
background-color: $value;
}
}
&--warning {
color: #000
}
// Individual toast position
&.v-toast__item--top, &.v-toast__item--bottom {
align-self: center;
}
&.v-toast__item--top-right, &.v-toast__item--bottom-right {
align-self: flex-end;
}
&.v-toast__item--top-left, &.v-toast__item--bottom-left {
align-self: flex-start;
}
}
&__text {
margin: 0;
padding: 0.5em 1em;
word-break: break-word;
}
&__icon {
display: none;
}
// Notice container positions
&.v-toast--top {
flex-direction: column;
}
&.v-toast--bottom {
flex-direction: column-reverse;
}
&.v-toast--custom-parent {
position: absolute;
}
@media screen and (max-width: 768px) {
padding: 0;
position: fixed !important;
}
}

@ -0,0 +1,9 @@
$toast-colors: (
) !default;
$toast-colors: map-merge(("success": #28a745,
"info": #17a2b8,
"warning": #ffc107,
"error": #dc3545,
"default": #343a40),
$toast-colors
);

@ -0,0 +1,3 @@
@import 'variables';
@import '../animations';
@import 'main';

@ -0,0 +1,12 @@
$toast-icons-path: "./icons" !default;
$toast-colors: () !default;
$toast-colors: map-merge(
(
"success": #47d78a,
"info": #1c85d5,
"warning": #febc22,
"error": #f7471c,
"default": #343a40
),
$toast-colors
);

@ -0,0 +1,36 @@
.v-toast {
&__item {
opacity: 1;
min-height: 4em;
.v-toast__text {
padding: 1.5em 1em;
}
.v-toast__icon {
display: block;
width: 27px;
min-width: 27px;
height: 27px;
margin-left: 1em;
// background: url(#{$toast-icons-path}/info.svg) no-repeat;
[dir="rtl"] & {
margin-left: unset;
margin-right: 1em;
}
}
&.v-toast__item--success .v-toast__icon {
// background: url(#{$toast-icons-path}/success.svg) no-repeat;
}
&.v-toast__item--error .v-toast__icon {
// background: url(#{$toast-icons-path}/error.svg) no-repeat;
}
&.v-toast__item--warning .v-toast__icon {
// background: url(#{$toast-icons-path}/warning.svg) no-repeat;
}
}
}

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 51.976 51.976">
<path fill="#fff"
d="M44.373 7.603c-10.137-10.137-26.632-10.138-36.77 0-10.138 10.138-10.137 26.632 0 36.77s26.632 10.138 36.77 0c10.137-10.138 10.137-26.633 0-36.77zm-8.132 28.638a2 2 0 01-2.828 0l-7.425-7.425-7.778 7.778a2 2 0 11-2.828-2.828l7.778-7.778-7.425-7.425a2 2 0 112.828-2.828l7.425 7.425 7.071-7.071a2 2 0 112.828 2.828l-7.071 7.071 7.425 7.425a2 2 0 010 2.828z"/>
</svg>

After

Width:  |  Height:  |  Size: 466 B

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 45.999 45.999">
<path fill="#fff"
d="M39.264 6.736c-8.982-8.981-23.545-8.982-32.528 0-8.982 8.982-8.981 23.545 0 32.528 8.982 8.98 23.545 8.981 32.528 0 8.981-8.983 8.98-23.545 0-32.528zM25.999 33a3 3 0 11-6 0V21a3 3 0 116 0v12zm-3.053-17.128c-1.728 0-2.88-1.224-2.844-2.735-.036-1.584 1.116-2.771 2.879-2.771 1.764 0 2.88 1.188 2.917 2.771-.001 1.511-1.152 2.735-2.952 2.735z"/>
</svg>

After

Width:  |  Height:  |  Size: 453 B

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<path fill="#fff"
d="M26 0C11.664 0 0 11.663 0 26s11.664 26 26 26 26-11.663 26-26S40.336 0 26 0zm14.495 17.329l-16 18a1.997 1.997 0 01-2.745.233l-10-8a2 2 0 012.499-3.124l8.517 6.813L37.505 14.67a2.001 2.001 0 012.99 2.659z"/>
</svg>

After

Width:  |  Height:  |  Size: 308 B

@ -0,0 +1,4 @@
<svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg">
<path fill="#000"
d="M49.466 41.26L29.216 6.85c-.69-1.16-1.89-1.85-3.22-1.85-1.32 0-2.53.69-3.21 1.85L2.536 41.26c-.71 1.2-.72 2.64-.03 3.85.68 1.18 1.89 1.89 3.24 1.89h40.51c1.35 0 2.56-.71 3.23-1.89.7-1.21.69-2.65-.02-3.85zm-25.53-21.405h3.381v3.187l-.724 8.92H24.66l-.725-8.92v-3.187zm2.97 17.344a1.712 1.712 0 01-1.267.543c-.491 0-.914-.181-1.268-.543a1.788 1.788 0 01-.531-1.297c0-.502.176-.935.53-1.297a1.712 1.712 0 011.269-.544c.49 0 .914.181 1.268.544s.53.795.53 1.297c0 .503-.176.934-.53 1.297z"/>
</svg>

After

Width:  |  Height:  |  Size: 589 B

@ -0,0 +1,4 @@
@import 'variables';
@import '../animations';
@import '../default/main';
@import 'icons';

@ -0,0 +1,24 @@
export default class Timer {
constructor(callback, delay) {
this.startedAt = Date.now();
this.callback = callback;
this.delay = delay;
this.timer = setTimeout(callback, delay);
}
pause() {
this.stop();
this.delay -= Date.now() - this.startedAt;
}
resume() {
this.stop();
this.startedAt = Date.now();
this.timer = setTimeout(this.callback, this.delay);
}
stop() {
clearTimeout(this.timer);
}
}
Loading…
Cancel
Save