Skip to content

Commit 308eb18

Browse files
committed
refactor(ui, docs): add NativeSelect component and integrate into docs
Introduced the `NativeSelect` component with associated subcomponents (`NativeSelectOption`, `NativeSelectOptGroup`). Registered the component in the docs app's components registry and created a comprehensive demo showcasing various use cases (basic, grouped, disabled, error states). Updated `ui` package index file to enable broader use and adjusted `Select` component icon for consistency.
1 parent 0346eeb commit 308eb18

File tree

6 files changed

+217
-2
lines changed

6 files changed

+217
-2
lines changed

.changeset/wicked-forks-ring.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@codefast/ui": patch
3+
---
4+
5+
refactor(ui, docs): add `NativeSelect` component and integrate into docs
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import type { JSX } from "react";
2+
3+
import { GridWrapper } from "@/components/grid-wrapper";
4+
import {
5+
NativeSelect,
6+
NativeSelectOptGroup,
7+
NativeSelectOption,
8+
Select,
9+
SelectContent,
10+
SelectGroup,
11+
SelectItem,
12+
SelectLabel,
13+
SelectTrigger,
14+
SelectValue,
15+
} from "@codefast/ui";
16+
17+
export function NativeSelectDemo(): JSX.Element {
18+
return (
19+
<GridWrapper className="*:flex *:flex-col *:gap-4">
20+
<div>
21+
<div className="text-muted-foreground text-sm font-medium">Basic Select</div>
22+
<div className="flex flex-col gap-3">
23+
<NativeSelect className="w-[200px]" defaultValue="">
24+
<NativeSelectOption value="">Select a fruit</NativeSelectOption>
25+
<NativeSelectOption value="apple">Apple</NativeSelectOption>
26+
<NativeSelectOption value="banana">Banana</NativeSelectOption>
27+
<NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
28+
<NativeSelectOption disabled value="grapes">
29+
Grapes
30+
</NativeSelectOption>
31+
<NativeSelectOption value="pineapple">Pineapple</NativeSelectOption>
32+
</NativeSelect>
33+
<Select>
34+
<SelectTrigger className="w-[200px]">
35+
<SelectValue placeholder="Select a fruit" />
36+
</SelectTrigger>
37+
<SelectContent>
38+
<SelectItem value="apple">Apple</SelectItem>
39+
<SelectItem value="banana">Banana</SelectItem>
40+
<SelectItem value="blueberry">Blueberry</SelectItem>
41+
<SelectItem disabled value="grapes">
42+
Grapes
43+
</SelectItem>
44+
<SelectItem value="pineapple">Pineapple</SelectItem>
45+
</SelectContent>
46+
</Select>
47+
</div>
48+
</div>
49+
50+
<div>
51+
<div className="text-muted-foreground text-sm font-medium">With Groups</div>
52+
<div className="flex flex-col gap-3">
53+
<NativeSelect className="w-[200px]" defaultValue="">
54+
<NativeSelectOption value="">Select a food</NativeSelectOption>
55+
<NativeSelectOptGroup label="Fruits">
56+
<NativeSelectOption value="apple">Apple</NativeSelectOption>
57+
<NativeSelectOption value="banana">Banana</NativeSelectOption>
58+
<NativeSelectOption value="blueberry">Blueberry</NativeSelectOption>
59+
</NativeSelectOptGroup>
60+
<NativeSelectOptGroup label="Vegetables">
61+
<NativeSelectOption value="carrot">Carrot</NativeSelectOption>
62+
<NativeSelectOption value="broccoli">Broccoli</NativeSelectOption>
63+
<NativeSelectOption value="spinach">Spinach</NativeSelectOption>
64+
</NativeSelectOptGroup>
65+
</NativeSelect>
66+
<Select>
67+
<SelectTrigger className="w-[200px]">
68+
<SelectValue placeholder="Select a food" />
69+
</SelectTrigger>
70+
<SelectContent>
71+
<SelectGroup>
72+
<SelectLabel>Fruits</SelectLabel>
73+
<SelectItem value="apple">Apple</SelectItem>
74+
<SelectItem value="banana">Banana</SelectItem>
75+
<SelectItem value="blueberry">Blueberry</SelectItem>
76+
</SelectGroup>
77+
<SelectGroup>
78+
<SelectLabel>Vegetables</SelectLabel>
79+
<SelectItem value="carrot">Carrot</SelectItem>
80+
<SelectItem value="broccoli">Broccoli</SelectItem>
81+
<SelectItem value="spinach">Spinach</SelectItem>
82+
</SelectGroup>
83+
</SelectContent>
84+
</Select>
85+
</div>
86+
</div>
87+
88+
<div>
89+
<div className="text-muted-foreground text-sm font-medium">Disabled State</div>
90+
<div className="flex flex-col gap-3">
91+
<NativeSelect disabled className="w-[200px]" defaultValue="">
92+
<NativeSelectOption value="">Disabled</NativeSelectOption>
93+
<NativeSelectOption value="apple">Apple</NativeSelectOption>
94+
<NativeSelectOption value="banana">Banana</NativeSelectOption>
95+
</NativeSelect>
96+
<Select disabled>
97+
<SelectTrigger className="w-[200px]">
98+
<SelectValue placeholder="Disabled" />
99+
</SelectTrigger>
100+
<SelectContent>
101+
<SelectItem value="apple">Apple</SelectItem>
102+
<SelectItem value="banana">Banana</SelectItem>
103+
</SelectContent>
104+
</Select>
105+
</div>
106+
</div>
107+
108+
<div>
109+
<div className="text-muted-foreground text-sm font-medium">Error State</div>
110+
<div className="flex flex-col gap-3">
111+
<NativeSelect aria-invalid="true" className="w-[200px]" defaultValue="">
112+
<NativeSelectOption value="">Error state</NativeSelectOption>
113+
<NativeSelectOption value="apple">Apple</NativeSelectOption>
114+
<NativeSelectOption value="banana">Banana</NativeSelectOption>
115+
</NativeSelect>
116+
<Select>
117+
<SelectTrigger aria-invalid="true" className="w-[200px]">
118+
<SelectValue placeholder="Error state" />
119+
</SelectTrigger>
120+
<SelectContent>
121+
<SelectItem value="apple">Apple</SelectItem>
122+
<SelectItem value="banana">Banana</SelectItem>
123+
</SelectContent>
124+
</Select>
125+
</div>
126+
</div>
127+
</GridWrapper>
128+
);
129+
}

