primitives
Native Select
Native Select — базовый примитив RevueUI (Reka UI + shadcn-совместимый API).
Загрузка превью…
src/ui/native-select/NativeSelect.vue
<!--
Не Radix-примитив: обёртка над нативным `<select>`. Иконка в оригинале —
`IconPlaceholder` (dev-обвязка сайта ReUI). Здесь и в кейсе используется
один и тот же инлайновый `<svg>` (путь lucide "chevron-down"), чтобы диф
сравнивал сам компонент, а не выбор библиотеки иконок.
-->
<script setup lang="ts">
import { onMounted, useTemplateRef, type HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
defineOptions({
inheritAttrs: false,
})
// ponytail: не расширяем SelectHTMLAttributes целиком — @vue/compiler-sfc
// не может разрешить его "extends HTMLAttributes" в этом воркспейсе (падает
// с "Failed to resolve extends base type" в дев-сервере, хотя vue-tsc это
// пропускает — расхождение между тайпчеком и рантайм-компилятором). Прочие
// атрибуты `<select>` (value/disabled/multiple/…) и так доходят через
// `v-bind="$attrs"` ниже — см. тот же приём в Input.vue.
const props = withDefaults(
defineProps<{
/** Доп. классы. Имя `class` — соглашение shadcn-vue (не `className`). */
class?: HTMLAttributes["class"]
size?: "sm" | "default"
/**
* Неуправляемое начальное значение — аналог React `defaultValue` на
* `<select>`. У нативного `<select>` во Vue эквивалента нет: HTML-атрибута
* `defaultvalue` не существует, поэтому просто пробросить проп через
* `v-bind="$attrs"` (как раньше) — баг: браузер молча его игнорирует и
* оставляет выбранным первый `<option>`. Поэтому проп объявлен явно и
* выставляется через DOM-свойство `select.value` при монтировании —
* см. docs/PORTING.md, "NativeSelect: defaultValue не существует во Vue".
*/
defaultValue?: string
}>(),
{
size: "default",
}
)
const selectRef = useTemplateRef<HTMLSelectElement>("select")
onMounted(() => {
if (props.defaultValue !== undefined && selectRef.value) {
selectRef.value.value = props.defaultValue
}
})
</script>
<template>
<div
:class="cn('cn-native-select-wrapper group/native-select relative w-fit has-[select:disabled]:opacity-50', props.class)"
data-slot="native-select-wrapper"
:data-size="props.size"
>
<select
ref="select"
data-slot="native-select"
:data-size="props.size"
class="cn-native-select outline-none disabled:pointer-events-none disabled:cursor-not-allowed"
v-bind="$attrs"
>
<slot />
</select>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="cn-native-select-icon pointer-events-none absolute select-none"
aria-hidden="true"
data-slot="native-select-icon"
>
<path d="m6 9 6 6 6-6" />
</svg>
</div>
</template>
src/ui/native-select/NativeSelectOptGroup.vue
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
// ponytail: не расширяем OptgroupHTMLAttributes целиком — та же причина,
// что в NativeSelectOption.vue (@vue/compiler-sfc не резолвит "extends
// HTMLAttributes" в дев-сервере). label/disabled доходят через fallthrough.
const props = defineProps<{
/** Доп. классы. Имя `class` — соглашение shadcn-vue (не `className`). */
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<optgroup data-slot="native-select-optgroup" :class="cn('bg-[Canvas] text-[CanvasText]', props.class)">
<slot />
</optgroup>
</template>
src/ui/native-select/NativeSelectOption.vue
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
// ponytail: не расширяем OptionHTMLAttributes целиком — @vue/compiler-sfc
// не может разрешить его "extends HTMLAttributes" в этом воркспейсе (падает
// с "Failed to resolve extends base type" в dev-сервере, хотя vue-tsc это
// пропускает). Остальные атрибуты (value/disabled/selected/label) и так
// доходят до <option> через автоматический fallthrough (inheritAttrs не
// переопределён, один корневой элемент) — см. Input.vue для того же приёма.
const props = defineProps<{
/** Доп. классы. Имя `class` — соглашение shadcn-vue (не `className`). */
class?: HTMLAttributes["class"]
}>()
</script>
<template>
<option data-slot="native-select-option" :class="cn('bg-[Canvas] text-[CanvasText]', props.class)">
<slot />
</option>
</template>
src/ui/native-select/index.ts
export { default as NativeSelect } from "./NativeSelect.vue"
export { default as NativeSelectOptGroup } from "./NativeSelectOptGroup.vue"
export { default as NativeSelectOption } from "./NativeSelectOption.vue"
Установка
npx shadcn-vue@latest add https://revueui.rootapi.dev/r/native-select.jsonЗависимости реестра
Источник: порт из ReUI (Keenthemes, MIT)