47 lines
1.4 KiB
Vue
47 lines
1.4 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
|
|
export type UpdateTone = 'neutral' | 'success' | 'info' | 'warning' | 'error'
|
|
|
|
const TONE_CLASSES: Record<UpdateTone, { block: string; icon: string }> = {
|
|
neutral: { block: 'bg-muted', icon: 'text-toned' },
|
|
success: { block: 'bg-(--rc-success-soft)', icon: 'text-success' },
|
|
info: { block: 'bg-(--rc-info-soft)', icon: 'text-info' },
|
|
warning: { block: 'bg-(--rc-warning-soft)', icon: 'text-warning' },
|
|
error: { block: 'bg-(--rc-error-soft)', icon: 'text-error' },
|
|
}
|
|
|
|
const props = defineProps<{
|
|
tone: UpdateTone
|
|
icon: string
|
|
title: string
|
|
text: string
|
|
meta?: string
|
|
live?: 'polite' | 'off'
|
|
}>()
|
|
|
|
const toneClasses = computed(() => TONE_CLASSES[props.tone])
|
|
const spinning = computed(() => props.icon === 'i-lucide-loader-circle')
|
|
</script>
|
|
|
|
<template>
|
|
<div :aria-live="live ?? 'polite'" class="flex gap-3 p-3.5" :class="toneClasses.block">
|
|
<span class="flex pt-0.5">
|
|
<UIcon
|
|
:name="icon"
|
|
class="size-4.5 shrink-0"
|
|
:class="[
|
|
toneClasses.icon,
|
|
spinning ? 'animate-spin motion-reduce:animate-none' : '',
|
|
]"
|
|
/>
|
|
</span>
|
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
<span class="text-base font-semibold text-highlighted">{{ title }}</span>
|
|
<span class="text-sm text-pretty text-toned">{{ text }}</span>
|
|
<slot />
|
|
<span v-if="meta" class="text-xs/5 text-muted">{{ meta }}</span>
|
|
</div>
|
|
</div>
|
|
</template>
|