apps/docs/src/app/(app)/components/registry-components.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,16 @@ export const registryComponents: Record<string, RegistryItem> = {
363363
slug: "menubar",
364364
title: "Menubar",
365365
},
366+
"native-select": {
367+
component: dynamic(async () =>
368+
import("@/app/(app)/components/_components/native-select-demo").then(
369+
(module_) => module_.NativeSelectDemo,
370+
),
371+
),
372+
description: "Native Select",
373+
slug: "native-select",
374+
title: "Native Select",
375+
},
366376
"navigation-menu": {
367377
component: dynamic(async () =>
368378
import("@/app/(app)/components/_components/navigation-menu-demo").then(
@@ -659,6 +669,7 @@ export const registryComponentGroups: RegistryGroup[] = [
659669
registryComponents["toggle-group"],
660670
registryComponents.textarea,
661671
registryComponents.combobox,
672+
registryComponents["native-select"],
662673
registryComponents.select,
663674
registryComponents.label,
664675
registryComponents.slider,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { ComponentProps, JSX } from "react";
2+
3+
import { ChevronDownIcon } from "lucide-react";
4+
5+
import { cn } from "@codefast/tailwind-variants";
6+
7+
/* -----------------------------------------------------------------------------
8+
* Component: NativeSelect
9+
* -------------------------------------------------------------------------- */
10+
11+
type NativeSelectProps = ComponentProps<"select">;
12+
13+
function NativeSelect({ className, ...props }: NativeSelectProps): JSX.Element {
14+
return (
15+
<div
16+
className="group/native-select relative w-fit has-[select:disabled]:opacity-50"
17+
data-slot="native-select-wrapper"
18+
>
19+
<select
20+
className={cn(
21+
"border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 h-9 w-full min-w-0 appearance-none rounded-lg border bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed",
22+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-3",
23+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
24+
className,
25+
)}
26+
data-slot="native-select"
27+
{...props}
28+
/>
29+
<ChevronDownIcon
30+
aria-hidden="true"
31+
className="text-muted-foreground pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 opacity-50 select-none"
32+
data-slot="native-select-icon"
33+
/>
34+
</div>
35+
);
36+
}
37+
38+
/* -----------------------------------------------------------------------------
39+
* Component: NativeSelectOption
40+
* -------------------------------------------------------------------------- */
41+
42+
type NativeSelectOptionProps = ComponentProps<"option">;
43+
44+
function NativeSelectOption({ ...props }: NativeSelectOptionProps): JSX.Element {
45+
return <option data-slot="native-select-option" {...props} />;
46+
}
47+
48+
/* -----------------------------------------------------------------------------
49+
* Component: NativeSelectOptGroup
50+
* -------------------------------------------------------------------------- */
51+
52+
type NativeSelectOptGroupProps = ComponentProps<"optgroup">;
53+
54+
function NativeSelectOptGroup({ className, ...props }: NativeSelectOptGroupProps): JSX.Element {
55+
return <optgroup className={cn(className)} data-slot="native-select-optgroup" {...props} />;
56+
}
57+
58+
/* -----------------------------------------------------------------------------
59+
* Exports
60+
* -------------------------------------------------------------------------- */
61+
62+
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
63+
export type { NativeSelectOptGroupProps, NativeSelectOptionProps, NativeSelectProps };

packages/ui/src/components/select.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import type { ComponentProps, JSX } from "react";
44

5-
import { CheckIcon, ChevronDownIcon, ChevronsUpDownIcon, ChevronUpIcon } from "lucide-react";
5+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
66

77
import type { VariantProps } from "@codefast/tailwind-variants";
88

@@ -65,7 +65,7 @@ function SelectTrigger({ children, className, size, ...props }: SelectTriggerPro
6565
>
6666
{children}
6767
<SelectPrimitive.Icon asChild className="size-4 shrink-0 opacity-50">
68-
<ChevronsUpDownIcon />
68+
<ChevronDownIcon />
6969
</SelectPrimitive.Icon>
7070
</SelectPrimitive.Trigger>
7171
);

packages/ui/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,13 @@ export type {
491491
NavigationMenuTriggerProps,
492492
} from "@/components/navigation-menu";
493493

494+
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption } from "@/components/native-select";
495+
export type {
496+
NativeSelectOptGroupProps,
497+
NativeSelectOptionProps,
498+
NativeSelectProps,
499+
} from "@/components/native-select";
500+
494501
export {
495502
Pagination,
496503
PaginationContent,

0 commit comments

Comments
 (0)