<template> <span class="tw-flex tw-items-center tw-gap-[.33em] tw-text-xs tw-px-[.75em] tw-py-[0.33em] tw-leading-snug tw-rounded-full tw-shrink-[100] tw-min-w-[3em] tw-transition-opacity tw-ease-in-out tw-select-none" :class="[stateClasses, showIndicator ? 'tw-duration-300' : 'tw-duration-500 tw-opacity-0']" :style="{ visibility }" :role="showIndicator ? 'alert' : 'presentation'" :aria-hidden="!showIndicator" :aria-live="showIndicator ? 'polite' : undefined" @transitionstart="setVisibility" @transitionend="setVisibility" > <span class="tw-flex-none tw-w-fit tw-flex tw-items-center tw-justify-start *:tw-flex-none *:tw-size-[1.5em]" > <icon-fluent-spinner-ios-20-regular v-if="state === 'pending'" class="tw-animate-spin tw-opacity-75" /> <icon-uis-check-circle v-else-if="state === 'success'" /> <icon-uis-times-circle v-else-if="state === 'failure'" /> </span> <span class="tw-truncate tw-min-w-0 tw-max-w-full tw-mr-[.25em]"> {{ t(`saveIndicator.label.${state}`) }} </span> </span> </template> <script lang="ts" setup> import { computed, ref, watchEffect } from 'vue' import { useI18n } from '@/i18n' defineOptions({ compatConfig: { MODE: 3 }, }) const props = defineProps<{ state: 'pending' | 'success' | 'failure' }>() const { t } = useI18n() const showIndicator = ref(false) const visibility = ref<'hidden' | 'visible'>('hidden') let showIndicatorSetter: ReturnType<typeof setTimeout> function setVisibility(event: TransitionEvent) { if (event.type === 'transitionstart' && showIndicator.value) visibility.value = 'visible' if (event.type === 'transitionend' && !showIndicator.value) visibility.value = 'hidden' } watchEffect(() => { clearTimeout(showIndicatorSetter) if (props.state === 'pending') showIndicator.value = true else if (props.state === 'failure') showIndicator.value = true else if (props.state === 'success') showIndicatorSetter = setTimeout(() => (showIndicator.value = false), 2 * 1000) }) const stateClasses = computed(() => ({ 'tw-bg-gray-100 tw-text-gray-800': props.state === 'pending', 'tw-bg-green-100 tw-text-green-800': props.state === 'success', 'tw-bg-rose-100 tw-text-rose-800': props.state === 'failure', })) </script>