feat: refactor components to utilize SelectField for improved UI consistency

Updated various components to replace traditional select implementations with the new SelectField component. This change enhances the user interface by providing a more consistent layout and improved accessibility. Additionally, refactored the dashboard and operations pages to utilize DataGridCard for better organization of content, streamlining the overall user experience.
This commit is contained in:
Denozordec
2026-07-09 11:53:43 +07:00
parent b321aa5321
commit 452f6b2db0
15 changed files with 279 additions and 276 deletions
+100
View File
@@ -0,0 +1,100 @@
import type { ComponentProps, ReactNode } from 'react'
import { Field, FieldDescription, FieldLabel } from '@evobgp/ui/components/field'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from '@evobgp/ui/components/select'
import { cn } from '@evobgp/ui/lib/utils'
export type SelectMenuItem<T extends string = string> = {
value: T
label: ReactNode
disabled?: boolean
}
type SelectRootProps = ComponentProps<typeof Select>
export interface SelectMenuProps<T extends string = string>
extends Omit<SelectRootProps, 'children' | 'onValueChange' | 'value' | 'defaultValue'> {
items: ReadonlyArray<SelectMenuItem<T>>
value?: T | null
defaultValue?: T | null
onValueChange?: (value: T | null) => void
placeholder?: string
id?: string
triggerClassName?: string
contentClassName?: string
size?: 'sm' | 'default'
side?: ComponentProps<typeof SelectContent>['side']
}
/** ReUI c-select-4: `items` на Root, опции в `SelectGroup`. */
export function SelectMenu<T extends string = string>({
items,
placeholder,
id,
triggerClassName,
contentClassName,
size = 'default',
side,
value,
defaultValue,
onValueChange,
...selectProps
}: SelectMenuProps<T>) {
return (
<Select
items={items}
value={value}
defaultValue={defaultValue}
onValueChange={(next) => onValueChange?.(next as T | null)}
{...selectProps}
>
<SelectTrigger id={id} className={cn('w-full', triggerClassName)} size={size}>
<SelectValue placeholder={placeholder} />
</SelectTrigger>
<SelectContent side={side} className={contentClassName}>
<SelectGroup>
{items.map((item) => (
<SelectItem key={item.value} value={item.value} disabled={item.disabled}>
{item.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
)
}
export interface SelectFieldProps<T extends string = string> extends SelectMenuProps<T> {
label?: ReactNode
description?: ReactNode
fieldClassName?: string
}
export function SelectField<T extends string = string>({
label,
description,
fieldClassName,
id,
...menuProps
}: SelectFieldProps<T>) {
return (
<Field className={fieldClassName}>
{label ? <FieldLabel htmlFor={id}>{label}</FieldLabel> : null}
<SelectMenu id={id} {...menuProps} />
{description ? (
typeof description === 'string' ? (
<FieldDescription className="font-mono text-xs">{description}</FieldDescription>
) : (
description
)
) : null}
</Field>
)
}