mirror of
https://github.com/HoshinoSuzumi/rayine-ui.git
synced 2025-04-10 01:28:50 +08:00
💥 refactor(message): BREAKING redesign component props
This commit is contained in:
parent
440613047a
commit
222d2e8f1d
@ -6,8 +6,8 @@ const route = useRoute()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header
|
<header
|
||||||
class="w-full flex justify-between items-center py-2 h-16 z-50 border-b sticky top-0 bg-white/90 dark:bg-neutral-900 transition-colors"
|
class="w-full flex justify-between items-center py-2 h-16 z-50 border-b sticky top-0 bg-white dark:bg-neutral-900 transition-colors"
|
||||||
:class="[route.path !== '/' ? 'border-b-neutral-100 dark:border-b-neutral-800' : 'border-b-transparent dark:border-b-transparent']"
|
:class="[route.path !== '/' ? 'border-b-neutral-200 dark:border-b-neutral-700' : 'border-b-transparent dark:border-b-transparent']"
|
||||||
>
|
>
|
||||||
<NuxtLink to="/" class="text-neutral-900 dark:text-neutral-100">
|
<NuxtLink to="/" class="text-neutral-900 dark:text-neutral-100">
|
||||||
<h1 class="font-medium text-xl">
|
<h1 class="font-medium text-xl">
|
||||||
|
33
docs/components/content/ComponentDefaults.vue
Normal file
33
docs/components/content/ComponentDefaults.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { camelCase, upperFirst } from 'scule';
|
||||||
|
import * as config from '#rayui/ui.config'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
slug: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const slug = props.slug || route.params.slug[route.params.slug.length - 1]
|
||||||
|
const componentCamelName = camelCase(slug)
|
||||||
|
const componentName = `Ray${upperFirst(componentCamelName)}`
|
||||||
|
|
||||||
|
const defaults = config[componentCamelName as keyof typeof config]
|
||||||
|
|
||||||
|
const { data: defaultsRender } = await useAsyncData(`${componentName}-defaults`, () => parseMarkdown(`
|
||||||
|
\`\`\`json
|
||||||
|
${JSON.stringify(defaults, null, 2)}
|
||||||
|
\`\`\`
|
||||||
|
`, {}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContentRenderer v-if="defaultsRender?.body" :value="defaultsRender" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -29,6 +29,14 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
options: {
|
||||||
|
type: Array as PropType<{ name: string, values: string[], restriction: 'expected' | 'included' | 'excluded' | 'only' }[]>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
excludedProps: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
filename: {
|
filename: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
@ -46,14 +54,17 @@ const componentProps = reactive({ ...props.props })
|
|||||||
|
|
||||||
const customizableOptions = (key: string, schema: { kind: string, type: string, schema: [] }) => {
|
const customizableOptions = (key: string, schema: { kind: string, type: string, schema: [] }) => {
|
||||||
let options: string[] = []
|
let options: string[] = []
|
||||||
const invalidTypes = ['string', 'array', 'boolean', 'object', 'number', 'Function']
|
const optionItem = props?.options?.find(item => item?.name === key) || null
|
||||||
const hasInvalidType = schema?.type?.split('|')?.map(item => item.trim()?.replaceAll('"', ''))?.some(type => invalidTypes.includes(type))
|
const types = schema?.type?.split('|')?.map(item => item.trim()?.replaceAll('"', '')) || []
|
||||||
const schemaOptions = Object.values(schema?.schema || {})
|
const invalidTypes = ['string', 'number', 'boolean', 'array', 'object', 'Function', 'undefined']
|
||||||
|
const hasInvalidType = types?.every(type => invalidTypes.includes(type))
|
||||||
|
|
||||||
if (key.toLowerCase().endsWith('color')) {
|
if (key.toLowerCase().endsWith('color')) {
|
||||||
options = [...appConfig.rayui.colors]
|
options = [...appConfig.rayui.colors]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const schemaOptions = Object.values(schema?.schema || {})
|
||||||
|
|
||||||
if (key.toLowerCase() === 'size' && schemaOptions?.length) {
|
if (key.toLowerCase() === 'size' && schemaOptions?.length) {
|
||||||
const baseSizeOrder = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
|
const baseSizeOrder = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
|
||||||
schemaOptions.sort((a: string, b: string) => {
|
schemaOptions.sort((a: string, b: string) => {
|
||||||
@ -65,24 +76,41 @@ const customizableOptions = (key: string, schema: { kind: string, type: string,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schemaOptions?.length > 0 && schema?.kind === 'enum' && !hasInvalidType) {
|
if (schemaOptions?.length > 0 && schema?.kind === 'enum' && !hasInvalidType && optionItem?.restriction !== 'only') {
|
||||||
options = schemaOptions.filter(option => option !== 'undefined' && typeof option === 'string').map((option: string) => option.replaceAll('"', ''))
|
options = schemaOptions.filter(option => typeof option === 'string' && option !== 'undefined').map((option: string) => option.replaceAll('"', ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionItem?.restriction === 'only') {
|
||||||
|
options = optionItem.values
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionItem?.restriction === 'expected') {
|
||||||
|
options = options.filter(item => optionItem.values.includes(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionItem?.restriction === 'included') {
|
||||||
|
options = [...options, ...optionItem.values]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionItem?.restriction === 'excluded') {
|
||||||
|
options = options.filter(item => !optionItem.values.includes(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
const customizableProps = computed(() => Object.keys(componentProps).map((k) => {
|
const customizableProps = computed(() => Object.keys(componentProps).map((k) => {
|
||||||
|
if (props.excludedProps.includes(k)) return null
|
||||||
const prop = componentMeta?.meta?.props?.find((prop: any) => prop.name === k)
|
const prop = componentMeta?.meta?.props?.find((prop: any) => prop.name === k)
|
||||||
const schema = prop?.schema || {}
|
const schema = prop?.schema || {}
|
||||||
const options = customizableOptions(k, schema)
|
const options = customizableOptions(k, schema)
|
||||||
return {
|
return {
|
||||||
name: k,
|
name: k,
|
||||||
type: prop?.type,
|
type: prop?.type || 'string',
|
||||||
label: camelCase(k),
|
label: camelCase(k),
|
||||||
options,
|
options,
|
||||||
}
|
}
|
||||||
}))
|
}).filter((prop) => prop !== null))
|
||||||
|
|
||||||
const code = computed(() => {
|
const code = computed(() => {
|
||||||
let code = `\`\`\`html
|
let code = `\`\`\`html
|
||||||
@ -99,22 +127,8 @@ const code = computed(() => {
|
|||||||
return code
|
return code
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: codeRender, error: codeRenderError } = await useAsyncData(`${componentName}-renderer-${JSON.stringify({ slots: props.slots, code: code.value })}`, async () => {
|
const { data: codeRender, error: codeRenderError } = await useAsyncData(`${componentName}-renderer-${JSON.stringify({ props: componentProps, slots: props.slots, code: code.value })}`, async () => {
|
||||||
let formatted = ''
|
return parseMarkdown(code.value, {})
|
||||||
try {
|
|
||||||
// @ts-ignore
|
|
||||||
formatted = await $prettier.format(code.value, {
|
|
||||||
trailingComma: 'none',
|
|
||||||
semi: false,
|
|
||||||
singleQuote: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
formatted = code.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseMarkdown(formatted, {
|
|
||||||
})
|
|
||||||
}, {
|
}, {
|
||||||
watch: [code],
|
watch: [code],
|
||||||
})
|
})
|
||||||
@ -131,32 +145,23 @@ const { data: codeRender, error: codeRenderError } = await useAsyncData(`${compo
|
|||||||
|
|
||||||
<div :class="['p-4 overflow-auto', !!codeRender ? 'border-b border-neutral-200 dark:border-neutral-700' : '']">
|
<div :class="['p-4 overflow-auto', !!codeRender ? 'border-b border-neutral-200 dark:border-neutral-700' : '']">
|
||||||
<component :is="componentName" v-bind="componentProps">
|
<component :is="componentName" v-bind="componentProps">
|
||||||
<slot />
|
<slot></slot>
|
||||||
</component>
|
</component>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="customizableProps.length > 0" class="border-b border-neutral-200 dark:border-neutral-700 flex">
|
<div v-if="customizableProps.length > 0" class="border-b border-neutral-200 dark:border-neutral-700 flex">
|
||||||
<div v-for="(prop, k) in customizableProps" :key="k" class="px-2 py-0.5 flex flex-col gap-0.5 border-r dark:border-neutral-700">
|
<div v-for="(prop, k) in customizableProps" :key="k"
|
||||||
|
class="px-2 py-0.5 flex flex-col gap-0.5 border-r dark:border-neutral-700">
|
||||||
<label :for="`${prop.name}-prop`" class="text-sm text-neutral-400">{{ prop.name }}</label>
|
<label :for="`${prop.name}-prop`" class="text-sm text-neutral-400">{{ prop.name }}</label>
|
||||||
<input
|
<input v-if="prop.type.startsWith('boolean')" :id="`${prop.name}-prop`" v-model="componentProps[prop.name]"
|
||||||
v-if="prop.type.startsWith('boolean')"
|
type="checkbox" class="mt-1 mb-2">
|
||||||
:id="`${prop.name}-prop`"
|
<select v-else-if="prop.options.length" :id="`${prop.name}-prop`" v-model="componentProps[prop.name]">
|
||||||
v-model="componentProps[prop.name]"
|
|
||||||
type="checkbox"
|
|
||||||
class="mt-1 mb-2"
|
|
||||||
>
|
|
||||||
<select v-else-if="prop.options.length > 0" :id="`${prop.name}-prop`" v-model="componentProps[prop.name]">
|
|
||||||
<option v-for="option in prop.options" :key="option" :value="option">
|
<option v-for="option in prop.options" :key="option" :value="option">
|
||||||
{{ option }}
|
{{ option }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<input
|
<input v-else :id="`${prop.name}-prop`" v-model="componentProps[prop.name]" type="text"
|
||||||
v-else
|
placeholder="type something...">
|
||||||
:id="`${prop.name}-prop`"
|
|
||||||
v-model="componentProps[prop.name]"
|
|
||||||
type="text"
|
|
||||||
placeholder="type something..."
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
highlight: {
|
highlight: {
|
||||||
langs: ['postcss', 'mdc', 'html', 'vue', 'ts', 'js', 'bash'],
|
langs: ['postcss', 'mdc', 'html', 'vue', 'ts', 'js', 'bash', 'yml'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mdc: {
|
mdc: {
|
||||||
|
@ -29,7 +29,7 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="grid grid-cols-12 gap-4 pb-10">
|
<div class="grid grid-cols-12 gap-4 pb-10">
|
||||||
<div class="hidden col-span-2 md:block">
|
<div class="hidden col-span-2 md:block">
|
||||||
<nav class="ml-1">
|
<nav class="ml-1 overflow-hidden overflow-y-auto sticky top-[calc(64px+16px)]">
|
||||||
<ContentNavigation v-slot="{ navigation }">
|
<ContentNavigation v-slot="{ navigation }">
|
||||||
<ul class="space-y-2">
|
<ul class="space-y-2">
|
||||||
<li v-for="link of navigation" :key="link._path">
|
<li v-for="link of navigation" :key="link._path">
|
||||||
@ -38,11 +38,8 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<ul v-if="link.children" class="pl-4 pt-2 space-y-1">
|
<ul v-if="link.children" class="pl-4 pt-2 space-y-1">
|
||||||
<li v-for="child in link.children" :key="child._path">
|
<li v-for="child in link.children" :key="child._path">
|
||||||
<NuxtLink
|
<NuxtLink :to="child._path" class="text-sm text-neutral-500 dark:text-neutral-400"
|
||||||
:to="child._path"
|
active-class="text-primary font-medium">
|
||||||
class="text-sm text-neutral-500 dark:text-neutral-400"
|
|
||||||
active-class="text-primary font-medium"
|
|
||||||
>
|
|
||||||
{{ child.title }}
|
{{ child.title }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
@ -68,8 +65,7 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="w-full flex justify-between gap-4 mt-12 pt-12 border-t border-t-neutral-200 dark:border-t-neutral-700"
|
class="w-full flex justify-between gap-4 mt-12 pt-12 border-t border-t-neutral-200 dark:border-t-neutral-700">
|
||||||
>
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<NuxtLink v-if="surround?.[0]" :to="surround[0]._path" class="surround-btn">
|
<NuxtLink v-if="surround?.[0]" :to="surround[0]._path" class="surround-btn">
|
||||||
<div>
|
<div>
|
||||||
@ -93,14 +89,14 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
|
|
||||||
<div v-if="hasToc" class="hidden" :class="{ 'col-span-2 md:block': hasToc }">
|
<div v-if="hasToc" class="hidden" :class="{ 'col-span-2 md:block': hasToc }">
|
||||||
<div
|
<div
|
||||||
class="bg-neutral-50 dark:bg-neutral-800/50 rounded-lg px-4 py-3 overflow-hidden overflow-y-auto sticky top-[calc(64px+16px)]"
|
class="bg-neutral-50 dark:bg-neutral-800/50 rounded-lg px-4 py-3 overflow-hidden overflow-y-auto sticky top-[calc(64px+16px)]">
|
||||||
>
|
|
||||||
<span class="text-xs text-neutral-600 dark:text-neutral-300 font-medium inline-block mb-2">
|
<span class="text-xs text-neutral-600 dark:text-neutral-300 font-medium inline-block mb-2">
|
||||||
Table of contents
|
Table of contents
|
||||||
</span>
|
</span>
|
||||||
<Toc :toc="page!.body!.toc!.links" />
|
<Toc :toc="page!.body!.toc!.links" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -126,10 +122,10 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.surround-btn {
|
.surround-btn {
|
||||||
@apply font-medium;
|
@apply font-medium;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
@apply bg-neutral-100 dark:bg-neutral-800 rounded-lg px-8 py-6 w-full flex flex-col gap-0 border border-transparent;
|
@apply bg-neutral-100 dark:bg-neutral-800 rounded-lg px-8 py-6 w-full h-full flex flex-col gap-0 border border-transparent;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@apply border-primary;
|
@apply border-primary;
|
||||||
@ -149,7 +145,7 @@ const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
@apply pt-2 text-sm font-normal text-neutral-500 dark:text-neutral-400;
|
@apply pt-2 text-sm font-normal text-neutral-500 dark:text-neutral-400 line-clamp-2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref, onMounted, defineComponent, type PropType, toRef, computed } from 'vue'
|
import { ref, onMounted, defineComponent, type PropType, toRef, computed } from 'vue'
|
||||||
import { twJoin, twMerge } from 'tailwind-merge'
|
import { twJoin, twMerge } from 'tailwind-merge'
|
||||||
import type { Message, MessageType } from '../../types/message'
|
import type { Message, MessageColor, MessageType } from '../../types/message'
|
||||||
import { message } from '../../ui.config'
|
import { message } from '../../ui.config'
|
||||||
import type { DeepPartial, Strategy } from '../../types'
|
import type { DeepPartial, Strategy } from '../../types'
|
||||||
import { useMessage, useRayUI } from '#build/imports'
|
import { useMessage, useRayUI } from '#build/imports'
|
||||||
@ -10,9 +10,25 @@ const config = message
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
message: {
|
type: {
|
||||||
type: Object as PropType<Message>,
|
type: String as PropType<MessageType>,
|
||||||
require: true,
|
default: config.default.type,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String as PropType<MessageColor>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
default: config.default.duration,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -26,28 +42,27 @@ export default defineComponent({
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
const { ui, attrs } = useRayUI('message', toRef(props, 'ui'), config)
|
const { ui, attrs } = useRayUI('message', toRef(props, 'ui'), config)
|
||||||
|
|
||||||
const resolvedColor = computed(() => {
|
|
||||||
if (!props.message?.type) return props.message?.color || ui.value.default.color || 'primary'
|
|
||||||
return ({
|
|
||||||
info: 'blue',
|
|
||||||
success: 'emerald',
|
|
||||||
warning: 'orange',
|
|
||||||
error: 'rose',
|
|
||||||
} as Record<MessageType, string>)[props.message.type]
|
|
||||||
})
|
|
||||||
|
|
||||||
const containerClass = computed(() => {
|
const containerClass = computed(() => {
|
||||||
|
const color = props.color ? props.color : (ui.value.type[props.type]?.color || ui.value.default.color)
|
||||||
return twMerge(twJoin(
|
return twMerge(twJoin(
|
||||||
ui.value.container,
|
ui.value.container,
|
||||||
ui.value.rounded,
|
ui.value.rounded,
|
||||||
ui.value.background.replaceAll('{color}', resolvedColor.value),
|
ui.value.shadow.replaceAll('{color}', color),
|
||||||
ui.value.content.replaceAll('{color}', resolvedColor.value),
|
ui.value.background.replaceAll('{color}', color),
|
||||||
ui.value.border.replaceAll('{color}', resolvedColor.value),
|
ui.value.content.replaceAll('{color}', color),
|
||||||
|
ui.value.border.replaceAll('{color}', color),
|
||||||
), props.class)
|
), props.class)
|
||||||
})
|
})
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const messageBody = ref<Message>(props.message as Message)
|
const messageBody = computed<Message>(() => {
|
||||||
|
return {
|
||||||
|
id: props.id || message.generateId(),
|
||||||
|
content: props.content,
|
||||||
|
duration: props.duration,
|
||||||
|
type: props.type,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -69,10 +84,10 @@ export default defineComponent({
|
|||||||
<template>
|
<template>
|
||||||
<div :class="ui.wrapper" v-bind="attrs">
|
<div :class="ui.wrapper" v-bind="attrs">
|
||||||
<div :class="containerClass">
|
<div :class="containerClass">
|
||||||
<IconCircleSuccess v-if="messageBody.type === 'success'" class="text-xl" />
|
<IconCircleSuccess v-if="messageBody?.type === 'success'" class="text-xl" />
|
||||||
<IconCircleWarning v-if="messageBody.type === 'warning'" class="text-xl" />
|
<IconCircleWarning v-if="messageBody?.type === 'warning'" class="text-xl" />
|
||||||
<IconCircleError v-if="messageBody.type === 'error'" class="text-xl" />
|
<IconCircleError v-if="messageBody?.type === 'error'" class="text-xl" />
|
||||||
<IconCircleInfo v-if="messageBody.type === 'info'" class="text-xl" />
|
<IconCircleInfo v-if="messageBody?.type === 'info'" class="text-xl" />
|
||||||
<span>
|
<span>
|
||||||
{{ messageBody.content }}
|
{{ messageBody.content }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -4,6 +4,10 @@ import { useState } from '#imports'
|
|||||||
export const useMessage = () => {
|
export const useMessage = () => {
|
||||||
const messages = useState<Message[]>('messages', () => [])
|
const messages = useState<Message[]>('messages', () => [])
|
||||||
|
|
||||||
|
const generateId = () => {
|
||||||
|
return (Date.now() + Math.random() * 100).toString(32).toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
const add = (message: Partial<Message>) => {
|
const add = (message: Partial<Message>) => {
|
||||||
const msg = {
|
const msg = {
|
||||||
id: (Date.now() + Math.random() * 100).toString(32).toUpperCase(),
|
id: (Date.now() + Math.random() * 100).toString(32).toUpperCase(),
|
||||||
@ -38,6 +42,7 @@ export const useMessage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
generateId,
|
||||||
add,
|
add,
|
||||||
update,
|
update,
|
||||||
remove,
|
remove,
|
||||||
|
19
src/runtime/types/message.d.ts
vendored
19
src/runtime/types/message.d.ts
vendored
@ -1,27 +1,16 @@
|
|||||||
import type { message } from '../ui.config'
|
import type { message } from '../ui.config'
|
||||||
import type colors from '#ray-colors'
|
import type colors from '#ray-colors'
|
||||||
|
import type { AppConfig } from 'nuxt/schema';
|
||||||
|
|
||||||
export type MessageType = 'success' | 'warning' | 'error' | 'info'
|
export type MessageType = keyof typeof message.type
|
||||||
export type MessageColor = (typeof colors)[number]
|
export type MessageColor = (typeof colors)[number]
|
||||||
|
|
||||||
export interface Message {
|
export interface Message {
|
||||||
id: string
|
id: string
|
||||||
content: string
|
content: string
|
||||||
type: MessageType
|
type?: MessageType
|
||||||
color?: MessageColor
|
color?: MessageColor
|
||||||
duration?: number
|
duration?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageOption = Omit<Message, 'id'>
|
// export type MessageOption = Omit<Message, 'id'>
|
||||||
|
|
||||||
export interface MessageApi {
|
|
||||||
info: (content: string, duration?: number) => void
|
|
||||||
success: (content: string, duration?: number) => void
|
|
||||||
warning: (content: string, duration?: number) => void
|
|
||||||
error: (content: string, duration?: number) => void
|
|
||||||
destroyAll: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MessageProviderApi {
|
|
||||||
destroy: (id: string) => void
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,28 @@
|
|||||||
export default {
|
export default {
|
||||||
wrapper: 'mx-auto w-fit pointer-events-auto',
|
wrapper: "mx-auto w-fit pointer-events-auto",
|
||||||
container: 'px-2 py-1.5 border flex items-center gap-1.5',
|
container: "px-2 py-1.5 border flex items-center gap-1.5",
|
||||||
rounded: 'rounded-md',
|
shadow: "shadow-md shadow-{color}-100 dark:shadow-{color}-900",
|
||||||
border: 'border-{color}-400 dark:border-{color}-600',
|
rounded: "rounded-md",
|
||||||
background: 'bg-{color}-50 dark:bg-{color}-900',
|
border: "border-{color}-400 dark:border-{color}-600",
|
||||||
content: 'text-xs font-sans font-bold text-{color}-500 dark:text-{color}-300',
|
background: "bg-{color}-50 dark:bg-{color}-900",
|
||||||
|
content: "text-xs font-sans font-bold text-{color}-500 dark:text-{color}-300",
|
||||||
|
type: {
|
||||||
|
success: {
|
||||||
|
color: "emerald",
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
color: "amber",
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
color: "blue",
|
||||||
|
},
|
||||||
|
},
|
||||||
default: {
|
default: {
|
||||||
|
type: 'info',
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
@ -63,98 +63,105 @@ export const setColors = (theme: TwConfig['theme']) => {
|
|||||||
|
|
||||||
const safelistForComponent: Record<
|
const safelistForComponent: Record<
|
||||||
string,
|
string,
|
||||||
(colors: string) => TwConfig['safelist']
|
(colors: string) => TwConfig["safelist"]
|
||||||
> = {
|
> = {
|
||||||
button: colorsToRegex => [
|
button: (colorsToRegex) => [
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-50$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-50$`),
|
||||||
variants: ['hover', 'disabled'],
|
variants: ["hover", "disabled"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-100$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-100$`),
|
||||||
variants: ['hover'],
|
variants: ["hover"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-400$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-400$`),
|
||||||
variants: ['dark', 'dark:disabled'],
|
variants: ["dark", "dark:disabled"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-500$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-500$`),
|
||||||
variants: ['disabled', 'dark:hover', 'dark:active'],
|
variants: ["disabled", "dark:hover", "dark:active"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-600$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-600$`),
|
||||||
variants: ['hover'],
|
variants: ["hover"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-700$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-700$`),
|
||||||
variants: ['active'],
|
variants: ["active"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-900$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-900$`),
|
||||||
variants: ['dark:hover'],
|
variants: ["dark:hover"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-950$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-950$`),
|
||||||
variants: ['dark', 'dark:hover', 'dark:disabled'],
|
variants: ["dark", "dark:hover", "dark:disabled"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^text-(${colorsToRegex})-400$`),
|
pattern: RegExp(`^text-(${colorsToRegex})-400$`),
|
||||||
variants: ['dark', 'dark:hover', 'dark:disabled'],
|
variants: ["dark", "dark:hover", "dark:disabled"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^text-(${colorsToRegex})-500$`),
|
pattern: RegExp(`^text-(${colorsToRegex})-500$`),
|
||||||
variants: ['dark:hover', 'disabled'],
|
variants: ["dark:hover", "disabled"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^text-(${colorsToRegex})-600$`),
|
pattern: RegExp(`^text-(${colorsToRegex})-600$`),
|
||||||
variants: ['hover'],
|
variants: ["hover"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^outline-(${colorsToRegex})-400$`),
|
pattern: RegExp(`^outline-(${colorsToRegex})-400$`),
|
||||||
variants: ['dark:focus-visible'],
|
variants: ["dark:focus-visible"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^outline-(${colorsToRegex})-500$`),
|
pattern: RegExp(`^outline-(${colorsToRegex})-500$`),
|
||||||
variants: ['focus-visible'],
|
variants: ["focus-visible"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^ring-(${colorsToRegex})-300$`),
|
pattern: RegExp(`^ring-(${colorsToRegex})-300$`),
|
||||||
variants: ['focus', 'dark:focus'],
|
variants: ["focus", "dark:focus"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^ring-(${colorsToRegex})-400$`),
|
pattern: RegExp(`^ring-(${colorsToRegex})-400$`),
|
||||||
variants: ['dark:focus-visible'],
|
variants: ["dark:focus-visible"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^ring-(${colorsToRegex})-500$`),
|
pattern: RegExp(`^ring-(${colorsToRegex})-500$`),
|
||||||
variants: ['focus-visible'],
|
variants: ["focus-visible"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
message: colorsToRegex => [
|
message: (colorsToRegex) => [
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-50$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-50$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^bg-(${colorsToRegex})-900$`),
|
pattern: RegExp(`^bg-(${colorsToRegex})-900$`),
|
||||||
variants: ['dark'],
|
variants: ["dark"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^text-(${colorsToRegex})-500$`),
|
pattern: RegExp(`^text-(${colorsToRegex})-500$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^text-(${colorsToRegex})-300$`),
|
pattern: RegExp(`^text-(${colorsToRegex})-300$`),
|
||||||
variants: ['dark'],
|
variants: ["dark"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^border-(${colorsToRegex})-400$`),
|
pattern: RegExp(`^border-(${colorsToRegex})-400$`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: RegExp(`^border-(${colorsToRegex})-600$`),
|
pattern: RegExp(`^border-(${colorsToRegex})-600$`),
|
||||||
variants: ['dark'],
|
variants: ["dark"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^shadow-(${colorsToRegex})-100$`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: RegExp(`^shadow-(${colorsToRegex})-900$`),
|
||||||
|
variants: ["dark"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
};
|
||||||
|
|
||||||
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
export const generateSafelist = (colors: string[], globalColors: string[]) => {
|
||||||
const safelist = Object.keys(safelistForComponent).flatMap(component =>
|
const safelist = Object.keys(safelistForComponent).flatMap(component =>
|
||||||
|
Loading…
Reference in New Issue
Block a user