Merge pull request #42 from themesberg/chank1e/develop
feat: toast-provider and useToast initial
This commit is contained in:
@@ -58,6 +58,7 @@ function getComponents() {
|
||||
function getUtils() {
|
||||
return [
|
||||
{ text: 'Flowbite Themable', link: '/components/flowbiteThemable/flowbiteThemable.md' },
|
||||
{ text: 'ToastProvider', link: '/components/toastProvider/toastProvider.md' },
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -4,4 +4,12 @@ import DefaultTheme from 'vitepress/theme'
|
||||
|
||||
import './clear.css'
|
||||
|
||||
export default DefaultTheme
|
||||
import { TransitionGroup } from "vue";
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
// strange thing, but there is no global transition-group component inside vitepress app
|
||||
app.component('TransitionGroup', TransitionGroup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import ToastClosableExample from './examples/ToastClosableExample.vue';
|
||||
import ToastIconExample from './examples/ToastIconExample.vue';
|
||||
import ToastDivideExample from './examples/ToastDivideExample.vue';
|
||||
import ToastMessageExample from './examples/ToastMessageExample.vue';
|
||||
import ToastProviderExample from './examples/ToastProviderExample.vue';
|
||||
import ToastInteractiveExample from './examples/ToastInteractiveExample.vue'
|
||||
</script>
|
||||
# Toast
|
||||
@@ -176,3 +177,6 @@ import { Toast } from 'flowbite-vue'
|
||||
```
|
||||
|
||||
<ToastIconExample />
|
||||
|
||||
---
|
||||
<ToastProviderExample />
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<toast-provider>
|
||||
<div class="vp-raw flex align-center gap-2 flex-wrap flex-col">
|
||||
<toast-provider-example-child />
|
||||
</div>
|
||||
</toast-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ToastProvider } from '../../../../src/index'
|
||||
import ToastProviderExampleChild from './ToastProviderExampleChild.vue'
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<input class="text-black" type="number" v-model="ms">
|
||||
<div class="flex gap-2">
|
||||
<Button @click="() => add('success')" color="green">success</Button>
|
||||
<Button @click="() => add('warning')" color="yellow">warning</Button>
|
||||
<Button @click="() => add('danger')" color="red">danger</Button>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<Button @click="remove" color="alternative">remove</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { FLOWBITE_TOAST_INJECTION_KEY } from '../../../../src/components/Toast/components/ToastProvider/injection/config'
|
||||
import { inject, ref } from 'vue'
|
||||
import { Button } from '../../../../src/index'
|
||||
|
||||
const ms = ref('5000')
|
||||
|
||||
const injected = inject(FLOWBITE_TOAST_INJECTION_KEY)
|
||||
const add = (type) => {
|
||||
injected.add({
|
||||
time: parseInt(ms.value) || 0,
|
||||
type: type,
|
||||
text: `${type} alert! Hello world!`,
|
||||
})
|
||||
}
|
||||
const remove = () => {
|
||||
injected.pop()
|
||||
}
|
||||
|
||||
</script>
|
||||
6
docs/components/toastProvider/toastProvider.md
Normal file
6
docs/components/toastProvider/toastProvider.md
Normal file
@@ -0,0 +1,6 @@
|
||||
<script setup>
|
||||
import ToastProviderExample from './examples/ToastProviderExample.vue';
|
||||
</script>
|
||||
# Toast provider
|
||||
|
||||
<ToastProviderExample />
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "flowbite-vue",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"repository": "https://github.com/themesberg/flowbite-vue.git",
|
||||
"author": "themesberg",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { ToastAlign, ToastPreset } from '@/components/Toast/types'
|
||||
import type { ToastAlign, ToastType } from '@/components/Toast/types'
|
||||
import type { PropType } from 'vue'
|
||||
import { useToastClasses } from './composables/useToastClasses'
|
||||
import { ref, toRefs } from 'vue'
|
||||
@@ -32,7 +32,7 @@ import FlowbiteThemableChild
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String as PropType<ToastPreset>,
|
||||
type: String as PropType<ToastType>,
|
||||
default: 'empty',
|
||||
},
|
||||
alignment: {
|
||||
@@ -59,5 +59,4 @@ const onClose = () => {
|
||||
emit('close')
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.list-enter-active,
|
||||
.list-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
.list-enter-from,
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, h, provide, ref, resolveComponent } 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 { Toast } from '@/index'
|
||||
import { useTimeoutFn } from '@vueuse/core'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Toast: Toast as any,
|
||||
},
|
||||
setup() {
|
||||
const toasts = ref<ToastItemWithId[]>([])
|
||||
|
||||
const runRemoveTimeout = (id: string, ms: number) => {
|
||||
useTimeoutFn(() => removeToast(id), ms)
|
||||
}
|
||||
|
||||
const addToast = (toast: ToastItem) => {
|
||||
const id = ((new Date()).getTime() * Math.random()).toString()
|
||||
toasts.value.push({
|
||||
id,
|
||||
...toast,
|
||||
})
|
||||
if(toast.time > 0)
|
||||
runRemoveTimeout(id, toast.time)
|
||||
}
|
||||
|
||||
const removeToast = (id: string) => {
|
||||
const index = toasts.value.findIndex(_ => _.id === id)
|
||||
toasts.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const popToast = () => {
|
||||
if(toasts.value.length === 0) return
|
||||
toasts.value.pop()
|
||||
}
|
||||
|
||||
provide(FLOWBITE_TOAST_INJECTION_KEY, {
|
||||
add: addToast,
|
||||
remove: removeToast,
|
||||
pop: popToast,
|
||||
})
|
||||
|
||||
return {
|
||||
toasts,
|
||||
removeToast,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
$slots,
|
||||
toasts,
|
||||
removeToast,
|
||||
} = this
|
||||
|
||||
return h('div', {}, [
|
||||
$slots.default ? $slots.default() : null, // rendering default slot
|
||||
h(resolveComponent('TransitionGroup'), {
|
||||
name: 'list',
|
||||
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',
|
||||
},
|
||||
toasts.map(_toast => // rendering every toast
|
||||
h(resolveComponent('Toast'), {
|
||||
closable: true,
|
||||
type: _toast.type,
|
||||
key: _toast.id,
|
||||
onClose: () => removeToast(_toast.id),
|
||||
}, _toast.text),
|
||||
),
|
||||
),
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped src="./ToastProvider.css"></style>
|
||||
@@ -0,0 +1,3 @@
|
||||
export function useToast() {
|
||||
return ''
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const FLOWBITE_TOAST_INJECTION_KEY = 'flowbite-toast-injection-key'
|
||||
14
src/components/Toast/components/ToastProvider/types.ts
Normal file
14
src/components/Toast/components/ToastProvider/types.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ToastType } from '@/components/Toast/types'
|
||||
|
||||
export type ToastItem = {
|
||||
time: number // ms
|
||||
type: ToastType
|
||||
text: string
|
||||
}
|
||||
|
||||
export type ToastItemWithId = ToastItem & {
|
||||
id: string
|
||||
}
|
||||
|
||||
export type ToastInjection = {
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import type { ToastPreset } from '@/components/Toast/types'
|
||||
import type { ToastType } from '@/components/Toast/types'
|
||||
import { simplifyTailwindClasses } from '@/utils/simplifyTailwindClasses'
|
||||
import type { ToastAlign } from '@/components/Toast/types'
|
||||
|
||||
@@ -11,12 +11,12 @@ type UseToastClassesReturns = {
|
||||
}
|
||||
|
||||
type UseToastClassesProps = {
|
||||
type: Ref<ToastPreset>
|
||||
type: Ref<ToastType>
|
||||
divide: Ref<boolean>
|
||||
alignment: Ref<ToastAlign>
|
||||
}
|
||||
|
||||
const typeClassesMap: Record<ToastPreset, string> = {
|
||||
const typeClassesMap: Record<ToastType, string> = {
|
||||
danger: 'text-red-500 bg-red-100 dark:bg-red-800 dark:text-red-200',
|
||||
empty: '',
|
||||
success: 'text-green-500 bg-green-100 dark:bg-green-800 dark:text-green-200',
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export type ToastPreset = 'success' | 'warning' | 'danger' | 'empty'
|
||||
export type ToastType = 'success' | 'warning' | 'danger' | 'empty'
|
||||
export type ToastAlign = 'start' | 'center' | 'end'
|
||||
|
||||
5
src/composables.ts
Normal file
5
src/composables.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { useToast } from '@/components/Toast/components/ToastProvider/composables/useToast'
|
||||
|
||||
export {
|
||||
useToast,
|
||||
}
|
||||
@@ -26,4 +26,7 @@ export { default as Sidebar } from './components/Sidebar/Sidebar.vue'
|
||||
export { default as Table } from './components/Table/Table.vue'
|
||||
export { default as Timeline } from './components/Timeline/Timeline.vue'
|
||||
export { default as Toast } from './components/Toast/Toast.vue'
|
||||
export { default as ToastProvider } from './components/Toast/components/ToastProvider/ToastProvider.vue'
|
||||
export { default as Tooltip } from './components/Tooltip/Tooltip.vue'
|
||||
|
||||
export * from './composables'
|
||||
|
||||
Reference in New Issue
Block a user