Merge pull request #46 from themesberg/chank1e/develop

feat: alert system + useToast works
This commit is contained in:
Alexandr P
2022-07-28 20:22:34 +03:00
committed by GitHub
6 changed files with 129 additions and 30 deletions

View File

@@ -1,5 +1,13 @@
<template>
<toast-provider>
<toast-provider :transition="transition">
<label for="countries" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400">Select transition</label>
<select v-model="transition" id="countries" class="mb-2 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="slide-left">Slide left</option>
<option value="slide-right">Slide right</option>
<option value="slide-top">Slide top</option>
<option value="slide-bottom">Slide bottom</option>
<option value="fade">Fade</option>
</select>
<div class="vp-raw flex align-center gap-2 flex-wrap flex-col">
<toast-provider-example-child />
</div>
@@ -8,4 +16,7 @@
<script setup>
import { ToastProvider } from '../../../../src/index'
import ToastProviderExampleChild from './ToastProviderExampleChild.vue'
import { ref } from 'vue'
const transition = ref('slide-left')
</script>

View File

@@ -1,6 +1,7 @@
<template>
<div class="flex flex-col gap-2">
<input class="text-black" type="number" v-model="ms">
<label for="ms" class="block text-sm font-medium text-gray-900 dark:text-gray-400">Duration(ms)</label>
<input v-model.number="ms" type="number" id="ms" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="John" required>
<div class="flex gap-2">
<Button @click="() => add('success')" color="green">success</Button>
<Button @click="() => add('warning')" color="yellow">warning</Button>
@@ -8,22 +9,21 @@
<Button @click="() => add('update')" color="purple">update</Button>
</div>
<div class="flex">
<Button @click="remove" color="alternative">remove</Button>
<Button @click="remove" color="alternative">pop</Button>
</div>
</div>
</template>
<script setup>
import { FLOWBITE_TOAST_INJECTION_KEY } from '../../../../src/components/Toast/components/ToastProvider/injection/config'
import { inject, ref, shallowRef } from 'vue'
import { Button } from '../../../../src/index'
import { ref, shallowRef } from 'vue'
import { Button, useToast } from '../../../../src/index'
import UpdateToast from './UpdateToast.vue'
const ms = ref('5000')
const ms = ref(5000)
const injected = inject(FLOWBITE_TOAST_INJECTION_KEY)
const toast = useToast()
const addUpdate = () => {
injected.add({
const id = toast.add({
time: parseInt(ms.value) || 0,
text: 'A new software version is available for download.',
component: shallowRef(UpdateToast),
@@ -36,7 +36,7 @@ const addUpdate = () => {
const add = (type) => {
if(type === 'update') return addUpdate()
injected.add({
toast.add({
type,
time: parseInt(ms.value) || 0,
text: `${type} alert! Hello world!`,
@@ -44,7 +44,7 @@ const add = (type) => {
}
const remove = () => {
injected.pop()
toast.pop()
}
</script>

View File

@@ -1,9 +1,49 @@
.list-enter-active,
.list-leave-active {
.slide-left-enter-active,
.slide-left-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
.slide-left-enter-from,
.slide-left-leave-to {
opacity: 0;
transform: translateX(30px);
}
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 0.5s ease;
}
.slide-right-enter-from,
.slide-right-leave-to {
opacity: 0;
transform: translateX(-30px);
}
.slide-top-enter-active,
.slide-top-leave-active {
transition: all 0.5s ease;
}
.slide-top-enter-from,
.slide-top-leave-to {
opacity: 0;
transform: translateY(30px);
}
.slide-bottom-enter-active,
.slide-bottom-leave-active {
transition: all 0.5s ease;
}
.slide-bottom-enter-from,
.slide-bottom-leave-to {
opacity: 0;
transform: translateY(-30px);
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

View File

@@ -1,14 +1,26 @@
<script lang="ts">
import { defineComponent, h, provide, ref, resolveComponent, TransitionGroup } from 'vue'
import { defineComponent, h, provide, ref, TransitionGroup } from 'vue'
import { FLOWBITE_TOAST_INJECTION_KEY } from '@/components/Toast/components/ToastProvider/injection/config'
import type { ToastItem, ToastItemWithId } from '@/components/Toast/components/ToastProvider/types'
import type {
ToastItem,
ToastItemWithId,
ToastTransition,
UseToastInjection,
} from '@/components/Toast/components/ToastProvider/types'
import { Toast } from '@/index'
import { useTimeoutFn } from '@vueuse/core'
import type { PropType } from 'vue'
export default defineComponent({
components: {
Toast: Toast as any,
},
props: {
transition: {
type: String as PropType<ToastTransition>,
default: 'slide-left',
},
},
setup() {
const toasts = ref<ToastItemWithId[]>([])
@@ -17,29 +29,33 @@ export default defineComponent({
}
const addToast = (toast: ToastItem) => {
const id = ((new Date()).getTime() * Math.random()).toString()
const id = parseInt(((new Date()).getTime() * Math.random()).toString()).toString()
toasts.value.push({
id,
...toast,
})
if (toast.time > 0)
runRemoveTimeout(id, toast.time)
return id
}
const popToast = () => {
if (toasts.value.length === 0) return ''
const lastId = toasts.value[toasts.value.length - 1].id
toasts.value.pop()
return lastId
}
const removeToast = (id: string) => {
const index = toasts.value.findIndex(_ => _.id === id)
toasts.value.splice(index, 1)
if(index >= 0) toasts.value.splice(index, 1)
return index >= 0
}
const popToast = () => {
if (toasts.value.length === 0) return
toasts.value.pop()
}
provide(FLOWBITE_TOAST_INJECTION_KEY, {
provide<UseToastInjection>(FLOWBITE_TOAST_INJECTION_KEY, {
add: addToast,
remove: removeToast,
pop: popToast,
remove: removeToast,
})
return {
@@ -49,6 +65,7 @@ export default defineComponent({
},
render() {
const {
$props,
$slots,
toasts,
removeToast,
@@ -57,7 +74,7 @@ export default defineComponent({
return h('div', {}, [
$slots.default ? $slots.default() : null, // rendering default slot
h(TransitionGroup, {
name: 'list',
name: $props.transition,
tag: 'div',
class: 'xl:w-1/6 md:w-1/4 sm:w-1/4 fixed top-3 right-3 flex flex-col gap-2 z-50',
},

View File

@@ -1,3 +1,29 @@
export function useToast() {
return ''
import { inject } from 'vue'
import type { ToastItem, UseToastInjection } from '@/components/Toast/components/ToastProvider/types'
import { FLOWBITE_TOAST_INJECTION_KEY } from '@/components/Toast/components/ToastProvider/injection/config'
export function useToast(): UseToastInjection {
const injection = inject<UseToastInjection | null>(FLOWBITE_TOAST_INJECTION_KEY, null)
if(injection === null) console.warn('Cannot use useToast outside <toast-provider> component. Please wrap your component with <toast-provider>')
const add = (toast: ToastItem): string => {
if(!injection) return ''
return injection?.add(toast)
}
const remove = (id: string): boolean => {
if(!injection) return false
return injection?.remove(id)
}
const pop = (): string => {
if(!injection) return ''
return injection?.pop()
}
return {
add,
remove,
pop,
}
}

View File

@@ -13,5 +13,10 @@ export type ToastItemWithId = ToastItem & {
id: string
}
export type ToastInjection = {
export type ToastTransition = 'slide-left' | 'slide-right' | 'fade' | 'slide-top' | 'slide-bottom'
export type UseToastInjection = {
add: (toast: ToastItem) => string
remove: (id: string) => boolean // true if removed, false if not found
pop: () => string // empty '' string if no toast to pop
}