mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-06 02:48:28 +00:00
feat(web): migrate shared select to radix
This commit is contained in:
parent
c116ac2167
commit
c36b74fda6
8 changed files with 317 additions and 93 deletions
|
|
@ -15,7 +15,13 @@ import {
|
|||
} from '@/shared/ui/dialog'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Label } from '@/shared/ui/label'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
|
||||
interface AddNamespaceMemberDialogProps {
|
||||
slug: string
|
||||
|
|
@ -212,16 +218,17 @@ export function AddNamespaceMemberDialog({ slug, children }: AddNamespaceMemberD
|
|||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="member-role">{t('members.roleLabel')}</Label>
|
||||
<Select
|
||||
id="member-role"
|
||||
value={role}
|
||||
onChange={(event) => setRole(event.target.value as NamespaceRole)}
|
||||
>
|
||||
{ROLE_OPTIONS.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{t(option === 'ADMIN' ? 'members.roleAdmin' : 'members.roleMember')}
|
||||
</option>
|
||||
))}
|
||||
<Select value={role} onValueChange={(value) => setRole(value as NamespaceRole)}>
|
||||
<SelectTrigger id="member-role">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{ROLE_OPTIONS.map((option) => (
|
||||
<SelectItem key={option} value={option}>
|
||||
{t(option === 'ADMIN' ? 'members.roleAdmin' : 'members.roleMember')}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@ import {
|
|||
import { Button } from '@/shared/ui/button'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Label } from '@/shared/ui/label'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
import { centeredToastOptions, toast } from '@/shared/lib/toast'
|
||||
import { formatLocalDateTime } from '@/shared/lib/date-time'
|
||||
import type { CreateTokenRequest, CreateTokenResponse } from '@/api/types'
|
||||
|
|
@ -176,18 +182,22 @@ export function CreateTokenDialog({ children, existingNames = [] }: CreateTokenD
|
|||
<div className="space-y-2">
|
||||
<Label htmlFor="token-expiration">{t('createToken.expirationLabel')}</Label>
|
||||
<Select
|
||||
id="token-expiration"
|
||||
value={expirationMode}
|
||||
onChange={(e) => {
|
||||
setExpirationMode(e.target.value as TokenExpirationMode)
|
||||
onValueChange={(value) => {
|
||||
setExpirationMode(value as TokenExpirationMode)
|
||||
setExpiresAtError(null)
|
||||
}}
|
||||
>
|
||||
<option value="never">{t('createToken.expirationNever')}</option>
|
||||
<option value="7d">{t('createToken.expiration7d')}</option>
|
||||
<option value="30d">{t('createToken.expiration30d')}</option>
|
||||
<option value="90d">{t('createToken.expiration90d')}</option>
|
||||
<option value="custom">{t('createToken.expirationCustom')}</option>
|
||||
<SelectTrigger id="token-expiration">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="never">{t('createToken.expirationNever')}</SelectItem>
|
||||
<SelectItem value="7d">{t('createToken.expiration7d')}</SelectItem>
|
||||
<SelectItem value="30d">{t('createToken.expiration30d')}</SelectItem>
|
||||
<SelectItem value="90d">{t('createToken.expiration90d')}</SelectItem>
|
||||
<SelectItem value="custom">{t('createToken.expirationCustom')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{expirationMode === 'custom' ? (
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@ import { centeredToastOptions, toast } from '@/shared/lib/toast'
|
|||
import { formatLocalDateTime } from '@/shared/lib/date-time'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/shared/ui/dialog'
|
||||
import { Label } from '@/shared/ui/label'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import type { ApiToken } from '@/api/types'
|
||||
import { resolveTokenExpiresAt, toLocalDateTimeInputValue, type TokenExpirationMode } from './token-expiration'
|
||||
|
|
@ -276,18 +282,22 @@ export function TokenList() {
|
|||
<div className="space-y-2">
|
||||
<Label htmlFor="token-expiration-mode">{t('createToken.expirationLabel')}</Label>
|
||||
<Select
|
||||
id="token-expiration-mode"
|
||||
value={expirationDialog.mode}
|
||||
onChange={(event) => setExpirationDialog((current) => ({
|
||||
onValueChange={(value) => setExpirationDialog((current) => ({
|
||||
...current,
|
||||
mode: event.target.value as TokenExpirationMode,
|
||||
mode: value as TokenExpirationMode,
|
||||
}))}
|
||||
>
|
||||
<option value="never">{t('createToken.expirationNever')}</option>
|
||||
<option value="7d">{t('createToken.expiration7d')}</option>
|
||||
<option value="30d">{t('createToken.expiration30d')}</option>
|
||||
<option value="90d">{t('createToken.expiration90d')}</option>
|
||||
<option value="custom">{t('createToken.expirationCustom')}</option>
|
||||
<SelectTrigger id="token-expiration-mode">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="never">{t('createToken.expirationNever')}</SelectItem>
|
||||
<SelectItem value="7d">{t('createToken.expiration7d')}</SelectItem>
|
||||
<SelectItem value="30d">{t('createToken.expiration30d')}</SelectItem>
|
||||
<SelectItem value="90d">{t('createToken.expiration90d')}</SelectItem>
|
||||
<SelectItem value="custom">{t('createToken.expirationCustom')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{expirationDialog.mode === 'custom' ? (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ import { formatLocalDateTime } from '@/shared/lib/date-time'
|
|||
import { Card } from '@/shared/ui/card'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
normalizeSelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
|
@ -42,6 +49,7 @@ const ACTION_OPTIONS = [
|
|||
*/
|
||||
export function AuditLogPage() {
|
||||
const { t, i18n } = useTranslation()
|
||||
const allActionFilterValue = '__all_actions__'
|
||||
const [actionFilter, setActionFilter] = useState<string>('')
|
||||
const [userIdFilter, setUserIdFilter] = useState('')
|
||||
const [requestIdFilter, setRequestIdFilter] = useState('')
|
||||
|
|
@ -105,15 +113,26 @@ export function AuditLogPage() {
|
|||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<Select value={actionFilter} onChange={(e) => {
|
||||
setActionFilter(e.target.value)
|
||||
setPage(0)
|
||||
}} className="w-[200px]">
|
||||
{ACTION_OPTIONS.map((option) => (
|
||||
<option key={option.value || 'all'} value={option.value}>
|
||||
{t(option.labelKey)}
|
||||
</option>
|
||||
))}
|
||||
<Select
|
||||
value={normalizeSelectValue(actionFilter) ?? allActionFilterValue}
|
||||
onValueChange={(value) => {
|
||||
setActionFilter(value === allActionFilterValue ? '' : value)
|
||||
setPage(0)
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[200px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{ACTION_OPTIONS.map((option) => (
|
||||
<SelectItem
|
||||
key={option.value || allActionFilterValue}
|
||||
value={option.value || allActionFilterValue}
|
||||
>
|
||||
{t(option.labelKey)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Input
|
||||
placeholder={t('auditLog.userIdPlaceholder')}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ import { formatLocalDateTime } from '@/shared/lib/date-time'
|
|||
import { Card } from '@/shared/ui/card'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
normalizeSelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
|
@ -31,6 +38,7 @@ import type { AdminUser } from '@/features/admin/use-admin-users'
|
|||
*/
|
||||
export function AdminUsersPage() {
|
||||
const { t, i18n } = useTranslation()
|
||||
const allStatusFilterValue = '__all_statuses__'
|
||||
const roleOptions = [
|
||||
{ value: 'USER', label: t('adminUsers.roleUser') },
|
||||
{ value: 'SKILL_ADMIN', label: t('adminUsers.roleReviewer') },
|
||||
|
|
@ -154,11 +162,19 @@ export function AdminUsersPage() {
|
|||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="admin-user-status">{t('adminUsers.filterLabel')}</Label>
|
||||
<Select id="admin-user-status" value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
|
||||
<option value="">{t('adminUsers.filterAll')}</option>
|
||||
<option value="ACTIVE">{t('adminUsers.filterActive')}</option>
|
||||
<option value="PENDING">{t('adminUsers.filterPending')}</option>
|
||||
<option value="DISABLED">{t('adminUsers.filterDisabled')}</option>
|
||||
<Select
|
||||
value={normalizeSelectValue(statusFilter) ?? allStatusFilterValue}
|
||||
onValueChange={(value) => setStatusFilter(value === allStatusFilterValue ? '' : value)}
|
||||
>
|
||||
<SelectTrigger id="admin-user-status">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={allStatusFilterValue}>{t('adminUsers.filterAll')}</SelectItem>
|
||||
<SelectItem value="ACTIVE">{t('adminUsers.filterActive')}</SelectItem>
|
||||
<SelectItem value="PENDING">{t('adminUsers.filterPending')}</SelectItem>
|
||||
<SelectItem value="DISABLED">{t('adminUsers.filterDisabled')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -288,12 +304,17 @@ export function AdminUsersPage() {
|
|||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="role">{t('adminUsers.roleLabel')}</Label>
|
||||
<Select id="role" value={newRole} onChange={(e) => setNewRole(e.target.value)}>
|
||||
{roleOptions.map((roleOption) => (
|
||||
<option key={roleOption.value} value={roleOption.value}>
|
||||
{roleOption.label}
|
||||
</option>
|
||||
))}
|
||||
<Select value={newRole} onValueChange={setNewRole}>
|
||||
<SelectTrigger id="role">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{roleOptions.map((roleOption) => (
|
||||
<SelectItem key={roleOption.value} value={roleOption.value}>
|
||||
{roleOption.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,13 @@ import { ConfirmDialog } from '@/shared/components/confirm-dialog'
|
|||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Card } from '@/shared/ui/card'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
import {
|
||||
useMyNamespaces,
|
||||
useNamespaceDetail,
|
||||
|
|
@ -192,17 +198,21 @@ export function NamespaceMembersPage() {
|
|||
{canManageMembers && !isOwner ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Select
|
||||
className="w-36"
|
||||
value={roleValue}
|
||||
onChange={(event) => {
|
||||
onValueChange={(value) => {
|
||||
setDraftRoles((current) => ({
|
||||
...current,
|
||||
[member.userId]: event.target.value,
|
||||
[member.userId]: value,
|
||||
}))
|
||||
}}
|
||||
>
|
||||
<option value="MEMBER">{t('members.roleMember')}</option>
|
||||
<option value="ADMIN">{t('members.roleAdmin')}</option>
|
||||
<SelectTrigger className="w-36">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="MEMBER">{t('members.roleMember')}</SelectItem>
|
||||
<SelectItem value="ADMIN">{t('members.roleAdmin')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,14 @@ import { useNavigate } from '@tanstack/react-router'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { UploadZone } from '@/features/publish/upload-zone'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Select } from '@/shared/ui/select'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
normalizeSelectValue,
|
||||
} from '@/shared/ui/select'
|
||||
import { Label } from '@/shared/ui/label'
|
||||
import { Card } from '@/shared/ui/card'
|
||||
import { useMyNamespaces, usePublishSkill } from '@/shared/hooks/use-skill-queries'
|
||||
|
|
@ -147,30 +154,34 @@ export function PublishPage() {
|
|||
<div className="h-11 animate-shimmer rounded-lg" />
|
||||
) : (
|
||||
<Select
|
||||
id="namespace"
|
||||
value={namespaceSlug}
|
||||
onChange={(e) => setNamespaceSlug(e.target.value)}
|
||||
value={normalizeSelectValue(namespaceSlug)}
|
||||
onValueChange={setNamespaceSlug}
|
||||
>
|
||||
<option value="">{t('publish.selectNamespace')}</option>
|
||||
{namespaces?.map((ns) => (
|
||||
<option key={ns.id} value={ns.slug}>
|
||||
{ns.displayName} (@{ns.slug})
|
||||
</option>
|
||||
))}
|
||||
<SelectTrigger id="namespace">
|
||||
<SelectValue placeholder={t('publish.selectNamespace')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{namespaces?.map((ns) => (
|
||||
<SelectItem key={ns.id} value={ns.slug}>
|
||||
{ns.displayName} (@{ns.slug})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="visibility" className="text-sm font-semibold font-heading">{t('publish.visibility')}</Label>
|
||||
<Select
|
||||
id="visibility"
|
||||
value={visibility}
|
||||
onChange={(e) => setVisibility(e.target.value)}
|
||||
>
|
||||
<option value="PUBLIC">{t('publish.visibilityOptions.public')}</option>
|
||||
<option value="NAMESPACE_ONLY">{namespaceOnlyLabel}</option>
|
||||
<option value="PRIVATE">{t('publish.visibilityOptions.private')}</option>
|
||||
<Select value={visibility} onValueChange={setVisibility}>
|
||||
<SelectTrigger id="visibility">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="PUBLIC">{t('publish.visibilityOptions.public')}</SelectItem>
|
||||
<SelectItem value="NAMESPACE_ONLY">{namespaceOnlyLabel}</SelectItem>
|
||||
<SelectItem value="PRIVATE">{t('publish.visibilityOptions.private')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
import * as React from 'react'
|
||||
import * as SelectPrimitive from '@radix-ui/react-select'
|
||||
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { cn } from '@/shared/lib/utils'
|
||||
|
||||
export const SELECT_TRIGGER_CLASS_NAME = cn(
|
||||
'flex h-11 w-full rounded-lg border border-border/60 bg-secondary/50 px-4 py-2 text-sm text-foreground',
|
||||
'flex h-11 w-full items-center justify-between gap-2 rounded-lg border border-border/60 bg-secondary/50 px-4 py-2 text-sm text-foreground',
|
||||
'ring-offset-background transition-all duration-200',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:border-primary/50',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50'
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1'
|
||||
)
|
||||
|
||||
export const SELECT_CONTENT_CLASS_NAME = cn(
|
||||
'z-50 overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md'
|
||||
'z-50 overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out',
|
||||
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
|
||||
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
|
||||
'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'
|
||||
)
|
||||
|
||||
export const SELECT_ITEM_CLASS_NAME = cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-md py-2 pl-8 pr-3 text-sm outline-none',
|
||||
'relative flex w-full cursor-default select-none items-center rounded-md py-2 pl-8 pr-8 text-sm outline-none',
|
||||
'focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
|
||||
)
|
||||
|
||||
|
|
@ -21,22 +29,150 @@ export function normalizeSelectValue(value?: string | null) {
|
|||
return typeof value === 'string' && value.length > 0 ? value : undefined
|
||||
}
|
||||
|
||||
interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {}
|
||||
const Select = SelectPrimitive.Root
|
||||
const SelectGroup = SelectPrimitive.Group
|
||||
const SelectValue = SelectPrimitive.Value
|
||||
|
||||
const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
||||
({ className, children, ...props }, ref) => {
|
||||
return (
|
||||
<select
|
||||
className={cn(SELECT_TRIGGER_CLASS_NAME, className)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(SELECT_TRIGGER_CLASS_NAME, className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
))
|
||||
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1 text-muted-foreground',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
))
|
||||
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1 text-muted-foreground',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
))
|
||||
|
||||
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
SELECT_CONTENT_CLASS_NAME,
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
'p-1',
|
||||
position === 'popper'
|
||||
&& 'h-[var(--radix-select-trigger-height)] min-w-[var(--radix-select-trigger-width)]'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
)
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
))
|
||||
|
||||
Select.displayName = 'Select'
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||
|
||||
export { Select }
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('px-2 py-1.5 text-sm font-semibold text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(SELECT_ITEM_CLASS_NAME, className)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-3 flex h-4 w-4 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
))
|
||||
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('-mx-1 my-1 h-px bg-border', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue