-
Notifications
You must be signed in to change notification settings - Fork 14
Open
Description
Summary
v1 will introduce a dual-API design:
- Low-level primitive components (Radix-Vue/Reka-UI style) for maximum flexibility and composition.
- High-level default export (Select) that assembles primitives to preserve today’s DX with sensible defaults.
- This is a mandatory requirement, as we still want to provide an easy solution for developers (especially when migrating from
vue-selector Vue 2 codebases or just for simple use-cases).
- This is a mandatory requirement, as we still want to provide an easy solution for developers (especially when migrating from
This will be a breaking release.
Motivation
- Provide escape hatches for complex, edge-case UX (infinite scrolling, floating-ui menu).
- Preserve a simple, batteries-included DX for common use cases.
- Improve type-safety, accessibility, and performance.
- Establish a clearer separation of concerns to keep code scannable and maintainable.
Goals
- Low-level primitives with single responsibilities and predictable APIs.
- Default assembled
<Select />providing near drop-in behavior for most users. - Strong TypeScript types for v-model, props, events, slots, and options.
- First-class accessibility following WAI-ARIA best-practices: keyboard nav, ARIA, focus management.
- Robust tests, playground demos, and docs.
- Ideally, migrate documentation template to NuxtUI docs template (which is free now, thanks to NuxtUI v4).
Breaking Changes
- Component API re-organization.
- Slots possibly removed and/or renamed.
- Option shape refined (e.g., normalized value, label; custom mappers replace implicit inference).
- Menu/indicator/multi-value rendering delegated to primitives by default (customizable via slots).
- Deprecation of ambiguous props and implicit behaviors; explicit mapping/configuration instead.
- Re-export paths may change (named exports under @/primitives, default export under root).
High-level design
- SelectRoot (state + context provider)
- SelectTrigger (button/input anchor)
- SelectInput (search input, optional)
- SelectIndicator (chevron/loading indicators)
- SelectValue (single/multi display)
- SelectPopover (portal + positioning)
- SelectListbox (virtualized list container)
- SelectOption (option item)
- SelectNoOptions (empty state)
- SelectTag (chip for multi values)
- SelectClear (clear selection)
- SelectSeparator, SelectGroup, SelectGroupLabel (optional)
Key principles thanks to this high-level design:
- Each primitive manages a single concern.
- Cross-component state is conveyed via provide/inject.
- Strong props/events per primitive; no hidden cross effects.
Example usage:
<script setup lang="ts">
import {
SelectRoot,
SelectTrigger,
SelectIndicator,
SelectInput,
SelectPopover,
SelectListbox,
SelectOption,
SelectValue,
SelectNoOptions,
SelectClear,
} from 'vue3-select-component/primitives'
const options = [
{ value: 'js', label: 'JavaScript' },
{ value: 'ts', label: 'TypeScript' },
]
const model = ref<string | null>(null)
</script>
<template>
<SelectRoot v-model="model" :options="options" searchable>
<SelectTrigger>
<SelectValue placeholder="Pick a language" />
<SelectIndicator />
<SelectClear />
</SelectTrigger>
<SelectPopover>
<SelectInput />
<SelectListbox>
<SelectNoOptions>No results</SelectNoOptions>
<SelectOption
v-for="opt in options"
:key="opt.value"
:value="opt.value"
:label="opt.label"
/>
</SelectListbox>
</SelectPopover>
</SelectRoot>
</template>High-level assembled <Select />
Example usage:
<script setup lang="ts">
import Select from 'vue3-select-component'
const options = [
{ value: 'cat', label: 'Cat' },
{ value: 'dog', label: 'Dog' },
]
const value = ref<string | null>(null)
</script>
<template>
<Select
v-model="value"
:options="options"
placeholder="Select an animal"
searchable
clearable
/>
</template>PlainBane
Metadata
Metadata
Assignees
Labels
No labels