🚧 wip(messages)

This commit is contained in:
Timothy Yin 2024-11-22 12:34:48 +08:00
parent 6d95d9f9a8
commit f95e068bbe
9 changed files with 153 additions and 107 deletions

View File

@ -5,11 +5,11 @@ provide('navigation', navigation)
</script>
<template>
<RayMessageProvider>
<RayMessages>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</RayMessageProvider>
</RayMessages>
</template>
<style>

View File

@ -4,8 +4,6 @@ import colors from 'tailwindcss/colors'
import module from '../src/module'
import { excludeColors } from '../src/runtime/utils/colors'
console.log(excludeColors(colors))
const { resolve } = createResolver(import.meta.url)
// https://nuxt.com/docs/api/configuration/nuxt-config

View File

@ -1,12 +1,12 @@
<script lang="ts" setup>
const router = useRouter()
const message = useMessage()
</script>
<template>
<div class="flex flex-col gap-8">
<div
class="py-20 flex flex-col justify-center items-center rounded-xl border border-neutral-200 dark:border-neutral-800 pattern"
>
class="py-20 flex flex-col justify-center items-center rounded-xl border border-neutral-200 dark:border-neutral-800 pattern">
<div class="text-xl text-neutral-600 dark:text-neutral-300 font-bold">
<h2 class="text-xl">
RayineUI <span class="font-medium">is a multi-purpose</span>
@ -26,6 +26,9 @@ const router = useRouter()
<RayButton variant="outline" to="/components">
Explore Components
</RayButton>
<RayButton variant="outline" @click="message.success('rayui')">
Message
</RayButton>
</div>
</div>
</template>

View File

@ -1,101 +0,0 @@
<script lang="ts" setup>
import { ref } from 'vue'
import type { Message, MessageType } from '../../types/message'
import { useNuxtApp } from '#app'
const props = defineProps({
max: {
type: Number,
default: 5,
},
})
const nuxtApp = useNuxtApp()
const messageList = ref<Message[]>([])
const createMessage = (content: string, type: MessageType, duration: number = 3000) => {
const { max } = props
messageList.value.push({
id: (Date.now() + Math.random() * 100).toString(32).toUpperCase(),
content,
type,
duration,
})
if (messageList.value.length > max) {
messageList.value.shift()
}
}
const providerApi = {
destroy: (id: string) => {
if (!messageList.value.find(message => message.id === id)) return
messageList.value.splice(messageList.value.findIndex(message => message.id === id), 1)
},
}
const api = {
info: (content: string, duration: number = 3000) => {
createMessage(content, 'info', duration)
},
success: (content: string, duration: number = 3000) => {
createMessage(content, 'success', duration)
},
warning: (content: string, duration: number = 3000) => {
createMessage(content, 'warning', duration)
},
error: (content: string, duration: number = 3000) => {
createMessage(content, 'error', duration)
},
}
nuxtApp.vueApp.provide('ray-message-provider', providerApi)
nuxtApp.vueApp.provide('ray-message', api)
</script>
<template>
<slot />
<teleport to="body">
<div id="message-provider">
<div class="message-wrapper">
<TransitionGroup name="message">
<RayMessage
v-for="(message) in messageList"
:key="message.id"
:message="message"
/>
</TransitionGroup>
</div>
</div>
</teleport>
</template>
<style scoped>
#message-provider .message-wrapper {
@apply z-[50000] fixed inset-0 flex flex-col items-center pointer-events-none;
}
.message-move,
.message-leave-active {
transition: all .8s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.message-enter-active {
transition: all .8s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.message-enter-from {
filter: blur(2px);
opacity: 0;
transform: translateY(-100%);
}
.message-leave-to {
filter: blur(6px);
opacity: 0;
transform: translateY(-20%);
}
.message-leave-active {
position: absolute;
}
</style>

View File

@ -0,0 +1,134 @@
<script lang="ts">
import { computed, defineComponent, ref, toRef, type PropType } from 'vue'
import type { Message, MessageType } from '../../types/message'
import { useNuxtApp } from '#app'
import { messages } from '../../ui.config';
import { twJoin, twMerge } from 'tailwind-merge';
import { useRayUI } from '#build/imports';
import type { DeepPartial, Strategy } from '../../types';
const config = messages
export default defineComponent({
props: {
max: {
type: Number,
default: 5,
},
class: {
type: String,
default: '',
},
ui: {
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
default: () => ({}),
}
},
setup(props) {
const { ui, attrs } = useRayUI('messages', toRef(props, 'ui'), config)
const nuxtApp = useNuxtApp()
const messageList = ref<Message[]>([])
const wrapperClass = computed(() => {
return twMerge(twJoin(
ui.value.wrapper,
ui.value.position
), props.class)
})
const createMessage = (content: string, type: MessageType, duration: number = 3000) => {
const { max } = props
messageList.value.push({
id: (Date.now() + Math.random() * 100).toString(32).toUpperCase(),
content,
type,
duration,
})
if (messageList.value.length > max) {
messageList.value.shift()
}
}
const providerApi = {
destroy: (id: string) => {
if (!messageList.value.find(message => message.id === id)) return
messageList.value.splice(messageList.value.findIndex(message => message.id === id), 1)
},
}
const api = {
info: (content: string, duration: number = 3000) => {
createMessage(content, 'info', duration)
},
success: (content: string, duration: number = 3000) => {
createMessage(content, 'success', duration)
},
warning: (content: string, duration: number = 3000) => {
createMessage(content, 'warning', duration)
},
error: (content: string, duration: number = 3000) => {
createMessage(content, 'error', duration)
},
}
nuxtApp.vueApp.provide('ray-message-provider', providerApi)
nuxtApp.vueApp.provide('ray-message', api)
return {
ui,
attrs,
messageList,
wrapperClass
}
}
})
</script>
<template>
<slot></slot>
<teleport to="body">
<div :class="wrapperClass">
<div :class="ui.container">
<TransitionGroup name="message">
<RayMessage
v-for="(message) in messageList"
:key="message.id"
:message="message"
/>
</TransitionGroup>
</div>
</div>
</teleport>
</template>
<style scoped>
#message-provider .message-wrapper {
@apply z-[50000] fixed inset-0 flex flex-col items-center pointer-events-none;
}
.message-move,
.message-leave-active {
transition: all .8s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.message-enter-active {
transition: all .8s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.message-enter-from {
filter: blur(2px);
opacity: 0;
transform: translateY(-100%);
}
.message-leave-to {
filter: blur(6px);
opacity: 0;
transform: translateY(-20%);
}
.message-leave-active {
position: absolute;
}
</style>

View File

@ -7,6 +7,8 @@ export interface Message {
duration?: number
}
export type MessageOption = Omit<Message, 'id'>
export interface MessageApi {
info: (content: string, duration?: number) => void
success: (content: string, duration?: number) => void

View File

@ -1,2 +1,6 @@
// elements
export { default as button } from './elements/button'
// overlays
export { default as message } from './overlays/message'
export { default as messages } from "./overlays/messages";

View File

@ -0,0 +1 @@
export default {}

View File

@ -0,0 +1,5 @@
export default {
wrapper: "fixed flex flex-col w-full pointer-events-none z-[500]",
position: "bottom-0 end-0",
container: "px-4 sm:px-6 py-6 space-y-3 overflow-y-auto",
};