Components
Select
A custom listbox select with keyboard support, wrapping Morphos's Select primitive.
Installation
npx kosmesis add selectpnpm dlx kosmesis add selectyarn dlx kosmesis add selectbunx kosmesis add selectInstall the following dependencies:
npm install @morphos/inputspnpm add @morphos/inputsyarn add @morphos/inputsbun add @morphos/inputsCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { Select as MorphosSelect, type SelectProps as MorphosSelectProps } from "@morphos/inputs";import { cn } from "@/lib/utils";export type SelectProps = MorphosSelectProps;/** * Morphos's `Select` takes a flat `options` array and renders its own trigger + listbox as one * unit — there's no `SelectTrigger`/`SelectContent`/`SelectItem` compound API to wrap (unlike * shadcn/ui's Radix-backed version). If you need children-based composition, see `Combobox` for * search-filtered lists, or `NativeSelect` for a plain `<select>`. */@Component()export class Select extends StatelessComponent<SelectProps> { render() { const { class: cls, ...rest } = this.props; return ( <MorphosSelect class={cn( "relative", "[&>button]:flex [&>button]:h-9 [&>button]:w-full [&>button]:items-center [&>button]:justify-between [&>button]:gap-2 [&>button]:rounded-md [&>button]:border [&>button]:border-input [&>button]:bg-transparent [&>button]:px-3 [&>button]:py-2 [&>button]:text-sm [&>button]:whitespace-nowrap [&>button]:shadow-xs [&>button]:outline-none", "[&>button[data-placeholder]]:text-muted-foreground", "[&>button:disabled]:cursor-not-allowed [&>button:disabled]:opacity-50", "[&>button[aria-expanded=true]]:border-ring [&>button[aria-expanded=true]]:ring-[3px] [&>button[aria-expanded=true]]:ring-ring/50", "[&>ul]:absolute [&>ul]:z-50 [&>ul]:mt-1 [&>ul]:max-h-60 [&>ul]:w-full [&>ul]:overflow-auto [&>ul]:rounded-md [&>ul]:border [&>ul]:bg-popover [&>ul]:p-1 [&>ul]:text-popover-foreground [&>ul]:shadow-md", "[&_li]:relative [&_li]:flex [&_li]:cursor-default [&_li]:items-center [&_li]:rounded-sm [&_li]:px-2 [&_li]:py-1.5 [&_li]:text-sm [&_li]:outline-none", "[&_li[data-active]]:bg-accent [&_li[data-active]]:text-accent-foreground", "[&_li[data-selected]]:font-medium", "[&_li[data-disabled]]:pointer-events-none [&_li[data-disabled]]:opacity-50", cls, )} {...rest} /> ); }}Install the following dependencies:
npm install @morphos/inputs @praxisjs/csspnpm add @morphos/inputs @praxisjs/cssyarn add @morphos/inputs @praxisjs/cssbun add @morphos/inputs @praxisjs/cssCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { Select as MorphosSelect, type SelectProps as MorphosSelectProps } from "@morphos/inputs";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class SelectStyles extends Stylesheet { $root = this.css({ position: "relative" }) .on("& > button", { display: "flex", height: "2.25rem", width: "100%", alignItems: "center", justifyContent: "space-between", gap: "0.5rem", borderRadius: `calc(${t.radius} - 2px)`, border: `1px solid ${t.input}`, backgroundColor: "transparent", padding: "0.5rem 0.75rem", fontSize: "0.875rem", whiteSpace: "nowrap", boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", outline: "none", }) .on("& > button[data-placeholder]", { color: t.mutedForeground }) .on("& > button:disabled", { cursor: "not-allowed", opacity: 0.5 }) .on('& > button[aria-expanded="true"]', { borderColor: t.ring, boxShadow: `0 0 0 3px color-mix(in oklab, ${t.ring} 50%, transparent)`, }) .on("& > ul", { position: "absolute", zIndex: 50, marginTop: "0.25rem", maxHeight: "15rem", width: "100%", overflow: "auto", borderRadius: `calc(${t.radius} - 2px)`, border: `1px solid ${t.border}`, backgroundColor: t.popover, padding: "0.25rem", color: t.popoverForeground, boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)", }) .on("& li", { position: "relative", display: "flex", cursor: "default", alignItems: "center", borderRadius: "0.125rem", padding: "0.375rem 0.5rem", fontSize: "0.875rem", outline: "none", }) .on("& li[data-active]", { backgroundColor: t.accent, color: t.accentForeground }) .on("& li[data-selected]", { fontWeight: 500 }) .on("& li[data-disabled]", { pointerEvents: "none", opacity: 0.5 });}export type SelectProps = MorphosSelectProps;/** * Morphos's `Select` takes a flat `options` array and renders its own trigger + listbox as one * unit — there's no `SelectTrigger`/`SelectContent`/`SelectItem` compound API to wrap (unlike * shadcn/ui's Radix-backed version). If you need children-based composition, see `Combobox` for * search-filtered lists, or `NativeSelect` for a plain `<select>`. */@Component()export class Select extends StatelessComponent<SelectProps> { @Styled(SelectStyles) $s!: SelectStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosSelect class={cx(this.$s.$root, cls)} {...rest} />; }}Examples
Usage
import { Select } from "@/components/ui/select";
<Select
placeholder="Select a fruit"
options={[
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "cherry", label: "Cherry" },
]}
onValueChange={(value) => console.log(value)}
/>Morphos's Select takes a flat options array and renders its own trigger + listbox as one
unit — there's no SelectTrigger/SelectContent/SelectItem compound API to compose (Radix
has one; Morphos deliberately doesn't). Need children-based composition or free-text search
instead? See Combobox. Need a plain native picker?
See Native Select.
Props
| Prop | Type |
|---|---|
options | { value: string; label: string; disabled?: boolean }[] |
value / defaultValue | string |
placeholder | string |
disabled / required / clearable | boolean |
name | string |
onValueChange | (value: string) => void |
onClear | () => void |