feat(module): support i18n in components (#2553)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Alex
2024-11-08 21:22:57 +05:00
committed by GitHub
parent 1e7638bd03
commit 26362408b1
30 changed files with 673 additions and 18 deletions

View File

@@ -74,6 +74,7 @@ const emits = defineEmits<AlertEmits>()
const slots = defineSlots<AlertSlots>()
const appConfig = useAppConfig()
const { t } = useLocale()
const multiline = computed(() => !!props.title && !!props.description)
@@ -123,7 +124,7 @@ const ui = computed(() => alert({
size="md"
color="neutral"
variant="link"
aria-label="Close"
:aria-label="t('ui.alert.close')"
v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close({ class: props.ui?.close })"
@click="emits('update:open', false)"

View File

@@ -1,11 +1,12 @@
<script lang="ts">
import type { ConfigProviderProps, TooltipProviderProps } from 'radix-vue'
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
import type { ToasterProps } from '../types'
import type { ToasterProps, Locale } from '../types'
export interface AppProps extends Omit<ConfigProviderProps, 'useId'> {
tooltip?: TooltipProviderProps
toaster?: ToasterProps | null
locale?: Locale
}
export interface AppSlots {
@@ -26,6 +27,7 @@ import { reactivePick } from '@vueuse/core'
import UToaster from './Toaster.vue'
import UModalProvider from './ModalProvider.vue'
import USlideoverProvider from './SlideoverProvider.vue'
import { localeContextInjectionKey } from '../composables/useLocale'
const props = defineProps<AppProps>()
defineSlots<AppSlots>()
@@ -33,6 +35,8 @@ defineSlots<AppSlots>()
const configProviderProps = useForwardProps(reactivePick(props, 'dir', 'scrollBody'))
const tooltipProps = toRef(() => props.tooltip)
const toasterProps = toRef(() => props.toaster)
provide(localeContextInjectionKey, computed(() => props.locale))
</script>
<template>

View File

@@ -134,6 +134,7 @@ const props = withDefaults(defineProps<CarouselProps<T>>(), {
defineSlots<CarouselSlots<T>>()
const appConfig = useAppConfig()
const { t } = useLocale()
const rootProps = useForwardProps(reactivePick(props, 'active', 'align', 'breakpoints', 'containScroll', 'dragFree', 'dragThreshold', 'duration', 'inViewThreshold', 'loop', 'skipSnaps', 'slidesToScroll', 'startIndex', 'watchDrag', 'watchResize', 'watchSlides', 'watchFocus'))
const ui = computed(() => carousel({
@@ -279,7 +280,7 @@ defineExpose({
size="md"
color="neutral"
variant="outline"
aria-label="Prev"
:aria-label="t('ui.carousel.prev')"
v-bind="typeof prev === 'object' ? prev : undefined"
:class="ui.prev({ class: props.ui?.prev })"
@click="scrollPrev"
@@ -290,7 +291,7 @@ defineExpose({
size="md"
color="neutral"
variant="outline"
aria-label="Next"
:aria-label="t('ui.carousel.next')"
v-bind="typeof next === 'object' ? next : undefined"
:class="ui.next({ class: props.ui?.next })"
@click="scrollNext"
@@ -300,7 +301,7 @@ defineExpose({
<div v-if="dots" :class="ui.dots({ class: props.ui?.dots })">
<template v-for="(_, index) in scrollSnaps" :key="index">
<button
:aria-label="`Go to slide ${index + 1}`"
:aria-label="t('ui.carousel.goto', { slide: index + 1 })"
:class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })"
@click="scrollTo(index)"
/>

View File

@@ -144,6 +144,7 @@ const slots = defineSlots<CommandPaletteSlots<G, T>>()
const searchTerm = defineModel<string>('searchTerm', { default: '' })
const appConfig = useAppConfig()
const { t } = useLocale()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'disabled', 'multiple', 'modelValue', 'defaultValue', 'selectedValue', 'resetSearchTermOnBlur'), emits)
const inputProps = useForwardProps(reactivePick(props, 'loading', 'loadingIcon', 'placeholder'))
@@ -245,7 +246,7 @@ const groups = computed(() => {
size="md"
color="neutral"
variant="ghost"
aria-label="Close"
:aria-label="t('ui.commandPalette.close')"
v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close({ class: props.ui?.close })"
@click="emits('update:open', false)"
@@ -259,7 +260,7 @@ const groups = computed(() => {
<ComboboxContent :class="ui.content({ class: props.ui?.content })" :dismissable="false">
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty" :search-term="searchTerm">
{{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
{{ searchTerm ? t('ui.commandPalette.noMatch', { searchTerm }) : t('ui.commandPalette.noData') }}
</slot>
</ComboboxEmpty>

View File

@@ -141,6 +141,7 @@ import { get, escapeRegExp } from '../utils'
import UIcon from './Icon.vue'
import UAvatar from './Avatar.vue'
import UChip from './Chip.vue'
import { useLocale } from '../composables/useLocale'
defineOptions({ inheritAttrs: false })
@@ -157,6 +158,7 @@ const slots = defineSlots<InputMenuSlots<T>>()
const searchTerm = defineModel<string>('searchTerm', { default: '' })
const appConfig = useAppConfig()
const { t } = useLocale()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'resetSearchTermOnBlur'), emits)
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, position: 'popper' }) as ComboboxContentProps)
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
@@ -347,7 +349,7 @@ defineExpose({
<ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty" :search-term="searchTerm">
{{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
{{ searchTerm ? t('ui.inputMenu.noMatch', { searchTerm }) : t('ui.inputMenu.noData') }}
</slot>
</ComboboxEmpty>

View File

@@ -103,6 +103,7 @@ const contentEvents = computed(() => {
})
const appConfig = useAppConfig()
const { t } = useLocale()
const ui = computed(() => modal({
transition: props.transition,
@@ -143,7 +144,7 @@ const ui = computed(() => modal({
size="md"
color="neutral"
variant="ghost"
aria-label="Close"
:aria-label="t('ui.modal.close')"
v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close({ class: props.ui?.close })"
/>

View File

@@ -147,6 +147,8 @@ const slots = defineSlots<SelectMenuSlots<T>>()
const searchTerm = defineModel<string>('searchTerm', { default: '' })
const appConfig = useAppConfig()
const { t } = useLocale()
const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'selectedValue', 'open', 'defaultOpen', 'resetSearchTermOnBlur'), emits)
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, position: 'popper' }) as ComboboxContentProps)
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
@@ -284,7 +286,7 @@ function onUpdateOpen(value: boolean) {
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty" :search-term="searchTerm">
{{ searchTerm ? `No results for ${searchTerm}` : 'No results' }}
{{ searchTerm ? t('ui.selectMenu.noMatch', { searchTerm }) : t('ui.selectMenu.noData') }}
</slot>
</ComboboxEmpty>

View File

@@ -102,6 +102,7 @@ const contentEvents = computed(() => {
})
const appConfig = useAppConfig()
const { t } = useLocale()
const ui = computed(() => slideover({
transition: props.transition,
@@ -142,7 +143,7 @@ const ui = computed(() => slideover({
size="md"
color="neutral"
variant="ghost"
aria-label="Close"
:aria-label="t('ui.slideover.close')"
v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close({ class: props.ui?.close })"
/>

View File

@@ -114,6 +114,7 @@ import { upperFirst } from 'scule'
const props = defineProps<TableProps<T>>()
defineSlots<TableSlots<T>>()
const { t } = useLocale()
const data = computed(() => props.data ?? [])
const columns = computed<TableColumn<T>[]>(() => props.columns ?? Object.keys(data.value[0] ?? {}).map((accessorKey: string) => ({ accessorKey, header: upperFirst(accessorKey) })))
@@ -231,7 +232,7 @@ defineExpose({
<tr v-else :class="ui.tr({ class: [props.ui?.tr] })">
<td :colspan="columns?.length" :class="ui.empty({ class: props.ui?.empty })">
<slot name="empty">
No results
{{ t('ui.table.noData') }}
</slot>
</td>
</tr>

View File

@@ -74,6 +74,7 @@ const emits = defineEmits<ToastEmits>()
const slots = defineSlots<ToastSlots>()
const appConfig = useAppConfig()
const { t } = useLocale()
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'defaultOpen', 'open', 'duration', 'type'), emits)
const multiline = computed(() => !!props.title && !!props.description)
@@ -151,7 +152,7 @@ defineExpose({
size="md"
color="neutral"
variant="link"
aria-label="Close"
:aria-label="t('ui.toast.close')"
v-bind="typeof close === 'object' ? close : undefined"
:class="ui.close({ class: props.ui?.close })"
@click.stop