From 40e5bf5effbccfbeb22a5e6de5145c408bee3bb5 Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 19 Jan 2026 16:41:52 +0400 Subject: [PATCH] feat: add native-select --- .../content/docs/components/base/menubar.mdx | 12 +- .../docs/components/base/native-select.mdx | 108 +++------------ .../content/docs/components/radix/menubar.mdx | 12 +- .../docs/components/radix/native-select.mdx | 108 +++------------ apps/v4/examples/__index__.tsx | 130 ------------------ apps/v4/examples/base/native-select-basic.tsx | 19 --- .../v4/examples/base/native-select-fields.tsx | 101 -------------- apps/v4/examples/base/native-select-sizes.tsx | 23 ---- .../base/native-select-with-field.tsx | 21 --- .../base/native-select-with-groups.tsx | 23 ---- .../v4/examples/radix/native-select-basic.tsx | 19 --- .../examples/radix/native-select-fields.tsx | 101 -------------- .../v4/examples/radix/native-select-sizes.tsx | 23 ---- .../radix/native-select-with-field.tsx | 21 --- .../radix/native-select-with-groups.tsx | 23 ---- 15 files changed, 44 insertions(+), 700 deletions(-) delete mode 100644 apps/v4/examples/base/native-select-basic.tsx delete mode 100644 apps/v4/examples/base/native-select-fields.tsx delete mode 100644 apps/v4/examples/base/native-select-sizes.tsx delete mode 100644 apps/v4/examples/base/native-select-with-field.tsx delete mode 100644 apps/v4/examples/base/native-select-with-groups.tsx delete mode 100644 apps/v4/examples/radix/native-select-basic.tsx delete mode 100644 apps/v4/examples/radix/native-select-fields.tsx delete mode 100644 apps/v4/examples/radix/native-select-sizes.tsx delete mode 100644 apps/v4/examples/radix/native-select-with-field.tsx delete mode 100644 apps/v4/examples/radix/native-select-with-groups.tsx diff --git a/apps/v4/content/docs/components/base/menubar.mdx b/apps/v4/content/docs/components/base/menubar.mdx index 0188fdbd2..8575c5e6d 100644 --- a/apps/v4/content/docs/components/base/menubar.mdx +++ b/apps/v4/content/docs/components/base/menubar.mdx @@ -58,9 +58,9 @@ npm install @base-ui/react import { Menubar, MenubarContent, + MenubarGroup, MenubarItem, MenubarMenu, - MenubarGroup, MenubarSeparator, MenubarShortcut, MenubarTrigger, @@ -72,11 +72,11 @@ import { File - - - New Tab ⌘T - - New Window + + + New Tab ⌘T + + New Window diff --git a/apps/v4/content/docs/components/base/native-select.mdx b/apps/v4/content/docs/components/base/native-select.mdx index 21d101189..94fec14ff 100644 --- a/apps/v4/content/docs/components/base/native-select.mdx +++ b/apps/v4/content/docs/components/base/native-select.mdx @@ -66,91 +66,34 @@ import { Apple Banana Blueberry - - Grapes - Pineapple ``` ## Examples -### With Groups +### Groups -Organize options using `NativeSelectOptGroup` for better categorization. +Use `NativeSelectOptGroup` to organize options into categories. -```tsx showLineNumbers - - Select a food - - Apple - Banana - Blueberry - - - Carrot - Broccoli - Spinach - - -``` +### Disabled -### Disabled State - -Disable individual options or the entire select component. +Add the `disabled` prop to the `NativeSelect` component to disable the select. -### Invalid State +### Invalid -Show validation errors with the `aria-invalid` attribute and error styling. +Use `aria-invalid` to show validation errors and the `data-invalid` attribute to the `Field` component for styling. -```tsx showLineNumbers - - Select a country - United States - United Kingdom - Canada - -``` - -### Form Integration - -Use with form libraries like React Hook Form for controlled components. - - - -### Input Group Integration - -Combine with `InputGroup` for complex input layouts. - - - ## Native Select vs Select -- Use `NativeSelect` when you need native browser behavior, better performance, or mobile-optimized dropdowns. -- Use `Select` when you need custom styling, animations, or complex interactions. - -The `NativeSelect` component provides native HTML select functionality with consistent styling that matches your design system. - -## Accessibility - -- The component maintains all native HTML select accessibility features. -- Screen readers can navigate through options using arrow keys. -- The chevron icon is marked as `aria-hidden="true"` to avoid duplication. -- Use `aria-label` or `aria-labelledby` for additional context when needed. - -```tsx showLineNumbers - - English - Spanish - French - -``` +- Use `NativeSelect` for native browser behavior, better performance, or mobile-optimized dropdowns. +- Use `Select` for custom styling, animations, or complex interactions. ## API Reference @@ -158,12 +101,6 @@ The `NativeSelect` component provides native HTML select functionality with cons The main select component that wraps the native HTML select element. -| Prop | Type | Default | -| ----------- | -------- | ------- | -| `className` | `string` | | - -All other props are passed through to the underlying `` element. Represents an individual option within the select. -| Prop | Type | Default | -| ----------- | --------- | ------- | -| `value` | `string` | | -| `disabled` | `boolean` | `false` | -| `className` | `string` | | - -All other props are passed through to the underlying `` element. +| Prop | Type | Default | +| ---------- | --------- | ------- | +| `label` | `string` | | +| `disabled` | `boolean` | `false` | ```tsx diff --git a/apps/v4/content/docs/components/radix/menubar.mdx b/apps/v4/content/docs/components/radix/menubar.mdx index 9aa640cbc..10b38faa9 100644 --- a/apps/v4/content/docs/components/radix/menubar.mdx +++ b/apps/v4/content/docs/components/radix/menubar.mdx @@ -58,9 +58,9 @@ npm install radix-ui import { Menubar, MenubarContent, + MenubarGroup, MenubarItem, MenubarMenu, - MenubarGroup, MenubarSeparator, MenubarShortcut, MenubarTrigger, @@ -72,11 +72,11 @@ import { File - - - New Tab ⌘T - - New Window + + + New Tab ⌘T + + New Window diff --git a/apps/v4/content/docs/components/radix/native-select.mdx b/apps/v4/content/docs/components/radix/native-select.mdx index 935910729..4d7a7b8a9 100644 --- a/apps/v4/content/docs/components/radix/native-select.mdx +++ b/apps/v4/content/docs/components/radix/native-select.mdx @@ -66,91 +66,34 @@ import { Apple Banana Blueberry - - Grapes - Pineapple ``` ## Examples -### With Groups +### Groups -Organize options using `NativeSelectOptGroup` for better categorization. +Use `NativeSelectOptGroup` to organize options into categories. -```tsx showLineNumbers - - Select a food - - Apple - Banana - Blueberry - - - Carrot - Broccoli - Spinach - - -``` +### Disabled -### Disabled State - -Disable individual options or the entire select component. +Add the `disabled` prop to the `NativeSelect` component to disable the select. -### Invalid State +### Invalid -Show validation errors with the `aria-invalid` attribute and error styling. +Use `aria-invalid` to show validation errors and the `data-invalid` attribute to the `Field` component for styling. -```tsx showLineNumbers - - Select a country - United States - United Kingdom - Canada - -``` - -### Form Integration - -Use with form libraries like React Hook Form for controlled components. - - - -### Input Group Integration - -Combine with `InputGroup` for complex input layouts. - - - ## Native Select vs Select -- Use `NativeSelect` when you need native browser behavior, better performance, or mobile-optimized dropdowns. -- Use `Select` when you need custom styling, animations, or complex interactions. - -The `NativeSelect` component provides native HTML select functionality with consistent styling that matches your design system. - -## Accessibility - -- The component maintains all native HTML select accessibility features. -- Screen readers can navigate through options using arrow keys. -- The chevron icon is marked as `aria-hidden="true"` to avoid duplication. -- Use `aria-label` or `aria-labelledby` for additional context when needed. - -```tsx showLineNumbers - - English - Spanish - French - -``` +- Use `NativeSelect` for native browser behavior, better performance, or mobile-optimized dropdowns. +- Use `Select` for custom styling, animations, or complex interactions. ## API Reference @@ -158,12 +101,6 @@ The `NativeSelect` component provides native HTML select functionality with cons The main select component that wraps the native HTML select element. -| Prop | Type | Default | -| ----------- | -------- | ------- | -| `className` | `string` | | - -All other props are passed through to the underlying `` element. Represents an individual option within the select. -| Prop | Type | Default | -| ----------- | --------- | ------- | -| `value` | `string` | | -| `disabled` | `boolean` | `false` | -| `className` | `string` | | - -All other props are passed through to the underlying `` element. +| Prop | Type | Default | +| ---------- | --------- | ------- | +| `label` | `string` | | +| `disabled` | `boolean` | `false` | ```tsx diff --git a/apps/v4/examples/__index__.tsx b/apps/v4/examples/__index__.tsx index d5d615687..166ca70a9 100644 --- a/apps/v4/examples/__index__.tsx +++ b/apps/v4/examples/__index__.tsx @@ -3294,19 +3294,6 @@ export const ExamplesIndex: Record> = { return { default: mod.default || mod[exportName] } }), }, - "native-select-basic": { - name: "native-select-basic", - filePath: "examples/radix/native-select-basic.tsx", - component: React.lazy(async () => { - const mod = await import("./radix/native-select-basic") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-basic" - return { default: mod.default || mod[exportName] } - }), - }, "native-select-demo": { name: "native-select-demo", filePath: "examples/radix/native-select-demo.tsx", @@ -3333,19 +3320,6 @@ export const ExamplesIndex: Record> = { return { default: mod.default || mod[exportName] } }), }, - "native-select-fields": { - name: "native-select-fields", - filePath: "examples/radix/native-select-fields.tsx", - component: React.lazy(async () => { - const mod = await import("./radix/native-select-fields") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-fields" - return { default: mod.default || mod[exportName] } - }), - }, "native-select-groups": { name: "native-select-groups", filePath: "examples/radix/native-select-groups.tsx", @@ -3372,45 +3346,6 @@ export const ExamplesIndex: Record> = { return { default: mod.default || mod[exportName] } }), }, - "native-select-sizes": { - name: "native-select-sizes", - filePath: "examples/radix/native-select-sizes.tsx", - component: React.lazy(async () => { - const mod = await import("./radix/native-select-sizes") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-sizes" - return { default: mod.default || mod[exportName] } - }), - }, - "native-select-with-field": { - name: "native-select-with-field", - filePath: "examples/radix/native-select-with-field.tsx", - component: React.lazy(async () => { - const mod = await import("./radix/native-select-with-field") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-with-field" - return { default: mod.default || mod[exportName] } - }), - }, - "native-select-with-groups": { - name: "native-select-with-groups", - filePath: "examples/radix/native-select-with-groups.tsx", - component: React.lazy(async () => { - const mod = await import("./radix/native-select-with-groups") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-with-groups" - return { default: mod.default || mod[exportName] } - }), - }, "navigation-menu-demo": { name: "navigation-menu-demo", filePath: "examples/radix/navigation-menu-demo.tsx", @@ -9109,19 +9044,6 @@ export const ExamplesIndex: Record> = { return { default: mod.default || mod[exportName] } }), }, - "native-select-basic": { - name: "native-select-basic", - filePath: "examples/base/native-select-basic.tsx", - component: React.lazy(async () => { - const mod = await import("./base/native-select-basic") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-basic" - return { default: mod.default || mod[exportName] } - }), - }, "native-select-demo": { name: "native-select-demo", filePath: "examples/base/native-select-demo.tsx", @@ -9148,19 +9070,6 @@ export const ExamplesIndex: Record> = { return { default: mod.default || mod[exportName] } }), }, - "native-select-fields": { - name: "native-select-fields", - filePath: "examples/base/native-select-fields.tsx", - component: React.lazy(async () => { - const mod = await import("./base/native-select-fields") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-fields" - return { default: mod.default || mod[exportName] } - }), - }, "native-select-groups": { name: "native-select-groups", filePath: "examples/base/native-select-groups.tsx", @@ -9187,45 +9096,6 @@ export const ExamplesIndex: Record> = { return { default: mod.default || mod[exportName] } }), }, - "native-select-sizes": { - name: "native-select-sizes", - filePath: "examples/base/native-select-sizes.tsx", - component: React.lazy(async () => { - const mod = await import("./base/native-select-sizes") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-sizes" - return { default: mod.default || mod[exportName] } - }), - }, - "native-select-with-field": { - name: "native-select-with-field", - filePath: "examples/base/native-select-with-field.tsx", - component: React.lazy(async () => { - const mod = await import("./base/native-select-with-field") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-with-field" - return { default: mod.default || mod[exportName] } - }), - }, - "native-select-with-groups": { - name: "native-select-with-groups", - filePath: "examples/base/native-select-with-groups.tsx", - component: React.lazy(async () => { - const mod = await import("./base/native-select-with-groups") - const exportName = - Object.keys(mod).find( - (key) => - typeof mod[key] === "function" || typeof mod[key] === "object" - ) || "native-select-with-groups" - return { default: mod.default || mod[exportName] } - }), - }, "navigation-menu-basic": { name: "navigation-menu-basic", filePath: "examples/base/navigation-menu-basic.tsx", diff --git a/apps/v4/examples/base/native-select-basic.tsx b/apps/v4/examples/base/native-select-basic.tsx deleted file mode 100644 index 195e6a7db..000000000 --- a/apps/v4/examples/base/native-select-basic.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - NativeSelect, - NativeSelectOption, -} from "@/examples/base/ui/native-select" - -export function NativeSelectBasic() { - return ( - - Select a fruit - Apple - Banana - Blueberry - - Grapes - - Pineapple - - ) -} diff --git a/apps/v4/examples/base/native-select-fields.tsx b/apps/v4/examples/base/native-select-fields.tsx deleted file mode 100644 index 83bb6ec2c..000000000 --- a/apps/v4/examples/base/native-select-fields.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - Field, - FieldDescription, - FieldGroup, - FieldLabel, -} from "@/examples/base/ui/field" -import { - NativeSelect, - NativeSelectOptGroup, - NativeSelectOption, -} from "@/examples/base/ui/native-select" -import { Select } from "@/examples/base/ui/select" - -export function NativeSelectFields() { - return ( - - - - Basic Native Select - - - Choose an option - Option 1 - Option 2 - Option 3 - - - - Country - - Select your country - United States - United Kingdom - Canada - - - Select the country where you currently reside. - - - - Timezone - - Choose your local timezone for accurate scheduling. - - - Select timezone - UTC - Eastern Time - Pacific Time - - - - Grouped Options - - Select a region - - United States - Canada - Mexico - - - United Kingdom - France - Germany - - - - Native select with grouped options using optgroup. - - - - - Invalid Native Select - - - - This field has an error - - Option 1 - Option 2 - Option 3 - - - This field contains validation errors. - - - - - Disabled Field - - - Cannot select - Option 1 - Option 2 - Option 3 - - This field is currently disabled. - - - ) -} diff --git a/apps/v4/examples/base/native-select-sizes.tsx b/apps/v4/examples/base/native-select-sizes.tsx deleted file mode 100644 index 4ed3168d5..000000000 --- a/apps/v4/examples/base/native-select-sizes.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { - NativeSelect, - NativeSelectOption, -} from "@/examples/base/ui/native-select" - -export function NativeSelectSizes() { - return ( -
- - Select a fruit - Apple - Banana - Blueberry - - - Select a fruit - Apple - Banana - Blueberry - -
- ) -} diff --git a/apps/v4/examples/base/native-select-with-field.tsx b/apps/v4/examples/base/native-select-with-field.tsx deleted file mode 100644 index 1348d5931..000000000 --- a/apps/v4/examples/base/native-select-with-field.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Field, FieldDescription, FieldLabel } from "@/examples/base/ui/field" -import { - NativeSelect, - NativeSelectOption, -} from "@/examples/base/ui/native-select" - -export function NativeSelectWithField() { - return ( - - Country - - Select a country - United States - United Kingdom - Canada - Australia - - Select your country of residence. - - ) -} diff --git a/apps/v4/examples/base/native-select-with-groups.tsx b/apps/v4/examples/base/native-select-with-groups.tsx deleted file mode 100644 index 3d9006bc9..000000000 --- a/apps/v4/examples/base/native-select-with-groups.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { - NativeSelect, - NativeSelectOptGroup, - NativeSelectOption, -} from "@/examples/base/ui/native-select" - -export function NativeSelectWithGroups() { - return ( - - Select a food - - Apple - Banana - Blueberry - - - Carrot - Broccoli - Spinach - - - ) -} diff --git a/apps/v4/examples/radix/native-select-basic.tsx b/apps/v4/examples/radix/native-select-basic.tsx deleted file mode 100644 index 62e9482e8..000000000 --- a/apps/v4/examples/radix/native-select-basic.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - NativeSelect, - NativeSelectOption, -} from "@/examples/radix/ui/native-select" - -export function NativeSelectBasic() { - return ( - - Select a fruit - Apple - Banana - Blueberry - - Grapes - - Pineapple - - ) -} diff --git a/apps/v4/examples/radix/native-select-fields.tsx b/apps/v4/examples/radix/native-select-fields.tsx deleted file mode 100644 index d35c33beb..000000000 --- a/apps/v4/examples/radix/native-select-fields.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - Field, - FieldDescription, - FieldGroup, - FieldLabel, -} from "@/examples/radix/ui/field" -import { - NativeSelect, - NativeSelectOptGroup, - NativeSelectOption, -} from "@/examples/radix/ui/native-select" -import { Select } from "@/examples/radix/ui/select" - -export function NativeSelectFields() { - return ( - - - - Basic Native Select - - - Choose an option - Option 1 - Option 2 - Option 3 - - - - Country - - Select your country - United States - United Kingdom - Canada - - - Select the country where you currently reside. - - - - Timezone - - Choose your local timezone for accurate scheduling. - - - Select timezone - UTC - Eastern Time - Pacific Time - - - - Grouped Options - - Select a region - - United States - Canada - Mexico - - - United Kingdom - France - Germany - - - - Native select with grouped options using optgroup. - - - - - Invalid Native Select - - - - This field has an error - - Option 1 - Option 2 - Option 3 - - - This field contains validation errors. - - - - - Disabled Field - - - Cannot select - Option 1 - Option 2 - Option 3 - - This field is currently disabled. - - - ) -} diff --git a/apps/v4/examples/radix/native-select-sizes.tsx b/apps/v4/examples/radix/native-select-sizes.tsx deleted file mode 100644 index 6efcf6df6..000000000 --- a/apps/v4/examples/radix/native-select-sizes.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { - NativeSelect, - NativeSelectOption, -} from "@/examples/radix/ui/native-select" - -export function NativeSelectSizes() { - return ( -
- - Select a fruit - Apple - Banana - Blueberry - - - Select a fruit - Apple - Banana - Blueberry - -
- ) -} diff --git a/apps/v4/examples/radix/native-select-with-field.tsx b/apps/v4/examples/radix/native-select-with-field.tsx deleted file mode 100644 index a84bde873..000000000 --- a/apps/v4/examples/radix/native-select-with-field.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Field, FieldDescription, FieldLabel } from "@/examples/radix/ui/field" -import { - NativeSelect, - NativeSelectOption, -} from "@/examples/radix/ui/native-select" - -export function NativeSelectWithField() { - return ( - - Country - - Select a country - United States - United Kingdom - Canada - Australia - - Select your country of residence. - - ) -} diff --git a/apps/v4/examples/radix/native-select-with-groups.tsx b/apps/v4/examples/radix/native-select-with-groups.tsx deleted file mode 100644 index ce74228ce..000000000 --- a/apps/v4/examples/radix/native-select-with-groups.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { - NativeSelect, - NativeSelectOptGroup, - NativeSelectOption, -} from "@/examples/radix/ui/native-select" - -export function NativeSelectWithGroups() { - return ( - - Select a food - - Apple - Banana - Blueberry - - - Carrot - Broccoli - Spinach - - - ) -}